.
├── 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
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
- 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
I changed the plugin version number to 2.x to match Vue.js 2.0 version.
By popular demand, <get-text>
has been renamed <translate>
.
Interpolation inside attributes are deprecated in Vue 2. See my question on the Vue.js forum:
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.
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
:
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.
- Webpack — Concepts
- Webpack — Configuration
- Webpack — The Missing Tutorial
- Webpack — The Confusing Parts
- Webpack + Express — The simplest Webpack and Express setup
- HMR — Hot Module Replacement with Webpack
- HMR — Understanding Webpack HMR
- HMR — Webpack HMR Tutorial
- HMR — Webpack Middleware and Hot Module Replacement
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
.
- NodeJs Best Practices: Environment-Specific Configuration
- Simple production environment with Webpack and Express
- How to load different .env.json files into the app depending on environment
- Why does Webpack's DefinePlugin require us to wrap everything in JSON.stringify?
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.
- ESLint
- Configuring ESLint
- JavaScript Standard Style
eslint-config-standard
- ESLint Shareable Config
eslint-loader
eslint-friendly-formatter
npm install --save-dev eslint-loader eslint-friendly-formatter
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
.
- Setting up ES6
- Babel
- Clearing up the Babel 6 Ecosystem
- Using ES6 and ES7 in the Browser, with Babel 6 and Webpack
- The Six Things You Need To Know About Babel 6
vue
npm install --save-dev vue
We use the standalone build which includes the compiler and supports the template option.
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.
html-loader
json-loader
npm install --save-dev html-loader json-loader
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.
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.
- Cheatsheets: mocha chai sinon sinon-chai
- The Ultimate Unit Testing Cheat-sheet
TODO: reporters and coverage.
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.