feat!: compose the federation runtime by default and remove the capability defines - #5142
ScriptedAlchemy wants to merge 48 commits into
Conversation
Every ModuleFederationPlugin build now plans and renders the composed bootstrap. The full bootstrap remains only for an externalized or aliased runtime, where the composed imports cannot reach the external.
🦋 Changeset detectedLatest commit: db5159f 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. |
…eShared The composed plan keeps the remotes and share-scope adapters when remote modules and a container are in the graph, and drops the kernel's remote and shared handlers.
@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: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 36c7e8f1f3
ℹ️ 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".
| compilation.warnings.push( | ||
| new WebpackError( | ||
| `experiments.composedRuntime is set, but this build uses the full federation runtime because ${legacyReason}.`, | ||
| `This build uses the full federation runtime because ${legacyReason}.`, |
There was a problem hiding this comment.
Fail builds when the runtime cannot be composed
When an installed runtime family is too old and lacks a required composition export, selectMode returns a legacyReason, but this path only adds a compilation warning and successfully falls back to the full runtime. This defeats the stated compatibility boundary for the new major release and can let mismatched runtime packages reach production; non-external legacy reasons should be added to compilation.errors and identify the required minimum version.
Useful? React with 👍 / 👎.
| } | ||
| // DefinePlugin reads its definitions per compilation, after the plan picked a mode. | ||
| compiler.hooks.compile.tap('ModuleFederationPlugin', () => { | ||
| if (!composedEntryOf(compiler)) return; |
There was a problem hiding this comment.
Remove capability defines from full-runtime builds
When composition selects legacy mode for a supported externalized or aliased runtime, composedEntryOf(compiler) is undefined and this early return preserves all deprecated FEDERATION_* entries in DefinePlugin. Those builds therefore still rewrite these identifiers—and optimization flags can still alter the supposedly full bootstrap—even though this release says the defines are removed; retain only ENV_TARGET regardless of the selected mode.
Useful? React with 👍 / 👎.
| if (!onceForCompiler.has(compiler)) { | ||
| const options = this.options; | ||
| if (options?.experiments?.composedRuntime) { | ||
| if (options) { |
There was a problem hiding this comment.
Reject the removed composedRuntime option
With this unconditional options check, experiments.composedRuntime: false no longer disables composition, yet the SDK type and generated ModuleFederationPlugin schemas still advertise and accept that property. Existing configurations therefore silently receive the opposite behavior instead of being told that the option was removed; remove it from the source schema/type and regenerate the validators.
Useful? React with 👍 / 👎.
Remove the fallback to federation.runtime.loadScriptNode. A platform without a Node loader now fails the load with a named error.
The webpack-bundler-runtime root no longer carries the @module-federation/runtime namespace and calls init directly. createFederation drops the loadScriptNode shim, and the bootstrap guard only checks bundlerRuntime.
… capability and build-id defines The runtime-core root constructs ModuleFederation with the full capability set, Module is always the remote module, and the webpack-bundler-runtime root always includes its four adapters. FEDERATION_BUILD_IDENTIFIER no longer sets the default id or matches instances; the bundle's current instance handles reuse.
…ines ModuleFederationPlugin in enhanced and in the rspack wrapper now defines only ENV_TARGET, and only when experiments.optimization.target is set.
…oo old to compose selectMode reports an unsupported mode for a runtime family that does not export the composition subpaths. ModuleFederationPlugin turns it into a build error that names the minimum runtime version instead of falling back to the full runtime.
… rfc5128/07-composed-default-major # Conflicts: # packages/rspack/src/ModuleFederationPlugin.ts
The wrapper always plans and redirects the native bundler runtime to the composed entry, defines only ENV_TARGET, fails the build for a runtime family that is too old, and keeps the full runtime without a warning for an externalized runtime.
The composed runtime is the only path, so the option is gone from the sdk type and the enhanced schema. The schema allows unknown experiments, so a config that still sets it builds unchanged.
The runtime size guide now shows how to compose the runtime from subpath imports in a project without ModuleFederationPlugin, and disableSnapshot is described in terms of the generated bootstrap.
…ull runtime selectMode flags the legacy mode that experiments.externalRuntime or provideExternalRuntime asks for, so both plugins skip the warning from one field instead of re-reading the experiments. The older-family test drops @ts-nocheck and types its stats.
Removing the build-id define left options.id undefined for init-created instances. Keep the empty string that getBuilderId returned without the define.
… rfc5128/07-composed-default-major # Conflicts: # packages/enhanced/src/lib/container/ModuleFederationPlugin.ts # packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts # packages/node/src/runtimePlugin.ts # packages/runtime-core/__tests__/kernel.spec.ts # packages/runtime-core/src/index.ts # packages/runtime/src/utils.ts
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.
rspack's async startup runtime installs its consumes chunk handler without shared modules and calls bundlerRuntime.consumes, so a composed plan without the adapter crashed at startup.
The rspack wrapper aliases the native plugin's absolute webpack-bundler-runtime path to a virtual composed module. Rstest externalized that path, so Node tried to import the virtual file from disk.
… rfc5128/07-composed-default-major # Conflicts: # packages/enhanced/src/lib/container/runtime/FederationCompositionPlugin.ts # packages/rspack/__tests__/composedRuntime.spec.ts # packages/rspack/src/ComposedRuntimePlugin.ts
… rfc5128/07-composed-default-major # Conflicts: # packages/rspack/__tests__/composedRuntime.spec.ts # packages/rstest/src/index.test.ts # packages/runtime-core/__tests__/kernel.spec.ts # packages/runtime-core/src/index.ts # packages/runtime/src/utils.ts # packages/webpack-bundler-runtime/__tests__/compose.spec.ts # packages/webpack-bundler-runtime/src/compose.ts # packages/webpack-bundler-runtime/src/index.ts
… rfc5128/07-composed-default-major # Conflicts: # packages/runtime-core/__tests__/kernel.spec.ts # packages/runtime-core/src/core.ts
The wrapper stopped aliasing @module-federation/runtime$ in the composed runtime work; the deprecated helper is removed in this major. The resolution fallback tests now run against resolveRspackRuntimeImplementation.
A beforeInit plugin can set options.id, but the shared handler registered the global share scope in its constructor, under the id from the constructor options. Register it from the init lifecycle instead.
Without FEDERATION_BUILD_IDENTIFIER, a build that falls back to the full runtime had no id, so its share scope registered under the plain name and two versions of one container collided. The template now sets initOptions.id to name:version with the same rule as the composed bootstrap.
rspack's native runtime passes no id to init, so a build that falls back to the full runtime keyed its share scope by name. A runtime plugin now sets name:version in beforeInit; the composed bootstrap already passes the same id.
The error for a missing subpath export now says the installed runtime family lacks the subpath exports the build needs and names MIN_RUNTIME_VERSION only as the release that added them. A renamed or unresolvable runtime package keeps its own message without the version.
…shared The consumes markers are absent from the no-shared build again. They are also absent from the full build now, because no build in this case configures shared and the plan leaves out the consumes adapter.
rstest is in the fixed release group, so it takes the major without its own entry.
…efines The architecture docs and the webpack-bundler-runtime README no longer describe the capability defines, getBuilderId, or the federation.runtime namespace.
@rspack/core 0.7 through 1.4 have no experiments.VirtualModulesPlugin, so every such build fell back to the full runtime with a warning. The fallback stays for safety.
@rspack/core before 1.6 takes runtime plugin paths only, so the [path, params] form broke the playground's build. The plugin is now an inline data: module with the id in its source.
Native runtimes before 2.0.0-beta.1 (web-infra-dev/rspack cd402a1ef) initialize through federation.runtime.init, which this major removes, so hosts built with them fail at startup with "Cannot read properties of undefined (reading 'init')".
…l-runtime root
The native runtime of @rspack/core before 2.0.0-beta.1 copies the default export's keys onto __webpack_require__.federation and calls federation.runtime.init. The root keeps runtime: { init } for it. The runtime namespace, loadScriptNode, and the named runtime export stay removed.
federation.runtime.init is back for the rspack 1.x native runtime, so the peer range returns to every release with experiments.VirtualModulesPlugin.
… rfc5128/07-composed-default-major # Conflicts: # packages/rspack/__tests__/composedRuntime.spec.ts # packages/webpack-bundler-runtime/__tests__/compose.spec.ts # packages/webpack-bundler-runtime/src/compose.ts
… rfc5128/07-composed-default-major # Conflicts: # packages/rspack/src/ComposedRuntimePlugin.ts
… rfc5128/07-composed-default-major # Conflicts: # packages/node/src/plugins/webpackChunkUtilities.ts # packages/node/src/runtimePlugin.ts # packages/webpack-bundler-runtime/__tests__/compose.spec.ts # packages/webpack-bundler-runtime/src/compose.ts
The changeset lists the unresolvable and renamed runtime package errors next to the older family error and states 2.10.0 as the release that adds the subpath exports. MIN_RUNTIME_VERSION says it must match the released minor.
Loading a remote entry before the federation runtime initialized reported a missing Node platform. It now says there is no federation instance.
… rfc5128/07-composed-default-major # Conflicts: # packages/runtime-core/__tests__/kernel.spec.ts # packages/runtime-core/src/index.ts # packages/runtime/src/instance.ts
…ding removed defines
…d of reading removed defines" This reverts commit 9f596f0.
… rfc5128/07-composed-default-major # Conflicts: # packages/runtime-core/__tests__/kernel.spec.ts # packages/runtime-core/src/index.ts # packages/runtime/src/instance.ts
This is the next major. Do not merge into a minor release line.
Part of RFC #5128 (alternate proposal to #5036).
Stack: 7 of 7. Base: #5141 (
rfc5128/06-rspack-composed). Next: none.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
The composed federation runtime becomes the default for
@module-federation/enhancedand@module-federation/rspack. A composed build imports only the runtime parts it uses, so unused capabilities are absent from the module graph in every optimization mode. A build that cannot compose gets the define-free full-runtime bootstrap, with a warning unless it asked for an external runtime. A build now fails when its runtime packages cannot compose. That covers a runtime package that cannot be resolved, one that resolves under another name, and an older release without the subpath exports. The last error names the missing subpath and the release that adds the subpath exports (2.10.0,MIN_RUNTIME_VERSIONin@module-federation/managers).This PR removes the following:
experiments.composedRuntime, with its schema, type, and every reader.FEDERATION_OPTIMIZE_NO_SHARED,FEDERATION_OPTIMIZE_NO_REMOTE,FEDERATION_OPTIMIZE_NO_SNAPSHOT_PLUGIN,FEDERATION_HAS_EXPOSES, andFEDERATION_BUILD_IDENTIFIERdefines. The enhanced and rspackModuleFederationPluginno longer emit them. The runtime-core root (legacyCapabilitiesand theModuleswitch),runtime-core/utils/env.ts,getBuilderIdinruntime/utils.ts, the webpack-bundler-runtime root, and the runtime-core, runtime, and esbuildglobal.d.tsno longer read or declare them.@module-federation/runtimenamespace onfederation.runtime,federation.runtime.loadScriptNode, the namedruntimeexport of@module-federation/webpack-bundler-runtime, and the Node fallback tofederation.runtime.loadScriptNode.resolveRspackRuntimeAliasfrom@module-federation/rspack/plugin.This PR keeps the following:
federation.runtime = { init }on the composed object and on the full-runtime root. The native runtime of@rspack/corebefore 2.0.0-beta.1 copies the bundler runtime's default export onto__webpack_require__.federationand callsfederation.runtime.init(initOptions).ENV_TARGET(the RFC defers it),FEDERATION_ALLOW_NEW_FUNCTION, andFEDERATION_DEBUG.@module-federation/rspackaccepts@rspack/core^1.5.0 || ^2.0.0-0, every release withexperiments.VirtualModulesPlugin. An@rspack/corewithout it still gets the full-runtime bootstrap with a warning.These behaviors need a reviewer's judgment:
experiments.externalRuntime,provideExternalRuntime, runtime externals, and user aliases on a runtime package are not on the RFC's removal list, and the composed imports cannot reach an external runtime. These builds keep the full bootstrap, which now imports a define-free webpack-bundler-runtime root.selectModemarks the two experiments as a requested full runtime, so only the externals and alias cases warn.MIN_RUNTIME_VERSIONis2.10.0, the next minor after 2.9.1, where the stack's subpaths first ship. Set it to the real release at publish time if that differs.disableRemoteordisableShared, the plan keeps the remotes and share-scope adapters when remote modules or a container are in the graph. The legacy defines dropped them.configCases/container/experiments-optimizationasserts the new plan.name:versionwith one ModuleFederationPlugin, which the build-id define used to supply. The full-runtime template setsinitOptions.id. Rspack adds an inlinedata:runtime plugin that sets it inbeforeInit, because its native runtime passes no id and rspack 1.x has no runtime plugin params. The kernel registers the global share scope once options are final, so two versions of one container do not collide in__FEDERATION__.__SHARE__.@module-federation/runtime. Its CI build warns about it. The proof harness's async-node checks and the enhanced configCases cover composition on Node. node-host does not.instance.platform.loadScriptNode, thenfederation.runtimeshrinks toinit. No app, nextjs-mf, modernjs, rsbuild-plugin, esbuild, or bridge package reads the removed defines or the removedfederation.runtimemembers.Verification
federation.runtime.init(initOptions). It failed withTypeError: Cannot read properties of undefined (reading 'init')and now initializes an instance (legacy-root.spec.ts). The same copy-keys test oncreateFederationasserts thatruntimeholds onlyinit(compose.spec.ts).loadScriptNode, and with its own error when there is no federation instance. It never falls back tofederation.runtime. Runtime-core, runtime, and the webpack-bundler-runtime root ignore the removed defines. Enhanced and rspack define onlyENV_TARGET. An older runtime family fails the build with the minimum version, in enhanced and in rspack. Rspack composes by default.prove.mjs --composed --bundler bothat953ceeb52(rspack 2.1.10) passes 32 of 32.--composed --bundler rspack --rspack-core 1.7.9passes 16 of 16. Ata5cf4b0e5, beforeruntime.initcame back, it failed 6 of 16 withreading 'init'.--bundler rspack --rspack-core 1.3.15(noVirtualModulesPlugin, full-runtime root) passes 16 of 16.packages/playgroundbuilds with the rslibmfformat on@rspack/core1.5.8, composed. Itsdist/mf/remoteEntry.jsloads in headless Chromium, andinit({})andget('.')return the exposed module with no page errors.packages/modernjsbuilds, its CJS plugin factory and ESM root and runtime entries load, and its 22 tests pass.953ceeb52pass. Enhanced runs 484 plus 6 tree-shaking tests, webpack-bundler-runtime 130, rspack 26 (composed host and remote on@rspack/core1.5.8 and 1.7.9, and the graph-check errors), and node 59. The runtime-tools, esbuild, rsbuild-plugin, runtime, runtime-core, sdk, and managers suites are green.f6d2e8bbd. The full review at953ceeb52found all 8 earlier audit findings resolved and checked thename:versionbuild id on every fallback path in both bundlers. The delta tof6d2e8bbdchanged the changeset wording and routed the missing-instance error todone(). The live headdb5159fcfhas the same patch over its parent.db5159fcfwith 18 checks, which include every e2e job except e2e-treeshake. The affected-suite selector skipped that one. e2e-next-dev and e2e-next-prod cover 3000-home, e2e-node covers node-host, and e2e-manifest covers the externalRuntime apps.--webpackit resolves next 14.2.35 from the root and fails on#/ui/boundary.Deviations from the RFC
None.
Overlaps with open PRs
This branch contains the #5143 fix through #5135, the #5144 fix through #5140, and #5126 through a merge of its branch. This PR carries no code from other open PRs. The PRs below change files this PR also changes. This PR does not include their changes.
Federationtype with capability selectors. Only one of the two RFCs lands.runtime-core/src/remote/disabled.ts.runtime-core/src/core.ts.FederationRuntimePlugin.ts, which renders the runtime entry this PR makes the default. fix(enhanced): make the generated runtime entry deterministic and valid #5123 and fix(enhanced): resolve package names in runtimePlugins #4954 also change its unit test.packages/rspack/src/ModuleFederationPlugin.ts. fix(rspack,manifest): load dts-plugin lazily when dts is disabled #5132 also changes its spec. fix(managers,rspack,enhanced): resolve canonical share keys for relative imports (#5042) #5065 also changes the enhancedModuleFederationPlugin.tsandmanagers/src/index.ts.runtime-core/src/core.tsand the noderuntimePlugin.tsand its test. feat(node,runtime-core,sdk): track and dispose evaluation-time side effects (#5031) #5067 also changesruntime-core/src/index.ts.runtimePlugin.ts. fix(node,sdk): compile remote code without eval and outside V8's compilation cache #5030 and fix(node): report HTTP failures before evaluating remote scripts #4811 also change its test.ModuleFederationPluginschema files andsdk/src/types/plugins/ModuleFederationPlugin.ts.runtime-core/src/shared/index.ts.ClearCacheOptions.remoteInfoin webpack-bundler-runtimetypes.ts, a different hunk from theFederation.runtimetype here.website-new/docs/en/configure/experiments.mdx.