Skip to content

refactor(runtime-core,sdk,runtime): split the kernel from its capabilities - #5135

Open
ScriptedAlchemy wants to merge 14 commits into
mainfrom
rfc5128/01-runtime-core-kernel
Open

ScriptedAlchemy wants to merge 14 commits into
mainfrom
rfc5128/01-runtime-core-kernel

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

Part of RFC #5128 (alternate proposal to #5036).
Stack: 1 of 7. Base: main. Next: #5137.
Landing order: #5135 → #5137 → #5139 → #5134 → #5140 → #5141 → #5142.
Companions on main: #5143 (disabled remote loadEntry), #5144 (enhanced duplicate serializer keys). #5126 (@rspack/core 2) is on main. PR 6 and 7 include it.

What changes

Importers of the @module-federation/runtime-core, @module-federation/runtime, and @module-federation/sdk roots see no change. Each package gains subpath entry points that let a build include only the runtime capabilities it uses. Later PRs in the stack use them to leave unused capabilities out of the module graph.

  • runtime-core/kernel exports FederationKernel. It takes the capabilities the caller passes (shared, remote, snapshot, platform) and builds disabled handlers for the rest. A missing platform rejects loads.
  • runtime/compose exports init and createInstance that take capabilities.
  • sdk/core and sdk/node split the sdk, so web builds no longer reach the Node loader through the sdk root.
  • The root ModuleFederation still composes the full set from the existing FEDERATION_OPTIMIZE_* defines and keeps its full handler types.

This PR only adds entry points. No bundler uses them until #5137 and #5140.

This PR also carries two runtime-core fixes. The disabled remote handler keeps the loadEntry hook, because shared tree-shaking fallback entries load through getRemoteEntry even when remotes are disabled. This is the same fix as #5143 on main. The shared handler registers the share scope under options.id when one is given, so a composed build keys its scope by the build id, as the build-id define did.

Verification

  • A shared tree-shaking fallback entry with remotes disabled threw Cannot read properties of undefined before the loadEntry fix and loads after it. Two tests in kernel.spec.ts cover it.
  • A FederationKernel built with id: 'kernel-share-id:1.0.0' registers its share scope under that id and not under the bare name. The test fails before the fix and passes after it.
  • A server platform in a browser-like environment resolves the SSR public path.
  • Kernel tests cover no capabilities, remote with a platform, a beforeInit plugin that injects remotes without a remote capability, and root composition from the defines. Compose tests cover bundle-local instance reuse ahead of the page-global lookup, and a set debug constructor is ignored. A type test reaches manifestLoading, registerRemote, and initTokens through the root class. The runtime-core, runtime, and sdk suites pass.
  • The legacy root's production main chunk changes against main by -5.4% (webpack ALL-OFF), +0.87% (webpack DEFAULT), +2.70% (rspack ALL-OFF), and +1.80% (rspack DEFAULT).
  • A build with the kernel, the remote capability, and the web platform has no shared, snapshot, sdk/node, platform/node, or root barrel modules in its chunks.
  • The end-to-end proof harness passes 32/32.
  • Independent verification passed at 7582cb458. The full review ran at 5177ef2b8. It proved the fixes, kept webpack within 1%, accepted the rspack size, confirmed that ESM builds without ./kernel never load it, and found the exports compatible. The fix(runtime-core): keep loadEntry hook when remotes are disabled #5143 port and the share-scope fix each passed a red/green delta review after it.
  • The live head b5733f471 adds a merge of main at 412f62e74. Its added and removed lines against main match the verified head exactly.
  • CI passes at b5733f471 with 24 checks. e2e-treeshake was skipped because the affected-suite selector did not pick it.
  • No bundler or app loads the new subpaths in this PR. feat(enhanced): compose the runtime bootstrap behind experiments.composedRuntime #5140 and feat(rspack): redirect the native bundler runtime to a composed entry #5141 test them end to end.

Deviations from the RFC

  • The root class and FederationKernel share a FederationCore base instead of the root extending the kernel. Running the kernel constructor would keep every disabled handler in builds that enable everything. The RFC now shows the shared base.
  • The legacy root on rspack grows +1.8% (DEFAULT) and +2.7% (ALL-OFF) against main, over the 1% budget. Rspack bundles the CommonJS runtime without tree shaking, so the split into platform modules adds module wrappers. Review accepted this size. The cost ends when a build opts into experiments.composedRuntime, or at the major. Webpack stays within 1%.
  • With remote and shared both disabled, the root composes no platform, because nothing in that build loads entries.
  • CommonJS requires of @module-federation/sdk/core and @module-federation/runtime-core/kernel resolve to the package root. CommonJS bundles cannot drop unused modules, so a second module graph only adds bytes.
  • getResourceUrl in the sdk takes an optional isBrowser argument, so runtime-core asks the platform.

Overlaps with open PRs

This PR carries the #5143 loadEntry fix in runtime-core/src/remote/disabled.ts. It carries no code from other open PRs. The PRs below change files this PR also changes. This PR does not include their changes.

@changeset-bot

changeset-bot Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b5733f4

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

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-25T13:37:08.885517Z 8fc7ade 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: 8fc7ade879

ℹ️ 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".

Comment thread packages/runtime/src/instance.ts Outdated
Comment on lines +28 to +31
const instance =
current && isMatchingInstance(current, name, version)
? current
: getGlobalFederationInstance(name, version);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Ignore cached current after a global reset

When resetFederationGlobalInfo() clears __INSTANCES__ and the caller subsequently initializes the same name/version, this branch reuses the stale module-local current instance instead of creating and registering a fresh one. The returned instance is therefore absent from CurrentGlobal.__FEDERATION__.__INSTANCES__, retains state from before the reset, and cannot be found through getInstance(finder); this also leaks state between existing runtime tests that reset globals after each case. Only reuse current if it is still registered globally, or clear it as part of the reset path.

AGENTS.md reference: AGENTS.md:L289-L289

Useful? React with 👍 / 👎.

Plugin hooks, globals, and helpers keep typing instances as ModuleFederation so module augmentation (bridge-react) still applies. Adds kernel and compose red-green tests.
@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@b5733f4

@module-federation/cli

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

create-module-federation

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

@module-federation/dts-plugin

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

@module-federation/enhanced

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

@module-federation/error-codes

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

@module-federation/esbuild

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

@module-federation/managers

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

@module-federation/manifest

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

@module-federation/metro

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

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

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

@module-federation/metro-plugin-rnef

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

@module-federation/metro-plugin-rock

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

@module-federation/modern-js

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

@module-federation/modern-js-v3

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

@module-federation/native-federation-tests

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

@module-federation/native-federation-typescript

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

@module-federation/nextjs-mf

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

@module-federation/node

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

@module-federation/observability-plugin

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

@module-federation/playground

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

@module-federation/retry-plugin

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

@module-federation/rsbuild-plugin

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

@module-federation/rspack

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

@module-federation/rspress-plugin

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

@module-federation/rstest

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

@module-federation/runtime

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

@module-federation/runtime-core

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

@module-federation/runtime-tools

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

@module-federation/sdk

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

@module-federation/storybook-addon

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

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

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

@module-federation/treeshake-frontend

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

@module-federation/treeshake-server

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

@module-federation/typescript

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

@module-federation/utilities

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

@module-federation/webpack-bundler-runtime

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

@module-federation/bridge-react

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

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

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

@module-federation/bridge-shared

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

@module-federation/bridge-vue3

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

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

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

commit: b5733f4

@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Bundle Size Report

15 package(s) changed, 28 unchanged.

Package dist + ESM entry

Package Total dist (raw) Delta ESM gzip Delta
@module-federation/bridge-react 281.8 kB -30043 B (-9.4%) 1.5 kB no change
@module-federation/bridge-vue3 194.0 kB +2.4 kB (+1.3%) 27.9 kB +533 B (+1.9%)
@module-federation/dts-plugin 337.4 kB +1.8 kB (+0.5%) 4.7 kB no change
@module-federation/inject-external-runtime-core-plugin 5.2 kB +26 B (+0.5%) 519 B no change
@module-federation/playground 16.01 MB -73506 B (-0.4%) 42.5 kB -3392 B (-7.2%)
@module-federation/runtime 23.4 kB +3.0 kB (+14.8%) 603 B -121 B (-16.7%)
@module-federation/runtime-core 327.2 kB +16.7 kB (+5.4%) 853 B +283 B (+49.6%)
@module-federation/runtime-tools 7.7 kB -65 B (-0.8%) 142 B no change
@module-federation/sdk 141.4 kB +12.5 kB (+9.7%) 784 B -1 B (-0.1%)

Bundle targets

Package Web bundle (gzip) Delta Node bundle (gzip) Delta
@module-federation/bridge-react 12.6 kB -960 B (-6.9%) 12.5 kB -1967 B (-13.4%)
@module-federation/bridge-vue3 20.6 kB +548 B (+2.7%) 21.5 kB +1.3 kB (+6.5%)
@module-federation/cli 2.3 kB -1 B (-0.0%) 2.4 kB -33 B (-1.3%)
@module-federation/core 1.0 kB -1 B (-0.1%) 1.0 kB -35 B (-3.2%)
@module-federation/devtools 30.3 kB no change 30.3 kB -25 B (-0.1%)
@module-federation/enhanced 2.7 kB +10 B (+0.4%) 2.8 kB -45 B (-1.6%)
@module-federation/metro-plugin-rnc-cli 416 B +1 B (+0.2%) 435 B -27 B (-5.8%)
@module-federation/node 9.1 kB +7 B (+0.1%) 9.2 kB -30 B (-0.3%)
@module-federation/playground 38.3 kB -3399 B (-8.0%) 38.3 kB -3399 B (-8.0%)
@module-federation/runtime 749 B +52 B (+7.5%) 749 B +52 B (+7.5%)
@module-federation/runtime-core 16.6 kB +445 B (+2.7%) 16.2 kB +413 B (+2.6%)
@module-federation/sdk 4.5 kB +2 B (+0.0%) 5.9 kB +6 B (+0.1%)

Tree-shakable entrypoints

Package Export Entry gzip Delta Web bundle (gzip) Delta Node bundle (gzip) Delta Gap (node-web) Delta
@module-federation/runtime ./bundler 184 B no change 749 B +52 B (+7.5%) 749 B +52 B (+7.5%) 0 B 0 B

Consumer scenarios

Scenario Web output (gzip) Delta Node output (gzip) Delta Gap (node-web) Delta
Enhanced remoteEntry 22.5 kB +205 B (+0.9%) 25.0 kB +1.1 kB (+4.8%) +2.5 kB +959 B

Total dist (raw): 23.34 MB (-66332 B (-0.3%))
Total ESM gzip: 109.0 kB (-2698 B (-2.4%))
Total web bundle (gzip): 250.1 kB (-3296 B (-1.3%))
Total node bundle (gzip): 252.1 kB (-3758 B (-1.4%))
Tracked ./bundler entry gzip: 563 B (no change)
Tracked ./bundler web bundle (gzip): 4.9 kB (+52 B (+1.0%))
Tracked ./bundler node bundle (gzip): 4.9 kB (+52 B (+1.0%))

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.

The root ModuleFederation and FederationKernel now share a FederationCore base that takes resolved capabilities. The kernel fills missing capabilities with disabled handlers; the root picks enabled or disabled capabilities from the defines, so bundlers fold the unused side away. With remote and shared both disabled the root composes no platform, since nothing loads entries.
RemoteHandler only needs getGlobalShareScope, which lived in utils/share and pulled share and semver into builds without the shared capability. It moves to global.ts; utils/share re-exports it for existing callers.
ModuleFederation types its handlers as the RemoteHandler, SharedHandler, and SnapshotHandler classes again, as on main. Only FederationKernel narrows them to the handler contracts, and the contracts shrink to what the disabled handlers implement on main. A type test compiles a fixture that reaches manifestLoading, registerRemote, and initTokens through the root class.
rspack's native plugin bundles the CommonJS builds without tree shaking, so every module the root touches ships. CommonJS consumers of sdk ./core and runtime-core ./kernel now load the package root, which rspack already bundles. The shared, remote, and snapshot capabilities live next to their handlers, the kernel class and the unavailable platform live in core, and runtime's instance lookup joins instance.ts.
getResourceUrl took the browser decision from the environment, while the snapshot and preload plugins next to it already asked the platform. It now takes an optional isBrowser argument, defaulting to isBrowserEnv(), and runtime-core passes origin.platform.isBrowser(). A server platform in a browser-like environment no longer joins its SSR entry to the browser public path.
getRemoteEntry emits remoteHandler.hooks.lifecycle.loadEntry for every entry, including shared tree-shaking fallbacks that load without remotes. The disabled remote handler had no hooks, so those loads threw "Cannot read properties of undefined". Same fix as #5143 on main.
The composed bootstrap passes the build id as options.id, but the shared handler registers the global share scope in the constructor, before options are merged. Take the default id from the user options so the scope is keyed by the build id, as the build-id define did.

(cherry picked from commit 8bdf981)
… capabilities

Each instance records runtimeCapabilities in the format main uses (#5145): remote, shared, snapshot, and the platform target. Public init and runtime/compose init skip page-global instances whose string differs from the caller's, and public createInstance constructs its own ModuleFederation class instead of __DEBUG_CONSTRUCTOR__.
…re-kernel

# Conflicts:
#	packages/runtime-core/src/remote/index.ts

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant