How to get a list of all files (or modules) included per chunk in Webpack

Can’t seem to find any debug option or plugin in webpack to show what exactly went into a chunk.

Why do I need this? I’ve noticed cases where code splitting literally makes everything much larger then just sticking everything in a single file. This is a bit counter intuitive since I don’t believe the bootstrap code from webpack is that significant; I suspect it might be minification/deduping, but with out knowing which modules are actually being chunked togheter it’s very hard to do some isolated tests to confirm.

My build process uses gulp; if that makes any difference.

Webpack 5.x:

$ webpack --stats-modules-space 999

Before Webpack 5.x:

It seems that it has the parameter called --display-modules to show all the modules as follows:

$ webpack --display-modules

Then you will get the list of used modules similar the following:

                   Asset     Size  Chunks             Chunk Names
javascripts/webpack/app.js   211 kB       0  [emitted]  javascripts/webpack/app
stylesheets/webpack/app.js  4.13 kB       1  [emitted]    stylesheets/webpack/app
stylesheets/webpack/app.scss  2.99 kB       1  [emitted]  stylesheets/webpack/app
[0] ./app/webpack/js/behaviors/lory-icon-slider.js.coffee 1.1 kB {0} [optional] [built]
[1] ./app/webpack/css/components (\.scss|\.css)$ 160 bytes {1} [built]
[2] ./app/webpack/js/behaviors (\.js|\.js.jsx|\.js.coffee)$ 252 bytes {0} [built]
[3] ./~/pickmeup/css/pickmeup.scss 41 bytes {1} [built]
[4] ./app/webpack/css/app.js 205 bytes {1} [built]
[5] ./app/webpack/js/app.js 250 bytes {0} [built]
[6] ./app/webpack/js/behaviors/pickmeup-fixed-calendar.js 3.47 kB {0} [optional] [built]
[7] ./~/lory.js/dist/jquery.lory.js 25 kB {0} [built]
[8] ./~/pickmeup/js/pickmeup.js 41.4 kB {0} [built]
[9] (webpack)/buildin/global.js 509 bytes {0} [built]
Child extract-text-webpack-plugin:
   [0] ./~/css-loader/lib/css-base.js 1.51 kB {0} [built]
   [1] ./~/css-loader!./~/sass-loader/lib/loader.js!./~/pickmeup/css/pickmeup.scss 3.23 kB {0} [built]

You can write a plugin that does this.

For example,

var PrintChunksPlugin = function() {};
PrintChunksPlugin.prototype.apply = function(compiler) {
    compiler.plugin('compilation', function(compilation, params) {
        compilation.plugin('after-optimize-chunk-assets', function(chunks) {
            console.log(chunks.map(function(c) {
                return {
                    id: c.id,
                    name: c.name,
                    includes: c.modules.map(function(m) {
                        return m.request;
                    })
                };
            }));
        });
    });
};

For more details, check this page http://webpack.github.io/docs/plugins.html . It contains documentation for all the places you can hook into. Find the proper hook which gets called with chunks: Chunk[] or chunk, and inside this you’ll be able to access everything you want.

Read More:   Uncaught Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement in React Hooks

Also, the stats object contains all the post-build information you’d ever need. It’s available in the done plugin.

This is an updated version of Boopathi Rajaa’s answer that will work for later versions of Webpack (I’m using 4.37.0)

This updated version worked for me:

class PrintChunksPlugin {
    apply (compiler) {
        compiler.plugin('compilation', compilation => {
            compilation.plugin('after-optimize-chunk-assets', chunks => {
                console.log(chunks.map(chunk => ({
                    id: chunk.id,
                    name: chunk.name,
                    modules: Array.from(chunk._modules).map(module => module.id)
                })))
            })
        })
    }
}

Usage:

plugins: [
    new PrintChunksPlugin(),
]

The big difference is that they now store the module information in _modules rather than modules and it isn’t a mappable object before the Array.from. I found module.id to be closer to what I needed, but module.request is still there if you need that.

And here is also a plugin compatible with webpack4, that outputs all your chunks to a single JSON file.

https://www.npmjs.com/package/chunks-2-json-webpack-plugin

Here is how you can use it:

1) In your webpack config file, import the plugin (after you’ve installed it of course 🙂 – npm i --save-dev chunks-2-json-webpack-plugin) and instantiate it under plugins key.

const Chunks2JsonPlugin = require('chunks-2-json-webpack-plugin');
const path = require('path');

const publicPath="/app/";

module.exports = {
  entry: './src/index.js',
  output: {
    filename: '[name].[hash].js',
    path: path.resolve(__dirname, 'dist'),
    publicPath
  },
  plugins: [
    new Chunks2JsonPlugin({ outputDir: 'dist/', publicPath })
  ]
};

And that’s pretty much it, what you will get as a result is a JSON file, that will look something like this:

{
  "chunk-vendors": {
    "js": ["/app/js/chunk-vendors.fc40696c.js"],
    "js.map": ["/app/js/chunk-vendors.fc40696c.js.map"]
  },
  "app": {
    "css": ["/app/css/app.eb829ccc.css"],
    "js": ["/app/js/app.dd31cdcb.js"],
    "js.map": ["/app/js/app.dd31cdcb.js.map"]
  }
}

Here, we have all our chunks in a single JSON file.

More information you can find on the link itself.

Read More:   Calculate the bounding box's X, Y, Height and Width of a rotated element via JavaScript

The solution is to write a script that parses the .js.map files, since they contain a sources entry which can be used to discern all files that were included in the chunk.


Here is a small gulp script that will get the job done,

var debugWebpackMapFile = function (file) {

    var cleanupRules = {
        // executed in order
        '/src/client/javascript/node_modules/': '',
        '/src/client/javascript/': 'static/'
    };

    return new Promise(function (resolve, reject) {
        var match = /\/([^\/]+).js.map$/.exec(file);
        if (match != null) {
            var filename = match[1];
            console.log("\n  " + filename + "\n  =======================\n");
            var mapjson = JSON.parse(fs.readFileSync(file));

            var dependencies = [];
            var sourcefiles = [];

            _.each(mapjson.sources, function (srcfile) {
                srcfile = srcfile.replace('webpack://source-code', '', srcfile);
                var match = /^\/node_modules\/([^\/]+)/.exec(srcfile);
                if (match == null) {
                    match = /^(\/src\/.*\.js)(\?.*)?/.exec(srcfile);
                    if (match != null) {
                        // project source file
                        srcfile = match[1];
                        _.each(cleanupRules, function (to, from) {
                            srcfile = srcfile.replace(from, to);
                        });

                        // the sources are in random order in the map file,
                        // so we'll need to sort before displaying anything
                        sourcefiles.push(srcfile);
                    }
                }
                else {
                    // dependency
                    var pkg = match[1];
                    if (dependencies.indexOf(pkg) == -1) {
                        dependencies.push(pkg);
                    }
                }
            });

            sourcefiles.sort();
            _.each(sourcefiles, function (srcfile) {
                console.log("  " + srcfile);
            });

            if (dependencies.length > 0) {

                console.log("\n  ---- 3rd Party ------------------\n");

                dependencies.sort();
                _.each(dependencies, function (pkg) {
                    console.log("  " + pkg);
                });
            }
        }

        console.log("\n\n");

        resolve();
    });
}

gulp.task('js:debug', function (cb) {
    var conf = webpackConf.mainjs;
    glob(conf.output.path + '/*.map', {}, function (er, files) {
        var promises = [];
        _.each(files, function (file) {
            promises.push(debugWebpackMapFile(file));
        });

        Promise.all(promises).lastly(cb);
    });
});

You’ll need to modify the script to meet your own configuration.

Just in case its not obvious, the webpack://source-code part is due to the devtool settings in webpack output settings, namely:

devtoolModuleFilenameTemplate: "webpack://source-code/[resource-path]",
devtoolFallbackModuleFilenameTemplate: "webpack://source-code/[resource-path]?[hash]",

The webpack/internal and node_modules are from the following normalization script (I don’t like webpack’s “~” replacement feature).

var normalizeMaps = function (conf, cb) {
    glob(conf.output.path + '/*.map', {}, function (er, files) {
        var promises = [];
        _.each(files, function (file) {
            promises.push(replaceInFile(file, [
                [ /\/~/g, '/node_modules' ],
                [ /\.\//g, ''],
                [ /source-code\/\(webpack\)/g, 'source-code/webpack/internal' ]
            ]));
        });

        Promise.all(promises).lastly(cb);
    });
};


The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .

Similar Posts