Skip to content

fix(enhanced): give worker entrypoints the federation runtime entry - #5125

Open
ScriptedAlchemy wants to merge 3 commits into
mainfrom
fix/worker-federation-runtime
Open

ScriptedAlchemy wants to merge 3 commits into
mainfrom
fix/worker-federation-runtime

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

Why

A worker created with new Worker(new URL('./worker.js', import.meta.url)) never received the federation runtime entry. compilation.addInclude only attaches the entry to static entrypoints, HoistContainerReferencesPlugin looked the worker's runtime chunk up in namedChunks by a hash key that no chunk carries, and EmbedFederationRuntimeModule skipped any chunk without a name. The worker bundle had the federation runtime module but no bundlerRuntime.init, so import('remote/x') from the worker threw Cannot read properties of undefined (reading 'remotes').

The fix gives each async entrypoint the runtime entry through webpack's own graph. FederationRuntimePlugin adds an AsyncEntrypointRuntimeDependency to every block with entryOptions from parser.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 and FlagDependencyUsagePlugin flags its exports for that runtime. HoistContainerReferencesPlugin maps runtime keys to runtime chunks with forEachRuntime, and cleanUpChunks removes a hoisted module from a chunk only when every runtime of that chunk holds the module in its runtime chunk.

Scope

  • AsyncEntrypointRuntimeDependency: new ModuleDependency with NullDependency.Template, registered in FederationRuntimePlugin.prependEntry next to a parser tap that adds it to async entrypoint blocks. It is registered with makeSerializable, 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.hoistModulesInChunks and cleanUpChunks: runtime-key to runtime-chunk map, one hoist loop for the three dependency sets, conditional removal.
  • EmbedFederationRuntimeModule.generate: no chunk.name guard.
  • New config case test/configCases/container/worker-runtime.
  • getAllReferencedModules is 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 optimizeChunks with the existing runtimeChunks set, put the entry and its transitive modules into the worker chunk, but the worker bundle then threw Class extends value undefined is not a constructor or null at var SyncWaterfallHook = class extends syncHook/* SyncHook */.J. FlagDependencyUsagePlugin runs before optimizeChunks and only flags exports for the runtimes a module is reachable from, so every export of the hoisted @module-federation/runtime-core modules 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 ModuleFederationPlugin build 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-runtime fails on main with the TypeError above and passes with the fix.
  • Replacing the conditional removal in cleanUpChunks with the old unconditional one fails the build with helper.js has no id assigned, so the second test detects that defect.
  • ConfigTestCases.basictest container category: 29 passed.
  • test/compiler-unit/container/HoistContainerReferencesPlugin.test.ts and test/unit/container: 76 passed.
  • pnpm --filter @module-federation/enhanced run test: 463 and 6 passed, 0 failed.
  • packages/node, packages/rspack, and packages/nextjs-mf checked for the same named-chunk lookup. rspack and nextjs-mf compare chunk.name only against the container's own entry name, and node does not look chunks up by name.

@changeset-bot

changeset-bot Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 68059ae

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

This PR includes changesets to release 48 packages
Name Type
@module-federation/enhanced Patch
@module-federation/modern-js-v3 Patch
@module-federation/modern-js Patch
@module-federation/nextjs-mf Patch
@module-federation/node Patch
@module-federation/rsbuild-plugin Patch
@module-federation/rspress-plugin Patch
@module-federation/rstest Patch
@module-federation/storybook-addon Patch
shared-tree-shaking-no-server-host Patch
shared-tree-shaking-no-server-provider Patch
shared-tree-shaking-with-server-host Patch
shared-tree-shaking-with-server-provider Patch
node-dynamic-remote-new-version Patch
node-dynamic-remote Patch
@module-federation/playground Patch
remote5 Patch
remote6 Patch
website-new Patch
@module-federation/devtools Patch
@module-federation/cli Patch
create-module-federation Patch
@module-federation/dts-plugin Patch
@module-federation/error-codes Patch
@module-federation/managers Patch
@module-federation/manifest Patch
@module-federation/metro Patch
@module-federation/metro-plugin-rnc-cli Patch
@module-federation/metro-plugin-rnef Patch
@module-federation/metro-plugin-rock Patch
@module-federation/retry-plugin Patch
@module-federation/rspack Patch
@module-federation/runtime-core Patch
@module-federation/runtime-tools Patch
@module-federation/runtime Patch
@module-federation/sdk Patch
@module-federation/third-party-dts-extractor Patch
@module-federation/treeshake-frontend Patch
@module-federation/treeshake-server Patch
@module-federation/webpack-bundler-runtime Patch
@module-federation/bridge-react-webpack-plugin Patch
@module-federation/bridge-react Patch
@module-federation/bridge-shared Patch
@module-federation/bridge-vue3 Patch
@module-federation/inject-external-runtime-core-plugin Patch
@module-federation/esbuild Patch
@module-federation/observability-plugin Patch
@module-federation/utilities 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

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-25T01:07:21.588528Z e0b3659 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 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".

@pkg-pr-new

pkg-pr-new Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@module-federation/devtools

pnpm add https://pkg.pr.new/@module-federation/devtools@68059ae

@module-federation/cli

pnpm add https://pkg.pr.new/@module-federation/cli@68059ae

create-module-federation

pnpm add https://pkg.pr.new/create-module-federation@68059ae

@module-federation/dts-plugin

pnpm add https://pkg.pr.new/@module-federation/dts-plugin@68059ae

@module-federation/enhanced

pnpm add https://pkg.pr.new/@module-federation/enhanced@68059ae

@module-federation/error-codes

pnpm add https://pkg.pr.new/@module-federation/error-codes@68059ae

@module-federation/esbuild

pnpm add https://pkg.pr.new/@module-federation/esbuild@68059ae

@module-federation/managers

pnpm add https://pkg.pr.new/@module-federation/managers@68059ae

@module-federation/manifest

pnpm add https://pkg.pr.new/@module-federation/manifest@68059ae

@module-federation/metro

pnpm add https://pkg.pr.new/@module-federation/metro@68059ae

@module-federation/metro-plugin-rnc-cli

pnpm add https://pkg.pr.new/@module-federation/metro-plugin-rnc-cli@68059ae

@module-federation/metro-plugin-rnef

pnpm add https://pkg.pr.new/@module-federation/metro-plugin-rnef@68059ae

@module-federation/metro-plugin-rock

pnpm add https://pkg.pr.new/@module-federation/metro-plugin-rock@68059ae

@module-federation/modern-js

pnpm add https://pkg.pr.new/@module-federation/modern-js@68059ae

@module-federation/modern-js-v3

pnpm add https://pkg.pr.new/@module-federation/modern-js-v3@68059ae

@module-federation/native-federation-tests

pnpm add https://pkg.pr.new/@module-federation/native-federation-tests@68059ae

@module-federation/native-federation-typescript

pnpm add https://pkg.pr.new/@module-federation/native-federation-typescript@68059ae

@module-federation/nextjs-mf

pnpm add https://pkg.pr.new/@module-federation/nextjs-mf@68059ae

@module-federation/node

pnpm add https://pkg.pr.new/@module-federation/node@68059ae

@module-federation/observability-plugin

pnpm add https://pkg.pr.new/@module-federation/observability-plugin@68059ae

@module-federation/playground

pnpm add https://pkg.pr.new/@module-federation/playground@68059ae

@module-federation/retry-plugin

pnpm add https://pkg.pr.new/@module-federation/retry-plugin@68059ae

@module-federation/rsbuild-plugin

pnpm add https://pkg.pr.new/@module-federation/rsbuild-plugin@68059ae

@module-federation/rspack

pnpm add https://pkg.pr.new/@module-federation/rspack@68059ae

@module-federation/rspress-plugin

pnpm add https://pkg.pr.new/@module-federation/rspress-plugin@68059ae

@module-federation/rstest

pnpm add https://pkg.pr.new/@module-federation/rstest@68059ae

@module-federation/runtime

pnpm add https://pkg.pr.new/@module-federation/runtime@68059ae

@module-federation/runtime-core

pnpm add https://pkg.pr.new/@module-federation/runtime-core@68059ae

@module-federation/runtime-tools

pnpm add https://pkg.pr.new/@module-federation/runtime-tools@68059ae

@module-federation/sdk

pnpm add https://pkg.pr.new/@module-federation/sdk@68059ae

@module-federation/storybook-addon

pnpm add https://pkg.pr.new/@module-federation/storybook-addon@68059ae

@module-federation/third-party-dts-extractor

pnpm add https://pkg.pr.new/@module-federation/third-party-dts-extractor@68059ae

@module-federation/treeshake-frontend

pnpm add https://pkg.pr.new/@module-federation/treeshake-frontend@68059ae

@module-federation/treeshake-server

pnpm add https://pkg.pr.new/@module-federation/treeshake-server@68059ae

@module-federation/typescript

pnpm add https://pkg.pr.new/@module-federation/typescript@68059ae

@module-federation/utilities

pnpm add https://pkg.pr.new/@module-federation/utilities@68059ae

@module-federation/webpack-bundler-runtime

pnpm add https://pkg.pr.new/@module-federation/webpack-bundler-runtime@68059ae

@module-federation/bridge-react

pnpm add https://pkg.pr.new/@module-federation/bridge-react@68059ae

@module-federation/bridge-react-webpack-plugin

pnpm add https://pkg.pr.new/@module-federation/bridge-react-webpack-plugin@68059ae

@module-federation/bridge-shared

pnpm add https://pkg.pr.new/@module-federation/bridge-shared@68059ae

@module-federation/bridge-vue3

pnpm add https://pkg.pr.new/@module-federation/bridge-vue3@68059ae

@module-federation/inject-external-runtime-core-plugin

pnpm add https://pkg.pr.new/@module-federation/inject-external-runtime-core-plugin@68059ae

commit: 68059ae

@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Bundle Size Report

6 package(s) changed, 37 unchanged.

Package dist + ESM entry

Package Total dist (raw) Delta ESM gzip Delta
@module-federation/enhanced 819.5 kB +1.8 kB (+0.2%) 672 B no change

Bundle targets

Package Web bundle (gzip) Delta Node bundle (gzip) Delta
@module-federation/cli 2.3 kB -8 B (-0.3%) 2.4 kB -33 B (-1.3%)
@module-federation/core 1.0 kB -3 B (-0.3%) 1.0 kB -34 B (-3.1%)
@module-federation/devtools 30.3 kB -1 B (-0.0%) 30.3 kB -25 B (-0.1%)
@module-federation/enhanced 2.7 kB +1 B (+0.0%) 2.8 kB -44 B (-1.5%)
@module-federation/metro-plugin-rnc-cli 416 B no change 435 B -26 B (-5.6%)
@module-federation/node 9.1 kB -1 B (-0.0%) 9.2 kB -29 B (-0.3%)

Consumer scenarios

Scenario Web output (gzip) Delta Node output (gzip) Delta Gap (node-web) Delta
Enhanced remoteEntry 22.3 kB -15 B (-0.1%) 23.8 kB -15 B (-0.1%) +1.6 kB 0 B

Total dist (raw): 23.41 MB (+1.8 kB (+0.0%))
Total ESM gzip: 111.7 kB (no change)
Total web bundle (gzip): 253.4 kB (-12 B (-0.0%))
Total node bundle (gzip): 255.6 kB (-191 B (-0.1%))
Tracked ./bundler entry gzip: 563 B (no change)
Tracked ./bundler web bundle (gzip): 4.9 kB (no change)
Tracked ./bundler node bundle (gzip): 4.9 kB (no change)

Bundle sizes are generated with rslib (Rspack). Package-root metrics preserve the historical report. Tracked subpath exports such as ./bundler are measured separately so ENV_TARGET-driven tree-shaking is visible. Bare imports are externalized to keep package-level sizes consistent, and assets are emitted as resources.

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.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant