webpackwebpack-dev-serverwebpack-hmrwebpack-5

Webpack 5 HMR issue - Cannot read property 'updatedChunkIds' of undefined


I've tried to add a HMR into my webpack 5 config and got a nasty error. First time it compiles properly, but when it comes to recompiling after code changes - it breaks with HookWebpackError: Cannot read property 'updatedChunkIds' of undefined. If I remove hot: true option - everything works properly.

My config:

const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CopyPlugin = require("copy-webpack-plugin");
const ImageminPlugin = require("imagemin-webpack-plugin").default;
const SVGSpritemapPlugin = require("svg-spritemap-webpack-plugin");

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

const PATHS = {
  src: path.join(__dirname, "./src"),
  dist: path.join(__dirname, "./dist"),
  icons: path.join(__dirname, "./src/assets/icons")
};

const PAGES_PUG = `${PATHS.src}/pug/`;
const PAGES_TO_CONVERT = fs
  .readdirSync(PAGES_PUG)
  .filter(filename => filename.endsWith(".pug"));

module.exports = (env, argv) => {
  const isEnvDevelopment = argv.mode === "development";
  const isEnvProduction = argv.mode === "production";

  return {
    entry: {
      app: [`${PATHS.src}/scripts/app.js`, `${PATHS.src}/scss/styles.scss`]
    },
    output: {
      path: `${PATHS.dist}`,
      filename: "./scripts/[name].[fullhash].min.js"
    },
    target: "web",
    devServer: {
      contentBase: path.join(__dirname, "dist"),
      publicPath: "/",
      open: true,
      watchContentBase: true,
      port: 8080,
      overlay: true,
      compress: true,
      hot: true
    },
    optimization: {
      splitChunks: {
        cacheGroups: {
          vendor: {
            name: "vendor",
            test: /node_modules/,
            chunks: "all",
            enforce: true
          }
        }
      }
    },
    resolve: {
      extensions: [".ts", ".js"]
    },
    devtool: isEnvDevelopment ? "eval-cheap-module-source-map" : "source-map",
    module: {
      rules: [
        {
          test: /\.pug$/,
          loader: "pug-loader",
          exclude: "/node_modules"
        },
        {
          test: /\.(scss|css)$/,
          use: [
            {
              loader: MiniCssExtractPlugin.loader
            },
            {
              loader: "css-loader",
              options: { sourceMap: true }
            },
            {
              loader: "postcss-loader",
              options: { sourceMap: true }
            },
            {
              loader: "resolve-url-loader"
            },
            {
              loader: "sass-loader",
              options: { sourceMap: true }
            }
          ],
          exclude: "/node_modules"
        },
        {
          test: /\.js$/,
          loader: "babel-loader",
          exclude: "/node_modules"
        },
        {
          test: /\.ts$/,
          loader: "ts-loader",
          exclude: "/node_modules"
        },
        {
          test: /.(jpg|jpeg|png|svg)$/,
          type: "asset/inline"
        },
        {
          test: /\.(woff(2)?|eot|ttf|otf)$/,
          type: "asset/inline"
        }
      ]
    },
    plugins: [
      new CleanWebpackPlugin(),
      ...PAGES_TO_CONVERT.map(
        page =>
          new HtmlWebpackPlugin({
            template: `${PAGES_PUG}/${page}`,
            filename: `./${page.replace(/\.pug/, ".html")}`
          })
      ),
      new MiniCssExtractPlugin({
        filename: `styles/styles.[hash].min.css`
      }),
      new CopyPlugin({
        patterns: [
          {
            from: "./src/assets/favicon",
            to: "assets/favicon",
            noErrorOnMissing: true
          },
          {
            from: "./src/assets/img",
            to: "assets/img",
            noErrorOnMissing: true
          },
          {
            from: "./src/assets/fonts",
            to: "assets/fonts",
            noErrorOnMissing: true
          }
        ]
      }),
      new SVGSpritemapPlugin("./src/assets/icons/icons-colored/**/*.svg", {
        output: {
          filename: "assets/sprites/sprites-colored/sprites.svg",
          svg4everybody: true,
          svgo: {
            plugins: [
              { inlineStyles: { onlyMatchedOnce: false } },
              { minifyStyles: true }
            ]
          }
        },
        sprite: {
          prefix: false
        }
      }),
      new SVGSpritemapPlugin(`./src/assets/icons/icons-solid/**/*.svg`, {
        output: {
          filename: "assets/sprites/sprites-solid/sprites.svg",
          svg4everybody: {
            polyfill: true
          },
          svgo: {
            plugins: [{ removeAttrs: { attrs: "(stroke|fill|style)" } }]
          }
        },
        sprite: {
          prefix: false
        }
      }),
      new ImageminPlugin({
        test: /\.(jpe?g|png|gif)$/i
      })
    ]
  };
};

I've tried to remove target option or change it to 'browserlist' - no effect. Also I've tried to remove hot option and init HMR like that in the plugin section:

isEnvDevelopment && new webpack.HotModuleReplacementPlugin()

and still the same error exists.

You could check all the build (dev branch) if you want to reproduce the bug.


Solution

  • This bug appears to be fixed in webpack release 5.27.2: https://github.com/webpack/webpack/releases/tag/v5.27.2