Skip to content

fix: stable shared module ids and runtime-chunk emission order#20860

Merged
alexander-akait merged 3 commits into
webpack:mainfrom
imccausl:stable-shared-module-ids
Apr 27, 2026
Merged

fix: stable shared module ids and runtime-chunk emission order#20860
alexander-akait merged 3 commits into
webpack:mainfrom
imccausl:stable-shared-module-ids

Conversation

@imccausl
Copy link
Copy Markdown
Contributor

Summary

Makes shared-module runtime output stable across builds. ProvideSharedModule.identifier() now separates the request path with | so makePathsRelative can strip the build root (previously, differing absolute paths across CI runners produced different module IDs). ConsumeSharedRuntimeModule now emits moduleToHandlerMapping entries in id-sorted order via getOrderedChunkModulesIterableBySourceType, instead of chunk-graph insertion order. Together these prevent spurious content-hash churn on otherwise-identical rebuilds. Fixes #20852.

What kind of change does this PR introduce?
A fix.

Did you add tests for your changes?
Yes

Does this PR introduce a breaking change?
No

If relevant, what needs to be documented once your changes are merged or what have you already documented?
Nothing that I am aware of

Use of AI
I used AI to help me with the tests.

Copilot AI review requested due to automatic review settings April 21, 2026 18:48
@linux-foundation-easycla
Copy link
Copy Markdown

linux-foundation-easycla Bot commented Apr 21, 2026

CLA Signed

The committers listed above are authorized under a signed CLA.

  • ✅ login: alexander-akait / name: Alexander Akait (22899cb)
  • ✅ login: alexander-akait / name: alexander-akait (3303d4f)
  • ✅ login: imccausl / name: Ian McCausland (1c959db)

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 21, 2026

🦋 Changeset detected

Latest commit: 22899cb

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
webpack Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes nondeterministic Module Federation output that can cause runtime and entry bundle content-hashes to drift between otherwise-identical builds (especially when absolute project roots differ across CI runners).

Changes:

  • Make ProvideSharedModule.identifier() path-normalizable by makePathsRelative via |-separated segments.
  • Emit ConsumeSharedRuntimeModule’s consume-shared module lists in stable, id-sorted order using getOrderedChunkModulesIterableBySourceType(..., compareModulesById(...)).
  • Add targeted tests: a unit test for identifier normalization and a config-case asserting stable runtime mapping order.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated no comments.

File Description
lib/sharing/ProvideSharedModule.js Switch identifier delimiter to `
lib/sharing/ConsumeSharedRuntimeModule.js Use ordered chunk-module iteration with an id comparator to stabilize runtime emission order.
test/SharingUtil.unittest.js Add unit coverage ensuring makePathsRelative produces stable identifiers across differing build roots.
test/configCases/sharing/consume-shared-stable-runtime/* Add a config case validating that moduleToHandlerMapping is emitted in sorted id order (and runtime/main bundles execute).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 22, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.62%. Comparing base (42ce793) to head (3303d4f).
⚠️ Report is 14 commits behind head on main.

❗ There is a different number of reports uploaded between BASE (42ce793) and HEAD (3303d4f). Click for more details.

HEAD has 7 uploads less than BASE
Flag BASE (42ce793) HEAD (3303d4f)
integration 35 28
Additional details and impacted files
@@             Coverage Diff             @@
##             main   #20860       +/-   ##
===========================================
- Coverage   91.45%   79.62%   -11.83%     
===========================================
  Files         562      522       -40     
  Lines       55490    53614     -1876     
  Branches    14656    14167      -489     
===========================================
- Hits        50746    42689     -8057     
- Misses       4744    10925     +6181     
Flag Coverage Δ
integration 76.63% <100.00%> (-13.78%) ⬇️
test262 46.01% <ø> (+0.02%) ⬆️
unit 36.24% <100.00%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 22, 2026

Merging this PR will degrade performance by 83.78%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 6 improved benchmarks
❌ 8 regressed benchmarks
✅ 130 untouched benchmarks

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation benchmark "cache-filesystem", scenario '{"name":"mode-production","mode":"production"}' 915.4 ms 752.7 ms +21.63%
Memory benchmark "side-effects-reexport", scenario '{"name":"mode-production","mode":"production"}' 5.8 MB 4.4 MB +30.7%
Memory benchmark "many-chunks-esm", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 144.6 KB 891.6 KB -83.78%
Memory benchmark "devtool-eval", scenario '{"name":"mode-development","mode":"development"}' 851 KB 1,128.3 KB -24.58%
Memory benchmark "many-chunks-commonjs", scenario '{"name":"mode-development","mode":"development"}' 809.4 KB 1,310.6 KB -38.24%
Memory benchmark "asset-modules-source", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 366.5 KB 160.7 KB ×2.3
Memory benchmark "react", scenario '{"name":"mode-development","mode":"development"}' 871.4 KB 1,359.8 KB -35.91%
Memory benchmark "many-chunks-commonjs", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 971.7 KB 461.3 KB ×2.1
Memory benchmark "side-effects-reexport", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 1,370.9 KB 849.6 KB +61.36%
Memory benchmark "many-modules-commonjs", scenario '{"name":"mode-development","mode":"development"}' 855.9 KB 1,141.3 KB -25.01%
Memory benchmark "asset-modules-source", scenario '{"name":"mode-development","mode":"development"}' 713.5 KB 4,329.1 KB -83.52%
Memory benchmark "many-modules-esm", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 244.6 KB 167.1 KB +46.32%
Memory benchmark "devtool-eval-source-map", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 276.9 KB 857.3 KB -67.71%
Memory benchmark "many-modules-esm", scenario '{"name":"mode-production","mode":"production"}' 7.9 MB 10 MB -21.14%

Comparing imccausl:stable-shared-module-ids (3303d4f) with main (bd7aec7)

Open in CodSpeed

@alexander-akait alexander-akait merged commit f08f19a into webpack:main Apr 27, 2026
1 check passed
@alexander-akait
Copy link
Copy Markdown
Member

Thanks

@github-actions
Copy link
Copy Markdown
Contributor

This PR is packaged and the instant preview is available (f08f19a).

Install it locally:

  • npm
npm i -D webpack@https://pkg.pr.new/webpack@f08f19a
  • yarn
yarn add -D webpack@https://pkg.pr.new/webpack@f08f19a
  • pnpm
pnpm add -D webpack@https://pkg.pr.new/webpack@f08f19a

@krassowski
Copy link
Copy Markdown

Of note, this seems to have broke downstreams using older versions of license-webpack-plugin, producing the following:

Details
      HookWebpackError: Cannot read properties of undefined (reading 'trim')
          at makeWebpackError (/Users/runner/work/extension-template/extension-template/myextension/node_modules/webpack/lib/errors/HookWebpackError.js:80:9)
          at /Users/runner/work/extension-template/extension-template/myextension/node_modules/webpack/lib/Compilation.js:3579:12
          at eval (eval at create (/Users/runner/work/extension-template/extension-template/myextension/node_modules/tapable/lib/HookCodeFactory.js:31:10), <anonymous>:23:1)
          at fn (/Users/runner/work/extension-template/extension-template/myextension/node_modules/webpack/lib/Compilation.js:623:17)
          at _next1 (eval at create (/Users/runner/work/extension-template/extension-template/myextension/node_modules/tapable/lib/HookCodeFactory.js:31:10), <anonymous>:21:1)
          at eval (eval at create (/Users/runner/work/extension-template/extension-template/myextension/node_modules/tapable/lib/HookCodeFactory.js:31:10), <anonymous>:37:1)
          at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
      -- inner error --
      TypeError: Cannot read properties of undefined (reading 'trim')
          at WebpackModuleFileIterator.getActualFilename (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackModuleFileIterator.js:47:42)
          at WebpackModuleFileIterator.internalCallback (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackModuleFileIterator.js:23:35)
          at WebpackModuleFileIterator.iterateFiles (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackModuleFileIterator.js:10:9)
          at /Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/PluginChunkReadHandler.js:20:32
          at WebpackChunkModuleIterator.iterateModules (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackChunkModuleIterator.js:42:21)
          at PluginChunkReadHandler.processChunk (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/PluginChunkReadHandler.js:19:29)
          at _loop_1 (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackCompilerHandler.js:135:37)
          at WebpackCompilerHandler.iterateChunks (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackCompilerHandler.js:160:17)
          at /Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackCompilerHandler.js:50:31
          at fn (/Users/runner/work/extension-template/extension-template/myextension/node_modules/webpack/lib/Compilation.js:621:10)
      caused by plugins in Compilation.hooks.processAssets
      TypeError: Cannot read properties of undefined (reading 'trim')
          at WebpackModuleFileIterator.getActualFilename (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackModuleFileIterator.js:47:42)
          at WebpackModuleFileIterator.internalCallback (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackModuleFileIterator.js:23:35)
          at WebpackModuleFileIterator.iterateFiles (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackModuleFileIterator.js:10:9)
          at /Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/PluginChunkReadHandler.js:20:32
          at WebpackChunkModuleIterator.iterateModules (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackChunkModuleIterator.js:42:21)
          at PluginChunkReadHandler.processChunk (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/PluginChunkReadHandler.js:19:29)
          at _loop_1 (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackCompilerHandler.js:135:37)
          at WebpackCompilerHandler.iterateChunks (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackCompilerHandler.js:160:17)
          at /Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackCompilerHandler.js:50:31
          at fn (/Users/runner/work/extension-template/extension-template/myextension/node_modules/webpack/lib/Compilation.js:621:10)
      /Users/runner/work/extension-template/extension-template/myextension/node_modules/@jupyterlab/builder/lib/build-labextension.js:104
                  throw new Error(err.details);
                        ^
      
      Error: caused by plugins in Compilation.hooks.processAssets
      TypeError: Cannot read properties of undefined (reading 'trim')
          at WebpackModuleFileIterator.getActualFilename (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackModuleFileIterator.js:47:42)
          at WebpackModuleFileIterator.internalCallback (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackModuleFileIterator.js:23:35)
          at WebpackModuleFileIterator.iterateFiles (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackModuleFileIterator.js:10:9)
          at /Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/PluginChunkReadHandler.js:20:32
          at WebpackChunkModuleIterator.iterateModules (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackChunkModuleIterator.js:42:21)
          at PluginChunkReadHandler.processChunk (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/PluginChunkReadHandler.js:19:29)
          at _loop_1 (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackCompilerHandler.js:135:37)
          at WebpackCompilerHandler.iterateChunks (/Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackCompilerHandler.js:160:17)
          at /Users/runner/work/extension-template/extension-template/myextension/node_modules/license-webpack-plugin/dist/WebpackCompilerHandler.js:50:31
          at fn (/Users/runner/work/extension-template/extension-template/myextension/node_modules/webpack/lib/Compilation.js:621:10)
          at compilerCallback (/Users/runner/work/extension-template/extension-template/myextension/node_modules/@jupyterlab/builder/lib/build-labextension.js:104:19)
          at /Users/runner/work/extension-template/extension-template/myextension/node_modules/@jupyterlab/builder/lib/build-labextension.js:146:21
          at /Users/runner/work/extension-template/extension-template/myextension/node_modules/webpack/lib/MultiCompiler.js:702:5
          at /Users/runner/work/extension-template/extension-template/myextension/node_modules/neo-async/async.js:2830:7
          at done (/Users/runner/work/extension-template/extension-template/myextension/node_modules/neo-async/async.js:2865:11)
          at /Users/runner/work/extension-template/extension-template/myextension/node_modules/neo-async/async.js:2818:7
          at /Users/runner/work/extension-template/extension-template/myextension/node_modules/webpack/lib/errors/HookWebpackError.js:101:2
          at Hook.eval [as callAsync] (eval at create (/Users/runner/work/extension-template/extension-template/myextension/node_modules/tapable/lib/HookCodeFactory.js:31:10), <anonymous>:6:1)
          at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (/Users/runner/work/extension-template/extension-template/myextension/node_modules/tapable/lib/Hook.js:21:14)
          at Cache.shutdown (/Users/runner/work/extension-template/extension-template/myextension/node_modules/webpack/lib/Cache.js:179:23)

We are not confident enough to open the issue because probably we should just update license-webpack-plugin but leaving the note in case if someone finds it useful when searching for a similar traceback.

@alexander-akait
Copy link
Copy Markdown
Member

@krassowski very strange, should not, can you create a small reproducible test repo, we will investigate

@krassowski
Copy link
Copy Markdown

@alexander-akait
Copy link
Copy Markdown
Member

@krassowski I see the problem, shorty - due to this https://github.com/webpack/webpack/pull/20860/changes#diff-3cba45872480f13cbdfebad2df98d03726db44829f8dfa74d985b2e4b59af40aR57, to be honestly internal identifier is not a part of stable API, yes you can use them as a key in Map/Object and they will be the same in the same webpack version, but they can be different between versions, so yes, better to fix it on the plugin side

Feel free to feedback

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 this pull request may close these issues.

Module Federation shared module ids and runtime-chunk emission order both drift between builds.

4 participants