Ensure you have Node/NPM installed, then make a project directory:
mkdir modern-js && cd modern-js
Create an empty package.json
file:
npm init -y
Install dependencies:
npm install --save-dev webpack \
webpack-cli \
webpack-dev-server \
@babel/core \
@babel/preset-env \
"babel-loader@^8.0.0-beta" \
style-loader \
css-loader \
sass-loader \
node-sass \
eslint@4.x babel-eslint@8
npm install --save @babel/polyfill
Note: the dev dependencies need to be installed in the order they're specified, otherwise npm will complain/fail.
Your package.json
will now have the following dependencies:
{
"name": "modern-js",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.1.2",
"@babel/preset-env": "^7.1.0",
"babel-eslint": "^8.2.6",
"babel-loader": "^8.0.4",
"css-loader": "^1.0.0",
"eslint": "^4.19.1",
"node-sass": "^4.9.3",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.0",
"webpack": "^4.20.2",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.9"
},
"dependencies": {
"@babel/polyfill": "^7.0.0"
}
}
- Babel: transpiler of modern JS into ES5 compatible code
- Webpack: a js module bundler (but also capable of transforming, bundling, packaging just about anything).
Now create a webpack config file:
webpack.config.js
Note: babel configuration will be placed inside of the
package.json
file, but can also be extracted into its own.babelrc
file if you prefer.
Add the following code to your webpack configuration:
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
}
}
]
}
}
Update your package.json
to include script commands for webpack:
{
...
"scripts": {
"build": "webpack --mode production",
"dev": "webpack-dev-server --mode=development --config webpack.config.js",
...
},
"babel": {
"presets": [
["@babel/env", {"modules": false}]
],
"plugins": ["syntax-dynamic-import"]
}
...
}
The webpack
package also supports watching files for changes --watch
but it doesn't handle 'hot reloading' (i.e. meaning you'll need to manually refresh your service, which if you've got a complex 'single page application' will result in you losing your current state).
Note: the babel configuration prevents babel from transpiling
import
andexport
statements into ES5, and enables dynamic imports.
Now create a JavaScript file that you want to transpile and bundle:
mkdir src dist
touch .eslintrc src/index.js src/component.js dist/index.html
Here's the contents of index.js
:
/*eslint no-undef: "error"*/
/*eslint-env browser*/
import '@babel/polyfill';
import c from './component.js';
console.log(c);
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a);
console.log(b);
const root = document.createElement('div');
root.innerHTML = `<p>Hello Webpack!</p>`;
document.body.appendChild(root);
Note: instead of using
eslint-env browser
you could add aglobals
field to your.eslintrc
(see below for example). You can also useeslint-env node
for server-side JS. See the docs for more information.
Here's the contents of component.js
:
const c = ['x', 'y', 'z'];
export default c;
Here's the contents of index.html
:
<!doctype html>
<html>
<head>
<title>Hello Webpack</title>
</head>
<body>
<script src="bundle.js"></script>
</body>
</html>
Here's the contents of .eslintrc
:
{
"parser": "babel-eslint",
"globals": {
"console": true,
"document": true,
"window": true
},
"rules": {
'brace-style': [2, '1tbs', {'allowSingleLine': true}],
'camelcase': 2,
'comma-spacing': 2,
'comma-style': 2,
'curly': 2,
'eol-last': 2,
'indent': [2, 2],
'key-spacing': 2,
'new-cap': 2,
'new-parens': 2,
'no-lonely-if': 2,
'no-multi-spaces': 2,
'no-multiple-empty-lines': [2, {'max': 2}],
'func-call-spacing': 2,
'no-trailing-spaces': 2,
'quotes': [2, 'single', {'allowTemplateLiterals': true}],
'semi': 2,
'semi-spacing': 2,
'space-before-blocks': 2,
'space-in-parens': 2,
'space-infix-ops': 2,
'space-unary-ops': 2,
'array-callback-return': 2,
'block-scoped-var': 2,
'consistent-return': 2,
'eqeqeq': 2,
'guard-for-in': 2,
'no-array-constructor': 2,
'no-caller': 2,
'no-cond-assign': 2,
'no-const-assign': 2,
'no-control-regex': 2,
'no-delete-var': 2,
'no-dupe-args': 2,
'no-dupe-class-members': 2,
'no-dupe-keys': 2,
'no-duplicate-case': 2,
'no-empty-character-class': 2,
'no-empty-pattern': 2,
'no-eval': 2,
'no-ex-assign': 2,
'no-extend-native': 2,
'no-extra-bind': 2,
'no-fallthrough': 2,
'no-func-assign': 2,
'no-implied-eval': 2,
'no-invalid-regexp': 2,
'no-iterator': 2,
'no-lone-blocks': 2,
'no-loop-func': 2,
'no-mixed-operators': [2, {
groups: [
['&', '|', '^', '~', '<<', '>>', '>>>'],
['==', '!=', '===', '!==', '>', '>=', '<', '<='],
['&&', '||'],
['in', 'instanceof']
],
allowSamePrecedence: false
}],
'no-multi-str': 2,
'no-native-reassign': 2,
'no-unneeded-ternary': 2,
'no-unsafe-negation': 2,
'no-new-func': 2,
'no-new-object': 2,
'no-new-symbol': 2,
'no-new-wrappers': 2,
'no-obj-calls': 2,
'no-octal': 2,
'no-octal-escape': 2,
'no-redeclare': 2,
'no-regex-spaces': 2,
'no-script-url': 2,
'no-self-assign': 2,
'no-self-compare': 2,
'no-sequences': 2,
'no-shadow-restricted-names': 2,
'no-shadow': 2,
'no-sparse-arrays': 2,
'no-template-curly-in-string': 2,
'no-this-before-super': 2,
'no-throw-literal': 2,
'no-undef': 2,
'no-unexpected-multiline': 2,
'no-unreachable': 2,
'no-unused-expressions': [2, {
'allowShortCircuit': true,
'allowTernary': true
}],
'no-unused-vars': 2,
'no-use-before-define': [2, 'nofunc'],
'no-useless-computed-key': 2,
'no-useless-concat': 2,
'no-useless-constructor': 2,
'no-useless-escape': 2,
'no-useless-rename': 2,
'no-with': 2,
'radix': 2,
'require-yield': 2,
'use-isnan': 2,
'valid-typeof': 2,
'wrap-iife': [2, 'any']
}
}