Monday, January 30, 2017

Dynamic vendor bundling and source maps with hjs-webpack

## Webpack bundling and chunking

The default in Webpack config is to put all the Javascript into a large bundle JS file. However this does not work well for many reasons. In my single page app, I use a lot of Javascript libraries which when put together form a very large source bundle file. 95% of the time, I will not be touching or changing any of these libraries. So here is my wishlist that comes from my C/C++ days.

## Wishlist
- It must work with [hjs-webpack](https://github.com/henrikjoreteg/hjs-webpack) which in the author's words __" is just a simplified, opinionated way to configure webpack for development and then build for production. That also supports easily generating more files.."
- Bundle together my Javascript into a separate bundle, and keep all the node_modules in a separate vendor bundle.
- When library files are not changed the generated vendor bundle should keep the same filename (to optimize CDN caching). So ensure Webpack generates the same filename when library files are not changed.
- Generate source-maps to enable easier debugging of my React apps.

## Analyzing your webpack

Run `webpack --profile --json > stats.json` to generate the stats.json file. Fire up the [Webpack Analyzer](http://webpack.github.io/analyse/#home) and upload the stats.json. The analyzer provides a nice overview (and details) of the bundles, chunks and modules in your webpack. Read more at [their documentation site](https://github.com/webpack/docs/wiki/build-performance).



## Dynamic Vendor bundling
[Jeremy Gayyed posted a blog about Dynamic vendor bundling](https://jeremygayed.com/dynamic-vendor-bundling-in-webpack-528993e48aab). It discussed the steps to configure Dynamic vendor bundling for webpack. I recommend reading this blog for a good understanding of the concepts of bundles, chunks and using CommonChunkPlugin. 

I've taken some of his ideas and adapted them for hjs-webpack .

### Install node modules
```sh
npm install --save-dev webpack-md5-hash 
npm install --save-dev assets-webpack-plugin
```

### Update your webpack.config.js with the following.

#### Some explanation required.

## Selecting modules that are in vendor bundle
```javascript
new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor',
    minChunks: ({ resource }) => /node_modules/.test(resource),
  }),
```
The .test will check if the resource is in the node_modules directory. Jeremy found [this comment](https://github.com/webpack/webpack/issues/2372#issuecomment-213149173) by Tobias Kopper (author of webpack). This is a better way than creating a list of modules that you must constantly maintain. Since you can write any logic, it is really powerful!

## Generate a consistent bundle filename in Webpack
Webpack will generate a new random filename each time you build the code. The main things that changes is the module import logic, so we split this off into a separate manifest file. The **webpack-md5-hash** will generate a deterministic hash which ensures the same hash if no content was changed. The following lines ensure the hash is used in the filename.

```javascript
config.output.filename='[name]-[chunkhash].js';
config.output.chunkFilename='[name]-[chunkhash].js';
```

## Source maps
To debug a packed file, you'll need a source map. The **devtool (optional, string, default='cheap-module-eval-source-map')** is a webpack developer tool to enhance debugging. [See the webpack docs for more options](https://webpack.github.io/docs/configuration.html#devtool). 
I've chosen `source-map` which is the largest option. Ensure that you have the following lines in your config for source maps.

```javascript
devtool: 'source-map',
```
and
```javascript
config.output.devtoolLineToLine=true;
```

No comments :