JDB
JDB

Reputation: 25810

How to load dynamic chunk without vendors splitting

Quick Summary

How can I dynamically import NPM modules without generating extra vendors~-prefixed chunks?

Details

Setup

webpack.config.js

const path = require("path");

module.exports = [{
    mode: "development",
    entry: path.resolve(__dirname, "js/index.js"),
}];

index.js

__webpack_public_path__ = "dist/";

loadMoment();

function loadMoment() {
    return require.ensure(
        "moment-timezone",
        require => {
            console.log("moment loaded");
            window.MY_GLOBAL = {
                moment: require("moment-timezone"),
            };
        },
        () => {},
        "lib/moment",
    );

    /*
    // Working on an existing project with an older version of ESLint, so the preferred import()
    // trips up the linter. Using the older require.ensure to avoid linter errors, but I tested
    // with async/await and import() and I get the same result.
    window.MY_GLOBAL = {
        moment: await import(
            /* webpackChunkName: "lib/moment" * /
            "moment-timezone"
        ),
    };
    */
}

Results

This is the undesired result I'm currently getting:

dist/
  lib/
    moment.js
  vendors~lib/
    moment.js

Note that there are 2 moment.js files!

The desired result:

dist/
  lib/
    moment.js

I would like to keep all of my NPM modules and whatever else I might want to load in a single chunk, named as I've specified, without any automatic prefixes.

More background

I'm working on an older project that previously copy/pasted the entire minified moment and moment-timezone libraries into a JS resource named "lib/moment.js". I'd like to remove them from our codebase and generate the "lib/moment.js" file via a dynamic import. It's important to preserve the existing file name and folder structure.

The dynamic import code seemed simple enough (webpack documentation), but I haven't been able to find any explanation for the vendors~ prefix or how to remove it. I can see it in the examples, of course, but that prefix is, as best I can tell, never directly addressed.

If I can't get rid of the prefix and bundle everything into just the lib/moment chunk, then I'm going to have to abandon this approach and just continue with the minified libraries stored in lib files (which is not ideal).

Upvotes: 0

Views: 2540

Answers (1)

JDB
JDB

Reputation: 25810

TL;DR

In short, you need to disable the defaultVendors cache group (or vendors prior to webpack 5):

webpack.config.js

const path = require("path");

module.exports = [{
    mode: "development",
    entry: path.resolve(__dirname, "js/index.js"),

    // ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
    optimization: {
      splitChunks: {
        cacheGroups: {
            defaultVendors: false,
            // prior to webpack 5:
            //vendors: false,
        },
      },
    },
    // ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲
}];

The what-now?

Webpack provides a default configuration for the optimization.splitChunks property which includes a simple "defaultVendors" rule (or "vendors" in v4) which splits NPM modules into a separate chunk:

cacheGroups: {
    defaultVendors: {
        test: /[\\/]node_modules[\\/]/,
        priority: -10,
        reuseExistingChunk: true,
    },
},

According to the docs, if you do not want to use these default cache groups, then you have to explicitly set them to false:

To disable any of the default cache groups, set them to false.

To disable the vendors cache group, simply set it to false in your webpack config:

cacheGroups: {
    defaultVendors: false,
},

Result

dist/
  lib/
    moment.js

Upvotes: 3

Related Questions