From 436fbfe24fd3810540561fd2d92c8f4173e9d22e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 9 Feb 2026 07:18:43 +0000 Subject: [PATCH 1/7] fix(runtime): resolve getInstance from global --- packages/runtime/__tests__/api.spec.ts | 23 +++++++- packages/runtime/src/index.ts | 75 ++++++++++++++++++-------- 2 files changed, 74 insertions(+), 24 deletions(-) diff --git a/packages/runtime/__tests__/api.spec.ts b/packages/runtime/__tests__/api.spec.ts index 8214cd6c945..dd493dc90a0 100644 --- a/packages/runtime/__tests__/api.spec.ts +++ b/packages/runtime/__tests__/api.spec.ts @@ -1,5 +1,5 @@ -import { describe, it, expect } from 'vitest'; -import { init } from '../src'; +import { describe, it, expect, vi } from 'vitest'; +import { init, getInstance, createInstance } from '../src'; // eslint-disable-next-line max-lines-per-function describe('api', () => { @@ -83,6 +83,25 @@ describe('api', () => { }); expect(FM3).not.toBe(FM4); }); + it('getInstance resolves the global instance after reset', async () => { + const FM = init({ + name: '@federation/get-instance-global', + remotes: [], + }); + + vi.resetModules(); + const { getInstance: getInstanceAfterReset } = await import('../src'); + expect(getInstanceAfterReset('@federation/get-instance-global')).toBe(FM); + }); + + it('createInstance updates getInstance', () => { + const FM = createInstance({ + name: '@federation/create-instance', + remotes: [], + }); + + expect(getInstance('@federation/create-instance')).toBe(FM); + }); it('alias check', () => { // 校验 alias 是否等于 remote.name 和 remote.alias 的前缀,如果是则报错 diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index d4253306f07..42b77ddb8da 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -1,6 +1,7 @@ import { ModuleFederation, type UserOptions, + CurrentGlobal, getGlobalFederationConstructor, setGlobalFederationInstance, assert, @@ -32,10 +33,35 @@ export function createInstance(options: UserOptions) { getGlobalFederationConstructor() || ModuleFederation; const instance = new ModuleFederationConstructor(options); setGlobalFederationInstance(instance); + FederationInstance = instance; return instance; } let FederationInstance: ModuleFederation | null = null; +function resolveFederationInstance( + name?: string, + version?: string, +): ModuleFederation | null { + if (FederationInstance) { + return FederationInstance; + } + + const globalInstance = getGlobalFederationInstance(name || '', version); + if (globalInstance) { + FederationInstance = globalInstance; + return globalInstance; + } + + if (!name && !version) { + const instances = CurrentGlobal.__FEDERATION__?.__INSTANCES__ || []; + if (instances.length === 1) { + FederationInstance = instances[0]; + return FederationInstance; + } + } + + return null; +} /** * @deprecated Use createInstance or getInstance instead */ @@ -58,67 +84,72 @@ export function init(options: UserOptions): ModuleFederation { export function loadRemote( ...args: Parameters ): Promise { - assert(FederationInstance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); - const loadRemote: typeof FederationInstance.loadRemote = - FederationInstance.loadRemote; + const instance = resolveFederationInstance(); + assert(instance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); + const loadRemote: typeof instance.loadRemote = instance.loadRemote; // eslint-disable-next-line prefer-spread - return loadRemote.apply(FederationInstance, args); + return loadRemote.apply(instance, args); } export function loadShare( ...args: Parameters ): Promise T | undefined)> { - assert(FederationInstance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); + const instance = resolveFederationInstance(); + assert(instance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); // eslint-disable-next-line prefer-spread - const loadShare: typeof FederationInstance.loadShare = - FederationInstance.loadShare; - return loadShare.apply(FederationInstance, args); + const loadShare: typeof instance.loadShare = instance.loadShare; + return loadShare.apply(instance, args); } export function loadShareSync( ...args: Parameters ): () => T | never { - assert(FederationInstance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); - const loadShareSync: typeof FederationInstance.loadShareSync = - FederationInstance.loadShareSync; + const instance = resolveFederationInstance(); + assert(instance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); + const loadShareSync: typeof instance.loadShareSync = + instance.loadShareSync; // eslint-disable-next-line prefer-spread - return loadShareSync.apply(FederationInstance, args); + return loadShareSync.apply(instance, args); } export function preloadRemote( ...args: Parameters ): ReturnType { - assert(FederationInstance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); + const instance = resolveFederationInstance(); + assert(instance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); // eslint-disable-next-line prefer-spread - return FederationInstance.preloadRemote.apply(FederationInstance, args); + return instance.preloadRemote.apply(instance, args); } export function registerRemotes( ...args: Parameters ): ReturnType { - assert(FederationInstance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); + const instance = resolveFederationInstance(); + assert(instance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); // eslint-disable-next-line prefer-spread - return FederationInstance.registerRemotes.apply(FederationInstance, args); + return instance.registerRemotes.apply(instance, args); } export function registerPlugins( ...args: Parameters ): ReturnType { - assert(FederationInstance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); + const instance = resolveFederationInstance(); + assert(instance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); // eslint-disable-next-line prefer-spread - return FederationInstance.registerPlugins.apply(FederationInstance, args); + return instance.registerPlugins.apply(instance, args); } -export function getInstance() { - return FederationInstance; +export function getInstance(name?: string, version?: string) { + return resolveFederationInstance(name, version); } export function registerShared( ...args: Parameters ): ReturnType { - assert(FederationInstance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); + const instance = resolveFederationInstance(); + assert(instance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); // eslint-disable-next-line prefer-spread - return FederationInstance.registerShared.apply(FederationInstance, args); + return instance.registerShared.apply(instance, args); } // Inject for debug From 81b7999422be68df47a5f7580b83b37013d280ac Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 9 Feb 2026 08:02:19 +0000 Subject: [PATCH 2/7] fix(runtime): resolve instance for router e2e --- .../router-remote1-2001/rsbuild.config.ts | 1 + .../router-remote1-2001/tsconfig.json | 1 + packages/runtime/src/index.ts | 47 +++++++++++++++---- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/apps/router-demo/router-remote1-2001/rsbuild.config.ts b/apps/router-demo/router-remote1-2001/rsbuild.config.ts index 0583fbeac60..a0480d313d6 100644 --- a/apps/router-demo/router-remote1-2001/rsbuild.config.ts +++ b/apps/router-demo/router-remote1-2001/rsbuild.config.ts @@ -36,6 +36,7 @@ export default defineConfig({ pluginReact(), pluginModuleFederation({ name: 'remote1', + dts: false, runtimePlugins: [ require.resolve('@module-federation/bridge-react/plugin'), ], diff --git a/apps/router-demo/router-remote1-2001/tsconfig.json b/apps/router-demo/router-remote1-2001/tsconfig.json index 77055e139c0..35ae0a9479f 100644 --- a/apps/router-demo/router-remote1-2001/tsconfig.json +++ b/apps/router-demo/router-remote1-2001/tsconfig.json @@ -10,6 +10,7 @@ "isolatedModules": true, "resolveJsonModule": true, "moduleResolution": "bundler", + "typeRoots": ["./node_modules/@types"], "useDefineForClassFields": true }, "include": ["src"] diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 42b77ddb8da..73dcab30c7a 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -3,6 +3,7 @@ import { type UserOptions, CurrentGlobal, getGlobalFederationConstructor, + matchRemoteWithNameAndExpose, setGlobalFederationInstance, assert, setGlobalFederationConstructor, @@ -12,7 +13,7 @@ import { getShortErrorMsg, RUNTIME_009, } from '@module-federation/error-codes'; -import { getGlobalFederationInstance } from './utils'; +import { getBuilderId, getGlobalFederationInstance } from './utils'; export { loadScript, @@ -41,15 +42,45 @@ let FederationInstance: ModuleFederation | null = null; function resolveFederationInstance( name?: string, version?: string, + remoteId?: string, ): ModuleFederation | null { - if (FederationInstance) { - return FederationInstance; + const buildId = getBuilderId(); + if (buildId) { + const buildInstance = getGlobalFederationInstance(name || '', version); + if (buildInstance) { + FederationInstance = buildInstance; + return buildInstance; + } } - const globalInstance = getGlobalFederationInstance(name || '', version); - if (globalInstance) { - FederationInstance = globalInstance; - return globalInstance; + if (name) { + const namedInstance = getGlobalFederationInstance(name, version); + if (namedInstance) { + FederationInstance = namedInstance; + return namedInstance; + } + } + + if (remoteId) { + const instances = CurrentGlobal.__FEDERATION__?.__INSTANCES__ || []; + const matchingInstance = instances.find((instance) => + matchRemoteWithNameAndExpose(instance.options.remotes, remoteId), + ); + if (matchingInstance) { + FederationInstance = matchingInstance; + return matchingInstance; + } + } + + if ( + FederationInstance && + (!remoteId || + matchRemoteWithNameAndExpose( + FederationInstance.options.remotes, + remoteId, + )) + ) { + return FederationInstance; } if (!name && !version) { @@ -84,7 +115,7 @@ export function init(options: UserOptions): ModuleFederation { export function loadRemote( ...args: Parameters ): Promise { - const instance = resolveFederationInstance(); + const instance = resolveFederationInstance(undefined, undefined, args[0]); assert(instance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); const loadRemote: typeof instance.loadRemote = instance.loadRemote; // eslint-disable-next-line prefer-spread From 81022e466dd8ecc9b5c2f10299291fc58d1cf43f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 9 Feb 2026 08:04:06 +0000 Subject: [PATCH 3/7] fix(router-remote1-2001): revert dts overrides --- apps/router-demo/router-remote1-2001/rsbuild.config.ts | 1 - apps/router-demo/router-remote1-2001/tsconfig.json | 1 - 2 files changed, 2 deletions(-) diff --git a/apps/router-demo/router-remote1-2001/rsbuild.config.ts b/apps/router-demo/router-remote1-2001/rsbuild.config.ts index a0480d313d6..0583fbeac60 100644 --- a/apps/router-demo/router-remote1-2001/rsbuild.config.ts +++ b/apps/router-demo/router-remote1-2001/rsbuild.config.ts @@ -36,7 +36,6 @@ export default defineConfig({ pluginReact(), pluginModuleFederation({ name: 'remote1', - dts: false, runtimePlugins: [ require.resolve('@module-federation/bridge-react/plugin'), ], diff --git a/apps/router-demo/router-remote1-2001/tsconfig.json b/apps/router-demo/router-remote1-2001/tsconfig.json index 35ae0a9479f..77055e139c0 100644 --- a/apps/router-demo/router-remote1-2001/tsconfig.json +++ b/apps/router-demo/router-remote1-2001/tsconfig.json @@ -10,7 +10,6 @@ "isolatedModules": true, "resolveJsonModule": true, "moduleResolution": "bundler", - "typeRoots": ["./node_modules/@types"], "useDefineForClassFields": true }, "include": ["src"] From 97d9a21cffad54f282cd6538cfd3164b58dcbd5a Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Wed, 11 Feb 2026 18:59:29 -0800 Subject: [PATCH 4/7] chore(core): add changeset coverage for pr #4396 --- .changeset/auto-pr-4396-coverage.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/auto-pr-4396-coverage.md diff --git a/.changeset/auto-pr-4396-coverage.md b/.changeset/auto-pr-4396-coverage.md new file mode 100644 index 00000000000..0291dfe6cb1 --- /dev/null +++ b/.changeset/auto-pr-4396-coverage.md @@ -0,0 +1,5 @@ +--- +'@module-federation/runtime': patch +--- + +Add contextual changeset coverage for packages modified in PR #4396. From db17875b416d747c2bcb2db1fa7b2a4b055be845 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Wed, 11 Feb 2026 19:11:06 -0800 Subject: [PATCH 5/7] chore(runtime): add contextual instance resolution changeset --- .changeset/auto-pr-4396-coverage.md | 5 ----- .changeset/ten-hounds-kick.md | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 .changeset/auto-pr-4396-coverage.md create mode 100644 .changeset/ten-hounds-kick.md diff --git a/.changeset/auto-pr-4396-coverage.md b/.changeset/auto-pr-4396-coverage.md deleted file mode 100644 index 0291dfe6cb1..00000000000 --- a/.changeset/auto-pr-4396-coverage.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@module-federation/runtime': patch ---- - -Add contextual changeset coverage for packages modified in PR #4396. diff --git a/.changeset/ten-hounds-kick.md b/.changeset/ten-hounds-kick.md new file mode 100644 index 00000000000..1a724edca27 --- /dev/null +++ b/.changeset/ten-hounds-kick.md @@ -0,0 +1,5 @@ +--- +'@module-federation/runtime': patch +--- + +Improve runtime instance resolution by selecting instances from global federation state (including remote-aware matching) and keeping `getInstance` stable after module resets. From 2c58b0cca04294903b457b7a3785256da7d67518 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Tue, 24 Feb 2026 15:11:44 -0800 Subject: [PATCH 6/7] fix(dts-plugin): align workspace entrypoints and RawSource typing Resolve the dts-plugin TYPE-001 failure by correcting package entry paths for workspace dependencies and updating RawSource usage for webpack typings. Co-authored-by: Cursor --- .../dts-plugin/src/plugins/GenerateTypesPlugin.ts | 6 ++---- packages/error-codes/package.json | 8 ++++---- packages/sdk/package.json | 12 ++++++------ 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/packages/dts-plugin/src/plugins/GenerateTypesPlugin.ts b/packages/dts-plugin/src/plugins/GenerateTypesPlugin.ts index a8b4aaeed5f..d103436ff60 100644 --- a/packages/dts-plugin/src/plugins/GenerateTypesPlugin.ts +++ b/packages/dts-plugin/src/plugins/GenerateTypesPlugin.ts @@ -172,8 +172,7 @@ export class GenerateTypesPlugin implements WebpackPluginInstance { compilation.emitAsset( zipName, new compiler.webpack.sources.RawSource( - fs.readFileSync(zipTypesPath), - false, + fs.readFileSync(zipTypesPath) as unknown as string, ), ); } @@ -186,8 +185,7 @@ export class GenerateTypesPlugin implements WebpackPluginInstance { compilation.emitAsset( apiFileName, new compiler.webpack.sources.RawSource( - fs.readFileSync(apiTypesPath), - false, + fs.readFileSync(apiTypesPath) as unknown as string, ), ); } diff --git a/packages/error-codes/package.json b/packages/error-codes/package.json index 126b794b07c..352010e14a9 100644 --- a/packages/error-codes/package.json +++ b/packages/error-codes/package.json @@ -25,14 +25,14 @@ "browser": { "url": false }, - "main": "./dist/index.cjs.js", - "module": "./dist/index.esm.mjs", + "main": "./dist/index.cjs", + "module": "./dist/index.mjs", "types": "./dist/index.d.ts", "exports": { ".": { "types": "./dist/index.d.ts", - "import": "./dist/index.esm.mjs", - "require": "./dist/index.cjs.js" + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" } }, "typesVersions": { diff --git a/packages/sdk/package.json b/packages/sdk/package.json index cc3f5bde8d4..85026bc4549 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -23,8 +23,8 @@ }, "author": "zhanghang ", "sideEffects": false, - "main": "./dist/index.cjs.cjs", - "module": "./dist/index.esm.js", + "main": "./dist/index.cjs", + "module": "./dist/index.js", "types": "./dist/index.d.ts", "browser": { "url": false @@ -33,21 +33,21 @@ ".": { "import": { "types": "./dist/index.d.ts", - "default": "./dist/index.esm.js" + "default": "./dist/index.js" }, "require": { "types": "./dist/index.d.ts", - "default": "./dist/index.cjs.cjs" + "default": "./dist/index.cjs" } }, "./normalize-webpack-path": { "import": { "types": "./dist/normalize-webpack-path.d.ts", - "default": "./dist/normalize-webpack-path.esm.js" + "default": "./dist/normalize-webpack-path.js" }, "require": { "types": "./dist/normalize-webpack-path.d.ts", - "default": "./dist/normalize-webpack-path.cjs.cjs" + "default": "./dist/normalize-webpack-path.cjs" } } }, From 30b18d24532de8c98f00985c3ed4971252a9e6cb Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Tue, 24 Feb 2026 19:15:58 -0800 Subject: [PATCH 7/7] fix(sdk): align package entrypoints with emitted artifacts Restore sdk and error-codes export paths to the filenames emitted by the current build so CI package resolution no longer fails on these branches. Co-authored-by: Cursor --- packages/error-codes/package.json | 8 ++++---- packages/sdk/package.json | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/error-codes/package.json b/packages/error-codes/package.json index 352010e14a9..126b794b07c 100644 --- a/packages/error-codes/package.json +++ b/packages/error-codes/package.json @@ -25,14 +25,14 @@ "browser": { "url": false }, - "main": "./dist/index.cjs", - "module": "./dist/index.mjs", + "main": "./dist/index.cjs.js", + "module": "./dist/index.esm.mjs", "types": "./dist/index.d.ts", "exports": { ".": { "types": "./dist/index.d.ts", - "import": "./dist/index.mjs", - "require": "./dist/index.cjs" + "import": "./dist/index.esm.mjs", + "require": "./dist/index.cjs.js" } }, "typesVersions": { diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 85026bc4549..cc3f5bde8d4 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -23,8 +23,8 @@ }, "author": "zhanghang ", "sideEffects": false, - "main": "./dist/index.cjs", - "module": "./dist/index.js", + "main": "./dist/index.cjs.cjs", + "module": "./dist/index.esm.js", "types": "./dist/index.d.ts", "browser": { "url": false @@ -33,21 +33,21 @@ ".": { "import": { "types": "./dist/index.d.ts", - "default": "./dist/index.js" + "default": "./dist/index.esm.js" }, "require": { "types": "./dist/index.d.ts", - "default": "./dist/index.cjs" + "default": "./dist/index.cjs.cjs" } }, "./normalize-webpack-path": { "import": { "types": "./dist/normalize-webpack-path.d.ts", - "default": "./dist/normalize-webpack-path.js" + "default": "./dist/normalize-webpack-path.esm.js" }, "require": { "types": "./dist/normalize-webpack-path.d.ts", - "default": "./dist/normalize-webpack-path.cjs" + "default": "./dist/normalize-webpack-path.cjs.cjs" } } },