Skip to content

Latest commit

 

History

History
422 lines (316 loc) · 16.4 KB

README_DEV.md

File metadata and controls

422 lines (316 loc) · 16.4 KB

vue-gettext

Table of contents

Project structure

.
├── build/            # Build and environment config files.
├── dev/              # Files used for the development of the plugin.
├── dist/             # The production version of the plugin.
├── src/              # Source code of the plugin.
├── test/             # Unit tests.
├── .babelrc          # Babel config
├── .eslintrc.js      # Eslint config
├── Makefile          # A Makefile to extract translations and generate .po files
└── package.json      # Build scripts and dependencies

Dev setup

Node v10+ is required for development.

# install deps
npm install

# serve examples at localhost:8080
npm run dev

# lint & run all tests
npm run test

Pull request guidelines

Inspired by Vue.

  • explain why/what you are doing in the PR description, so that anybody can quickly understand what you want
  • all development should be done in dedicated branches
  • do not touch files in dist because they are automatically generated at release time
  • add accompanying test case(s)
  • make sure npm test passes

Implementation notes

Version number

I changed the plugin version number to 2.x to match Vue.js 2.0 version.

New component tag name

By popular demand, <get-text> has been renamed <translate>.

Interpolation in the <translate> component

Interpolation inside attributes are deprecated in Vue 2. See my question on the Vue.js forum:

What is the Vue 2 vm.$interpolate alternative?

This breaks the old vue-gettext 1 component.

The solution I have reached is to use a set of custom delimiters for placeholders in component templates together with a custom interpolation function, e.g.:

<translate>Hello %{name}, I am the translation key!</translate>
                    ↓
Hello %{name}, I am the translation key!
                    ↓
Hello John, I am the translation key!

Drawbacks:

  • vue-gettext 2 works only with Vue 2.0
  • it add a minimal hook to your templates code

But it works very well while waiting for something better. Practicality beats purity I guess.

Dev setup notes

My notes about the plugin setup.

I wanted to explore the Webpack ecosystem and some choices made in the Webpack template for vue-cli.

npm's package.json:

1) Webpack and HMR (Hot Module Replacement)

express
html-webpack-plugin
webpack
webpack-dev-middleware
webpack-hot-middleware
npm install --save-dev express html-webpack-plugin webpack webpack-dev-middleware webpack-hot-middleware

Webpack is a module bundler. It takes a bunch of files (JS, CSS, Images, HTML etc.), treating each as a module, figuring out the dependencies between them, and bundle them into static assets that are ready for deployment.

"Hot Module Replacement" (HMR) is a Webpack development feature to inject updated modules into the active runtime.

There are 3 ways to set up HMR. We use webpack-hot-middleware to run a Webpack dev server with HMR inside an express server. Compilation should be faster because the packaged files are written to memory rather than to disk.

In Express, a middleware is a function that receives the request and response objects of an HTTP request/response cycle. It may modify (transform) these objects before passing them to the next middleware function in the chain. It may decide to write to the response; it may also end the response without continuing the chain.

The Webpack Hot Middleware installs itself as a Webpack plugin, and listens for compiler events. Each connected client gets a Server Sent Events connection, the server will publish notifications to connected clients on compiler events. When the client receives a message, it will check to see if the local code is up to date. If it isn't up to date, it will trigger Webpack Hot Module Replacement.

HMR is opt-in, so we also need to put some code at chosen points of our application. This had not yet been done since we have to dive into the HMR JavaScript API (but state preserving hot-reload is implemented in vue-webpack-boilerplate).

The HTML Webpack Plugin will generate an index.html entry point to our application, and auto inject our Webpack bundles. This is especially useful for multiple environment builds, to stop the HTML getting out of sync between environments, avoiding hard-written paths and simplifying the cache busting process. Here's how the entry point is automatically added: in Webpack there is a make plugin hook on the compilation in which entry points can be added ; see this, this and this.

Note: the Webpack template serves static files with Express.

2) Splitting the Webpack configuration for multiple environments

There are several ways of splitting the Webpack configuration for multiple environments.

Some people prefer maintaining configuration within a single file and branch there, other prefer partial configurations files that then can be merged together using specialized tools like webpack-merge.

We took a different approach here: one file per environment, because we don't provide production environment so we just have one file for the development environment. The distribution build is made with Rollup (cf section 11 of this file).

The current environment is set in an environment variable. Node.js provides the process.env property containing the user environment and NODE_ENV is an environment variable made popular by the Express webserver framework.

If NODE_ENV is not set explicitly, it will be undefined. So we explicitly set it in JavaScript for the Express application, e.g.: process.env.NODE_ENV = 'development'.

Sometimes we also need to use environment variables (or other constants) in the client code. They can be exposed as global constants via webpack.DefinePlugin.

3) Linting with ESLint

eslint
eslint-config-standard
eslint-plugin-promise        // Required by `eslint-config-standard`.
eslint-plugin-standard       // Required by `eslint-config-standard`.
npm install --save-dev eslint eslint-config-standard eslint-plugin-promise eslint-plugin-standard

We use the Standard preset with some small customizations, see rules in .eslintrc.js.

Note: I'm using the JavaScript ESLint TextMate Bundle, in a personal capacity.

4) Linting with Webpack

eslint-loader
eslint-friendly-formatter
npm install --save-dev eslint-loader eslint-friendly-formatter

5) Babel

babel-core                            // Babel compiler core.
babel-loader                          // Allows transpiling JavaScript files using Babel and Webpack.
babel-preset-es2015                   // ES6/ES2015 support.
babel-plugin-transform-runtime        // Avoid repeated inclusion of Babel's helper functions.
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-plugin-transform-runtime

See the Babel configuration in .babelrc.

6) Vue.js

vue
npm install --save-dev vue

We use the standalone build which includes the compiler and supports the template option.

7) Vue loader

vue-loader
vue-template-compiler
eslint-plugin-html
npm install --save-dev vue-loader vue-template-compiler eslint-plugin-html

vue-loader is a loader for Webpack that can transform Vue components into a plain JavaScript module.

Since Vue 2.1.0, vue-template-compiler is a peer dependency of vue-loader instead of a direct dependency.

We also need the eslint-html-plugin with supports extracting and linting the JavaScript inside *.vue files and enable it in the .eslintrc.js config file.

8) Common loaders

html-loader
json-loader
npm install --save-dev html-loader json-loader

9) CSS

style-loader            // Adds CSS to the DOM by injecting a style tag.
css-loader
postcss-loader
postcss-cssnext
postcss-import
npm install --save-dev css-loader style-loader postcss-loader postcss-cssnext postcss-import

This is how I use scoped CSS in components for the development of the plugin:

Component's styles are locally scoped in each of them to avoid class name conflicts. This is done via css-loader's Local scope.

The PostCSS-cssnext syntax is used across components: it's a PostCSS plugin that let us use the latest CSS syntax today.

postcss-import lets us import CSS variables like this: @import './styles/variables.css';.

There are other way to scope CSS:

Note: CSS are buried inside our Javascript bundles by default. We can use the ExtractTextPlugin to extracts them into external .css files in a production environment.

10) Unit tests

karma
mocha
karma-mocha
puppeteer
karma-chrome-launcher
chai                         // Required by `karma-sinon-chai`.
sinon                        // Required by `karma-sinon-chai`.
sinon-chai                   // Required by `karma-sinon-chai`.
karma-sinon-chai
karma-webpack
npm install --save-dev karma mocha karma-mocha karma-chrome-launcher chai sinon sinon-chai karma-sinon-chai karma-webpack

Karma is a JavaScript command line tool that can be used to spawn a web server which loads application's source code, executes tests and reports the results. It runs on Node.js and is available as an NPM package.

Mocha is the test framework that we write test specs with. karma-mocha lets Karma use Mocha as the test framework.

karma-chrome-launcher lets Karma run tests with Headless Chrome. Puppeteer is a Node library which provides a high-level API to control headless Chrome or Chromium over the DevTools Protocol.

Chai and Sinon (Sinon-Chai) are integrated using karma-sinon-chai, so all Chai interfaces (should, expect, assert) and sinon are globally available in test files. Just let .eslintrc know about them.

karma-webpack uses Webpack to preprocess files in Karma.

TODO: reporters and coverage.

11) Using rollup for packaging

rollup
buble
rollup-plugin-buble
rollup-plugin-commonjs
uglify-js
npm install --save-dev rollup buble rollup-plugin-buble rollup-plugin-commonjs

Using Rollup for packaging seems fast.

Rollup plugins change the behaviour of Rollup at key points in the bundling process.