Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d092714
test(enhanced): assert the composed bootstrap for a host with remotes…
ScriptedAlchemy Sep 25, 2026
a3c075c
feat(enhanced): compose the runtime bootstrap behind experiments.comp…
ScriptedAlchemy Sep 25, 2026
c468fe5
test(enhanced): cover composed profiles, late and standalone particip…
ScriptedAlchemy Sep 25, 2026
b9cb0c2
fix(enhanced): register participants without reading compiler plugins
ScriptedAlchemy Sep 25, 2026
0a88a0b
refactor(enhanced): drop comments the composed-runtime names already …
ScriptedAlchemy Sep 25, 2026
499736f
Merge remote-tracking branch 'origin/rfc5128/04-managers-composition'…
ScriptedAlchemy Sep 25, 2026
f14543c
fix(enhanced): take optionsParticipant from managers and exempt only …
ScriptedAlchemy Sep 25, 2026
8845864
Merge remote-tracking branch 'origin/rfc5128/04-managers-composition'…
ScriptedAlchemy Sep 25, 2026
c23f4cd
chore(enhanced): untrack generated tree-shaking fixture packages
ScriptedAlchemy Sep 25, 2026
f1ae514
test(enhanced): cover both graph-check error rows in composed builds
ScriptedAlchemy Sep 25, 2026
c2df876
fix(enhanced): check the imported name of external modules in the gra…
ScriptedAlchemy Sep 25, 2026
6235133
test(enhanced): expect a warning when the composition plan never ran
ScriptedAlchemy Sep 25, 2026
367f765
fix(enhanced): warn when experiments.composedRuntime is set but the p…
ScriptedAlchemy Sep 25, 2026
2f12b64
refactor(enhanced): drop the unreachable SharedContainerPlugin check …
ScriptedAlchemy Sep 25, 2026
9bafa4e
Merge remote-tracking branch 'origin/rfc5128/04-managers-composition'…
ScriptedAlchemy Sep 25, 2026
317c038
refactor(enhanced): pass external import names as managers' externalU…
ScriptedAlchemy Sep 25, 2026
aa6e936
fix(enhanced): key cache serializers by install directory
ScriptedAlchemy Sep 25, 2026
c2aea32
test(enhanced): load the second enhanced copy without skipping serial…
ScriptedAlchemy Sep 25, 2026
dc85518
Merge remote-tracking branch 'origin/rfc5128/04-managers-composition'…
ScriptedAlchemy Sep 25, 2026
fbf42e6
Merge remote-tracking branch 'origin/rfc5128/04-managers-composition'…
ScriptedAlchemy Sep 25, 2026
07124c3
Merge remote-tracking branch 'origin/rfc5128/04-managers-composition'…
ScriptedAlchemy Sep 25, 2026
232dbe3
Merge remote-tracking branch 'origin/rfc5128/04-managers-composition'…
ScriptedAlchemy Sep 25, 2026
bd049b4
Merge remote-tracking branch 'origin/rfc5128/04-managers-composition'…
ScriptedAlchemy Sep 25, 2026
f2eb03c
Merge remote-tracking branch 'origin/rfc5128/04-managers-composition'…
ScriptedAlchemy Sep 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/composed-runtime-enhanced.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@module-federation/enhanced': minor
'@module-federation/sdk': minor
---

Add `experiments.composedRuntime` to `ModuleFederationPlugin`. When the installed runtime exports its capability subpaths, the plugin generates a federation bootstrap that imports only the runtime parts the build uses, and checks the module graph for federation modules whose adapter is missing. Builds without the flag are unchanged.
16 changes: 15 additions & 1 deletion packages/enhanced/src/lib/container/ContainerPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import type {
} from 'webpack';
import type { containerPlugin } from '@module-federation/sdk';
import FederationRuntimePlugin from './runtime/FederationRuntimePlugin';
import FederationCompositionPlugin, {
type CoveredByOptions,
} from './runtime/FederationCompositionPlugin';
import FederationModulesPlugin from './runtime/FederationModulesPlugin';
import FederationRuntimeDependency from './runtime/FederationRuntimeDependency';
import type { OptimizationSplitChunksCacheGroup } from 'webpack/lib/optimize/SplitChunksPlugin';
Expand All @@ -34,8 +37,13 @@ const PLUGIN_NAME = 'ContainerPlugin';
class ContainerPlugin {
_options: containerPlugin.ContainerPluginOptions;
name: string;
private _covered?: CoveredByOptions;

constructor(options: containerPlugin.ContainerPluginOptions) {
constructor(
options: containerPlugin.ContainerPluginOptions,
covered?: CoveredByOptions,
) {
this._covered = covered;
// validate(options);
this.name = PLUGIN_NAME;

Expand Down Expand Up @@ -164,6 +172,12 @@ class ContainerPlugin {
ContainerPlugin.patchChunkSplit(compiler, this._options.name);
}

if (!this._covered) {
FederationCompositionPlugin.register(compiler, {
kind: 'needs',
needs: ['container'],
});
}
const federationRuntimePluginInstance = new FederationRuntimePlugin();
federationRuntimePluginInstance.apply(compiler);

Expand Down
13 changes: 13 additions & 0 deletions packages/enhanced/src/lib/container/ContainerReferencePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import type {
moduleFederationPlugin,
} from '@module-federation/sdk';
import FederationRuntimePlugin from './runtime/FederationRuntimePlugin';
import FederationCompositionPlugin, {
type CoveredByOptions,
} from './runtime/FederationCompositionPlugin';
import FederationModulesPlugin from './runtime/FederationModulesPlugin';

const { ExternalsPlugin } = require(
Expand All @@ -30,9 +33,13 @@ class ContainerReferencePlugin {
private _remoteType: moduleFederationPlugin.ExternalsType;
private _remotes: [string, moduleFederationPlugin.RemotesConfig][];

private _covered?: CoveredByOptions;

constructor(
options: containerReferencePlugin.ContainerReferencePluginOptions,
covered?: CoveredByOptions,
) {
this._covered = covered;
// validate(options);

this._remoteType = options.remoteType;
Expand All @@ -58,6 +65,12 @@ class ContainerReferencePlugin {
*/
apply(compiler: Compiler): void {
const { _remotes: remotes, _remoteType: remoteType } = this;
if (!this._covered) {
FederationCompositionPlugin.register(compiler, {
kind: 'needs',
needs: ['remotes'],
});
}
new FederationRuntimePlugin().apply(compiler);
/** @type {Record<string, string>} */
const remoteExternals: Record<string, string> = {};
Expand Down
61 changes: 43 additions & 18 deletions packages/enhanced/src/lib/container/ModuleFederationPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ import SharePlugin from '../sharing/SharePlugin';
import ContainerPlugin from './ContainerPlugin';
import ContainerReferencePlugin from './ContainerReferencePlugin';
import FederationRuntimePlugin from './runtime/FederationRuntimePlugin';
import FederationCompositionPlugin, {
COVERED_BY_OPTIONS,
composedEntryOf,
optionsParticipant,
} from './runtime/FederationCompositionPlugin';
import { RemoteEntryPlugin } from '@module-federation/rspack/remote-entry-plugin';
import StartupChunkDependenciesPlugin from '../startup/MfStartupChunkDependenciesPlugin';
import FederationModulesPlugin from './runtime/FederationModulesPlugin';
Expand Down Expand Up @@ -155,6 +160,16 @@ class ModuleFederationPlugin implements WebpackPluginInstance {
// No inference for ENV_TARGET. If not manually set and valid, it's not defined.

new compiler.webpack.DefinePlugin(definePluginOptions).apply(compiler);

if (experiments?.composedRuntime) {
// DefinePlugin reads its definitions per compilation, after the plan picked a mode.
compiler.hooks.compile.tap('ModuleFederationPlugin', () => {
if (!composedEntryOf(compiler)) return;
for (const key of Object.keys(definePluginOptions)) {
if (key !== 'ENV_TARGET') delete definePluginOptions[key];
}
});
}
}

/**
Expand Down Expand Up @@ -200,6 +215,7 @@ class ModuleFederationPlugin implements WebpackPluginInstance {

// federation hooks
new FederationModulesPlugin().apply(compiler);
FederationCompositionPlugin.register(compiler, optionsParticipant(options));

if (experiments?.asyncStartup) {
new StartupChunkDependenciesPlugin({
Expand Down Expand Up @@ -251,36 +267,45 @@ class ModuleFederationPlugin implements WebpackPluginInstance {

compiler.hooks.afterPlugins.tap('ModuleFederationPlugin', () => {
if (useContainerPlugin) {
new ContainerPlugin({
name,
library,
filename: options.filename,
runtime: options.runtime,
shareScope: options.shareScope,
exposes: options.exposes!,
runtimePlugins: options.runtimePlugins,
}).apply(compiler);
new ContainerPlugin(
{
name,
library,
filename: options.filename,
runtime: options.runtime,
shareScope: options.shareScope,
exposes: options.exposes!,
runtimePlugins: options.runtimePlugins,
},
COVERED_BY_OPTIONS,
).apply(compiler);
}
if (
remotes &&
(Array.isArray(remotes)
? remotes.length > 0
: Object.keys(remotes).length > 0)
) {
new ContainerReferencePlugin({
remoteType: containerRemoteType,
shareScope,
remotes,
}).apply(compiler);
new ContainerReferencePlugin(
{
remoteType: containerRemoteType,
shareScope,
remotes,
},
COVERED_BY_OPTIONS,
).apply(compiler);
}
if (shared) {
new TreeShakingSharedPlugin({
mfConfig: options,
}).apply(compiler);
new SharePlugin({
shared,
shareScope,
}).apply(compiler);
new SharePlugin(
{
shared,
shareScope,
},
COVERED_BY_OPTIONS,
).apply(compiler);
}
});

Expand Down
Loading
Loading