.. _vue-js: Vue.js ====== `Vue.js `_ is an open-source JavaScript component-based framework that makes it easy to develop reactive applications. Here you will find some guidelines explaining how you have to proceed to build up you application. Before beginning ---------------- Vue.js can be used along with some additional libraries like `VueX `_ or `Vue-router `_. To know when you have to use these libraries, here is a table that will help you to decide: +-------------------------------------------------+----------------------------+ | Your app | Which lib should I use? | +=================================================+============================+ | Has a component with few responsibilities | Vue.js | +-------------------------------------------------+----------------------------+ | Is medium-sized and has complex workflows | Vue.js + Vuex | +-------------------------------------------------+----------------------------+ | Has several pages to display | Vue.js + Vuex + Vue-router | +-------------------------------------------------+----------------------------+ .. NOTE:: We strongly suggest you to install the `vue-devtools browser extension `_. It provides nice features that ease the development of your applications. Folder structure of a Vue application ------------------------------------- A Vue app has to be split out in distinct parts. Here is the folder structure you have to follow: .. code-block:: bash my-plugin/ |-- build-manifest.json # Edit it to declare your app for translations |-- jest.config.js # Unit tests bootstrapping |-- package.json # Declares the App, its dependencies and its build script. |-- package-lock.json # Generated by npm. Never edit manually. |-- tsconfig.json # Typescript configuration |-- webpack.common.js # Webpack configuration to build the App |-- webpack.dev.js # Inherits webpack configuration for production |-- webpack.prod.js # Inherits webpack configuration for development |-- scripts/ |-- my-vue-app/ |-- po/ # Localization strings |-- fr_FR.po # Localized strings for French |-- src/ # The app source-code |-- api/ # REST API consumers |-- components/ # Vue components |-- MyFeature/ |-- OtherFeature/ |-- store/ # Vuex store modules (actions/mutations/getters) |-- router/ # Vue-router modules |-- index.ts # App bootstrapping |-- themes/ |-- _my-vue-app.scss # All SCSS rules should go here |-- my-vue-app-blue.scss # All the following files provide color themes |-- my-vue-app-blue-condensed.scss |-- my-vue-app-green.scss |-- my-vue-app-green-condensed.scss |-- my-vue-app-grey.scss |-- my-vue-app-grey-condensed.scss |-- my-vue-app-orange.scss |-- my-vue-app-orange-condensed.scss |-- my-vue-app-purple.scss |-- my-vue-app-purple-condensed.scss |-- my-vue-app-red.scss |-- my-vue-app-red-condensed.scss Build your Vue application -------------------------- The build system will read ``build-manifest.json`` to understand how and where it needs to extract translated strings. .. code-block:: JavaScript // tuleap/plugins/my-plugin/build-manifest.json { "name": "my-plugin", "gettext-vue": { "my-vue-app": { "src": "scripts/my-vue-app/src", "po": "scripts/my-vue-app/po" } } } To build up your application, you will have to update or create a ``webpack.common.js`` file. This file should be located in ``my-plugin/``. .. code-block:: JavaScript // tuleap/plugins/my-plugin/webpack.config.js const path = require("path"); const webpack_configurator = require("../../tools/utils/scripts/webpack-configurator.js"); const context = __dirname; const output = webpack_configurator.configureOutput( path.resolve(__dirname, "../../src/www/assets/my-plugin"), "/assets/my-plugin/" ); const entry_points = { "my-vue-app": "./scripts/my-vue-app/src/index.ts" }; const colors = ["blue", "green", "grey", "orange", "purple", "red"]; for (const color of colors) { entry_points[`my-vue-app-${color}`] = `./themes/my-vue-app-${color}.scss`; entry_points[ `my-vue-app-${color}-condensed` ] = `./themes/my-vue-app-${color}-condensed.scss`; } module.exports = [ { entry: entry_points, context, output, externals: { tlp: "tlp" }, module: { rules: [ ...webpack_configurator.configureTypescriptRules( webpack_configurator.babel_options_ie11 ), webpack_configurator.rule_easygettext_loader, webpack_configurator.rule_vue_loader ] }, plugins: [ webpack_configurator.getManifestPlugin(), webpack_configurator.getVueLoaderPlugin(), webpack_configurator.getTypescriptCheckerPlugin(true) ], resolveLoader: { alias: webpack_configurator.easygettext_loader_alias } } ]; .. _npm_scripts: Once you have a webpack config, you will need a ``package.json`` in ``my-plugin/``. .. code-block:: JavaScript // tuleap/plugins/my-plugin/package.json { "author": "Enalean Team", // or yourself "name": "@tuleap/plugin-my-plugin", "homepage": "https://tuleap.org", // or your plugin's homepage "license": "GPL-2.0-or-later", // or your license "private": true, "dependencies": { "vue": "^2.6.10", "vue-gettext": "^2.1.0", "vuex": "^3.1.1" }, "devDependencies": {}, "config": { "bin": "../../node_modules/.bin" // This should point to the node_modules/.bin folder in tuleap/ root folder }, "scripts": { "build": "$npm_package_config_bin/webpack --config webpack.prod.js", "watch": "$npm_package_config_bin/webpack --config webpack.dev.js --watch", "test": "$npm_package_config_bin/jest" } } .. NOTE:: All the webpack/jest dependencies are available at the tuleap root folder, hence the ``config.bin``. Use the npm scripts to build up the application or to launch the unit tests. .. code-block:: bash npm run build # For a production build, outputs minified code. npm run watch # Build the app in watch mode. npm test # Run the :ref:`Jest ` unit tests only once. Once you have a ``package.json`` file, you will also need a ``tsconfig.json`` file to configure Typescript. .. code-block:: JavaScript // tuleap/plugins/my-plugin/tsconfig.json { "extends": "../../tsconfig.json", "compilerOptions": { "module": "esNext" }, "include": [ "scripts/my-vue-app/**/*" ] } Bootstrap your application -------------------------- This section will explain you how to properly integrate your application in Tuleap. Create a mount point ^^^^^^^^^^^^^^^^^^^^ To allow your app to run in Tuleap, you may need to create a mount point in a mustache template. Your mount point needs to have a unique identifier in order to be easily retrieved from the DOM. This is also the place where you can pass some data from PHP to JavaScript via ``data-*`` attributes: .. code-block:: html
Once your mount point is ready, head to your ``index.ts`` file. .. code-block:: TypeScript // tuleap/plugins/my-plugin/scripts/my-vue-app/src/index.ts import Vue from "vue"; import { initVueGettext } from "../../../../src/www/scripts/tuleap/gettext/vue-gettext-init"; import MyVueApp from "./components/MyVueApp.vue"; document.addEventListener("DOMContentLoaded", async () => { // Retrieve the mount point from the DOM const vue_mount_point_id = "my-vue-app-mount-point"; const vue_mount_point = document.getElementById(vue_mount_point_id); if (!vue_mount_point) { throw new Error(`Could not find Vue mount point ${vue_mount_point_id}`); } // Dynamically import the translations relevant to the current user's language. await initVueGettext(Vue, (locale: string) => import(/* webpackChunkName: "my-vue-app-po-" */ `../po/${locale}.po`) ); // Retrieve the JSON data from the mount point const user_data = vue_mount_point.dataset.user; if (!user_data) { throw new Error("Missing data-user attribute on vue mount point"); } const MyVueAppComponent = Vue.extend(MyVueApp); new MyVueAppComponent({ // Create a new component propsData: { user: JSON.parse(user_data) // Pass the mount-point data to the vue app } }).$mount(vue_mount_point); // Mount the app on the moint point }); Vue and Typescript ------------------ The reference language to use with Vue.js is now `Typescript `_. Best-practices for Tuleap ------------------------- When you submit a patch for review, we may request changes to better match the following best practices. Please try to follow them. Many rules are already enforced by the pre-commit hook that runs eslint_ with `eslint-plugin-vue`_. * Please avoid the usage of `vue directives shorthands `_. Shorthands are nice to use but it is not obvious for the others to figure out which directive you are actually using. * Always use ``PascalCase`` for component names. * Always use multi-word names for components, for example: "DocumentSearch". In templates, this translates as ````. See `the dedicated Vue Style Guide rule `_. * Always use ``snake_case`` for computed properties. I know, there are parentheses when we define them, but they really are *properties*, not methods. See :ref:`Tuleap coding standards `. * Always use ``snake_case`` for props. They follow the same rule as variables. * Always use ``camelCase`` for methods. * Always use ``snake_case`` for Vuex State properties and Getters. They are properties too. * Always use ``camelCase`` for Vuex Mutations and Actions. They are methods. * Always name files and folders inside ``components/`` with ``PascalCase`` (just like component names). * Always name javascript files (in all other folders) with ``dash-case``. * Avoid having too many components that depend on ``this.$route``. Inject what you need via props instead. * Always use named exports in Vuex Getters, Mutations and Actions. Default export may be used for State definition. Named exports make it easier to import only what we want. * Always use the inline export syntax ``export function myAction()`` or ``export const myMutation() => {}``. It makes it easy to add "private" (non-exported) functions that will be reused. * Be carfull with translations, when ``translate`` is used in a ``