<aside> 🧑‍🎓 To remove unwanted modules, we must make sure module is indeed unwanted:

How to inspect bundle contents?

</aside>

To remove an unwanted modules means to de-prioritize the module, by moving from critical chunk, to a noncritical one. To achieve it, the following actions are required:

1. Learn why is a module included in a bundle

To find why is a module included in a bundle, the Webpack compilation stats are required. To generate them, build the application with corresponding flag:

yarn build --stats # using Yarn
npm run build -- --stats # using NPM

<aside> 🧠

Alternatively, try using https://statoscope.tech/ to get a UI to play around with!

</aside>

The build/bundle-stats.json will be generated as a result. Next, add the reason-finding utility to the root of your project. Create a reason.js file and paste the following content:

const fs = require('fs');
const path = require('path');

const moduleNameToLookup = process.argv[2] || 'CategoryPage.container.js';
console.log(`lookup of "${moduleNameToLookup}"...`)
console.log()

const statsPath = path.resolve(__dirname, './build/bundle-stats.json');
const stats = JSON.parse(fs.readFileSync(statsPath));

console.log('NOTE: preview is generated from compiled source, original module contents may differ.')

stats.modules.forEach((module) => {
    const { name, reasons, chunks } = module;

    if (!name.includes(moduleNameToLookup)) {
        return;
    }

    const pChunks = chunks.map((chunk) => {
        const { names, files } = stats.chunks[chunk];
        const fileNames = files.map(file => file.split('/').slice(-1)[0]).join(', ');

        if (!names) {
            return `${fileNames}`;
        }

        return `${fileNames} (part of ${names.map(name => `${name}`)} chunk group)`;
    });

    console.log('\\n' + Array.from({ length: 30 }, () => '=/').join('') + '\\n');
    console.log('File:');
    console.log(`\\t${name}`)
    console.log();
    console.log(`Chunks:`);
    pChunks.forEach((pChunk, i) => {
        console.log(`\\t ${i}. ${pChunk}`);
    });

    const validReasons = reasons.reduce((acc, reason) => {
        const { loc, moduleName } = reason;
        const reasonModule = stats.modules.find((module) => module.name === moduleName);

        if (!reasonModule || !loc) {
            // skipping if no module or no location
            return acc;
        }

        const [lineFrom, rest] = loc.split(':');
        const [charFrom, charTo] = rest.split('-');
        const lines = reasonModule.source.split('\\n');
        const segment = lines.slice(lineFrom - 1).join('\\n');
        const preview = segment.slice(charFrom, charTo);

        if (!preview.includes('export') && !preview.includes('import')) {
            // skipping if preview does not contain any import / export
            return acc;
        }

        const isReasonAlreadyIncluded = acc.some((otherReason) => (
            otherReason.preview === preview
            && otherReason.moduleName === moduleName
        ));

        if (isReasonAlreadyIncluded) {
            return acc;
        }

        return [
            ...acc,
            {
                preview,
                moduleName,
            }
        ]
    }, []);

    validReasons.forEach((reason, i) => {
        const { type, moduleName, preview } = reason;
        console.log('\\n' + Array.from({ length: 60 }, () => '-').join('') + '\\n');
        console.log(`${i + 1}. reason`);
        console.log();
        console.log(`File: ${moduleName}`);
        console.log(`Preview: ${preview}`);
    });
})

Next, run it, passing the module reason of which inclusion into the bundle we are interested to find:

node reason.js CategoryPage.container.js

It will output the similar result:

<aside> 🧠 If the imports of a module do not lead you anywhere directly, try searching for one of the reasons (reason file name) of a module of interest.

</aside>

Untitled

2. Make all module imports dynamic

<aside> 🧠 Static import is an import made using import A from B syntax. For example:

import { CATEGORY } from 'Component/Header/Header.config';

Dynamic import is an import made using import(A) syntax. For example:

const CartDispatcher = import(
  /* webpackMode: "lazy", webpackChunkName: "cart" */
  'Store/Cart/Cart.dispatcher'
);

The magic comments (i.e. webpackChunkName) are commonly used to hint Webpack a name of a chunk group to place a module into.

</aside>

If the module is included in a bundle, likely, it is statically imported in it. The goal is to transform these static imports into dynamic ones.

<aside> 🧠 All imports of a module must be dynamic for it to be moved into another chunk, if one of many will be dynamic, the module might become duplicated across chunks!

</aside>

How to make any import dynamic?