Webpack
Webpack

Webpack

The situation: suppose that CommonsChunkPlugin is generating common.js file for you. You can either apply uglify on it and get only a minified file or don’t apply uglify and you will only get an unminified file. What if you want to get both the minified and the unminified?

Well, you could apply the following option to uglify plugin:

include: /\.min\.js$/,

And now all you need is to somehow copy common.js into common.min.js and then the uglify will minify common.min.js and keep the original common.js as is, as desired.

The problems are that:

  • CommonsChunkPlugin does not support the desired copy action
  • You need to do the copying in memory in the webpack chunks generation process before the uglify plugin is called so that the uglify will manage to operate on common.min.js because the uglify operates on the chunks javascript objects and not on the file system.

SOLUTION:

Hook into webpack plugins event to which uglify plugin is registered, before uglify, and perform deep cloning of the chunk object and modify the name of the cloned chunk and add it to the chunks array.

Problems encountered:

new EventHooksPlugin({
        compilation: (webpackCompilationObject, options) => {
            if (!isCssBundle) {
                webpackCompilationObject.plugin('optimize-chunk-assets', function (chunks, callback) {
                    if (webpackCompilationObject.duplicatedCommonChunk) {
                        return;
                    }
                    webpackCompilationObject.duplicatedCommonChunk = true;
                    for (let i = 0; i < chunks.length; ++i) {
                        if (chunks[i].name === "common." + device) {
                            /*
                                Tested deep copies which failed:
                                - lodash failed
                                - extend package failed
                                - json parse stringify failed
                                - 
                             */
                            const commonChunkDuplicate = deepCloneWithCircularReferencesSupport(chunks[i]);
                            commonChunkDuplicate.files[0] = commonChunkDuplicate.files[0].replace(".js", ".min.js");
                            commonChunkDuplicate.name = commonChunkDuplicate.name + ".min";
                            chunks.push(commonChunkDuplicate);
                            webpackCompilationObject.assets[commonChunkDuplicate.files[0]] =
                                deepCloneWithCircularReferencesSupport(webpackCompilationObject.assets[chunks[i].files[0]]);
                            break;
                        }
                    }
                    callback();
                });
            }
        }
})

As you see, just cloning the chunk turned out to not be enough, it caused errors when executing the generated minified script in the browser. The problem is that it was not clear what is required to be initialized to generate a new chunk and also I did not find information about this in the internet. So after looking at the uglify plugin source code I found out that there is also an object in compilation assets which needs to be cloned. So finally after cloning it too with the new file name, it all worked and I got both the unminified common.js and the minified common.min.js working perfectly in the browser.

Notes:

  • The deepCloneWithCircularReferencesSupport function in that code block is a rename of the deepClone function in the stackoverflow link. Need to put the function code somewhere in the webpack config js file.
  • Need to insert this code as a plugin in the plugins array of webpack config before the uglify plugin.
  • Need to import EventHooksPlugin in the beggining of the webpack config js file.

Leave your thought here