Adventures with Webpack manifest and CommonsChunkPlugin

Recently I wanted to optimize our current projects frontend build process further and digged into the CommonsChunkPlugin more. Before, we had only split out stuff into the most usual two: the app and vendor bundles. I wanted to take it further and start splitting our application to dynamically imported chunks (this is a topic for a blog post of its own).

// Entries
entry: {
app: path.join(__dirname, 'src', 'index.js'),
vendor: ['react', ...]
},
// Plugins section, outputs a separate vendor.js file.
new webpack.optimize.CommonsChunkPlugin({
name: ‘vendor’
});

But before getting into dynamically importing separate application chunks, I though it would be a good idea to also split the Webpack bootstrap logic into its own file, called the manifest by their documentation. This wasn’t too easy though, I tried searching for help on SO and Medium, but didn’t find too many helpful articles or posts about the stuff at hand.

The main problem with the Webpack manifest file is that it uses Promises to bootstrap the Webpack bundled modules. Promises are not native to all the browsers yet, so at least IE11 and older would fail when loading the manifest first because the Promise is missing in the browser. This is why we need to polyfill the Promise before loading the manifest file.

Polyfilling the promise so that it is loaded first, before the manifest, isn’t that straightforward. The reason is, because if you bundle the promise polyfill (with babel-polyfill, core-js, es6-promise..) with Webpack, the module will (obviously.. ) be wrapped in the Webpack module logic and it will throw an webpackJsonp is not defined error. You could just include the polyfill in a separate script tag the old fashioned way and it would work, but I wanted to explore how to put the manifest and the polyfills in to the same Webpack created chunk.

First, with the help of the Webpack documentation I improved building the vendor bundle putting all the node_modules dependencies into their own chunk by giving the CommonsChunkPlugin a minChunks function. This is better, because then we don’t need to list all the vendor dependencies separately in the entry section manually. The part containing css and scss filtering are related to getting the minChunks function to work correctly with the ExtractTextPlugin.

new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module) {
if (module.resource &&
(/^.*\.(css|scss)$/).test(module.resource)) {
return false;
}
    return module.context 
&& module.context.includes("node_modules");
}
})

After doing this I tried various ways of getting the polyfills we use together in the same chunk with the webpack manifest. After a lot of trial and error I found this combination to work.

entry: {
app: [
// Polyfills added as an entry in the array
path.join(__dirname, 'src', 'polyfills.js'),
path.join(__dirname, 'src', 'index.jsx')
]
},
// Added to plugins section
new webpack.optimize.CommonsChunkPlugin({
names: ['polyfills', 'manifest'],
filename: 'polyfills.js',
minChunks: function (module) {
return module.resource && (/core-js/).test(module.resource);
}
})

The above improvements will put the required polyfills together with the application code. Then, the CommonsChunkPlugin will extract the polyfills to their own bundle together with the Webpack manifest. In this case, I am pulling out core-js dependencies out of the app entry point, but it might as well be babel-polyfill or some other implementation. The filename property there is important, otherwise Webpack would (apparently..) set the chunk file name to the last item in the names array, which is manifest.

Im not sure if this information will help anybody, but I at least wanted to document my struggles that I had with Webpack and its plugins. More to come in the future, probably!

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.