Minimal reproduction for an rspack production-build panic:
RealContentHashPlugin: circular hash dependency
| Package | Version |
|---|---|
@rspack/core |
1.6.8 |
@module-federation/enhanced |
2.8.0 |
yarn install
yarn build:mfExpected output:
crates/rspack_plugin_real_content_hash/src/lib.rs:464:9
RealContentHashPlugin: circular hash dependency
RealContentHashPlugin scans asset contents for provisional [contenthash] strings and topologically sorts them for replacement. When multiple async chunks each embed the same chunk-id→hash map (as production MF remotes do via __webpack_require__.u), every chunk references every other chunk's hash while also owning its own → topological sort fails.
This repo uses:
- A small MF remote (
entry: {}, 10 exposes, 200 lazy async chunks by default). output.chunkFilename: 'chunks/[id]/chunk-[contenthash].js'withoptimization.realContentHash: true.DuplicateChunkHashMapPlugin— copies the container's hash map into 2 async chunks at stagePROCESS_ASSETS_STAGE_OPTIMIZE_HASH - 1, matching the multi-runtime structure observed in production (~10 chunks there; 2 is enough to form a cycle).
Without step 3, standalone MF builds only put the full map in the container entry and do not panic.
| Command | Panics? |
|---|---|
yarn build:mf |
Yes |
yarn build:mf:natural |
No (no duplicate-map plugin) |
REAL_CONTENT_HASH=false yarn build:mf |
No |
FEATURE_COUNT=10 LAZY_PER_FEATURE=20 DUPLICATE_COUNT=2 yarn build:mf| Variable | Default | Purpose |
|---|---|---|
FEATURE_COUNT |
10 | MF exposes |
LAZY_PER_FEATURE |
20 | Dynamic imports per expose (need ~200+ total chunks for map ≥ 100 entries) |
DUPLICATE_COUNT |
2 | Async chunks to inject the full map into (≥ 2 for a cycle) |
REAL_CONTENT_HASH |
true |
Set false to skip the failing pass |
REAL_CONTENT_HASH=false yarn build:mf
# or use [chunkhash] in async chunkFilename instead of [contenthash]MIT