fix(rspack): use contenthash for chunkFilename to prevent stale chunks#36136
Conversation
Production builds could serve stale lazy or vendor chunks after a redeploy, crashing with "Cannot read properties of null (reading 'call')". NxAppRspackPlugin set output.chunkFilename to [chunkhash] while output.filename used [contenthash]. rspack's [chunkhash] hashes a chunk's module bodies but omits each module's assigned id, so a deterministic-moduleIds renumber changes the chunk's emitted bytes without changing its [chunkhash] filename; long-term-cached clients then keep the old chunk against an entry that references the new id. realContentHash does not help, since it only re-hashes [contenthash]. chunkFilename now uses [contenthash], matching output.filename and the defaults of Angular CLI, create-react-app, and Vue CLI, so the filename changes whenever the chunk's content does.
✅ Deploy Preview for nx-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for nx-dev ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
View your CI Pipeline Execution ↗ for commit 8ba329f
☁️ Nx Cloud last updated this comment at |
There was a problem hiding this comment.
Important
At least one additional CI pipeline execution has run since the conclusion below was written and it may no longer be applicable.
Nx Cloud has identified a possible root cause for your failed CI:
We investigated the two failing tasks (eslint:test and vue:test) and confirmed they are unrelated to this PR's changes — both fail due to stale test snapshots that expect package versions of 0.0.1 while the environment now reports 24.0.0. Our changes only touch packages/rspack/src/plugins/utils/hash-format.ts and have no effect on ESLint or Vue package version resolution.
No code changes were suggested for this issue.
Trigger a rerun:
🎓 Learn more about Self-Healing CI on nx.dev
#36136) ## Current Behavior `NxAppRspackPlugin` maps `outputHashing: 'all'` (the default) to two different hash placeholders: `output.filename` uses `[contenthash]`, but `output.chunkFilename` uses `[chunkhash]`. rspack's `[chunkhash]` hashes a chunk's module bodies but not each contained module's assigned id. With the production default `optimization.moduleIds: 'deterministic'`, a module's id can be renumbered by an unrelated change to the module graph, which changes a chunk's emitted bytes without changing its `[chunkhash]` filename. Long-term-cached clients then keep the old chunk against a freshly built entry that references the new id and crash with `Uncaught TypeError: Cannot read properties of null (reading 'call')`. `optimization.realContentHash` does not mitigate this, since it only re-hashes `[contenthash]`. ## Expected Behavior `output.chunkFilename` uses `[contenthash]`, matching `output.filename`, so a chunk's filename always changes when its emitted content does. This is rspack's recommended hash for long-term caching and matches the defaults of Angular CLI, create-react-app, and Vue CLI. ## Implementation Details `getOutputHashFormat` in `packages/rspack/src/plugins/utils/hash-format.ts` now uses `[contenthash]` for `chunk` in both the `bundles` and `all` options. Confirmed against rspack source: `[contenthash]` folds the module id into the hash (web-infra-dev/rspack#2292, released in v0.1.2, below the `@rspack/core ^1 || ^2` support floor) while `[chunkhash]` does not. `@nx/webpack` carries the same `[chunkhash]` default but was deliberately left unchanged: webpack's `[chunkhash]` already folds module ids into the chunk hash (via `getModuleHash`), so it does not exhibit this stale-chunk problem. ## Related Issue(s) Fixes #36014 <!-- polygraph-session-start --> --- [View session information ↗](https://app.trypolygraph.com/orgs/6a061dcb561c062131116eca/sessions/gh-36014-54fdd58d) <!-- polygraph-session-end --> (cherry picked from commit 15871f0)
Current Behavior
NxAppRspackPluginmapsoutputHashing: 'all'(the default) to two different hash placeholders:output.filenameuses[contenthash], butoutput.chunkFilenameuses[chunkhash]. rspack's[chunkhash]hashes a chunk's module bodies but not each contained module's assigned id. With the production defaultoptimization.moduleIds: 'deterministic', a module's id can be renumbered by an unrelated change to the module graph, which changes a chunk's emitted bytes without changing its[chunkhash]filename. Long-term-cached clients then keep the old chunk against a freshly built entry that references the new id and crash withUncaught TypeError: Cannot read properties of null (reading 'call').optimization.realContentHashdoes not mitigate this, since it only re-hashes[contenthash].Expected Behavior
output.chunkFilenameuses[contenthash], matchingoutput.filename, so a chunk's filename always changes when its emitted content does. This is rspack's recommended hash for long-term caching and matches the defaults of Angular CLI, create-react-app, and Vue CLI.Implementation Details
getOutputHashFormatinpackages/rspack/src/plugins/utils/hash-format.tsnow uses[contenthash]forchunkin both thebundlesandalloptions. Confirmed against rspack source:[contenthash]folds the module id into the hash (web-infra-dev/rspack#2292, released in v0.1.2, below the@rspack/core ^1 || ^2support floor) while[chunkhash]does not.@nx/webpackcarries the same[chunkhash]default but was deliberately left unchanged: webpack's[chunkhash]already folds module ids into the chunk hash (viagetModuleHash), so it does not exhibit this stale-chunk problem.Related Issue(s)
Fixes #36014
View session information ↗