Html-webpack-plugin: Double compilation with webpack 5, webpack-dev-server & HTMLWebpackPlugin

Created on 18 Dec 2020  ·  9Comments  ·  Source: jantimon/html-webpack-plugin

Current behaviour 💣


Since I upgraded to webpack 5, I noticed that I get my code compiled twice.
It only happens when I use the combination of webpack-dev-server and HTMLWebpackPlugin.
Removing HTMLWebpackPlugin fixes the issue, and running webpack instead of webpack serve also fixes it.

I noticed this since I am using a custom plugin that relies on the compiler.hooks.afterEmit hook, and this one is now called twice.
Also, observe two Compiled successfully. messages in the terminal output below:


See full console output

yarn run v1.22.5
$ webpack serve
<i> [webpack-dev-server] Project is running at http://127.0.0.1:8080/
<i> [webpack-dev-server] Content not from webpack is served from /Users/thomas/code/webpack-double-compile
<i> [webpack-dev-middleware] asset main.js 136 KiB [emitted] [minimized] (name: main) 1 related asset
<i> asset index.html 236 bytes [emitted]
<i> runtime modules 24.9 KiB 10 modules
<i> cacheable modules 193 KiB
<i>   modules by path ./node_modules/webpack-dev-server/client/ 71 KiB 12 modules
<i>   modules by path ./node_modules/webpack/hot/*.js 4.3 KiB 4 modules
<i>   modules by path ./node_modules/html-entities/lib/*.js 58.1 KiB 4 modules
<i>   modules by path ./node_modules/querystring/*.js 4.51 KiB 3 modules
<i>   modules by path ./node_modules/url/*.js 23.1 KiB
<i>     ./node_modules/url/url.js 22.8 KiB [built] [code generated]
<i>     ./node_modules/url/util.js 314 bytes [built] [code generated]
<i>   ./src/index.js 1 bytes [built] [code generated]
<i>   ./node_modules/ansi-html/index.js 4.16 KiB [built] [code generated]
<i>   ./node_modules/events/events.js 13.8 KiB [built] [code generated]
<i>   ./node_modules/punycode/punycode.js 14.3 KiB [built] [code generated]
<i> ./node_modules/webpack/hot/ sync nonrecursive ^\.\/log$ 170 bytes [built] [code generated]
<i> webpack 5.11.0 compiled successfully in 1987 ms
<i> [webpack-dev-middleware] Compiled successfully.
<i> [webpack-dev-middleware] Compiling...
<i> [webpack-dev-middleware] assets by path *.js 136 KiB
<i>   asset main.js 136 KiB [emitted] [minimized] (name: main) 1 related asset
<i>   asset 179.3fff2da0c901c34188c0.hot-update.js 111 bytes [emitted] [immutable] [hmr] [minimized] (name: main)
<i> asset index.html 236 bytes [emitted]
<i> asset main.3fff2da0c901c34188c0.hot-update.json 25 bytes [emitted] [immutable] [hmr]
<i> Entrypoint main 136 KiB = main.js 136 KiB 179.3fff2da0c901c34188c0.hot-update.js 111 bytes
<i> runtime modules 24.9 KiB 10 modules
<i> cacheable modules 193 KiB
<i>   modules by path ./node_modules/webpack-dev-server/client/ 71 KiB 12 modules
<i>   modules by path ./node_modules/webpack/hot/*.js 4.3 KiB 4 modules
<i>   modules by path ./node_modules/html-entities/lib/*.js 58.1 KiB 4 modules
<i>   modules by path ./node_modules/querystring/*.js 4.51 KiB 3 modules
<i>   modules by path ./node_modules/url/*.js 23.1 KiB
<i>     ./node_modules/url/url.js 22.8 KiB [built] [code generated]
<i>     ./node_modules/url/util.js 314 bytes [built] [code generated]
<i>   ./src/index.js 1 bytes [built] [code generated]
<i>   ./node_modules/ansi-html/index.js 4.16 KiB [built] [code generated]
<i>   ./node_modules/events/events.js 13.8 KiB [built] [code generated]
<i>   ./node_modules/punycode/punycode.js 14.3 KiB [built] [code generated]
<i> ./node_modules/webpack/hot/ sync nonrecursive ^\.\/log$ 170 bytes [built] [code generated]
<i> webpack 5.11.0 compiled successfully in 1573 ms
<i> [webpack-dev-middleware] Compiled successfully.

Expected behaviour ☀️


I only expect one compilation run, i.e. one call of compiler.hooks.afterEmit and only one output like this in the console after running webpack serve:

<i> webpack 5.11.0 compiled successfully in 1573 ms
<i> [webpack-dev-middleware] Compiled successfully.

Reproduction Example 👾

I created a repro example here: https://github.com/tzimmermann/webpack-double-compile
But afterwards, I noticed that also the codesandbox template inhibits this bug already.

Environment 🖥

Node.js v14.2.0
darwin 19.6.0
NPM 6.14.7
[email protected]
[email protected]

Similar issues:
https://github.com/webpack/webpack/issues/10789
https://github.com/webpack/webpack/issues/12140

Most helpful comment

Hi, I ran into this issue as well. Besides being annoying occasionally, it also messes with some of our automated builds. I've been doing some debugging to try and see if I can find the reason for this double compilation. I've found something, and I believe the source of the issue comes from html-webpack-plugin.

In my specific case, the recompile is triggered from webpack's watcher. This statement is reached via a series of callbacks, originating from the _go function. _go is executed because webpack's Watcher is invalidated. This invalidation occurs in a callback because watchpack's aggregated event is triggered after a timeout.

In _onTimeout, my debugger shows no changes, but it does list a removal in removals:

'data:text/javascript,__webpack_public_path__ = htmlWebpackPluginPublicPath;'

I'm not sure where this removal is coming from exactly, but it looks to come from this plugin, and then I found this issue here, so I figured posting this here might be relevant.

watchpack-debugger-screenshot

All 9 comments

Thanks for your feedback

html-webpack-plugin is creating a child compilation maybe this is not done correctly..

otherwise it is a bug in the webpack-dev-server

I created a sandbox with a minimal reproduction case:

https://codesandbox.io/s/html-webpack-plugin-5x-double-compilation-pkk33?file=/webpack.config.js:0-433

const HtmlWebpackPlugin = require("html-webpack-plugin");

class IsDonePlugin {
  apply(compiler) {
    compiler.hooks.afterEmit.tap("IsDonePlugin", () => {
      console.log("Compilation afterEmit", new Date());
    });
    compiler.hooks.done.tap("IsDonePlugin", () => {
      console.log("Compilation is done", new Date());
    });
  }
}

module.exports = {
  plugins: [
    new IsDonePlugin(),
    new HtmlWebpackPlugin()
  ]
};

And for the initial run it executes the logs twice

Compilation afterEmit 2020-12-18T10:45:07.341Z
Compilation is done 2020-12-18T10:45:07.343Z

and

Compilation afterEmit 2020-12-18T10:45:12.477Z
Compilation is done 2020-12-18T10:45:12.477Z

Full log:

webpack serve --port 8080
ℹ 「wds」: Project is running at http://localhost:8080/
ℹ 「wds」: webpack output is served from undefined
ℹ 「wds」: Content not from webpack is served from /sandbox
Compilation afterEmit 2020-12-18T10:45:07.341Z
Compilation is done 2020-12-18T10:45:07.343Z
ℹ 「wdm」: asset main.js 146 KiB [emitted] [minimized] (name: main) 1 related asset
asset index.html 177 bytes [emitted]
runtime modules 430 bytes 3 modules
cacheable modules 330 KiB
  modules by path ./node_modules/webpack-dev-server/ 21.2 KiB
    modules by path ./node_modules/webpack-dev-server/client/ 20.9 KiB 10 modules
    modules by path ./node_modules/webpack-dev-server/node_modules/ 296 bytes 2 modules
  modules by path ./node_modules/html-entities/lib/*.js 57.9 KiB 4 modules
  modules by path ./node_modules/querystring/*.js 4.51 KiB 3 modules
  modules by path ./node_modules/webpack/hot/*.js 1.42 KiB
    ./node_modules/webpack/hot/emitter.js 75 bytes [built] [code generated]
    ./node_modules/webpack/hot/log.js 1.34 KiB [built] [code generated]
  modules by path ./node_modules/url/*.js 23.1 KiB
    ./node_modules/url/url.js 22.8 KiB [built] [code generated]
    ./node_modules/url/util.js 314 bytes [built] [code generated]
./node_modules/webpack/hot/ sync nonrecursive ^\.\/log$ 170 bytes [built] [code generated]
webpack 5.3.2 compiled successfully in 4671 ms
ℹ 「wdm」: Compiled successfully.
ℹ 「wdm」: Compiling...
Compilation afterEmit 2020-12-18T10:45:12.477Z
Compilation is done 2020-12-18T10:45:12.477Z
ℹ 「wdm」: asset main.js 146 KiB [emitted] [minimized] (name: main) 1 related asset
asset index.html 177 bytes [emitted]
runtime modules 430 bytes 3 modules
cacheable modules 330 KiB
  modules by path ./node_modules/webpack-dev-server/ 21.2 KiB
    modules by path ./node_modules/webpack-dev-server/client/ 20.9 KiB 10 modules
    modules by path ./node_modules/webpack-dev-server/node_modules/ 296 bytes 2 modules
  modules by path ./node_modules/html-entities/lib/*.js 57.9 KiB 4 modules
  modules by path ./node_modules/querystring/*.js 4.51 KiB 3 modules
  modules by path ./node_modules/webpack/hot/*.js 1.42 KiB
    ./node_modules/webpack/hot/emitter.js 75 bytes [built] [code generated]
    ./node_modules/webpack/hot/log.js 1.34 KiB [built] [code generated]
  modules by path ./node_modules/url/*.js 23.1 KiB
    ./node_modules/url/url.js 22.8 KiB [built] [code generated]
    ./node_modules/url/util.js 314 bytes [built] [code generated]
./node_modules/webpack/hot/ sync nonrecursive ^\.\/log$ 170 bytes [built] [code generated]
webpack 5.3.2 compiled successfully in 4854 ms
ℹ 「wdm」: Compiled successfully.

Maybe it's also worth mentioning that if you change a file once the server runs it will compile only once

It might be worth noting that [email protected] seemingly doesn't explicitly support webpack@5 or webpack-cli@4, but [email protected] does (see the release notes). As this is the first beta release for webpack-dev-server, it might be worth creating a Github issue in its repo if it can be narrowed down to being a problem with webpack-dev-server (and an issue doesn't currently exist for this).

It might be worth noting that [email protected] seemingly doesn't explicitly support webpack@5 or webpack-cli@4, but [email protected] does (see the release notes). As this is the first beta release for webpack-dev-server, it might be worth creating a Github issue in its repo if it can be narrowed down to being a problem with webpack-dev-server (and an issue doesn't currently exist for this).

@joealden I did try with the 4.0.0-beta of webpack-dev-server in my repro-repo above and found the same buggy behavior.
Do you have an idea how to figure out if the bug lies in html-webpack-plugin or in webpack-dev-server?

I guess if no one has any further idea here, I'll go on creating an issue in the webpack repo, it seems that https://github.com/webpack/webpack/issues/12140 is very similar.

Hi, I ran into this issue as well. Besides being annoying occasionally, it also messes with some of our automated builds. I've been doing some debugging to try and see if I can find the reason for this double compilation. I've found something, and I believe the source of the issue comes from html-webpack-plugin.

In my specific case, the recompile is triggered from webpack's watcher. This statement is reached via a series of callbacks, originating from the _go function. _go is executed because webpack's Watcher is invalidated. This invalidation occurs in a callback because watchpack's aggregated event is triggered after a timeout.

In _onTimeout, my debugger shows no changes, but it does list a removal in removals:

'data:text/javascript,__webpack_public_path__ = htmlWebpackPluginPublicPath;'

I'm not sure where this removal is coming from exactly, but it looks to come from this plugin, and then I found this issue here, so I figured posting this here might be relevant.

watchpack-debugger-screenshot

I upgraded the sandbox reproduction example to the latest package version and the recompilation is still executed twice:

html-webpack-plugin_5_x_double_compilation_-_CodeSandbox

    "html-webpack-plugin": "5.0.0-beta.1"
    "webpack": "5.11.1"
    "webpack-cli": "4.3.0"
    "webpack-dev-server": "4.0.0-beta.0"

Wow @pvdstel that might be a good point here - the data:text/javascript does obviously not exist on hard disk so maybe it's a bug in webpack watcher.

I can reproduce the issue without the html-webpack-plugin:

let counterAfterEmit = 0,
  counterAfterDone = 0;

class IsDonePlugin {
  apply(compiler) {
    compiler.hooks.afterEmit.tap("IsDonePlugin", () => {
      console.log(
        "👀  ",
        ++counterAfterEmit,
        ". compilation afterEmit",
        new Date()
      );
    });
    compiler.hooks.done.tap("IsDonePlugin", () => {
      console.log(
        "👀  ",
        ++counterAfterDone,
        ". compilation is done",
        new Date()
      );
    });
  }
}

module.exports = {
  entry: {
    main: "./src/index.js",
    inline: "data:text/javascript,"
  },
  mode: "development",
  plugins: [new IsDonePlugin()]
};

https://codesandbox.io/s/webpack-5-double-compilation-b14b9?file=/webpack.config.js:0-631

The webpack core team is on it: https://github.com/webpack/webpack/issues/12283 so I'll close this issue here :)

Great, thanks for posting the issue there! 🎉

Was this page helpful?
0 / 5 - 0 ratings