jantimon / html-webpack-plugin

Simplifies creation of HTML files to serve your webpack bundles
MIT License
10.71k stars 1.31k forks source link

Control HTML Emit stage #1700

Closed serkanyersen closed 9 months ago

serkanyersen commented 2 years ago

Hi,

I have a plugin that generates a custom manifest about the assets generated by webpack, I also need to embed the finalized manifest into the html.

My problem right now is that contents of the assets are not finalized until the PROCESS_ASSETS_STAGE_OPTIMIZE_HASH stage is done, because some chunks might be altered with the updated hashes. However, html-webpack-plugin is emitting the final HTML at PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE stage.

Which is very early and there is no way to go back to update the HTML generated.

It would be nice if there is a way to control the stage when the HTML is emitted here

https://github.com/jantimon/html-webpack-plugin/blob/9c9786a7134e15e9dfc6f55e7388967233c2e836/index.js#L216-L223

If I set this to PROCESS_ASSETS_STAGE_REPORT everything works as expected for me.

is there a way to achieve this without this change? if not can we make stage configurable?

serkanyersen commented 2 years ago

if I locally patch the package to run at PROCESS_ASSETS_STAGE_REPORT

or intercept like this

compilation.hooks.processAssets.intercept({
  register: (tap) => {
    if (tap.name === 'HtmlWebpackPlugin') {
      tap.stage = webpack.Compilation.PROCESS_ASSETS_STAGE_REPORT;
    }
    return tap;
  },
})

would it cause any issues?

maudnals commented 2 years ago

We have a similar issue with our plugin that uses HtmlWebpackPlugin as well. @jantimon, would the fix suggested by @serkanyersen cause any issues? Thank you!

jantimon commented 2 years ago

There was a long discussion about this topic here:

https://github.com/webpack/webpack/issues/11822

in the end @sokra told us to use PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE

maybe we can ask him for advice

serkanyersen commented 2 years ago

I did use the intercept for a while and it worked fine but later on I replaced that logic with the following.

compiler.hooks.compilation.tap(this.constructor.name, compilation => {
  const hooks = this.options.htmlWebpackPlugin.getHooks(compilation);
  hooks.beforeEmit.tap(this.constructor.name, data => {
    // modify HTML
    return data;
  });
});

compiler.hooks.thisCompilation.tap(this.constructor.name, compilation => {
  if (compilation.hooks.processAssets) {
    compilation.hooks.processAssets.tap(
      {
        name: this.constructor.name,
        stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ANALYSE,
        additionalAssets: true,
      },
      assets => {
        // correct hashes here including modified HTML
      },
    );
  }
});

Which gave me better results without intercepting. hope this helps.

Edit: I made this change because I no longer needed to embed the manifest into the HTML. which resolved my conflict.

alexander-akait commented 9 months ago

Close due https://github.com/webpack/webpack/issues/11822, most often the problem is in other plugins and they use the wrong stage, anyway feel free to feedback