barbarian meets coding

WebDev, UX & a Pinch of Fantasy

Webpack

This article is part of my personal wiki where I write personal notes while I am learning new technologies. You are welcome to use it for your own learning!

The Case for Webpack

Modern JavaScript projects aren’t what they used to be. In order to have an awesome development environment and produce world class applications you need a front-end build pipeline that helps you with all these goodness:

  • bundling
  • minification
  • transpilation
  • dependency management and module loading
  • linting
  • other optimizations (tree shaking, remove unused css, etc)
  • framework specific improvements

Webpack offers a solution to all these needs! Behold!

Webpack Basics

The easiest way to get started with webpack is by using the cli. You can install webpack using npm:

1
$ npm i -g webpack

You can use webpack directly on a bunch of javascript files and package them together in a bundle:

1
$ webpack app/main.js bundle.js

Or you can use a configuration which is far more common in real world projects. We usually name the configuration webpack.config.js:

1
2
3
4
5
6
module.exports = {
  entry: 'app/main.js', // it can also be an array
  output: {
    filename: 'bundle.js'
  }
};

As you can appreciate from the example above, a webpack configuration file is just a vanilla commonJS module. This configuration is the equivalent to webpack app/**/*.js bundle.js. In order to run webpack with a configuration file you just type:

1
$ webpack

Just like other common task runners, webpack has a watch mode. This means that you can tell webpack to watch a series of files for changes and run again every time one of these files changes. You can enable watch mode like this:

1
2
3
4
5
6
7
module.exports = {
  entry: 'app/main.js', // it can also be an array
  output: {
    filename: 'bundle.js'
  },
  watch: true
};
1
$ webpack --watch app/**/*.js bundle.js

A cool thing that differences webpack from other task runners is that webpack includes a web server called dev server. It is distributed as a separate npm package called webpack-dev-server. Again you can install it globally via npm:

1
$ npm i -g webpack-dev-server

You can use it in a similar way than the webpack cli:

1
2
# show status bar
$ webpack-dev-server
1
2
# don't show status bar
$ webpack-dev-server --inline

This will start an http server and serve your webpack packaged application.

Power Up Webpack with Loaders

You can add more functionality to your webpack pipeline through loaders. For instance, you can have a loader to transpile your SASS style sheets, or to transpile your ES6 code or to lint your code. Loaders are distributed as npm packages, you can install them like thisÖ

1
$ npm i --save-dev babel-loader babel-core eslint-loader eslint webpack

And add them to your webpack configuration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
module.exports = {
  entry: 'app/main.js', // it can also be an array
  output: {
    filename: 'bundle.js'
  },
  watch: true,

  module: {
    // pre-loaders run before loaders
    preLoaders: [
      {
         test: /\.js$/,            // files to process (all js files are es6 files)
         loader: "eslint-loader",  // loader
         exclude: /node_modules/   // files to exclude
      }
    ],

    // loaders
    loaders: [
      {
        test: /\.js$/,              // files to process (all js files are es6 files)
        loader: 'babel-loader'      // loader to use
        exclude: /node_modules/,    // files to exclude
      }
    ]
  },

  resolve: {
    extensions: ['', '.js']         // valid extensions in modules
  }
};

Improve the Dev Experience of Running Webpack with npm scripts

Running webpack-dev-server can get tedious, particularly when you start adding parameters to it. A useful thing to do is to take advantage of npm scripts to launch webpack. For instance:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
  "name": "barbarian-meets-webpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "run": "webpack-dev-server"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.14.0",
    "babel-loader": "^6.2.5",
    "babel-preset-es2015": "^6.14.0",
    "eslint": "^3.5.0",
    "eslint-loader": "^1.5.0",
    "webpack": "^1.13.2"
  }
}

Now you can write npm run and npm will kick off your webpack dev server.

Create Environment Specific Webpack Configurations

You can create multiple webpack config files for different environments. For instance create a new file webpack.prod.config.js and extend your dev configuration:

1
2
3
4
5
6
7
8
9
10
11
var devConfig = require('webpack.config');
var StripLoader = require('strip-loader');
/* prod configuration */

devConfig.loaders.push({
    test: [/\.js$/],
    exclude : /node_modules/,
    loader: StripLoader.loader('console.log')  // remove logging
};

module.exports = devConfig;

To run this configuration you specify it as a parameter to webpack:

1
$ webpack --config webpack.prod.config.js

You can minify your code by calling webpack -p, to use the production configuration plus minification you can create an npm script that executes webpack -p --config webpack.prod.config.js.

More Advanced Configurations

With a folder structure like this:

1
2
3
4
5
6
7
8
---- app:     source es6 files
 |    |--- main.js, etc
 |
 |-- wwwroot: website public root
 |    |--- index.html
 |    |--- app/.... public path for built files
 |
 |-- build:   build files

We can use the following configuration with context, output.path, output.publickPath and devServer.wwwroot:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
var path = require('path');

module.exports = {
  context: path.resolve('app'),         // context where files to transform are located
  entry: ['./main'],                    // could be an array of files
  output: {
      path: path.resolve('build/app/'), // build path (during development)
      publicPath: 'app/',               // public build path (how it will be requested by the browser in production)
      filename: 'bundle.js'
  },

  devServer: {
      contentBase: 'wwwroot'              // content base for dev server
  },

  watch: true,

  module: {
    // pre-loaders run before loaders
    preLoaders: [
      {
         test: /\.js$/,            // files to process (all js files are es6 files)
         loader: "eslint-loader",  // loader
         exclude: /node_modules/   // files to exclude
      }
    ],

    loaders: [

      {
        test: /\.js$/,             // files to process (all js files are es6 files)
        exclude: /node_modules/,   // files to exclude
        loader: 'babel-loader'     // loader to use
      }
    ]
  },

  resolve: {
    extensions: ['', '.js']         // valid extensions in modules
  }
}

Generate Source Maps

Using the -d option will automatically generate source maps:

1
2
$ webpack -d
$ webpack-dev-server -d

You can also combine it with minification

1
2
$ webpack -d -p
$ webpack-dev-server -d -p

Multiple Bundles

TODO/REVIEW

References

Comments