From 94faa2dde619ee1434ba215697d7f768cc2f1245 Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Mon, 17 Mar 2025 19:29:28 +0800 Subject: [PATCH 1/6] fix(enhanced): use default runtime export --- .changeset/chatty-months-share.md | 5 +++++ .../lib/container/runtime/FederationRuntimePlugin.ts | 12 +++--------- 2 files changed, 8 insertions(+), 9 deletions(-) create mode 100644 .changeset/chatty-months-share.md diff --git a/.changeset/chatty-months-share.md b/.changeset/chatty-months-share.md new file mode 100644 index 00000000000..6cae67540b3 --- /dev/null +++ b/.changeset/chatty-months-share.md @@ -0,0 +1,5 @@ +--- +'@module-federation/enhanced': patch +--- + +fix(enhanced): use default runtime export diff --git a/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts b/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts index 3ad99431bb4..8dd648b12b1 100644 --- a/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts +++ b/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts @@ -47,12 +47,6 @@ const BundlerRuntimePath = require.resolve( const RuntimePath = require.resolve('@module-federation/runtime', { paths: [RuntimeToolsPath], }); -const EmbeddedRuntimePath = require.resolve( - '@module-federation/runtime/embedded', - { - paths: [RuntimeToolsPath], - }, -); const federationGlobal = getFederationGlobalScope(RuntimeGlobals); @@ -331,11 +325,11 @@ class FederationRuntimePlugin { } setRuntimeAlias(compiler: Compiler) { - const { experiments, implementation } = this.options || {}; - let runtimePath = EmbeddedRuntimePath; + const { implementation } = this.options || {}; + let runtimePath = RuntimePath; if (implementation) { - runtimePath = require.resolve(`@module-federation/runtime/embedded`, { + runtimePath = require.resolve(`@module-federation/runtime`, { paths: [implementation], }); } From 506274255fd0f3cc27cd1fd8c2fced7e8f8789ee Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Mon, 17 Mar 2025 20:07:46 +0800 Subject: [PATCH 2/6] chore: use esm runtime --- .../runtime/FederationRuntimePlugin.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts b/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts index 8dd648b12b1..4a330731fa0 100644 --- a/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts +++ b/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts @@ -44,9 +44,12 @@ const BundlerRuntimePath = require.resolve( paths: [RuntimeToolsPath], }, ); -const RuntimePath = require.resolve('@module-federation/runtime', { - paths: [RuntimeToolsPath], -}); +const RuntimePath = require.resolve( + '@module-federation/runtime/dist/index.esm.mjs', + { + paths: [RuntimeToolsPath], + }, +); const federationGlobal = getFederationGlobalScope(RuntimeGlobals); @@ -329,9 +332,12 @@ class FederationRuntimePlugin { let runtimePath = RuntimePath; if (implementation) { - runtimePath = require.resolve(`@module-federation/runtime`, { - paths: [implementation], - }); + runtimePath = require.resolve( + `@module-federation/runtime/dist/index.esm.mjs`, + { + paths: [implementation], + }, + ); } const alias: any = compiler.options.resolve.alias || {}; From c18c58f6fe6cd022e0ee59e30b6a49be735f220c Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Mon, 17 Mar 2025 20:14:58 +0800 Subject: [PATCH 3/6] chore: revert --- .../runtime/FederationRuntimePlugin.ts | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts b/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts index 4a330731fa0..8dd648b12b1 100644 --- a/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts +++ b/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts @@ -44,12 +44,9 @@ const BundlerRuntimePath = require.resolve( paths: [RuntimeToolsPath], }, ); -const RuntimePath = require.resolve( - '@module-federation/runtime/dist/index.esm.mjs', - { - paths: [RuntimeToolsPath], - }, -); +const RuntimePath = require.resolve('@module-federation/runtime', { + paths: [RuntimeToolsPath], +}); const federationGlobal = getFederationGlobalScope(RuntimeGlobals); @@ -332,12 +329,9 @@ class FederationRuntimePlugin { let runtimePath = RuntimePath; if (implementation) { - runtimePath = require.resolve( - `@module-federation/runtime/dist/index.esm.mjs`, - { - paths: [implementation], - }, - ); + runtimePath = require.resolve(`@module-federation/runtime`, { + paths: [implementation], + }); } const alias: any = compiler.options.resolve.alias || {}; From 0189ca261df6210abe319c03c110ce98c2d3210d Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Mon, 17 Mar 2025 20:25:50 +0800 Subject: [PATCH 4/6] chore: use alias runtime path first --- .../container/runtime/FederationRuntimePlugin.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts b/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts index 8dd648b12b1..f349c6a415e 100644 --- a/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts +++ b/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts @@ -327,14 +327,18 @@ class FederationRuntimePlugin { setRuntimeAlias(compiler: Compiler) { const { implementation } = this.options || {}; let runtimePath = RuntimePath; + const alias: any = compiler.options.resolve.alias || {}; - if (implementation) { - runtimePath = require.resolve(`@module-federation/runtime`, { - paths: [implementation], - }); + if (alias['@module-federation/runtime$']) { + runtimePath = alias['@module-federation/runtime$']; + } else { + if (implementation) { + runtimePath = require.resolve(`@module-federation/runtime`, { + paths: [implementation], + }); + } } - const alias: any = compiler.options.resolve.alias || {}; alias['@module-federation/runtime$'] = alias['@module-federation/runtime$'] || runtimePath; alias['@module-federation/runtime-tools$'] = From 128ecf459e53fabe68da3b1ccf8e0c2d153cf135 Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Tue, 18 Mar 2025 10:52:08 +0800 Subject: [PATCH 5/6] chore: use specify alias --- .../lib/container/runtime/FederationRuntimePlugin.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts b/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts index f349c6a415e..2ea4110b7f2 100644 --- a/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts +++ b/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts @@ -324,7 +324,7 @@ class FederationRuntimePlugin { ); } - setRuntimeAlias(compiler: Compiler) { + getRuntimeAlias(compiler: Compiler) { const { implementation } = this.options || {}; let runtimePath = RuntimePath; const alias: any = compiler.options.resolve.alias || {}; @@ -339,6 +339,13 @@ class FederationRuntimePlugin { } } + return runtimePath; + } + + setRuntimeAlias(compiler: Compiler) { + const { implementation } = this.options || {}; + const alias: any = compiler.options.resolve.alias || {}; + const runtimePath = this.getRuntimeAlias(compiler); alias['@module-federation/runtime$'] = alias['@module-federation/runtime$'] || runtimePath; alias['@module-federation/runtime-tools$'] = @@ -406,11 +413,12 @@ class FederationRuntimePlugin { new HoistContainerReferences().apply(compiler); + const runtimePath = this.getRuntimeAlias(compiler); new compiler.webpack.NormalModuleReplacementPlugin( /@module-federation\/runtime/, (resolveData) => { if (/webpack-bundler-runtime/.test(resolveData.contextInfo.issuer)) { - resolveData.request = RuntimePath; + resolveData.request = runtimePath; if (resolveData.createData) { resolveData.createData.request = resolveData.request; From cf3fb027d2d52ce592d4dd480029b44cef90d8c7 Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Tue, 18 Mar 2025 13:54:10 +0800 Subject: [PATCH 6/6] chore: remove embeded file --- .../src/plugins/NextFederationPlugin/index.ts | 7 - packages/runtime/__tests__/sync.spec.ts | 737 ------------------ packages/runtime/package.json | 5 - packages/runtime/project.json | 3 +- packages/runtime/rollup.config.js | 1 - packages/runtime/src/embedded.ts | 366 --------- 6 files changed, 1 insertion(+), 1118 deletions(-) delete mode 100644 packages/runtime/__tests__/sync.spec.ts delete mode 100644 packages/runtime/src/embedded.ts diff --git a/packages/nextjs-mf/src/plugins/NextFederationPlugin/index.ts b/packages/nextjs-mf/src/plugins/NextFederationPlugin/index.ts index 950cb70abe5..41503540289 100644 --- a/packages/nextjs-mf/src/plugins/NextFederationPlugin/index.ts +++ b/packages/nextjs-mf/src/plugins/NextFederationPlugin/index.ts @@ -100,13 +100,6 @@ export class NextFederationPlugin { (message) => /your target environment does not appear/.test(message), ]; } - compiler.hooks.afterPlugins.tap('PatchAliasWebpackPlugin', () => { - compiler.options.resolve.alias = { - ...compiler.options.resolve.alias, - //useing embedded runtime - // '@module-federation/runtime$': runtimeESMPath, - }; - }); } private validateOptions(compiler: Compiler): boolean { diff --git a/packages/runtime/__tests__/sync.spec.ts b/packages/runtime/__tests__/sync.spec.ts deleted file mode 100644 index 3e263550da5..00000000000 --- a/packages/runtime/__tests__/sync.spec.ts +++ /dev/null @@ -1,737 +0,0 @@ -import { describe, it, expect, beforeAll, afterAll, assert } from 'vitest'; -import { matchRemoteWithNameAndExpose } from '@module-federation/runtime-core'; -import { - addGlobalSnapshot, - getGlobalSnapshot, - Global, - setGlobalFederationConstructor, -} from '@module-federation/runtime-core'; - -import { requestList } from './mock/env'; - -// Helper function to check if a method is private -function isPrivate(methodName: string): boolean { - return methodName.startsWith('_'); -} - -describe('Embed Module Proxy', async () => { - // Dynamically import the index module - const Index = await import('../src/index'); - - beforeAll(async () => { - // Mock the global __webpack_require__ to provide the runtime - //@ts-ignore - globalThis.__webpack_require__ = { - federation: { - runtime: Index, - }, - }; - }); - - afterAll(async () => { - // Clean up the global __webpack_require__ mock - //@ts-ignore - delete globalThis.__webpack_require__; - }); - - // Dynamically import the embedded module - const Embedded = await import('../src/embedded'); - describe('Api Sync', () => { - it('should have the same exports in embedded.ts and index.ts', () => { - // Compare the exports of embedded.ts and index.ts - const embeddedExports = Object.keys(Embedded).sort(); - const indexExports = Object.keys(Index).sort(); - expect(embeddedExports).toEqual(indexExports); - }); - - it('FederationHost class should have the same methods in embedded.ts and index.ts', () => { - // Create instances of FederationHost from both embedded.ts and index.ts - const embeddedHost = new Embedded.FederationHost({ - name: '@federation/test', - remotes: [], - }); - const indexHost = new Index.FederationHost({ - name: '@federation/test', - remotes: [], - }); - - // Get the method names of FederationHost instances, excluding private methods - const embeddedMethods = Object.getOwnPropertyNames( - Object.getPrototypeOf(embeddedHost), - ) - .filter( - (prop) => - typeof embeddedHost[prop] === 'function' && !isPrivate(prop), - ) - .sort(); - const indexMethods = Object.getOwnPropertyNames( - Object.getPrototypeOf(indexHost), - ) - .filter( - (prop) => typeof indexHost[prop] === 'function' && !isPrivate(prop), - ) - .sort(); - - // Compare the method names - expect(embeddedMethods).toEqual(indexMethods); - }); - - it('Module class should have the same methods in embedded.ts and index.ts', () => { - // Create instances of Module from both embedded.ts and index.ts - const embeddedModule = new Embedded.Module({ - remoteInfo: { - name: '@federation/test', - entry: '', - type: '', - entryGlobalName: '', - shareScope: '', - }, - host: new Embedded.FederationHost({ - name: '@federation/test', - remotes: [], - }), - }); - const indexModule = new Index.Module({ - remoteInfo: { - name: '@federation/test', - entry: '', - type: '', - entryGlobalName: '', - shareScope: '', - }, - host: new Index.FederationHost({ - name: '@federation/test', - remotes: [], - }), - }); - - // Get the method names of Module instances, excluding private methods - const embeddedMethods = Object.getOwnPropertyNames( - Object.getPrototypeOf(embeddedModule), - ) - .filter( - (prop) => - typeof embeddedModule[prop] === 'function' && !isPrivate(prop), - ) - .sort(); - const indexMethods = Object.getOwnPropertyNames( - Object.getPrototypeOf(indexModule), - ) - .filter( - (prop) => typeof indexModule[prop] === 'function' && !isPrivate(prop), - ) - .sort(); - // Compare the method names - expect(embeddedMethods).toEqual(indexMethods); - }); - }); - describe('General API Tests', () => { - describe('matchRemote', () => { - it('match default export with pkgName', () => { - const matchInfo = matchRemoteWithNameAndExpose( - [ - { - name: '@federation/matchRemote', - version: '1.0.0', - }, - { - name: '@federation/matchRemote2', - version: '1.0.0', - }, - ], - '@federation/matchRemote', - ); - assert(matchInfo, 'matchRemote should return a matchInfo'); - const { expose, remote } = matchInfo; - expect(expose).toBe('.'); - expect(remote).toMatchObject({ - name: '@federation/matchRemote', - version: '1.0.0', - }); - }); - - it('match default export with alias', () => { - const matchInfo = matchRemoteWithNameAndExpose( - [ - { - name: '@federation/matchRemote', - version: '1.0.0', - alias: 'hello', - }, - { - name: '@federation/matchRemote2', - version: '1.0.0', - }, - ], - 'hello', - ); - assert(matchInfo, 'matchRemote should return a matchInfo'); - const { expose, remote } = matchInfo; - expect(expose).toBe('.'); - expect(remote).toMatchObject({ - name: '@federation/matchRemote', - version: '1.0.0', - alias: 'hello', - }); - }); - - it('match pkgName', () => { - const matchInfo = matchRemoteWithNameAndExpose( - [ - { - name: '@federation/matchRemote', - version: '1.0.0', - }, - { - name: '@federation/matchRemote2', - version: '1.0.0', - }, - ], - '@federation/matchRemote/util', - ); - assert(matchInfo, 'matchRemote should return a matchInfo'); - const { expose, remote } = matchInfo; - expect(expose).toBe('./util'); - expect(remote).toMatchObject({ - name: '@federation/matchRemote', - version: '1.0.0', - }); - }); - - it('match alias', () => { - const matchInfo = matchRemoteWithNameAndExpose( - [ - { - name: '@federation/matchRemote', - version: '1.0.0', - }, - { - name: '@federation/matchRemote2', - alias: '@matchRemote2', - version: '1.0.0', - }, - ], - '@matchRemote2/utils/add', - ); - assert(matchInfo, 'matchRemote should return a matchInfo'); - const { expose, remote } = matchInfo; - expect(expose).toBe('./utils/add'); - expect(remote).toMatchObject({ - name: '@federation/matchRemote2', - alias: '@matchRemote2', - version: '1.0.0', - }); - }); - }); - - // eslint-disable-next-line max-lines-per-function - describe('loadRemote', () => { - it('api', () => { - const FederationInstance = new Embedded.FederationHost({ - name: '@federation-test/loadRemote-api', - remotes: [], - }); - expect(FederationInstance.loadRemote).toBeInstanceOf(Function); - }); - - it('loadRemote from global', async () => { - const reset = addGlobalSnapshot({ - '@federation-test/globalinfo': { - globalName: '', - buildVersion: '', - publicPath: '', - remoteTypes: '', - shared: [], - remoteEntry: '', - remoteEntryType: 'global', - modules: [], - version: '0.0.1', - remotesInfo: { - '@federation-test/app2': { - matchedVersion: '0.0.1', - }, - }, - }, - '@federation-test/app2:0.0.1': { - globalName: '', - publicPath: '', - remoteTypes: '', - shared: [], - buildVersion: 'custom', - remotesInfo: {}, - remoteEntryType: 'global', - modules: [], - version: '0.0.1', - remoteEntry: - 'http://localhost:1111/resources/app2/federation-remote-entry.js', - }, - }); - - const FederationInstance = new Embedded.FederationHost({ - name: '@federation-test/globalinfo', - remotes: [ - { - name: '@federation-test/app2', - version: '*', - }, - ], - }); - - const module = await FederationInstance.loadRemote<() => string>( - '@federation-test/app2/say', - ); - assert(module, 'module should be a function'); - expect(module()).toBe('hello app2'); - reset(); - }); - - it('loadRemote from global without hostSnapshot', async () => { - const reset = addGlobalSnapshot({ - '@load-remote/app1': { - globalName: `__FEDERATION_${'@load-remote/app1:custom'}__`, - publicPath: 'http://localhost:1111/resources/load-remote/app1/', - remoteTypes: '', - shared: [], - buildVersion: 'custom', - remotesInfo: {}, - remoteEntryType: 'global', - modules: [], - version: '0.0.1', - remoteEntry: 'federation-remote-entry.js', - }, - '@load-remote/app2:0.0.1': { - globalName: '', - publicPath: 'http://localhost:1111/resources/load-remote/app2/', - remoteTypes: '', - shared: [], - buildVersion: 'custom', - remotesInfo: {}, - remoteEntryType: 'global', - modules: [], - version: '0.0.1', - remoteEntry: 'federation-remote-entry.js', - }, - }); - - const FM = new Embedded.FederationHost({ - name: 'xxxxx', - remotes: [ - { - name: '@load-remote/app2', - version: '0.0.1', - }, - { - name: '@load-remote/app1', - version: '0.0.1', - }, - ], - }); - - const module = await FM.loadRemote<() => string>( - '@load-remote/app1/say', - ); - assert(module, 'module should be a function'); - expect(module()).toBe('hello app1'); - - const module2 = await FM.loadRemote<() => string>( - '@load-remote/app2/say', - ); - assert(module2, 'module should be a function'); - expect(module2()).toBe('hello app2'); - reset(); - }); - - it('compatible with old structor', async () => { - const reset = addGlobalSnapshot({ - '@federation-test/compatible': { - globalName: '', - buildVersion: '', - publicPath: '', - remoteTypes: '', - shared: [], - remoteEntry: '', - remoteEntryType: 'global', - modules: [], - version: '0.0.1', - remotesInfo: { - '@federation-test/app2': { - matchedVersion: '0.0.1', - }, - }, - }, - '@federation-test/app2:0.0.1': { - globalName: '', - publicPath: '', - remoteTypes: '', - shared: [], - buildVersion: 'custom', - remotesInfo: {}, - remoteEntryType: 'global', - modules: [], - version: '0.0.1', - remoteEntry: - 'http://localhost:1111/resources/app2/federation-remote-entry.js', - }, - }); - - const FederationInstance = new Embedded.FederationHost({ - name: '@federation-test/compatible', - remotes: [ - { - name: '@federation-test/app2', - version: '*', - }, - ], - }); - const module = await FederationInstance.loadRemote<() => string>( - '@federation-test/app2/say', - ); - assert(module, 'module should be a function'); - expect(module()).toBe('hello app2'); - reset(); - }); - - it('remote entry url with query', async () => { - const FederationInstance = new Embedded.FederationHost({ - name: '@federation-test/compatible', - remotes: [ - { - name: '__FEDERATION_@federation-test/app2:custom__', - alias: 'app2', - entry: - 'http://localhost:1111/resources/app2/federation-remote-entry.js?kk=2', - }, - ], - }); - const module = - await FederationInstance.loadRemote<() => string>('app2/say'); - assert(module, 'module should be a function'); - expect(module()).toBe('hello app2'); - }); - - it('different instance with same module', async () => { - const reset = addGlobalSnapshot({ - '@module-federation/load-remote-different-instance': { - buildVersion: 'custom', - publicPath: 'xx', - remoteEntry: 'xx', - remotesInfo: { - '@module-federation/sub1': { - matchedVersion: '1.0.2', - }, - }, - remoteEntryType: 'global', - modules: [], - version: '0.0.1', - globalName: '', - remoteTypes: 'index.d.ts', - shared: [], - }, - '@module-federation/sub1:1.0.2': { - buildVersion: '1.0.2', - globalName: '__FEDERATION_@module-federation/sub1:1.0.2__', - modules: [], - remoteEntryType: 'global', - remoteTypes: 'index.d.ts', - version: '0.0.1', - remotesInfo: {}, - shared: [], - publicPath: - 'http://localhost:1111/resources/load-remote/diff-instance/', - remoteEntry: 'federation-remote-entry.js', - }, - }); - const vmOptions = { - remotes: [ - { - name: '@module-federation/sub1', - version: '1.0.2', - }, - ], - plugins: [ - { - name: 'load-resouce-inbrowser', - beforeInit(args: any) { - args.options.inBrowser = true; - return args; - }, - }, - ], - }; - const FM = new Embedded.FederationHost({ - name: '@module-federation/load-remote-different-instance', - ...vmOptions, - }); - const FM2 = new Embedded.FederationHost({ - name: '@module-federation/load-remote-different-instance2', - ...vmOptions, - }); - const [res1, res2] = await Promise.all([ - FM.loadRemote<() => string>('@module-federation/sub1'), - FM2.loadRemote<() => string>('@module-federation/sub1'), - ]); - assert(res1, `res1 can't be null`); - assert(res2, `res2 can't be null`); - expect(res1()).toBe(res2()); - expect((globalThis as any).execTime).toBe(1); - reset(); - }); - }); - - describe('loadRemote with manifest.json', () => { - it('duplicate request manifest.json', async () => { - const FM = new Embedded.FederationHost({ - name: '@demo/host', - remotes: [ - { - name: '@demo/main', - entry: - 'http://localhost:1111/resources/main/federation-manifest.json', - }, - ], - }); - - const FM2 = new Embedded.FederationHost({ - name: '@demo/host2', - remotes: [ - { - name: '@demo/main', - entry: - 'http://localhost:1111/resources/main/federation-manifest.json', - }, - ], - }); - - const [module, , module2] = await Promise.all([ - FM.loadRemote string>>('@demo/main/say'), - FM.loadRemote string>>('@demo/main/add'), - FM2.loadRemote string>>('@demo/main/say'), - ]); - assert(module); - assert(module2); - expect(module()).toBe(module2()); - expect(module()).toBe('hello world'); - expect( - requestList.get( - 'http://localhost:1111/resources/main/federation-manifest.json', - ), - ).toBe(1); - }); - - it('circulate deps', async () => { - setGlobalFederationConstructor(Embedded.FederationHost, true); - const FM = Embedded.init({ - name: '@circulate-deps/app1', - remotes: [ - { - name: '@circulate-deps/app2', - entry: - 'http://localhost:1111/resources/load-remote/circulate-dep-app2/federation-manifest.json', - }, - ], - }); - - const app1Module = await FM.loadRemote string>>( - '@circulate-deps/app2/say', - ); - assert(app1Module); - const res = await app1Module(); - expect(res).toBe('@circulate-deps/app2'); - - Global.__FEDERATION__.__INSTANCES__ = []; - setGlobalFederationConstructor(undefined, true); - }); - - it('manifest.json with query', async () => { - const FM = new Embedded.FederationHost({ - name: '@demo/host', - remotes: [ - { - name: '@demo/main', - entry: - 'http://localhost:1111/resources/main/federation-manifest.json?query=2', - }, - ], - }); - - const [module] = await Promise.all([ - FM.loadRemote string>>('@demo/main/say'), - ]); - assert(module); - expect(module()).toBe('hello world'); - }); - }); - - describe('lazy loadRemote add remote into snapshot', () => { - it('load remoteEntry', async () => { - const reset = addGlobalSnapshot({ - '@demo/app2': { - buildVersion: '1.0.2', - globalName: `__FEDERATION_${'@load-remote/app2:custom'}__`, - modules: [], - remoteEntryType: 'global', - remoteTypes: 'index.d.ts', - version: '0.0.1', - remotesInfo: {}, - shared: [], - publicPath: 'http://localhost:1111/resources/load-remote/app2/', - remoteEntry: 'federation-remote-entry.js', - }, - '@demo/app1': { - consumerList: ['@demo/app2:0.0.1'], - globalName: `__FEDERATION_${'@load-remote/app1:custom'}__`, - publicPath: 'http://localhost:1111/resources/load-remote/app1/', - remoteTypes: '', - shared: [], - buildVersion: 'custom', - remotesInfo: {}, - remoteEntryType: 'global', - modules: [], - version: '0.0.1', - remoteEntry: 'federation-remote-entry.js', - }, - }); - const federationInstance = new Embedded.FederationHost({ - name: '@demo/app1', - remotes: [ - { - name: '@demo/app2', - alias: 'app2', - version: '', - }, - ], - }); - const snapshot = getGlobalSnapshot(); - const hostModuleInfo = snapshot['@demo/app1']; - assert( - hostModuleInfo && 'remotesInfo' in hostModuleInfo, - 'hostModuleInfo Cannot be empty', - ); - const beforeHostRemotesInfo = hostModuleInfo.remotesInfo; - const beforeRemotesLength = Object.keys(beforeHostRemotesInfo).length; - expect(beforeRemotesLength).toBe(0); - - await federationInstance.loadRemote('app2/say'); - const afterHostRemotesInfo = hostModuleInfo.remotesInfo; - const afterRemotesLength = Object.keys(afterHostRemotesInfo).length; - expect(afterRemotesLength).toBe(1); - reset(); - }); - - it('load manifest', async () => { - const reset = addGlobalSnapshot({ - '@demo/app1': { - globalName: `__FEDERATION_${'@load-remote/app1:custom'}__`, - publicPath: 'http://localhost:1111/resources/load-remote/app1/', - remoteTypes: '', - shared: [], - buildVersion: 'custom', - remotesInfo: {}, - remoteEntryType: 'global', - modules: [], - version: '0.0.1', - remoteEntry: 'federation-remote-entry.js', - }, - }); - - const federationInstance = new Embedded.FederationHost({ - name: '@demo/app1', - remotes: [ - { - name: '@demo/main', - alias: 'main', - entry: - 'http://localhost:1111/resources/main/federation-manifest.json', - }, - ], - }); - const snapshot = getGlobalSnapshot(); - const hostModuleInfo = snapshot['@demo/app1']; - assert( - hostModuleInfo && 'remotesInfo' in hostModuleInfo, - 'hostModuleInfo Cannot be empty', - ); - const beforeHostRemotesInfo = hostModuleInfo.remotesInfo; - const beforeRemotesLength = Object.keys(beforeHostRemotesInfo).length; - expect(beforeRemotesLength).toBe(0); - - await federationInstance.loadRemote('main/say'); - const afterHostRemotesInfo = hostModuleInfo.remotesInfo; - const afterRemotesLength = Object.keys(afterHostRemotesInfo).length; - expect(afterRemotesLength).toBe(1); - reset(); - }); - }); - - describe('loadRemote', () => { - it('api', async () => { - const jsSyncAssetPath = 'resources/load-remote/app2/say.sync.js'; - const remotePublicPath = 'http://localhost:1111/'; - const reset = addGlobalSnapshot({ - '@federation-test/globalinfo': { - globalName: '', - buildVersion: '', - publicPath: '', - remoteTypes: '', - shared: [], - remoteEntry: '', - remoteEntryType: 'global', - modules: [], - version: '0.0.1', - remotesInfo: { - '@federation-test/app2': { - matchedVersion: '0.0.1', - }, - }, - }, - '@federation-test/app2:0.0.1': { - globalName: '', - publicPath: remotePublicPath, - remoteTypes: '', - shared: [], - buildVersion: 'custom', - remotesInfo: {}, - remoteEntryType: 'global', - modules: [ - { - moduleName: 'say', - assets: { - css: { - sync: ['sub2/say.sync.css'], - async: ['sub2/say.async.css'], - }, - js: { - sync: [jsSyncAssetPath], - async: [], - }, - }, - }, - ], - version: '0.0.1', - remoteEntry: 'resources/app2/federation-remote-entry.js', - }, - }); - - const FederationInstance = new Embedded.FederationHost({ - name: '@federation-test/globalinfo', - remotes: [ - { - name: '@federation-test/app2', - version: '*', - }, - ], - }); - - await FederationInstance.loadRemote<() => string>( - '@federation-test/app2/say', - ); - // @ts-ignore fakeSrc is local mock attr, which value is the same as src - const loadedSrcs = [...document.querySelectorAll('script')].map( - (i) => (i as any).fakeSrc, - ); - expect(loadedSrcs.includes(`${remotePublicPath}${jsSyncAssetPath}`)); - reset(); - }); - }); - }); -}); diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 617952f88d6..571919293fe 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -39,11 +39,6 @@ "import": "./dist/core.esm.mjs", "require": "./dist/core.cjs.js" }, - "./embedded": { - "types": "./dist/embedded.cjs.d.ts", - "import": "./dist/embedded.esm.mjs", - "require": "./dist/embedded.cjs.js" - }, "./*": "./*" }, "typesVersions": { diff --git a/packages/runtime/project.json b/packages/runtime/project.json index 40af3399688..0ec67124878 100644 --- a/packages/runtime/project.json +++ b/packages/runtime/project.json @@ -14,8 +14,7 @@ "main": "packages/runtime/src/index.ts", "additionalEntryPoints": [ "packages/runtime/src/types.ts", - "packages/runtime/src/helpers.ts", - "packages/runtime/src/embedded.ts" + "packages/runtime/src/helpers.ts" ], "tsConfig": "packages/runtime/tsconfig.lib.json", "assets": [], diff --git a/packages/runtime/rollup.config.js b/packages/runtime/rollup.config.js index 9069278988b..a96be7b65bd 100644 --- a/packages/runtime/rollup.config.js +++ b/packages/runtime/rollup.config.js @@ -8,7 +8,6 @@ module.exports = (rollupConfig, projectOptions) => { index: 'packages/runtime/src/index.ts', types: 'packages/runtime/src/types.ts', helpers: 'packages/runtime/src/helpers.ts', - embedded: 'packages/runtime/src/embedded.ts', core: 'packages/runtime/src/core.ts', }; diff --git a/packages/runtime/src/embedded.ts b/packages/runtime/src/embedded.ts deleted file mode 100644 index d0dd896eed8..00000000000 --- a/packages/runtime/src/embedded.ts +++ /dev/null @@ -1,366 +0,0 @@ -import type * as IndexModule from './index'; - -function getRuntime(): typeof IndexModule { - // @ts-ignore - const runtime = __webpack_require__.federation.runtime as typeof IndexModule; - if (!runtime) { - throw new Error( - 'Federation runtime accessed before instantiation or installation', - ); - } - return runtime; -} - -export const registerGlobalPlugins: typeof IndexModule.registerGlobalPlugins = ( - ...args -) => { - return getRuntime().registerGlobalPlugins(...args); -}; - -export const getRemoteEntry: typeof IndexModule.getRemoteEntry = (...args) => { - return getRuntime().getRemoteEntry(...args); -}; - -export const getRemoteInfo: typeof IndexModule.getRemoteInfo = (...args) => { - return getRuntime().getRemoteInfo(...args); -}; - -export const loadScript: typeof IndexModule.loadScript = (...args) => { - return getRuntime().loadScript(...args); -}; - -export const loadScriptNode: typeof IndexModule.loadScriptNode = (...args) => { - return getRuntime().loadScriptNode(...args); -}; - -export const init: typeof IndexModule.init = (...args) => { - return getRuntime().init(...args); -}; - -export const loadRemote: typeof IndexModule.loadRemote = (...args) => { - return getRuntime().loadRemote(...args); -}; - -export const loadShare: typeof IndexModule.loadShare = (...args) => { - return getRuntime().loadShare(...args); -}; - -export const loadShareSync: typeof IndexModule.loadShareSync = (...args) => { - return getRuntime().loadShareSync(...args); -}; - -export const preloadRemote: typeof IndexModule.preloadRemote = (...args) => { - return getRuntime().preloadRemote(...args); -}; - -export const registerRemotes: typeof IndexModule.registerRemotes = ( - ...args -) => { - return getRuntime().registerRemotes(...args); -}; - -export const registerPlugins: typeof IndexModule.registerPlugins = ( - ...args -) => { - return getRuntime().registerPlugins(...args); -}; - -export const getInstance: typeof IndexModule.getInstance = (...args) => { - return getRuntime().getInstance(...args); -}; - -export class FederationHost implements IndexModule.FederationHost { - private _instance: IndexModule.FederationHost | null = null; - private _args: ConstructorParameters; - - constructor( - ...args: ConstructorParameters - ) { - this._args = args; - const RealFederationHost = getRuntime().FederationHost; - this._instance = new RealFederationHost(...this._args); - } - - private _getInstance(): IndexModule.FederationHost { - if (!this._instance) { - const RealFederationHost = getRuntime().FederationHost; - this._instance = new RealFederationHost(...this._args); - } - return this._instance; - } - - get options() { - return this._getInstance().options; - } - - set options(value) { - this._getInstance().options = value; - } - - get hooks() { - return this._getInstance().hooks; - } - - get version() { - return this._getInstance().version; - } - - get name() { - return this._getInstance().name; - } - - get moduleCache() { - return this._getInstance().moduleCache; - } - - get snapshotHandler() { - return this._getInstance().snapshotHandler; - } - - get sharedHandler() { - return this._getInstance().sharedHandler; - } - - get remoteHandler() { - return this._getInstance().remoteHandler; - } - - get shareScopeMap() { - return this._getInstance().shareScopeMap; - } - - get loaderHook() { - return this._getInstance().loaderHook; - } - - get bridgeHook() { - return this._getInstance().bridgeHook; - } - - initOptions(...args: Parameters) { - return this._getInstance().initOptions(...args); - } - - loadShare(...args: Parameters) { - return this._getInstance().loadShare(...args); - } - - loadShareSync( - ...args: Parameters - ) { - return this._getInstance().loadShareSync(...args); - } - - initializeSharing( - ...args: Parameters - ) { - return this._getInstance().initializeSharing(...args); - } - - initRawContainer( - ...args: Parameters - ) { - return this._getInstance().initRawContainer(...args); - } - - loadRemote(...args: Parameters) { - return this._getInstance().loadRemote(...args); - } - - preloadRemote( - ...args: Parameters - ) { - return this._getInstance().preloadRemote(...args); - } - - initShareScopeMap( - ...args: Parameters - ) { - return this._getInstance().initShareScopeMap(...args); - } - - registerPlugins( - ...args: Parameters - ) { - return this._getInstance().registerPlugins(...args); - } - - registerRemotes( - ...args: Parameters - ) { - return this._getInstance().registerRemotes(...args); - } - - formatOptions( - ...args: Parameters - ) { - //@ts-ignore - return this._getInstance().formatOptions(...args); - } -} - -export interface ModuleInterface { - remoteInfo: IndexModule.Module['remoteInfo']; - inited: IndexModule.Module['inited']; - lib: IndexModule.Module['lib']; - host: IndexModule.Module['host']; - - getEntry( - ...args: Parameters - ): ReturnType; - get( - ...args: Parameters - ): ReturnType; -} - -export class Module implements ModuleInterface { - private _instance: IndexModule.Module | null = null; - private _args: ConstructorParameters; - - constructor(...args: ConstructorParameters) { - this._args = args; - } - - private _getInstance(): IndexModule.Module { - if (!this._instance) { - const RealModule = getRuntime().Module; - this._instance = new RealModule(...this._args); - } - return this._instance; - } - - get remoteInfo() { - return this._getInstance().remoteInfo; - } - - set remoteInfo(value) { - this._getInstance().remoteInfo = value; - } - - get inited() { - return this._getInstance().inited; - } - - set inited(value) { - this._getInstance().inited = value; - } - - get lib() { - return this._getInstance().lib; - } - - set lib(value) { - this._getInstance().lib = value; - } - - get host() { - return this._getInstance().host; - } - - set host(value) { - this._getInstance().host = value; - } - - async getEntry(...args: Parameters) { - return this._getInstance().getEntry(...args); - } - - async get(...args: Parameters) { - return this._getInstance().get(...args); - } - - private wraperFactory( - ...args: Parameters - ) { - //@ts-ignore - return this._getInstance().wraperFactory(...args); - } -} - -//maybe use proxy? -//export class Module implements ModuleInterface { -// private _instance: IndexModule.Module | null = null; -// private _args: ConstructorParameters; -// constructor(...args: ConstructorParameters) { -// this._args = args; -// return new Proxy(this, { -// get(target, prop) { -// if (prop in target) { -// return target[prop as keyof Module]; -// } -// const instance = target._getInstance(); -// const value = instance[prop as keyof IndexModule.Module]; -// return typeof value === 'function' ? value.bind(instance) : value; -// }, -// set(target, prop, value) { -// const instance = target._getInstance(); -// instance[prop as keyof IndexModule.Module] = value; -// return true; -// }, -// }); -// } -// private _getInstance(): IndexModule.Module { -// if (!this._instance) { -// const RealModule = getRuntime().Module; -// this._instance = new RealModule(...this._args); -// } -// return this._instance; -// } -// // Keep only the methods that have custom logic -// private wraperFactory(...args: Parameters) { -// return this._getInstance().wraperFactory(...args); -// } -// } -//export class FederationHost implements IndexModule.FederationHost { -// private _instance: IndexModule.FederationHost | null = null; -// private _args: ConstructorParameters; -// constructor(...args: ConstructorParameters) { -// this._args = args; -// return new Proxy(this, { -// get(target, prop) { -// if (prop in target) { -// return target[prop as keyof FederationHost]; -// } -// const instance = target._getInstance(); -// const value = instance[prop as keyof IndexModule.FederationHost]; -// return typeof value === 'function' ? value.bind(instance) : value; -// }, -// set(target, prop, value) { -// const instance = target._getInstance(); -// instance[prop as keyof IndexModule.FederationHost] = value; -// return true; -// }, -// }); -// } -// private _getInstance(): IndexModule.FederationHost { -// if (!this._instance) { -// const RealFederationHost = getRuntime().FederationHost; -// this._instance = new RealFederationHost(...this._args); -// } -// return this._instance; -// } -// // Keep only the methods that have custom logic -// formatOptions(...args: Parameters) { -// return this._getInstance().formatOptions(...args); -// } -// } -//function createRuntimeFunction( -// name: T -// ): typeof IndexModule[T] { -// return (...args: any[]) => { -// return getRuntime()[name](...args); -// }; -// } -// export const registerGlobalPlugins = createRuntimeFunction('registerGlobalPlugins'); -// export const getRemoteEntry = createRuntimeFunction('getRemoteEntry'); -// export const getRemoteInfo = createRuntimeFunction('getRemoteInfo'); -// export const loadScript = createRuntimeFunction('loadScript'); -// export const loadScriptNode = createRuntimeFunction('loadScriptNode'); -// export const init = createRuntimeFunction('init'); -// export const loadRemote = createRuntimeFunction('loadRemote'); -// export const loadShare = createRuntimeFunction('loadShare'); -// export const loadShareSync = createRuntimeFunction('loadShareSync'); -// export const preloadRemote = createRuntimeFunction('preloadRemote'); -// export const registerRemotes = createRuntimeFunction('registerRemotes'); -// export const registerPlugins = createRuntimeFunction('registerPlugins'); -// export const getInstance = createRuntimeFunction('getInstance');