Repository navigation
fix(enhanced): give worker entrypoints the federation runtime entry - #5125
ScriptedAlchemy wants to merge 3 commits into
Conversation
🦋 Changeset detectedLatest commit: 68059ae The changes in this PR will be included in the next version bump. This PR includes changesets to release 48 packages
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 |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e0b365931e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
@module-federation/devtools
@module-federation/cli
create-module-federation
@module-federation/dts-plugin
@module-federation/enhanced
@module-federation/error-codes
@module-federation/esbuild
@module-federation/managers
@module-federation/manifest
@module-federation/metro
@module-federation/metro-plugin-rnc-cli
@module-federation/metro-plugin-rnef
@module-federation/metro-plugin-rock
@module-federation/modern-js
@module-federation/modern-js-v3
@module-federation/native-federation-tests
@module-federation/native-federation-typescript
@module-federation/nextjs-mf
@module-federation/node
@module-federation/observability-plugin
@module-federation/playground
@module-federation/retry-plugin
@module-federation/rsbuild-plugin
@module-federation/rspack
@module-federation/rspress-plugin
@module-federation/rstest
@module-federation/runtime
@module-federation/runtime-core
@module-federation/runtime-tools
@module-federation/sdk
@module-federation/storybook-addon
@module-federation/third-party-dts-extractor
@module-federation/treeshake-frontend
@module-federation/treeshake-server
@module-federation/typescript
@module-federation/utilities
@module-federation/webpack-bundler-runtime
@module-federation/bridge-react
@module-federation/bridge-react-webpack-plugin
@module-federation/bridge-shared
@module-federation/bridge-vue3
@module-federation/inject-external-runtime-core-plugin
commit: |
Bundle Size Report6 package(s) changed, 37 unchanged. Package dist + ESM entry
Bundle targets
Consumer scenarios
Total dist (raw): 23.41 MB (+1.8 kB (+0.0%)) Bundle sizes are generated with rslib (Rspack). Package-root metrics preserve the historical report. Tracked subpath exports such as |
A worker created with new Worker(new URL(...)) gets its own runtime chunk with no name. The case imports a remote from that worker and imports the internal container module from a second entry that never uses the remote.
The runtime entry is added with addInclude, which webpack only attaches to static entrypoints. A worker created with new Worker(new URL(...)) is an async entrypoint with an unnamed runtime chunk, so HoistContainerReferencesPlugin found no chunk in namedChunks and EmbedFederationRuntimeModule skipped the chunk. The worker bundle then had the federation runtime module but no bundlerRuntime.init, and its first remote load threw a TypeError. FederationRuntimePlugin now adds an AsyncEntrypointRuntimeDependency to every block with entryOptions from parser.hooks.finish. The entry becomes an entry module of the worker's entrypoint, so the chunk graph places it in the worker's runtime chunk and FlagDependencyUsagePlugin flags its exports for that runtime. Hoisting the entry into the runtime chunk at optimizeChunks is not enough: usage is computed earlier, and the hoisted modules had every export pruned in production mode. HoistContainerReferencesPlugin resolves runtime keys through the runtime chunks with forEachRuntime instead of namedChunks, and cleanUpChunks only removes a hoisted module from a chunk when every runtime of that chunk holds the module in its runtime chunk.
Modules that carry an async entrypoint block hold this dependency, so the persistent cache serializes it. Without a registered serializer webpack skips the module with "No serializer registered" and rebuilds it every run.
c673fd9 to
68059ae
Compare
Why
A worker created with
new Worker(new URL('./worker.js', import.meta.url))never received the federation runtime entry.compilation.addIncludeonly attaches the entry to static entrypoints,HoistContainerReferencesPluginlooked the worker's runtime chunk up innamedChunksby a hash key that no chunk carries, andEmbedFederationRuntimeModuleskipped any chunk without a name. The worker bundle had the federation runtime module but nobundlerRuntime.init, soimport('remote/x')from the worker threwCannot read properties of undefined (reading 'remotes').The fix gives each async entrypoint the runtime entry through webpack's own graph.
FederationRuntimePluginadds anAsyncEntrypointRuntimeDependencyto every block withentryOptionsfromparser.hooks.finish, so the entry becomes an entry module of the worker's entrypoint. The chunk graph places it in the worker's runtime chunk andFlagDependencyUsagePluginflags its exports for that runtime.HoistContainerReferencesPluginmaps runtime keys to runtime chunks withforEachRuntime, andcleanUpChunksremoves a hoisted module from a chunk only when every runtime of that chunk holds the module in its runtime chunk.Scope
AsyncEntrypointRuntimeDependency: newModuleDependencywithNullDependency.Template, registered inFederationRuntimePlugin.prependEntrynext to a parser tap that adds it to async entrypoint blocks. It is registered withmakeSerializable, since modules that carry an async entrypoint block hold it and the persistent cache serializes it; a unit test round-trips it through webpack's serializer.HoistContainerReferencesPlugin.hoistModulesInChunksandcleanUpChunks: runtime-key to runtime-chunk map, one hoist loop for the three dependency sets, conditional removal.EmbedFederationRuntimeModule.generate: nochunk.nameguard.test/configCases/container/worker-runtime.getAllReferencedModulesis untouched so fix(enhanced): skip inactive connections when hoisting runtime modules #5116 rebases cleanly.Tradeoffs
The smaller fix, hoisting the runtime entry into every runtime chunk at
optimizeChunkswith the existingruntimeChunksset, put the entry and its transitive modules into the worker chunk, but the worker bundle then threwClass extends value undefined is not a constructor or nullatvar SyncWaterfallHook = class extends syncHook/* SyncHook */.J.FlagDependencyUsagePluginruns beforeoptimizeChunksand only flags exports for the runtimes a module is reachable from, so every export of the hoisted@module-federation/runtime-coremodules was unused for the worker runtime and production mode pruned them. Adding the dependency at parse time lets webpack compute placement and usage itself.Blast Radius
Every
ModuleFederationPluginbuild that creates workers, shared workers, or other async entrypoints now bundles the runtime entry into each of those runtime chunks. Static entrypoints keep the same include and hoist path. Builds without async entrypoints are unchanged apart from the runtime-key lookup, which resolves the same named chunks as before.Verification
container worker-runtimefails onmainwith the TypeError above and passes with the fix.cleanUpChunkswith the old unconditional one fails the build withhelper.js has no id assigned, so the second test detects that defect.ConfigTestCases.basictestcontainer category: 29 passed.test/compiler-unit/container/HoistContainerReferencesPlugin.test.tsandtest/unit/container: 76 passed.pnpm --filter @module-federation/enhanced run test: 463 and 6 passed, 0 failed.packages/node,packages/rspack, andpackages/nextjs-mfchecked for the same named-chunk lookup.rspackandnextjs-mfcomparechunk.nameonly against the container's own entry name, andnodedoes not look chunks up by name.