webpackwebpack-splitchunks

Webpack 5 vendors chunks naming


In webpack 4, vendors chunks had names such as vendors~main~secondary.js, which were referring to the chunks they where related to. Now, in webpack 5, vendors chunks names are like this : vendors-node_modules_react-dom_index_js.js, which is really less readable and understandable.

Any tips on how to go back to webpack 4's behaviour while using webpack 5?

I guess I'll have to do something with splitChunks.name, but I can't find the right function to do that.

EDIT

While @MrP01's answer is more thorough and gives more insight into using splitChunks.name, here's a short snippet that I ended up using and that allowed me to go back to the exact old behaviour.

optimization: {
  splitChunks: {
    chunks: 'all',
    name: (module, chunks, cacheGroupKey) => {
      const allChunksNames = chunks.map((chunk) => chunk.name).join('~');
      const prefix = cacheGroupKey === 'defaultVendors' ? 'vendors' : cacheGroupKey;
      return `${prefix}~${allChunksNames}`;
    },
  },
},

Solution

  • I feel very similarly about the new naming schemes in webpack 5. With quite a bit of effort and testing, I came up with the following, by passing function handles to the filename properties.

    To get 'prettier' names - which is of course subject to everyone's personal judgment - the following function normalizes names and strips out large and unnecessary parts of them.

    function normalizeName(name) {
      return name.replace(/node_modules/g, "nodemodules").replace(/[\-_.|]+/g, " ")
        .replace(/\b(vendors|nodemodules|js|modules|es)\b/g, "")
        .trim().replace(/ +/g, "-");
    }
    

    The main problem was the naming of the chunks that were split out. The current documentation is not very explicit about this, but the cacheGroup settings configured in config.optimization.splitChunks, without a specific cacheGroup, apply to all cacheGroups.

    I've also enabled the normalization for chunk, asset names and the extracted css.

    module.exports = async () => {
      return {
        config: {
          context: BASE,
          entry: entrypoints,
          output: {
            path: path.resolve(`./.dev/bundles/${locale}`),
            publicPath: `/static/bundles/${locale}/`,
            filename: (pathData) => {
              return normalizeName(pathData.chunk.name) + ".js";
            },
            chunkFilename: (pathData) => {
              return normalizeName(pathData.chunk.id) + ".js";
            },
          },
          devtool: false,
          optimization: {
            splitChunks: {
              chunks: "all",
              name(module, chunks, cacheGroupKey) {
                const moduleFileName = module.identifier().split("/").reduceRight((item) => item);
                // const allChunksNames = chunks.map((item) => item.name).join("-");
                return normalizeName(moduleFileName.replace(/[\/]/g, "-"));
              }
            }
          },
        },
        module: {
          rules: [
          {
            test: /\.css$/,
            use: [
              MiniCssExtractPlugin.loader,
              cssLoader,
              postCssLoader
            ]
          },
          {
            test: /\.(ttf|woff|eot|png|jpg|jpeg|svg)$/,
            type: "javascript/auto",
            loader: "file-loader",
            options: {
              name: (resourcePath, resourceQuery) => {
                let ext = path.extname(resourcePath);  // for instance ".jpg"
                return normalizeName(path.basename(resourcePath).slice(0, -ext.length)) + ext;
              }
            }
          }]
        },
        plugins: [
          new MiniCssExtractPlugin({
            filename: (pathData) => normalizeName(pathData.chunk.name) + ".css",
            chunkFilename: (pathData) => normalizeName(pathData.chunk.id) + ".css"
          }),
        ],
      };
    };
    

    This resulted in filenames exceeding the name limit to much shorter and concise filenames in the resulting output folder.