Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

using [name].[contenthash].css for filename is not deterministic #701

Closed
pieh opened this issue Feb 16, 2021 · 5 comments · Fixed by #702
Closed

using [name].[contenthash].css for filename is not deterministic #701

pieh opened this issue Feb 16, 2021 · 5 comments · Fixed by #702

Comments

@pieh
Copy link

pieh commented Feb 16, 2021

  • Operating System: macOS BigSur 11.1
  • Node Version: 12.18.2
  • NPM Version: 7.5.2
  • webpack Version: 5.22.0
  • mini-css-extract-plugin Version: 1.3.7

Expected Behavior

contentHash to be stable and deterministic

Actual Behavior

Building same project without making changes between build will "randomly" result in different contentHash sometimes

Code

I don't have minimal reproduction, but I did bit of a code dive so hopefully following explanation could replace reproduction setup.

In

compilation.hooks.contentHash.tap(pluginName, (chunk) => {
const { outputOptions, chunkGraph } = compilation;
const modules = isWebpack4
? Array.from(this.getChunkModules(chunk, chunkGraph)).filter(
(module) => module.type === MODULE_TYPE
)
: chunkGraph.getChunkModulesIterableBySourceType(chunk, MODULE_TYPE);
if (modules) {
const { hashFunction, hashDigest, hashDigestLength } = outputOptions;
const createHash = compiler.webpack
? compiler.webpack.util.createHash
: webpack.util.createHash;
const hash = createHash(hashFunction);
for (const m of modules) {
m.updateHash(hash, { chunkGraph });
}
// eslint-disable-next-line no-param-reassign
chunk.contentHash[MODULE_TYPE] = hash
.digest(hashDigest)
.substring(0, hashDigestLength);
}
});
for webpack@5 chunkGraph is used. chunkGraph.getChunkModulesIterableBySourceType(chunk, MODULE_TYPE); return SortedSet of modules, however they are actually not sorted. Per https://github.com/webpack/webpack/blob/911ec1aa67011e25aa1449610f5b0b557edd5459/test/SortableSet.unittest.js it seems SortedSet means that it exposes .sort() method to get the Set sorted.

Later on m.updateHash(hash, { chunkGraph }); is called in order of modules in the SortedSet (which actually isn't sorted, and order of modules actually seem that can change between builds). Finally this result in contentHash that is generated not to be stable/deterministic.

For part about getChunkModulesIterableBySourceType returning unsorted modules I find minor mention in https://github.com/webpack/webpack/projects/6 :

Should getChunkModulesIterableBySourceType be sorted in general?

And following code for that method ( https://github.com/webpack/webpack/blob/05768d9029b9024d9a8d5f1aa56e649d9b963f57/lib/ChunkGraph.js#L538-L549 for entry), we can see that SortedSet is used (in modulesBySourceType implementation), but .sort() or .sortWith() is not called on it resulting in preserving order of module creation (?) which itself is probably not deterministic due to lot of async execution.

How Do We Reproduce?

As mentioned above - I don't have minimal reproduction for this problem, but I hope my research and description explain the issue well?

@sokra
Copy link
Member

sokra commented Feb 16, 2021

There is a ordered method called getOrderedChunkModulesIterableBySourceType for this use case.

@pieh
Copy link
Author

pieh commented Feb 16, 2021

There is a ordered method called getOrderedChunkModulesIterableBySourceType for this use case.

What would be good thing to use in comparator to ensure deterministic order? <Module>.identifier()? (I'm pretty clueless when it comes to inner machinery of webpack :( )

@sokra
Copy link
Member

sokra commented Feb 17, 2021

Yes identifier is a good choice. There are a few comparators in webpack.utils.comparators that you can/should use.

@pieh
Copy link
Author

pieh commented Feb 17, 2021

I'll open PR changing from getChunkModulesIterableBySourceType to getOrderedChunkModulesIterableBySourceType (but most likely sometime in the upcomming weekend, unless someone beat me to it).

But looking through existing issues and PRs I noticed #454 / #482 which is somewhat related (just "reverse" problem) and thinking if changing order here will not make that issue worse.

I also found now closed similar issue #512 which pretty much sounds like same thing I described and was marked as solved (was the issue re-introduced or was it never actually fixed?)

Lastly - I do wonder how I could add reliable tests for this - it seems to me to ensure that things are tested I would have to construct chunkGraph artifically and changing order of inserted modules between runs and asserting that resulting contentHash remain stable?

@alexander-akait
Copy link
Member

alexander-akait commented Feb 17, 2021

@pieh We will fix it today (I think), I think #454 can be related, I will check it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants