html-webpack-plugin icon indicating copy to clipboard operation
html-webpack-plugin copied to clipboard

[contenthash] doesn't match between the assets in hooks and emitted output

Open psimk opened this issue 3 years ago • 11 comments

Current behaviour 💣

When using the assetTags from the callback of alterAssetTags or alterAssetTagGroups hooks (didn't test other hooks), the contenthash portion of the asset filename differs from the contenthash of the same asset in the emitted output.

For example:

given filename: "bundle.[contenthash].js"

In alterAssetTags hook, assetTags.scripts[0].attributes.src evaluates to: bundle.1817f49c37ed2ca083b2.js

whereas in the final output and also the emitted index.html it is: bundle.2e55dea7e3e197f9902d.js

Expected behaviour ☀️

The content hashes in the hooks filename and in the emitted output should be the same.

Reproduction Example 👾

https://github.com/psimk/html-plugin-contenthash-reproducible

Environment 🖥

  • Node.js v14.16.0
  • OS: linux 5.4.0-72-generic
  • NPM 6.14.11
  • Webpack 5.34.0
  • HTMLWebpackPlugin 5.3.1

psimk avatar Apr 20 '21 15:04 psimk

the same error

  • "html-webpack-plugin":"5.3.1"
  • "webpack": "5.31.2"

codeonquer avatar Apr 27 '21 04:04 codeonquer

[Forget my poor English!]

Hi,I have tried to find out where is the problem. Then I find that the orders between html-webpack-plugin and webpack/optimize/RealContentHashPlugin may be the key.

html-webpack-plugin gets contenthash-A. Then html-webpack-plugin uses it to generate html, passes it by hooks (beforeAssetTagGeneration ... beforeEmit) to us. Finnaly html-webpack-plugin sends html to webpack.

After html is sent, webpack uses RealContentHashPlugin to update contenthash and gets contenthash-B. webpack updates html with new contenthash-B.

So we get contenthash-A. But html uses correct contenthash-B for assets.

@psimk @jantimon

codeonquer avatar Apr 27 '21 08:04 codeonquer

@codeonquer

it seems you know the problem, what is the solution?

hamid814 avatar May 02 '21 21:05 hamid814

@codeonquer

it seems you know the problem, what is the solution?

In my case, i need contenthash to generate new file for some reason, so i send my content to webpack too, like:

compilation.emitAsset(`${outputFilename}`, new webpack.sources.RawSource(`${content}`, false), {});

webpack updates correct contenthash automatically.

if i just want to know the correct contenthash, i mabye use compiler.hooks.done.

This is my solution for now. I hope it can help you.

codeonquer avatar May 03 '21 15:05 codeonquer

I also have this kind of problem with just html-webpack-plugin and html-webpack-harddisk-plugin and with webpack config output.filename: "[name].[contenthash:8].js". The contenthash in a html file that is created by the html-webpack-harddisk-plugin is different (wrong) than the final contenthash that is created by webpack. Thanks to @codeonquer I finally got what's going on.

Temporary solution for me is to disable realContentHash in webpack config:

optimization: {
        // other config ...
        realContentHash: false,
    },

See https://webpack.js.org/configuration/optimization/#optimizationrealcontenthash and https://webpack.js.org/plugins/internal-plugins/#realcontenthashplugin for more info:

Adds an additional hash compilation pass after the assets have been processed to get the correct asset content hashes. If realContentHash is set to false, internal data is used to calculate the hash and it can change when assets are identical.

@jantimon is there any chance for a fix of this behaviour please?

rodlukas avatar Sep 05 '21 18:09 rodlukas

Hi am using:

    "html-loader": "^2.1.2",
    "html-webpack-plugin": "^5.3.2",

html file:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>React Min</title>
  </head>

  <body>
    <div id="root"></div>
    <script src="script.file.js"></script>
  </body>
</html>

As one can see there is a custom script added. So it requires html-loader for parsing right! I was running in same issue : "The hash in index.html was different then that of generated file."

webpack config:

    mode: 'production',
    output: {
        filename: '[name].[contenthash].js',
        chunkFilename: '[id].[contenthash].js',
        assetModuleFilename: `assets/[name].[contenthash].[ext]`,
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                include: path.resolve(__dirname, "src"),
                use: [{
                    loader: 'babel-loader',
                }]
            },
            {
                test: /\.html$/i,
                use: [{ loader: 'html-loader' }]
            }

as mentioned by @rodlukas, setting realContentHash to false worked:

    optimization: {
        realContentHash: false,

generated index.html file

<!doctype html>
<html lang="en">
<head>
...
</head>
<body>
  <div id="root"></div>
  <script src="/assets/script.file.a4b0e773e61e7afa29f5..js"></script>
  ... /* bundles */
</body>
</html>

nikhilnayyar002 avatar Oct 11 '21 17:10 nikhilnayyar002

This issue had no activity for at least half a year. It's subject to automatic issue closing if there is no activity in the next 15 days.

stale[bot] avatar Apr 16 '22 06:04 stale[bot]

If I'm not mistaken, this issue is still relevant.

apepper avatar Apr 19 '22 07:04 apepper

yes! the issue is still relevant since there was no commit that would fix the behaviour

rodlukas avatar Apr 19 '22 07:04 rodlukas

I had to wrestle with the same as we needed to create double-bundle (one for ES6-and-up browsers and one for ES5-and-down browsers) and inject in a single html file, so ended up creating my own plugin on top of html-webpack-plugin. Our use-case wasn't trivial so it won't fit your use-cases most likely.

TLDR; There is a bit of a disconnect (from what I can see) between webpack maintaners and @jantimon, so things aren't quite as we (consumers) might expect and one would need to dig quite deep into the weeds of webpack and html-webpack-plugin implementation details to understand what's going on and rewrite pieces that don't fit into a particular use-case. While html-webpack-plugin basic use-case is trivial, it is maybe trying to do too much outside of a basic use-case, something that better to leave to webpack itself, that's what I'm personally deducing (and my opinion does not necessarily represent reality). And I must admit that it is not possible for a tool to cover all possible use-cases (something like double-bundle), but something as obvious as real content hash must be supported (in my opinion) because we don't want a change in our source files formatting/comments to result in a chunk/entrypoint removal from all caches. See https://github.com/webpack/webpack/issues/11822 for some highlights.

To improve things, webpack maintainers (@alexander-akait) suggest to move html-webpack-plugin over to webpack-contrib, which (from what I can see) @jantimon doesn't want to. See: https://github.com/webpack/webpack/issues/16399#issuecomment-1287562315 https://github.com/webpack/webpack/issues/16312#issuecomment-1270223729 https://github.com/webpack/webpack/issues/13275#issuecomment-937274003

Issues backlog in this repo hints me that html-webpack-plugin is not being actively maintained so I'm not sure about its future and better alternatives. We're using NX on some of our projects and next.js on others, they provide their own custom way of injecting assets into html; there's also other (than webpack) "bleeding edge (as always)" bundlers, nothing is more stable in JS world than community shift towards yet another hyped library, so it's hard to recommend anything.

My personal take - I'd rather see webpack and html-webpack-plugin evolving and community focusing on them, rather than jumping on another hype train, as they are fundamental web tools for me.

g3tr1ght avatar Dec 28 '22 02:12 g3tr1ght

Is there any progress on this issue?

hangaoke1 avatar Jan 05 '23 12:01 hangaoke1