From 1629991dbf42ecd4df717b94903a0f361b8062db Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Tue, 1 Jul 2025 14:34:20 +0800 Subject: [PATCH 01/14] docs: add FederationConstructor --- .../en/guide/basic/runtime/runtime-api.mdx | 30 +++++++++++++++++++ .../zh/guide/basic/runtime/runtime-api.mdx | 30 +++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx b/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx index 4beaf9e5b3..f88c90f0a9 100644 --- a/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx +++ b/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx @@ -416,4 +416,34 @@ registerPlugins([ ]); ``` +## FederationConstructor +In addition to exposing APIs, the Federation Runtime also provides a FederationConstructor class, which you can use to create Federation instance. + +* What is a `Federation` instance? + +A `Federation` instance is an instance of the `FederationConstructor` class, which contains all the functionality of the `Federation` runtime. + +> You can enter `__FEDERATION__.__INSTANCES__` in the console to view the created instances. + +* When to use `FederationConstructor`? + +To ensure the uniqueness of the FederationHost instance, after the build plugin creates an instance, it will be stored in memory. The exported APIs all first obtain the FederationHost instance from memory and then call the APIs of the FederationHost instance. This is also why APIs like loadRemote can be used directly from the `module-federation/runtime` package and inherently understand what application container they are attached to. + +However, this singleton pattern also limits the ability to create multiple instances, as it assumes that there is only one instance per bundle. Therefore, if you need to create a new instance , you can use the FederationHost class to create a new one. + +```ts +import { FederationConstructor } from '@vmok/kit/runtime'; + +const host = new FederationConstructor({ + name: '@demo/host', + remotes: [ + { + name: '@demo/sub1', + entry: 'http://localhost:8080/vmok-manifest.json' + } + ] +}); + +host.loadRemote('@demo/sub1/util').then((m) => m.add(1, 2, 3)); +``` diff --git a/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx b/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx index 0fba9c823d..6ec4ee1b5a 100644 --- a/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx +++ b/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx @@ -416,4 +416,34 @@ registerPlugins([ ]); ``` +## FederationConstructor +`Federation Runtime` 除了暴露出 API 外,还提供了 `FederationConstructor` 类,你可以使用它来创建一个 `Federation` 实例。 + +* 什么是 `Federation` 实例 ? + +`Federation` 实例是 `FederationConstructor` 类的实例,它包含了 `Federation` 运行时的所有功能。 + +> 你可以在控制台输入 `__FEDERATION__.__INSTANCES__` 来查看已经创建好的实例。 + +* 什么时候使用 `FederationConstructor` ? + +为了保证 `FederationConstructor` 实例的唯一性,我们在构建插件创建实例后,会将其存储到内存中,导出的 API 都是先从内存中获取 `FederationConstructor` 实例,然后再调用 `FederationConstructor` 实例的 API。这也是为什么 `loadRemote` 等 API 可以直接使用的原因。 + +但是这种单例模式同时也限制无法创建多份实例,因此如果你需要**创建新的 FederationConstructor 实例**,那么你可以使用 `FederationConstructor` 类来创建一个新的实例。 + +```ts +import { FederationConstructor } from '@module-federation/enhanced/runtime'; + +const host = new FederationConstructor({ + name: 'host', + remotes: [ + { + name: 'sub1', + entry: 'http://localhost:8080/mf-manifest.json' + } + ] +}); + +host.loadRemote('@demo/sub1/util').then((m) => m.add(1, 2, 3)); +``` From f1834fa17d3cc3002e52f7f27f9e2fc35c8e9ba2 Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Wed, 2 Jul 2025 18:37:51 +0800 Subject: [PATCH 02/14] chore: rename FederationHost to ModuleFederation --- .changeset/poor-pillows-study.md | 11 ++++ .../en/guide/basic/runtime/runtime-api.mdx | 20 +++---- .../en/guide/basic/runtime/runtime-hooks.mdx | 30 +++++----- apps/website-new/docs/en/plugin/dev/index.mdx | 30 +++++----- .../zh/guide/basic/runtime/runtime-api.mdx | 18 +++--- .../zh/guide/basic/runtime/runtime-hooks.mdx | 30 +++++----- apps/website-new/docs/zh/plugin/dev/index.mdx | 30 +++++----- .../bridge-react/src/provider/plugin.ts | 4 +- .../data-prefetch/__tests__/react.spec.ts | 4 +- packages/data-prefetch/src/prefetch.ts | 4 +- packages/modernjs/src/utils/dataFetch.ts | 4 +- packages/modernjs/src/utils/index.ts | 4 +- .../node/src/__tests__/runtimePlugin.test.ts | 18 +++--- packages/node/src/runtimePlugin.ts | 4 +- packages/runtime-core/CHANGELOG.md | 8 +-- packages/runtime-core/__tests__/hooks.spec.ts | 10 ++-- .../runtime-core/__tests__/instance.spec.ts | 6 +- .../__tests__/register-remotes.spec.ts | 10 ++-- .../runtime-core/__tests__/snapshot.spec.ts | 4 +- packages/runtime-core/src/core.ts | 12 ++-- packages/runtime-core/src/global.ts | 12 ++-- packages/runtime-core/src/index.ts | 2 +- packages/runtime-core/src/module/index.ts | 6 +- .../src/plugins/generate-preload-assets.ts | 4 +- .../src/plugins/snapshot/SnapshotHandler.ts | 12 ++-- packages/runtime-core/src/remote/index.ts | 30 +++++----- packages/runtime-core/src/shared/index.ts | 12 ++-- packages/runtime-core/src/type/plugin.ts | 4 +- packages/runtime-core/src/utils/load.ts | 10 ++-- packages/runtime-core/src/utils/plugin.ts | 10 ++-- packages/runtime-core/src/utils/preload.ts | 4 +- packages/runtime/CHANGELOG.md | 8 +-- packages/runtime/__tests__/hooks.spec.ts | 10 ++-- packages/runtime/__tests__/instance.spec.ts | 6 +- .../runtime/__tests__/load-remote.spec.ts | 30 +++++----- .../__tests__/register-remotes.spec.ts | 10 ++-- packages/runtime/__tests__/shares.spec.ts | 56 +++++++++---------- packages/runtime/__tests__/snapshot.spec.ts | 4 +- packages/runtime/src/index.ts | 30 +++++----- packages/runtime/src/utils.ts | 4 +- packages/webpack-bundler-runtime/src/types.ts | 6 +- 41 files changed, 271 insertions(+), 260 deletions(-) create mode 100644 .changeset/poor-pillows-study.md diff --git a/.changeset/poor-pillows-study.md b/.changeset/poor-pillows-study.md new file mode 100644 index 0000000000..3bc8d64389 --- /dev/null +++ b/.changeset/poor-pillows-study.md @@ -0,0 +1,11 @@ +--- +'@module-federation/webpack-bundler-runtime': patch +'@module-federation/bridge-react': patch +'@module-federation/data-prefetch': patch +'@module-federation/runtime-core': patch +'@module-federation/modern-js': patch +'@module-federation/runtime': patch +'@module-federation/node': patch +--- + +chore: rename FederationHost to ModuleFederation diff --git a/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx b/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx index f88c90f0a9..aeb8ac45b4 100644 --- a/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx +++ b/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx @@ -416,26 +416,26 @@ registerPlugins([ ]); ``` -## FederationConstructor +## ModuleFederation -In addition to exposing APIs, the Federation Runtime also provides a FederationConstructor class, which you can use to create Federation instance. +In addition to exposing APIs, the Runtime also provides the ModuleFederation class, which you can use to create ModuleFederation instance. -* What is a `Federation` instance? +* What is a `ModuleFederation` instance? -A `Federation` instance is an instance of the `FederationConstructor` class, which contains all the functionality of the `Federation` runtime. +A `ModuleFederation` instance is an instance of the `ModuleFederation` class, which contains all the functionality of the `ModuleFederation` runtime. > You can enter `__FEDERATION__.__INSTANCES__` in the console to view the created instances. -* When to use `FederationConstructor`? +* When to use `ModuleFederation` class? -To ensure the uniqueness of the FederationHost instance, after the build plugin creates an instance, it will be stored in memory. The exported APIs all first obtain the FederationHost instance from memory and then call the APIs of the FederationHost instance. This is also why APIs like loadRemote can be used directly from the `module-federation/runtime` package and inherently understand what application container they are attached to. +To ensure the uniqueness of the ModuleFederation instance, after the build plugin creates an instance, it will be stored in memory. The exported APIs all first obtain the ModuleFederation instance from memory and then call the APIs of the ModuleFederation instance. This is also why APIs like loadRemote can be used directly from the `module-federation/runtime` package and inherently understand what application container they are attached to. -However, this singleton pattern also limits the ability to create multiple instances, as it assumes that there is only one instance per bundle. Therefore, if you need to create a new instance , you can use the FederationHost class to create a new one. +However, this singleton pattern also limits the ability to create multiple instances, as it assumes that there is only one instance per bundle. Therefore, if you need to create a new instance , you can use the ModuleFederation class to create a new one. ```ts -import { FederationConstructor } from '@vmok/kit/runtime'; +import { ModuleFederation } from '@module-federation/enhanced/runtime'; -const host = new FederationConstructor({ +const mf = new ModuleFederation({ name: '@demo/host', remotes: [ { @@ -445,5 +445,5 @@ const host = new FederationConstructor({ ] }); -host.loadRemote('@demo/sub1/util').then((m) => m.add(1, 2, 3)); +mf.loadRemote('@demo/sub1/util').then((m) => m.add(1, 2, 3)); ``` diff --git a/apps/website-new/docs/en/guide/basic/runtime/runtime-hooks.mdx b/apps/website-new/docs/en/guide/basic/runtime/runtime-hooks.mdx index cd6b8f436a..c3f60af36e 100644 --- a/apps/website-new/docs/en/guide/basic/runtime/runtime-hooks.mdx +++ b/apps/website-new/docs/en/guide/basic/runtime/runtime-hooks.mdx @@ -13,12 +13,12 @@ function beforeInit(args: BeforeInitOptions): BeforeInitOptions type BeforeInitOptions ={ userOptions: UserOptions; - options: FederationRuntimeOptions; - origin: FederationHost; + options: ModuleFederationRuntimeOptions; + origin: ModuleFederation; shareInfo: ShareInfos; } -interface FederationRuntimeOptions { +interface ModuleFederationRuntimeOptions { id?: string; name: string; version?: string; @@ -41,8 +41,8 @@ Called after the MF instance is initialized. function init(args: InitOptions): void type InitOptions ={ - options: FederationRuntimeOptions; - origin: FederationHost; + options: ModuleFederationRuntimeOptions; + origin: ModuleFederation; } ``` @@ -59,8 +59,8 @@ async function beforeRequest(args: BeforeRequestOptions): Promise; options: Options; - origin: FederationHost; + origin: ModuleFederation; } ``` @@ -322,7 +322,7 @@ Used to control the generation of resources that need to be preloaded. async function generatePreloadAssets(args: GeneratePreloadAssetsOptions): Promise type GeneratePreloadAssetsOptions ={ - origin: FederationHost; + origin: ModuleFederation; preloadOptions: PreloadOptions[number]; remote: Remote; remoteInfo: RemoteInfo; diff --git a/apps/website-new/docs/en/plugin/dev/index.mdx b/apps/website-new/docs/en/plugin/dev/index.mdx index f27151f3bd..a9b1f5621f 100644 --- a/apps/website-new/docs/en/plugin/dev/index.mdx +++ b/apps/website-new/docs/en/plugin/dev/index.mdx @@ -118,12 +118,12 @@ function beforeInit(args: BeforeInitOptions): BeforeInitOptions; type BeforeInitOptions = { userOptions: UserOptions; - options: FederationRuntimeOptions; - origin: FederationHost; + options: ModuleFederationRuntimeOptions; + origin: ModuleFederation; shareInfo: ShareInfos; }; -interface FederationRuntimeOptions { +interface ModuleFederationRuntimeOptions { id?: string; name: string; version?: string; @@ -146,8 +146,8 @@ Called during the initialization of the remote container. function init(args: InitOptions): void; type InitOptions = { - options: FederationRuntimeOptions; - origin: FederationHost; + options: ModuleFederationRuntimeOptions; + origin: ModuleFederation; }; ``` @@ -166,8 +166,8 @@ async function beforeRequest( type BeforeRequestOptions = { id: string; - options: FederationRuntimeOptions; - origin: FederationHost; + options: ModuleFederationRuntimeOptions; + origin: ModuleFederation; }; ``` @@ -189,8 +189,8 @@ type AfterResolveOptions = { pkgNameOrAlias: string; expose: string; remote: Remote; - options: FederationRuntimeOptions; - origin: FederationHost; + options: ModuleFederationRuntimeOptions; + origin: ModuleFederation; remoteInfo: RemoteInfo; remoteSnapshot?: ModuleInfo; }; @@ -213,7 +213,7 @@ type OnLoadOptions = { pkgNameOrAlias: string; remote: Remote; options: ModuleOptions; - origin: FederationHost; + origin: ModuleFederation; exposeModule: any; exposeModuleFactory: any; moduleInstance: Module; @@ -221,7 +221,7 @@ type OnLoadOptions = { type ModuleOptions = { remoteInfo: RemoteInfo; - host: FederationHost; + host: ModuleFederation; }; interface RemoteInfo { @@ -278,7 +278,7 @@ type ErrorLoadRemoteOptions = { options?: any; from: 'build' | 'runtime'; lifecycle: 'onLoad' | 'beforeRequest'; - origin: FederationHost; + origin: ModuleFederation; }; ``` @@ -337,7 +337,7 @@ type BeforeLoadShareOptions = { pkgName: string; shareInfo?: Shared; shared: Options['shared']; - origin: FederationHost; + origin: ModuleFederation; }; ``` @@ -440,7 +440,7 @@ async function beforePreloadRemote( type BeforePreloadRemoteOptions = { preloadOps: Array; options: Options; - origin: FederationHost; + origin: ModuleFederation; }; ``` @@ -458,7 +458,7 @@ async function generatePreloadAssets( ): Promise; type GeneratePreloadAssetsOptions = { - origin: FederationHost; + origin: ModuleFederation; preloadOptions: PreloadOptions[number]; remote: Remote; remoteInfo: RemoteInfo; diff --git a/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx b/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx index 6ec4ee1b5a..a2bb297512 100644 --- a/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx +++ b/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx @@ -416,26 +416,26 @@ registerPlugins([ ]); ``` -## FederationConstructor +## ModuleFederation -`Federation Runtime` 除了暴露出 API 外,还提供了 `FederationConstructor` 类,你可以使用它来创建一个 `Federation` 实例。 +`Runtime` 除了暴露出 API 外,还提供了 `ModuleFederation` 类,你可以使用它来创建一个 `ModuleFederation` 实例。 -* 什么是 `Federation` 实例 ? +* 什么是 `ModuleFederation` 实例 ? -`Federation` 实例是 `FederationConstructor` 类的实例,它包含了 `Federation` 运行时的所有功能。 +`ModuleFederation` 实例是 `ModuleFederation` 类的实例,它包含了 `ModuleFederation` 运行时的所有功能。 > 你可以在控制台输入 `__FEDERATION__.__INSTANCES__` 来查看已经创建好的实例。 -* 什么时候使用 `FederationConstructor` ? +* 什么时候使用 `ModuleFederation` class? -为了保证 `FederationConstructor` 实例的唯一性,我们在构建插件创建实例后,会将其存储到内存中,导出的 API 都是先从内存中获取 `FederationConstructor` 实例,然后再调用 `FederationConstructor` 实例的 API。这也是为什么 `loadRemote` 等 API 可以直接使用的原因。 +为了保证 `ModuleFederation` 实例的唯一性,我们在构建插件创建实例后,会将其存储到内存中,导出的 API 都是先从内存中获取 `ModuleFederation` 实例,然后再调用 `ModuleFederation` 实例的 API。这也是为什么 `loadRemote` 等 API 可以直接使用的原因。 -但是这种单例模式同时也限制无法创建多份实例,因此如果你需要**创建新的 FederationConstructor 实例**,那么你可以使用 `FederationConstructor` 类来创建一个新的实例。 +但是这种单例模式同时也限制无法创建多份实例,因此如果你需要**创建新的 ModuleFederation 实例**,那么你可以使用 `ModuleFederation` 类来创建一个新的实例。 ```ts -import { FederationConstructor } from '@module-federation/enhanced/runtime'; +import { ModuleFederation } from '@module-federation/enhanced/runtime'; -const host = new FederationConstructor({ +const mf = new ModuleFederation({ name: 'host', remotes: [ { diff --git a/apps/website-new/docs/zh/guide/basic/runtime/runtime-hooks.mdx b/apps/website-new/docs/zh/guide/basic/runtime/runtime-hooks.mdx index 7cb43306ed..2745a082c7 100644 --- a/apps/website-new/docs/zh/guide/basic/runtime/runtime-hooks.mdx +++ b/apps/website-new/docs/zh/guide/basic/runtime/runtime-hooks.mdx @@ -13,12 +13,12 @@ function beforeInit(args: BeforeInitOptions): BeforeInitOptions type BeforeInitOptions ={ userOptions: UserOptions; - options: FederationRuntimeOptions; - origin: FederationHost; + options: ModuleFederationRuntimeOptions; + origin: ModuleFederation; shareInfo: ShareInfos; } -interface FederationRuntimeOptions { +interface ModuleFederationRuntimeOptions { id?: string; name: string; version?: string; @@ -41,8 +41,8 @@ interface FederationRuntimeOptions { function init(args: InitOptions): void type InitOptions ={ - options: FederationRuntimeOptions; - origin: FederationHost; + options: ModuleFederationRuntimeOptions; + origin: ModuleFederation; } ``` @@ -59,8 +59,8 @@ async function beforeRequest(args: BeforeRequestOptions): Promise; options: Options; - origin: FederationHost; + origin: ModuleFederation; } ``` @@ -324,7 +324,7 @@ type BeforePreloadRemoteOptions ={ async function generatePreloadAssets(args: GeneratePreloadAssetsOptions): Promise type GeneratePreloadAssetsOptions ={ - origin: FederationHost; + origin: ModuleFederation; preloadOptions: PreloadOptions[number]; remote: Remote; remoteInfo: RemoteInfo; diff --git a/apps/website-new/docs/zh/plugin/dev/index.mdx b/apps/website-new/docs/zh/plugin/dev/index.mdx index aa7ee48e35..e806542643 100644 --- a/apps/website-new/docs/zh/plugin/dev/index.mdx +++ b/apps/website-new/docs/zh/plugin/dev/index.mdx @@ -119,12 +119,12 @@ function beforeInit(args: BeforeInitOptions): BeforeInitOptions; type BeforeInitOptions = { userOptions: UserOptions; - options: FederationRuntimeOptions; - origin: FederationHost; + options: ModuleFederationRuntimeOptions; + origin: ModuleFederation; shareInfo: ShareInfos; }; -interface FederationRuntimeOptions { +interface ModuleFederationRuntimeOptions { id?: string; name: string; version?: string; @@ -147,8 +147,8 @@ interface FederationRuntimeOptions { function init(args: InitOptions): void; type InitOptions = { - options: FederationRuntimeOptions; - origin: FederationHost; + options: ModuleFederationRuntimeOptions; + origin: ModuleFederation; }; ``` @@ -167,8 +167,8 @@ async function beforeRequest( type BeforeRequestOptions = { id: string; - options: FederationRuntimeOptions; - origin: FederationHost; + options: ModuleFederationRuntimeOptions; + origin: ModuleFederation; }; ``` @@ -190,8 +190,8 @@ type AfterResolveOptions = { pkgNameOrAlias: string; expose: string; remote: Remote; - options: FederationRuntimeOptions; - origin: FederationHost; + options: ModuleFederationRuntimeOptions; + origin: ModuleFederation; remoteInfo: RemoteInfo; remoteSnapshot?: ModuleInfo; }; @@ -214,7 +214,7 @@ type OnLoadOptions = { pkgNameOrAlias: string; remote: Remote; options: ModuleOptions; - origin: FederationHost; + origin: ModuleFederation; exposeModule: any; exposeModuleFactory: any; moduleInstance: Module; @@ -222,7 +222,7 @@ type OnLoadOptions = { type ModuleOptions = { remoteInfo: RemoteInfo; - host: FederationHost; + host: ModuleFederation; }; interface RemoteInfo { @@ -279,7 +279,7 @@ type ErrorLoadRemoteOptions = { options?: any; from: 'build' | 'runtime'; lifecycle: 'onLoad' | 'beforeRequest'; - origin: FederationHost; + origin: ModuleFederation; }; ``` @@ -337,7 +337,7 @@ type BeforeLoadShareOptions = { pkgName: string; shareInfo?: Shared; shared: Options['shared']; - origin: FederationHost; + origin: ModuleFederation; }; ``` @@ -440,7 +440,7 @@ async function beforePreloadRemote( type BeforePreloadRemoteOptions = { preloadOps: Array; options: Options; - origin: FederationHost; + origin: ModuleFederation; }; ``` @@ -458,7 +458,7 @@ async function generatePreloadAssets( ): Promise; type GeneratePreloadAssetsOptions = { - origin: FederationHost; + origin: ModuleFederation; preloadOptions: PreloadOptions[number]; remote: Remote; remoteInfo: RemoteInfo; diff --git a/packages/bridge/bridge-react/src/provider/plugin.ts b/packages/bridge/bridge-react/src/provider/plugin.ts index b754c917f0..dea20e6bac 100644 --- a/packages/bridge/bridge-react/src/provider/plugin.ts +++ b/packages/bridge/bridge-react/src/provider/plugin.ts @@ -1,10 +1,10 @@ import type { FederationRuntimePlugin, - FederationHost, + ModuleFederation, } from '@module-federation/runtime'; export type FederationRuntimeType = { - instance: FederationHost | null; + instance: ModuleFederation | null; }; export const federationRuntime: FederationRuntimeType = { instance: null }; diff --git a/packages/data-prefetch/__tests__/react.spec.ts b/packages/data-prefetch/__tests__/react.spec.ts index 2e11360973..728ce4ba6a 100644 --- a/packages/data-prefetch/__tests__/react.spec.ts +++ b/packages/data-prefetch/__tests__/react.spec.ts @@ -1,4 +1,4 @@ -import { FederationHost, init } from '@module-federation/runtime'; +import { ModuleFederation, init } from '@module-federation/runtime'; import { renderHook, act } from '@testing-library/react-hooks'; import * as ModuleFederationSDK from '@module-federation/sdk'; import { usePrefetch } from '../src/react'; @@ -46,7 +46,7 @@ describe('usePrefetch', () => { Promise.resolve(params ? params : testData), ); - new FederationHost({ + new ModuleFederation({ name: options.name, remotes: [], }); diff --git a/packages/data-prefetch/src/prefetch.ts b/packages/data-prefetch/src/prefetch.ts index 1dbd23ff00..11f2078ff0 100644 --- a/packages/data-prefetch/src/prefetch.ts +++ b/packages/data-prefetch/src/prefetch.ts @@ -1,5 +1,5 @@ import { - FederationHost, + ModuleFederation, getRemoteEntry, getRemoteInfo, } from '@module-federation/runtime'; @@ -26,7 +26,7 @@ type PrefetchExports = Record; export interface DataPrefetchOptions { name: string; remote?: Remote; - origin?: FederationHost; + origin?: ModuleFederation; remoteSnapshot?: ModuleInfo; } diff --git a/packages/modernjs/src/utils/dataFetch.ts b/packages/modernjs/src/utils/dataFetch.ts index bca456a730..6a40932951 100644 --- a/packages/modernjs/src/utils/dataFetch.ts +++ b/packages/modernjs/src/utils/dataFetch.ts @@ -18,7 +18,7 @@ import type { MF_DATA_FETCH_MAP, NoSSRRemoteInfo, } from '../interfaces/global'; -import type { FederationHost } from '@module-federation/enhanced/runtime'; +import type { ModuleFederation } from '@module-federation/enhanced/runtime'; export const getDataFetchInfo = ({ name, @@ -195,7 +195,7 @@ export function getDataFetchMapKey( } export async function loadDataFetchModule( - instance: FederationHost, + instance: ModuleFederation, id: string, ) { return instance.loadRemote(id).then((m) => { diff --git a/packages/modernjs/src/utils/index.ts b/packages/modernjs/src/utils/index.ts index 9cb30ab351..543a1a5814 100644 --- a/packages/modernjs/src/utils/index.ts +++ b/packages/modernjs/src/utils/index.ts @@ -1,8 +1,8 @@ -import type { FederationHost } from '@module-federation/enhanced/runtime'; +import type { ModuleFederation } from '@module-federation/enhanced/runtime'; export function getLoadedRemoteInfos( id: string, - instance: FederationHost | null, + instance: ModuleFederation | null, ) { if (!instance) { return; diff --git a/packages/node/src/__tests__/runtimePlugin.test.ts b/packages/node/src/__tests__/runtimePlugin.test.ts index c0418b0151..9d5f638bdb 100644 --- a/packages/node/src/__tests__/runtimePlugin.test.ts +++ b/packages/node/src/__tests__/runtimePlugin.test.ts @@ -14,7 +14,7 @@ import runtimePlugin, { } from '../runtimePlugin'; import type { FederationRuntimePlugin, - FederationHost, + ModuleFederation, Federation, } from '@module-federation/runtime'; import * as runtimePluginModule from '../runtimePlugin'; @@ -94,7 +94,7 @@ const mockModule = { }, inited: false, lib: {}, - host: {} as FederationHost, + host: {} as ModuleFederation, getEntry: () => 'http://localhost:3001/remoteEntry.js', init: () => Promise.resolve({}), get: () => Promise.resolve({}), @@ -829,7 +829,7 @@ describe('runtimePlugin', () => { }, }, }, - } as unknown as FederationHost, + } as unknown as ModuleFederation, } as any; if (plugin.beforeInit) { @@ -854,7 +854,7 @@ describe('runtimePlugin', () => { }, }, }, - } as unknown as FederationHost, + } as unknown as ModuleFederation, } as any; if (plugin.beforeInit) { @@ -903,7 +903,7 @@ describe('runtimePlugin', () => { }, }, }, - } as unknown as FederationHost, + } as unknown as ModuleFederation, } as any; if (plugin.beforeInit) { @@ -986,7 +986,7 @@ describe('runtimePlugin', () => { }, }, }, - } as unknown as FederationHost, + } as unknown as ModuleFederation, } as any; if (plugin.beforeInit) { @@ -1041,7 +1041,7 @@ describe('runtimePlugin', () => { }, }, }, - } as unknown as FederationHost, + } as unknown as ModuleFederation, } as any; if (plugin.beforeInit) { @@ -1087,7 +1087,7 @@ describe('runtimePlugin', () => { }, }, }, - } as unknown as FederationHost, + } as unknown as ModuleFederation, } as any; if (plugin.beforeInit) { @@ -1145,7 +1145,7 @@ describe('runtimePlugin', () => { }, }, }, - } as unknown as FederationHost, + } as unknown as ModuleFederation, } as any; if (plugin.beforeInit) { diff --git a/packages/node/src/runtimePlugin.ts b/packages/node/src/runtimePlugin.ts index fbacd099dd..1905596b54 100644 --- a/packages/node/src/runtimePlugin.ts +++ b/packages/node/src/runtimePlugin.ts @@ -1,6 +1,6 @@ import type { FederationRuntimePlugin, - FederationHost, + ModuleFederation, } from '@module-federation/runtime'; type WebpackRequire = { (id: string): any; @@ -22,7 +22,7 @@ type WebpackRequire = { options: { attrs: { globalName: string } }, ) => Promise; }; - instance: FederationHost; + instance: ModuleFederation; chunkMatcher?: (chunkId: string) => boolean; rootOutputDir?: string; initOptions: { diff --git a/packages/runtime-core/CHANGELOG.md b/packages/runtime-core/CHANGELOG.md index 6acfc9cbac..322f2ddb17 100644 --- a/packages/runtime-core/CHANGELOG.md +++ b/packages/runtime-core/CHANGELOG.md @@ -265,19 +265,19 @@ - 9e32644: Added comprehensive integration tests for the API synchronization and enhanced the embedded module proxy implementation. - Added detailed integration tests for API consistency between embedded and index modules. - - Tests include export comparison and method consistency for `FederationHost` and `Module` classes. + - Tests include export comparison and method consistency for `ModuleFederation` and `Module` classes. - Introduced and updated the `embedded.ts` file to dynamically access the runtime modules at runtime. - Included detailed implementations for accessing and wrapping existing runtime functions. - - Exposed the previously private `formatOptions` method in the `FederationHost` class publicly. + - Exposed the previously private `formatOptions` method in the `ModuleFederation` class publicly. - Enhanced error handling for uninstantiated or unregistered runtime access. - 9e32644: - Refactor `embedded.ts` to use a proxy pattern for better runtime compatibility: - - Implement FederationHost and Module classes that delegate to the actual runtime implementation + - Implement ModuleFederation and Module classes that delegate to the actual runtime implementation - Expose all public methods and properties from the original classes - Use a lazy initialization approach to ensure proper runtime loading - Add comprehensive test suite for API synchronization between embedded.ts and index.ts - Introduce new test file `sync.spec.ts` with extensive tests for API compatibility - - Ensure FederationHost and Module classes have the same methods in both files + - Ensure ModuleFederation and Module classes have the same methods in both files - Test various scenarios including remote loading, manifest handling, and circular dependencies - Modify `core.ts` to make `formatOptions` method public - Updated dependencies [9e32644] diff --git a/packages/runtime-core/__tests__/hooks.spec.ts b/packages/runtime-core/__tests__/hooks.spec.ts index 0f9ec6dd24..87e7da7079 100644 --- a/packages/runtime-core/__tests__/hooks.spec.ts +++ b/packages/runtime-core/__tests__/hooks.spec.ts @@ -1,5 +1,5 @@ import { assert, describe, test, it } from 'vitest'; -import { FederationHost } from '../src/core'; +import { ModuleFederation } from '../src/core'; import { FederationRuntimePlugin } from '../src/type/plugin'; import { mockStaticServer, removeScriptTags } from './mock/utils'; import { addGlobalSnapshot } from '../src/global'; @@ -59,7 +59,7 @@ describe('hooks', () => { ], plugins: [testPlugin()], }; - const GM = new FederationHost(options); + const GM = new ModuleFederation(options); assert(beforeInitArgs, "beforeInitArgs can't be undefined"); expect(beforeInitArgs).toMatchObject({ options: { @@ -149,7 +149,7 @@ describe('hooks', () => { }, }); - const INSTANCE = new FederationHost({ + const INSTANCE = new ModuleFederation({ name: '@loader-hooks/globalinfo', remotes: [ { @@ -242,7 +242,7 @@ describe('hooks', () => { } }, }); - const INSTANCE = new FederationHost({ + const INSTANCE = new ModuleFederation({ name: '@loader-hooks/fetch', remotes: [ { @@ -320,7 +320,7 @@ describe('hooks', () => { } as any; }; - const INSTANCE = new FederationHost({ + const INSTANCE = new ModuleFederation({ name: '@loader-hooks/fetch', remotes: [ { diff --git a/packages/runtime-core/__tests__/instance.spec.ts b/packages/runtime-core/__tests__/instance.spec.ts index 185c285dc3..7867f40e15 100644 --- a/packages/runtime-core/__tests__/instance.spec.ts +++ b/packages/runtime-core/__tests__/instance.spec.ts @@ -1,9 +1,9 @@ import { assert, describe, test, it } from 'vitest'; -import { FederationHost } from '../src/index'; +import { ModuleFederation } from '../src/index'; -describe('FederationHost', () => { +describe('ModuleFederation', () => { it('should initialize with provided arguments', () => { - const GM = new FederationHost({ + const GM = new ModuleFederation({ name: '@federation/instance', version: '1.0.1', remotes: [], diff --git a/packages/runtime-core/__tests__/register-remotes.spec.ts b/packages/runtime-core/__tests__/register-remotes.spec.ts index b8295c8dbd..174d914c49 100644 --- a/packages/runtime-core/__tests__/register-remotes.spec.ts +++ b/packages/runtime-core/__tests__/register-remotes.spec.ts @@ -1,9 +1,9 @@ import { assert, describe, it, expect } from 'vitest'; -import { FederationHost } from '../src/index'; +import { ModuleFederation } from '../src/index'; -describe('FederationHost', () => { +describe('ModuleFederation', () => { it('registers new remotes and loads them correctly', async () => { - const FM = new FederationHost({ + const FM = new ModuleFederation({ name: '@federation/instance', version: '1.0.1', remotes: [ @@ -37,7 +37,7 @@ describe('FederationHost', () => { expect(res).toBe('hello app2'); }); it('does not merge loaded remote by default', async () => { - const FM = new FederationHost({ + const FM = new ModuleFederation({ name: '@federation/instance', version: '1.0.1', remotes: [ @@ -65,7 +65,7 @@ describe('FederationHost', () => { expect(app1Res).toBe('hello app1 entry1'); }); it('merges loaded remote by setting "force: true"', async () => { - const FM = new FederationHost({ + const FM = new ModuleFederation({ name: '@federation/instance', version: '1.0.1', remotes: [ diff --git a/packages/runtime-core/__tests__/snapshot.spec.ts b/packages/runtime-core/__tests__/snapshot.spec.ts index 64e31c0051..ef2bfb0449 100644 --- a/packages/runtime-core/__tests__/snapshot.spec.ts +++ b/packages/runtime-core/__tests__/snapshot.spec.ts @@ -1,5 +1,5 @@ import { assert, describe, it } from 'vitest'; -import { FederationHost } from '../src'; +import { ModuleFederation } from '../src'; import { getGlobalSnapshot, resetFederationGlobalInfo } from '../src/global'; describe('snapshot', () => { @@ -12,7 +12,7 @@ describe('snapshot', () => { 'http://localhost:1111/resources/snapshot/remote1/federation-manifest.json'; const Remote2Entry = 'http://localhost:1111/resources/snapshot/remote2/federation-manifest.json'; - const FM1 = new FederationHost({ + const FM1 = new ModuleFederation({ name: '@snapshot/host', version: '0.0.3', remotes: [ diff --git a/packages/runtime-core/src/core.ts b/packages/runtime-core/src/core.ts index ae9451f844..15369242c0 100644 --- a/packages/runtime-core/src/core.ts +++ b/packages/runtime-core/src/core.ts @@ -44,20 +44,20 @@ const USE_SNAPSHOT = ? !FEDERATION_OPTIMIZE_NO_SNAPSHOT_PLUGIN : true; // Default to true (use snapshot) when not explicitly defined -export class FederationHost { +export class ModuleFederation { options: Options; hooks = new PluginSystem({ beforeInit: new SyncWaterfallHook<{ userOptions: UserOptions; options: Options; - origin: FederationHost; + origin: ModuleFederation; shareInfo: ShareInfos; }>('beforeInit'), init: new SyncHook< [ { options: Options; - origin: FederationHost; + origin: ModuleFederation; }, ], void @@ -68,7 +68,7 @@ export class FederationHost { initScope: InitScope; remoteEntryInitOptions: RemoteEntryInitOptions; remoteInfo: RemoteInfo; - origin: FederationHost; + origin: ModuleFederation; }>('beforeInitContainer'), // maybe will change, temporarily for internal use only initContainer: new AsyncWaterfallHook<{ @@ -77,7 +77,7 @@ export class FederationHost { remoteEntryInitOptions: RemoteEntryInitOptions; remoteInfo: RemoteInfo; remoteEntryExports: RemoteEntryExports; - origin: FederationHost; + origin: ModuleFederation; id: string; remoteSnapshot?: ModuleInfo; }>('initContainer'), @@ -126,7 +126,7 @@ export class FederationHost { [ { getRemoteEntry: typeof getRemoteEntry; - origin: FederationHost; + origin: ModuleFederation; remoteInfo: RemoteInfo; remoteEntryExports?: RemoteEntryExports | undefined; globalLoading: Record< diff --git a/packages/runtime-core/src/global.ts b/packages/runtime-core/src/global.ts index 85f3d9a9db..6fa56d1717 100644 --- a/packages/runtime-core/src/global.ts +++ b/packages/runtime-core/src/global.ts @@ -1,4 +1,4 @@ -import { FederationHost } from './core'; +import { ModuleFederation } from './core'; import { RemoteEntryExports, GlobalShareScopeMap, @@ -18,8 +18,8 @@ export interface Federation { __GLOBAL_PLUGIN__: Array; __DEBUG_CONSTRUCTOR_VERSION__?: string; moduleInfo: GlobalModuleInfo; - __DEBUG_CONSTRUCTOR__?: typeof FederationHost; - __INSTANCES__: Array; + __DEBUG_CONSTRUCTOR__?: typeof ModuleFederation; + __INSTANCES__: Array; __SHARE__: GlobalShareScopeMap; __MANIFEST_LOADING__: Record>; __PRELOADED_MAP__: Map; @@ -119,19 +119,19 @@ export function resetFederationGlobalInfo(): void { } export function setGlobalFederationInstance( - FederationInstance: FederationHost, + FederationInstance: ModuleFederation, ): void { CurrentGlobal.__FEDERATION__.__INSTANCES__.push(FederationInstance); } export function getGlobalFederationConstructor(): - | typeof FederationHost + | typeof ModuleFederation | undefined { return CurrentGlobal.__FEDERATION__.__DEBUG_CONSTRUCTOR__; } export function setGlobalFederationConstructor( - FederationConstructor: typeof FederationHost | undefined, + FederationConstructor: typeof ModuleFederation | undefined, isDebug = isDebugMode(), ): void { if (isDebug) { diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 400f85d47c..603ffb421e 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -1,5 +1,5 @@ import helpers, { type IGlobalUtils, type IShareUtils } from './helpers'; -export { FederationHost } from './core'; +export { ModuleFederation } from './core'; export { type Federation, CurrentGlobal, diff --git a/packages/runtime-core/src/module/index.ts b/packages/runtime-core/src/module/index.ts index 8d700707d8..9d670b3c7b 100644 --- a/packages/runtime-core/src/module/index.ts +++ b/packages/runtime-core/src/module/index.ts @@ -6,7 +6,7 @@ import { runtimeDescMap, } from '@module-federation/error-codes'; import { getRemoteEntry, getRemoteEntryUniqueKey } from '../utils/load'; -import { FederationHost } from '../core'; +import { ModuleFederation } from '../core'; import { RemoteEntryExports, RemoteInfo, InitScope } from '../type'; import { globalLoading } from '../global'; @@ -17,14 +17,14 @@ class Module { inited = false; remoteEntryExports?: RemoteEntryExports; lib: RemoteEntryExports | undefined = undefined; - host: FederationHost; + host: ModuleFederation; constructor({ remoteInfo, host, }: { remoteInfo: RemoteInfo; - host: FederationHost; + host: ModuleFederation; }) { this.remoteInfo = remoteInfo; this.host = host; diff --git a/packages/runtime-core/src/plugins/generate-preload-assets.ts b/packages/runtime-core/src/plugins/generate-preload-assets.ts index 22f15509f8..fdaf295081 100644 --- a/packages/runtime-core/src/plugins/generate-preload-assets.ts +++ b/packages/runtime-core/src/plugins/generate-preload-assets.ts @@ -18,7 +18,7 @@ import { } from '../type'; import { assignRemoteInfo } from './snapshot'; import { getInfoWithoutType, getPreloaded, setPreloaded } from '../global'; -import { FederationHost } from '../core'; +import { ModuleFederation } from '../core'; import { defaultPreloadArgs, normalizePreloadExposes } from '../utils/preload'; import { getRegisteredShare } from '../utils/share'; import { @@ -110,7 +110,7 @@ const isExisted = (type: 'link' | 'script', url: string) => { // eslint-disable-next-line max-lines-per-function export function generatePreloadAssets( - origin: FederationHost, + origin: ModuleFederation, preloadOptions: PreloadOptions[number], remote: RemoteInfoOptionalVersion, globalSnapshot: GlobalModuleInfo, diff --git a/packages/runtime-core/src/plugins/snapshot/SnapshotHandler.ts b/packages/runtime-core/src/plugins/snapshot/SnapshotHandler.ts index b996c4ebdf..fe470edcf4 100644 --- a/packages/runtime-core/src/plugins/snapshot/SnapshotHandler.ts +++ b/packages/runtime-core/src/plugins/snapshot/SnapshotHandler.ts @@ -23,12 +23,12 @@ import { getInfoWithoutType, } from '../../global'; import { PluginSystem, AsyncHook, AsyncWaterfallHook } from '../../utils/hooks'; -import { FederationHost } from '../../core'; +import { ModuleFederation } from '../../core'; import { assert } from '../../utils/logger'; export function getGlobalRemoteInfo( moduleInfo: Remote, - origin: FederationHost, + origin: ModuleFederation, ): { hostGlobalSnapshot: ModuleInfo | undefined; globalSnapshot: ReturnType; @@ -69,7 +69,7 @@ export function getGlobalRemoteInfo( export class SnapshotHandler { loadingHostSnapshot: Promise | null = null; - HostInstance: FederationHost; + HostInstance: ModuleFederation; manifestCache: Map = new Map(); hooks = new PluginSystem({ beforeLoadRemoteSnapshot: new AsyncHook< @@ -98,17 +98,17 @@ export class SnapshotHandler { }>('loadRemoteSnapshot'), afterLoadSnapshot: new AsyncWaterfallHook<{ id?: string; - host: FederationHost; + host: ModuleFederation; options: Options; moduleInfo: Remote; remoteSnapshot: ModuleInfo; }>('afterLoadSnapshot'), }); - loaderHook: FederationHost['loaderHook']; + loaderHook: ModuleFederation['loaderHook']; manifestLoading: Record> = Global.__FEDERATION__.__MANIFEST_LOADING__; - constructor(HostInstance: FederationHost) { + constructor(HostInstance: ModuleFederation) { this.HostInstance = HostInstance; this.loaderHook = HostInstance.loaderHook; } diff --git a/packages/runtime-core/src/remote/index.ts b/packages/runtime-core/src/remote/index.ts index 3b2de0e2b7..9b4ccfb9bb 100644 --- a/packages/runtime-core/src/remote/index.ts +++ b/packages/runtime-core/src/remote/index.ts @@ -27,7 +27,7 @@ import { RemoteEntryExports, CallFrom, } from '../type'; -import { FederationHost } from '../core'; +import { ModuleFederation } from '../core'; import { PluginSystem, AsyncHook, @@ -54,28 +54,28 @@ export interface LoadRemoteMatch { expose: string; remote: Remote; options: Options; - origin: FederationHost; + origin: ModuleFederation; remoteInfo: RemoteInfo; remoteSnapshot?: ModuleInfo; } export class RemoteHandler { - host: FederationHost; + host: ModuleFederation; idToRemoteMap: Record; hooks = new PluginSystem({ beforeRegisterRemote: new SyncWaterfallHook<{ remote: Remote; - origin: FederationHost; + origin: ModuleFederation; }>('beforeRegisterRemote'), registerRemote: new SyncWaterfallHook<{ remote: Remote; - origin: FederationHost; + origin: ModuleFederation; }>('registerRemote'), beforeRequest: new AsyncWaterfallHook<{ id: string; options: Options; - origin: FederationHost; + origin: ModuleFederation; }>('beforeRequest'), onLoad: new AsyncHook< [ @@ -85,7 +85,7 @@ export class RemoteHandler { pkgNameOrAlias: string; remote: Remote; options: ModuleOptions; - origin: FederationHost; + origin: ModuleFederation; exposeModule: any; exposeModuleFactory: any; moduleInstance: Module; @@ -101,7 +101,7 @@ export class RemoteHandler { remote: Remote; remoteSnapshot: ModuleInfo; preloadConfig: PreloadRemoteArgs; - origin: FederationHost; + origin: ModuleFederation; }, ], void @@ -118,7 +118,7 @@ export class RemoteHandler { | 'beforeLoadShare' | 'afterResolve' | 'onLoad'; - origin: FederationHost; + origin: ModuleFederation; }, ], void | unknown @@ -128,14 +128,14 @@ export class RemoteHandler { { preloadOps: Array; options: Options; - origin: FederationHost; + origin: ModuleFederation; }, ] >('beforePreloadRemote'), generatePreloadAssets: new AsyncHook< [ { - origin: FederationHost; + origin: ModuleFederation; preloadOptions: PreloadOptions[number]; remote: Remote; remoteInfo: RemoteInfo; @@ -149,12 +149,12 @@ export class RemoteHandler { afterPreloadRemote: new AsyncHook<{ preloadOps: Array; options: Options; - origin: FederationHost; + origin: ModuleFederation; }>(), loadEntry: new AsyncHook< [ { - loaderHook: FederationHost['loaderHook']; + loaderHook: ModuleFederation['loaderHook']; remoteInfo: RemoteInfo; remoteEntryExports?: RemoteEntryExports; }, @@ -163,7 +163,7 @@ export class RemoteHandler { >(), }); - constructor(host: FederationHost) { + constructor(host: ModuleFederation) { this.host = host; this.idToRemoteMap = {}; } @@ -342,7 +342,7 @@ export class RemoteHandler { })) as { id: string; options: Options; - origin: FederationHost; + origin: ModuleFederation; }; if (!loadRemoteArgs) { diff --git a/packages/runtime-core/src/shared/index.ts b/packages/runtime-core/src/shared/index.ts index c1d6728af6..f2c742fffa 100644 --- a/packages/runtime-core/src/shared/index.ts +++ b/packages/runtime-core/src/shared/index.ts @@ -17,7 +17,7 @@ import { InitTokens, CallFrom, } from '../type'; -import { FederationHost } from '../core'; +import { ModuleFederation } from '../core'; import { PluginSystem, AsyncHook, @@ -35,7 +35,7 @@ import { DEFAULT_SCOPE } from '../constant'; import { LoadRemoteMatch } from '../remote'; export class SharedHandler { - host: FederationHost; + host: ModuleFederation; shareScopeMap: ShareScopeMap; hooks = new PluginSystem({ afterResolve: new AsyncWaterfallHook('afterResolve'), @@ -43,10 +43,10 @@ export class SharedHandler { pkgName: string; shareInfo?: Shared; shared: Options['shared']; - origin: FederationHost; + origin: ModuleFederation; }>('beforeLoadShare'), // not used yet - loadShare: new AsyncHook<[FederationHost, string, ShareInfos]>(), + loadShare: new AsyncHook<[ModuleFederation, string, ShareInfos]>(), resolveShare: new SyncWaterfallHook<{ shareScopeMap: ShareScopeMap; scope: string; @@ -59,13 +59,13 @@ export class SharedHandler { initContainerShareScopeMap: new SyncWaterfallHook<{ shareScope: ShareScopeMap[string]; options: Options; - origin: FederationHost; + origin: ModuleFederation; scopeName: string; hostShareScopeMap?: ShareScopeMap; }>('initContainerShareScopeMap'), }); initTokens: InitTokens; - constructor(host: FederationHost) { + constructor(host: ModuleFederation) { this.host = host; this.shareScopeMap = {}; this.initTokens = {}; diff --git a/packages/runtime-core/src/type/plugin.ts b/packages/runtime-core/src/type/plugin.ts index 2d57568220..1da9b0829f 100644 --- a/packages/runtime-core/src/type/plugin.ts +++ b/packages/runtime-core/src/type/plugin.ts @@ -1,10 +1,10 @@ -import { FederationHost } from '../core'; +import { ModuleFederation } from '../core'; import { Module } from '../module'; import { SnapshotHandler } from '../plugins/snapshot/SnapshotHandler'; import { SharedHandler } from '../shared'; import { RemoteHandler } from '../remote'; -type CoreLifeCycle = FederationHost['hooks']['lifecycle']; +type CoreLifeCycle = ModuleFederation['hooks']['lifecycle']; type CoreLifeCyclePartial = Partial<{ [k in keyof CoreLifeCycle]: Parameters[0]; }>; diff --git a/packages/runtime-core/src/utils/load.ts b/packages/runtime-core/src/utils/load.ts index 50cc67bb23..ebdeff2b13 100644 --- a/packages/runtime-core/src/utils/load.ts +++ b/packages/runtime-core/src/utils/load.ts @@ -5,7 +5,7 @@ import { isBrowserEnv, } from '@module-federation/sdk'; import { DEFAULT_REMOTE_TYPE, DEFAULT_SCOPE } from '../constant'; -import { FederationHost } from '../core'; +import { ModuleFederation } from '../core'; import { globalLoading, getRemoteEntryExports } from '../global'; import { Remote, RemoteEntryExports, RemoteInfo } from '../type'; import { assert } from './logger'; @@ -109,7 +109,7 @@ async function loadEntryScript({ name: string; globalName: string; entry: string; - loaderHook: FederationHost['loaderHook']; + loaderHook: ModuleFederation['loaderHook']; }): Promise { const { entryExports: remoteEntryExports } = getRemoteEntryExports( name, @@ -160,7 +160,7 @@ async function loadEntryDom({ }: { remoteInfo: RemoteInfo; remoteEntryExports?: RemoteEntryExports; - loaderHook: FederationHost['loaderHook']; + loaderHook: ModuleFederation['loaderHook']; }) { const { entry, entryGlobalName: globalName, name, type } = remoteInfo; switch (type) { @@ -179,7 +179,7 @@ async function loadEntryNode({ loaderHook, }: { remoteInfo: RemoteInfo; - loaderHook: FederationHost['loaderHook']; + loaderHook: ModuleFederation['loaderHook']; }) { const { entry, entryGlobalName: globalName, name, type } = remoteInfo; const { entryExports: remoteEntryExports } = getRemoteEntryExports( @@ -225,7 +225,7 @@ export async function getRemoteEntry({ remoteEntryExports, remoteInfo, }: { - origin: FederationHost; + origin: ModuleFederation; remoteInfo: RemoteInfo; remoteEntryExports?: RemoteEntryExports | undefined; }): Promise { diff --git a/packages/runtime-core/src/utils/plugin.ts b/packages/runtime-core/src/utils/plugin.ts index 36f84a26ad..8dca108499 100644 --- a/packages/runtime-core/src/utils/plugin.ts +++ b/packages/runtime-core/src/utils/plugin.ts @@ -1,4 +1,4 @@ -import { FederationHost } from '../core'; +import { ModuleFederation } from '../core'; import { UserOptions } from '../type'; import { Module } from '../module'; import { getGlobalHostPlugins } from '../global'; @@ -6,10 +6,10 @@ import { getGlobalHostPlugins } from '../global'; export function registerPlugins( plugins: UserOptions['plugins'], hookInstances: Array< - | FederationHost['hooks'] - | FederationHost['snapshotHandler']['hooks'] - | FederationHost['sharedHandler']['hooks'] - | FederationHost['remoteHandler']['hooks'] + | ModuleFederation['hooks'] + | ModuleFederation['snapshotHandler']['hooks'] + | ModuleFederation['sharedHandler']['hooks'] + | ModuleFederation['remoteHandler']['hooks'] | Module['host']['loaderHook'] | Module['host']['bridgeHook'] >, diff --git a/packages/runtime-core/src/utils/preload.ts b/packages/runtime-core/src/utils/preload.ts index ed99ee6c3b..cadca6251c 100644 --- a/packages/runtime-core/src/utils/preload.ts +++ b/packages/runtime-core/src/utils/preload.ts @@ -10,7 +10,7 @@ import { } from '../type'; import { matchRemote } from './manifest'; import { assert } from './logger'; -import { FederationHost } from '../core'; +import { ModuleFederation } from '../core'; import { getRemoteEntry } from './load'; export function defaultPreloadArgs( @@ -66,7 +66,7 @@ export function normalizePreloadExposes(exposes?: string[]): string[] { export function preloadAssets( remoteInfo: RemoteInfo, - host: FederationHost, + host: ModuleFederation, assets: PreloadAssets, // It is used to distinguish preload from load remote parallel loading useLinkPreload = true, diff --git a/packages/runtime/CHANGELOG.md b/packages/runtime/CHANGELOG.md index c9ffa4d39a..7e7663290e 100644 --- a/packages/runtime/CHANGELOG.md +++ b/packages/runtime/CHANGELOG.md @@ -424,19 +424,19 @@ - 9e32644: Added comprehensive integration tests for the API synchronization and enhanced the embedded module proxy implementation. - Added detailed integration tests for API consistency between embedded and index modules. - - Tests include export comparison and method consistency for `FederationHost` and `Module` classes. + - Tests include export comparison and method consistency for `ModuleFederation` and `Module` classes. - Introduced and updated the `embedded.ts` file to dynamically access the runtime modules at runtime. - Included detailed implementations for accessing and wrapping existing runtime functions. - - Exposed the previously private `formatOptions` method in the `FederationHost` class publicly. + - Exposed the previously private `formatOptions` method in the `ModuleFederation` class publicly. - Enhanced error handling for uninstantiated or unregistered runtime access. - 9e32644: - Refactor `embedded.ts` to use a proxy pattern for better runtime compatibility: - - Implement FederationHost and Module classes that delegate to the actual runtime implementation + - Implement ModuleFederation and Module classes that delegate to the actual runtime implementation - Expose all public methods and properties from the original classes - Use a lazy initialization approach to ensure proper runtime loading - Add comprehensive test suite for API synchronization between embedded.ts and index.ts - Introduce new test file `sync.spec.ts` with extensive tests for API compatibility - - Ensure FederationHost and Module classes have the same methods in both files + - Ensure ModuleFederation and Module classes have the same methods in both files - Test various scenarios including remote loading, manifest handling, and circular dependencies - Modify `core.ts` to make `formatOptions` method public - Updated dependencies [9e32644] diff --git a/packages/runtime/__tests__/hooks.spec.ts b/packages/runtime/__tests__/hooks.spec.ts index a322b7e7f6..4f43797276 100644 --- a/packages/runtime/__tests__/hooks.spec.ts +++ b/packages/runtime/__tests__/hooks.spec.ts @@ -1,6 +1,6 @@ import { assert, describe, test, it } from 'vitest'; import { - FederationHost, + ModuleFederation, addGlobalSnapshot, } from '@module-federation/runtime-core'; import { FederationRuntimePlugin } from '@module-federation/runtime-core/types'; @@ -61,7 +61,7 @@ describe('hooks', () => { ], plugins: [testPlugin()], }; - const GM = new FederationHost(options); + const GM = new ModuleFederation(options); assert(beforeInitArgs, "beforeInitArgs can't be undefined"); expect(beforeInitArgs).toMatchObject({ options: { @@ -151,7 +151,7 @@ describe('hooks', () => { }, }); - const INSTANCE = new FederationHost({ + const INSTANCE = new ModuleFederation({ name: '@loader-hooks/globalinfo', remotes: [ { @@ -244,7 +244,7 @@ describe('hooks', () => { } }, }); - const INSTANCE = new FederationHost({ + const INSTANCE = new ModuleFederation({ name: '@loader-hooks/fetch', remotes: [ { @@ -322,7 +322,7 @@ describe('hooks', () => { } as any; }; - const INSTANCE = new FederationHost({ + const INSTANCE = new ModuleFederation({ name: '@loader-hooks/fetch', remotes: [ { diff --git a/packages/runtime/__tests__/instance.spec.ts b/packages/runtime/__tests__/instance.spec.ts index 185c285dc3..7867f40e15 100644 --- a/packages/runtime/__tests__/instance.spec.ts +++ b/packages/runtime/__tests__/instance.spec.ts @@ -1,9 +1,9 @@ import { assert, describe, test, it } from 'vitest'; -import { FederationHost } from '../src/index'; +import { ModuleFederation } from '../src/index'; -describe('FederationHost', () => { +describe('ModuleFederation', () => { it('should initialize with provided arguments', () => { - const GM = new FederationHost({ + const GM = new ModuleFederation({ name: '@federation/instance', version: '1.0.1', remotes: [], diff --git a/packages/runtime/__tests__/load-remote.spec.ts b/packages/runtime/__tests__/load-remote.spec.ts index b072baccd6..6801cd1dc6 100644 --- a/packages/runtime/__tests__/load-remote.spec.ts +++ b/packages/runtime/__tests__/load-remote.spec.ts @@ -1,6 +1,6 @@ import 'whatwg-fetch'; import { assert, describe, it } from 'vitest'; -import { FederationHost, init } from '../src/index'; +import { ModuleFederation, init } from '../src/index'; import { mockRemoteSnapshot } from './mock/utils'; import { matchRemoteWithNameAndExpose } from '@module-federation/runtime-core'; import { @@ -109,7 +109,7 @@ describe('matchRemote', () => { // eslint-disable-next-line max-lines-per-function describe('loadRemote', () => { it('api functionality', () => { - const FederationInstance = new FederationHost({ + const FederationInstance = new ModuleFederation({ name: '@federation-test/loadRemote-api', remotes: [], }); @@ -149,7 +149,7 @@ describe('loadRemote', () => { }, }); - const FederationInstance = new FederationHost({ + const FederationInstance = new ModuleFederation({ name: '@federation-test/globalinfo', remotes: [ { @@ -195,7 +195,7 @@ describe('loadRemote', () => { }, }); - const FM = new FederationHost({ + const FM = new ModuleFederation({ name: 'xxxxx', remotes: [ { @@ -251,7 +251,7 @@ describe('loadRemote', () => { }, }); - const FederationInstance = new FederationHost({ + const FederationInstance = new ModuleFederation({ name: '@federation-test/compatible', remotes: [ { @@ -268,7 +268,7 @@ describe('loadRemote', () => { reset(); }); it('handles remote entry URL with query', async () => { - const FederationInstance = new FederationHost({ + const FederationInstance = new ModuleFederation({ name: '@federation-test/compatible', remotes: [ { @@ -333,11 +333,11 @@ describe('loadRemote', () => { }, ], }; - const FM = new FederationHost({ + const FM = new ModuleFederation({ name: '@module-federation/load-remote-different-instance', ...vmOptions, }); - const FM2 = new FederationHost({ + const FM2 = new ModuleFederation({ name: '@module-federation/load-remote-different-instance2', ...vmOptions, }); @@ -355,7 +355,7 @@ describe('loadRemote', () => { describe('loadRemote with manifest.json', () => { it('handles duplicate request to manifest.json', async () => { - const FM = new FederationHost({ + const FM = new ModuleFederation({ name: '@demo/host', remotes: [ { @@ -366,7 +366,7 @@ describe('loadRemote with manifest.json', () => { ], }); - const FM2 = new FederationHost({ + const FM2 = new ModuleFederation({ name: '@demo/host2', remotes: [ { @@ -393,7 +393,7 @@ describe('loadRemote with manifest.json', () => { ).toBe(1); }); it('handles circular dependencies', async () => { - setGlobalFederationConstructor(FederationHost, true); + setGlobalFederationConstructor(ModuleFederation, true); const FM = init({ name: '@circulate-deps/app1', remotes: [ @@ -416,7 +416,7 @@ describe('loadRemote with manifest.json', () => { setGlobalFederationConstructor(undefined, true); }); it('handles manifest.json with query', async () => { - const FM = new FederationHost({ + const FM = new ModuleFederation({ name: '@demo/host', remotes: [ { @@ -463,7 +463,7 @@ describe('lazy loadRemote and add remote into snapshot', () => { remoteEntry: 'federation-remote-entry.js', }, }); - const federationInstance = new FederationHost({ + const federationInstance = new ModuleFederation({ name: '@demo/app1', remotes: [ { @@ -505,7 +505,7 @@ describe('lazy loadRemote and add remote into snapshot', () => { }, }); - const federationInstance = new FederationHost({ + const federationInstance = new ModuleFederation({ name: '@demo/app1', remotes: [ { @@ -583,7 +583,7 @@ describe('loadRemote', () => { }, }); - const FederationInstance = new FederationHost({ + const FederationInstance = new ModuleFederation({ name: '@federation-test/globalinfo', remotes: [ { diff --git a/packages/runtime/__tests__/register-remotes.spec.ts b/packages/runtime/__tests__/register-remotes.spec.ts index b8295c8dbd..174d914c49 100644 --- a/packages/runtime/__tests__/register-remotes.spec.ts +++ b/packages/runtime/__tests__/register-remotes.spec.ts @@ -1,9 +1,9 @@ import { assert, describe, it, expect } from 'vitest'; -import { FederationHost } from '../src/index'; +import { ModuleFederation } from '../src/index'; -describe('FederationHost', () => { +describe('ModuleFederation', () => { it('registers new remotes and loads them correctly', async () => { - const FM = new FederationHost({ + const FM = new ModuleFederation({ name: '@federation/instance', version: '1.0.1', remotes: [ @@ -37,7 +37,7 @@ describe('FederationHost', () => { expect(res).toBe('hello app2'); }); it('does not merge loaded remote by default', async () => { - const FM = new FederationHost({ + const FM = new ModuleFederation({ name: '@federation/instance', version: '1.0.1', remotes: [ @@ -65,7 +65,7 @@ describe('FederationHost', () => { expect(app1Res).toBe('hello app1 entry1'); }); it('merges loaded remote by setting "force: true"', async () => { - const FM = new FederationHost({ + const FM = new ModuleFederation({ name: '@federation/instance', version: '1.0.1', remotes: [ diff --git a/packages/runtime/__tests__/shares.spec.ts b/packages/runtime/__tests__/shares.spec.ts index 7194bee6e5..f67eb01724 100644 --- a/packages/runtime/__tests__/shares.spec.ts +++ b/packages/runtime/__tests__/shares.spec.ts @@ -11,7 +11,7 @@ import { shareInfoWithoutLibAndGetProvider, } from './share'; // import { assert } from '../src/utils/logger'; -import { FederationHost } from '@module-federation/runtime-core'; +import { ModuleFederation } from '@module-federation/runtime-core'; import { UserOptions, ShareScopeMap, @@ -108,8 +108,8 @@ describe('shared', () => { }, }; - const FederationInstance = new FederationHost(gmConfig1); - const FederationInstance2 = new FederationHost(gmConfig2); + const FederationInstance = new ModuleFederation(gmConfig1); + const FederationInstance2 = new ModuleFederation(gmConfig2); const reactInstance = await FederationInstance.loadShare<{ version: string; @@ -156,7 +156,7 @@ describe('shared', () => { }, }; - const FederationInstance = new FederationHost(gmConfig1); + const FederationInstance = new ModuleFederation(gmConfig1); const [reactInstance1, reactInstance2] = await Promise.all([ FederationInstance.loadShare<{ @@ -214,7 +214,7 @@ describe('shared', () => { }, }; - const GM1 = new FederationHost(gmConfig1); + const GM1 = new ModuleFederation(gmConfig1); GM1.initOptions({ name: '@federation/runtime-deps', remotes: [], @@ -226,7 +226,7 @@ describe('shared', () => { }, }); await GM1.loadShare<{ from: string; version: string }>('runtime-react'); - const FederationInstance2 = new FederationHost(gmConfig2); + const FederationInstance2 = new ModuleFederation(gmConfig2); const shared = await FederationInstance2.loadShare<{ from: string; version: string; @@ -238,7 +238,7 @@ describe('shared', () => { }); // it('share deps', async () => { - // const GM1 = new FederationHost({ + // const GM1 = new ModuleFederation({ // name: '@federation/load-deps', // remotes: [], // shared: { @@ -334,12 +334,12 @@ describe('single shared', () => { }, }; - const FM1 = new FederationHost(vmConfig1); + const FM1 = new ModuleFederation(vmConfig1); await FM1.loadShare<{ from: string; version: string }>('runtime-react'); - const FM3 = new FederationHost(vmConfig3); + const FM3 = new ModuleFederation(vmConfig3); await FM3.loadShare<{ from: string; version: string }>('runtime-react'); - const FM2 = new FederationHost(vmConfig2); + const FM2 = new ModuleFederation(vmConfig2); const shared = await FM2.loadShare<{ from: string; version: string }>( 'runtime-react', ); @@ -394,8 +394,8 @@ describe('eager shared', () => { }, }; - const FM = new FederationHost(federationConfig1); - const FM2 = new FederationHost(federationConfig2); + const FM = new ModuleFederation(federationConfig1); + const FM2 = new ModuleFederation(federationConfig2); const reactInstanceFactory = FM.loadShareSync<{ version: string; @@ -454,8 +454,8 @@ describe('eager shared', () => { }, }; - const FM = new FederationHost(federationConfig1); - const FM2 = new FederationHost(federationConfig2); + const FM = new ModuleFederation(federationConfig1); + const FM2 = new ModuleFederation(federationConfig2); const reactInstance2 = FM2.loadShareSync<{ version: string; @@ -485,7 +485,7 @@ describe('eager shared', () => { }, }, }; - const FM = new FederationHost(federationConfig1); + const FM = new ModuleFederation(federationConfig1); expect(function () { FM.loadShareSync<{ @@ -530,8 +530,8 @@ describe('strictVersion shared', () => { }, }; - const FM1 = new FederationHost(federationConfig1); - const FM2 = new FederationHost(federationConfig2); + const FM1 = new ModuleFederation(federationConfig1); + const FM2 = new ModuleFederation(federationConfig2); await FM1.loadShare<{ from: string; version: string }>('runtime-react'); FM2.initShareScopeMap('default', FM1.shareScopeMap['default']); @@ -577,8 +577,8 @@ describe('strictVersion shared', () => { }, }; - const FM1 = new FederationHost(federationConfig1); - const FM2 = new FederationHost(federationConfig2); + const FM1 = new ModuleFederation(federationConfig1); + const FM2 = new ModuleFederation(federationConfig2); await FM1.loadShare<{ from: string; version: string }>('runtime-react'); FM2.initShareScopeMap('default', FM1.shareScopeMap['default']); @@ -660,7 +660,7 @@ describe('with shareScope shared', () => { }, }; - const FM1 = new FederationHost(federationConfig1); + const FM1 = new ModuleFederation(federationConfig1); // initShareScopeMap will be called while container.init execute FM1.initShareScopeMap('default', existedShareScopeMap['default']); @@ -740,7 +740,7 @@ describe('with shareScope shared', () => { }, }; - const FM1 = new FederationHost(federationConfig1); + const FM1 = new ModuleFederation(federationConfig1); // initShareScopeMap will be called while container.init execute FM1.initShareScopeMap('old', existedShareScopeMap['old']); @@ -771,7 +771,7 @@ describe('load share with customize consume info', () => { }, }; - const FM1 = new FederationHost(federationConfig1); + const FM1 = new ModuleFederation(federationConfig1); const shared = await FM1.loadShare<{ from: string }>('runtime-react'); assert(shared, "shared can't be null"); const sharedRes = shared(); @@ -793,7 +793,7 @@ describe('load share with customize consume info', () => { describe('load share with different strategy', () => { it('register all shared to shareScopeMap while strategy is "version-first"', async () => { - setGlobalFederationConstructor(FederationHost, true); + setGlobalFederationConstructor(ModuleFederation, true); const federationConfig1: UserOptions = { name: '@shared-test/app1', @@ -817,7 +817,7 @@ describe('load share with different strategy', () => { }, }; - const FM1 = new FederationHost(federationConfig1); + const FM1 = new ModuleFederation(federationConfig1); const shared = await FM1.loadShare<{ from: string }>('runtime-react'); // should register remote shared to share scope map @@ -835,7 +835,7 @@ describe('load share with different strategy', () => { }); it('register only self shared to shareScopeMap while strategy is "loaded-first"', async () => { - setGlobalFederationConstructor(FederationHost, true); + setGlobalFederationConstructor(ModuleFederation, true); const federationConfig1: UserOptions = { name: '@shared-test/app1', @@ -859,7 +859,7 @@ describe('load share with different strategy', () => { }, }; - const FM1 = new FederationHost(federationConfig1); + const FM1 = new ModuleFederation(federationConfig1); const shared = await FM1.loadShare<{ from: string }>('runtime-react'); // should not register remote shared to share scope map @@ -903,7 +903,7 @@ describe('load share while shared has multiple versions', () => { }, }; - const FM1 = new FederationHost(federationConfig1); + const FM1 = new ModuleFederation(federationConfig1); const shared = await FM1.loadShare<{ version: string }>('runtime-react'); assert(shared, "shared can't be null"); const sharedRes = shared(); @@ -936,7 +936,7 @@ describe('load share while shared has multiple versions', () => { }, }; - const FM1 = new FederationHost(federationConfig1); + const FM1 = new ModuleFederation(federationConfig1); const shared = await FM1.loadShare<{ version: string }>('runtime-react', { resolver: (sharedOptions) => { return ( diff --git a/packages/runtime/__tests__/snapshot.spec.ts b/packages/runtime/__tests__/snapshot.spec.ts index 483140c188..8b798022a6 100644 --- a/packages/runtime/__tests__/snapshot.spec.ts +++ b/packages/runtime/__tests__/snapshot.spec.ts @@ -1,5 +1,5 @@ import { assert, describe, it } from 'vitest'; -import { FederationHost } from '../src'; +import { ModuleFederation } from '../src'; import { getGlobalSnapshot, resetFederationGlobalInfo, @@ -15,7 +15,7 @@ describe('snapshot', () => { 'http://localhost:1111/resources/snapshot/remote1/federation-manifest.json'; const Remote2Entry = 'http://localhost:1111/resources/snapshot/remote2/federation-manifest.json'; - const FM1 = new FederationHost({ + const FM1 = new ModuleFederation({ name: '@snapshot/host', version: '0.0.3', remotes: [ diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 4b23d6b2e3..dda18c669e 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -1,5 +1,5 @@ import { - FederationHost, + ModuleFederation, type UserOptions, getGlobalFederationConstructor, setGlobalFederationInstance, @@ -19,16 +19,16 @@ export { type Federation, } from '@module-federation/runtime-core'; -export { FederationHost }; +export { ModuleFederation }; -let FederationInstance: FederationHost | null = null; -export function init(options: UserOptions): FederationHost { +let FederationInstance: ModuleFederation | null = null; +export function init(options: UserOptions): ModuleFederation { // Retrieve the same instance with the same name const instance = getGlobalFederationInstance(options.name, options.version); if (!instance) { // Retrieve debug constructor const FederationConstructor = - getGlobalFederationConstructor() || FederationHost; + getGlobalFederationConstructor() || ModuleFederation; FederationInstance = new FederationConstructor(options); setGlobalFederationInstance(FederationInstance); return FederationInstance; @@ -43,7 +43,7 @@ export function init(options: UserOptions): FederationHost { } export function loadRemote( - ...args: Parameters + ...args: Parameters ): Promise { assert(FederationInstance, 'Please call init first'); const loadRemote: typeof FederationInstance.loadRemote = @@ -53,7 +53,7 @@ export function loadRemote( } export function loadShare( - ...args: Parameters + ...args: Parameters ): Promise T | undefined)> { assert(FederationInstance, 'Please call init first'); // eslint-disable-next-line prefer-spread @@ -63,7 +63,7 @@ export function loadShare( } export function loadShareSync( - ...args: Parameters + ...args: Parameters ): () => T | never { assert(FederationInstance, 'Please call init first'); const loadShareSync: typeof FederationInstance.loadShareSync = @@ -73,24 +73,24 @@ export function loadShareSync( } export function preloadRemote( - ...args: Parameters -): ReturnType { + ...args: Parameters +): ReturnType { assert(FederationInstance, 'Please call init first'); // eslint-disable-next-line prefer-spread return FederationInstance.preloadRemote.apply(FederationInstance, args); } export function registerRemotes( - ...args: Parameters -): ReturnType { + ...args: Parameters +): ReturnType { assert(FederationInstance, 'Please call init first'); // eslint-disable-next-line prefer-spread return FederationInstance.registerRemotes.apply(FederationInstance, args); } export function registerPlugins( - ...args: Parameters -): ReturnType { + ...args: Parameters +): ReturnType { assert(FederationInstance, 'Please call init first'); // eslint-disable-next-line prefer-spread return FederationInstance.registerPlugins.apply(FederationInstance, args); @@ -101,4 +101,4 @@ export function getInstance() { } // Inject for debug -setGlobalFederationConstructor(FederationHost); +setGlobalFederationConstructor(ModuleFederation); diff --git a/packages/runtime/src/utils.ts b/packages/runtime/src/utils.ts index 0dd3fefb12..01338aee94 100644 --- a/packages/runtime/src/utils.ts +++ b/packages/runtime/src/utils.ts @@ -1,4 +1,4 @@ -import { FederationHost } from '@module-federation/runtime-core'; +import { ModuleFederation } from '@module-federation/runtime-core'; import { CurrentGlobal } from '@module-federation/runtime-core'; // injected by bundler, so it can not use runtime-core stuff @@ -13,7 +13,7 @@ export function getBuilderId(): string { export function getGlobalFederationInstance( name: string, version: string | undefined, -): FederationHost | undefined { +): ModuleFederation | undefined { const buildId = getBuilderId(); return CurrentGlobal.__FEDERATION__.__INSTANCES__.find((GMInstance) => { if (buildId && GMInstance.options.id === getBuilderId()) { diff --git a/packages/webpack-bundler-runtime/src/types.ts b/packages/webpack-bundler-runtime/src/types.ts index 110089f043..4905a1806b 100644 --- a/packages/webpack-bundler-runtime/src/types.ts +++ b/packages/webpack-bundler-runtime/src/types.ts @@ -15,10 +15,10 @@ type NonUndefined = ExcludeUndefined; type InitOptions = Parameters[0]; -type ModuleCache = runtime.FederationHost['moduleCache']; +type ModuleCache = runtime.ModuleFederation['moduleCache']; type InferModule = T extends Map ? U : never; type InferredModule = InferModule; -export type ShareScopeMap = runtime.FederationHost['shareScopeMap']; +export type ShareScopeMap = runtime.ModuleFederation['shareScopeMap']; type InitToken = Record>; @@ -128,7 +128,7 @@ export interface InitContainerEntryOptions { export interface Federation { runtime?: typeof runtime; - instance?: runtime.FederationHost; + instance?: runtime.ModuleFederation; initOptions?: InitOptions; installInitialConsumes?: (options: InstallInitialConsumesOptions) => any; bundlerRuntime?: { From 028d6454e8380a9b8db2b7e9a6487e273fe7c117 Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Thu, 3 Jul 2025 14:37:02 +0800 Subject: [PATCH 03/14] docs: fix typo --- apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx | 2 +- apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx b/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx index aeb8ac45b4..059163b97d 100644 --- a/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx +++ b/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx @@ -428,7 +428,7 @@ A `ModuleFederation` instance is an instance of the `ModuleFederation` class, wh * When to use `ModuleFederation` class? -To ensure the uniqueness of the ModuleFederation instance, after the build plugin creates an instance, it will be stored in memory. The exported APIs all first obtain the ModuleFederation instance from memory and then call the APIs of the ModuleFederation instance. This is also why APIs like loadRemote can be used directly from the `module-federation/runtime` package and inherently understand what application container they are attached to. +To ensure the uniqueness of the ModuleFederation instance, after the build plugin creates an instance, it will be stored in memory. The exported APIs all first obtain the ModuleFederation instance from memory and then call the APIs of the ModuleFederation instance. This is also why APIs like loadRemote can be used directly from the `@module-federation/enhanced/runtime` package and inherently understand what application container they are attached to. However, this singleton pattern also limits the ability to create multiple instances, as it assumes that there is only one instance per bundle. Therefore, if you need to create a new instance , you can use the ModuleFederation class to create a new one. diff --git a/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx b/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx index a2bb297512..0473cb1aa5 100644 --- a/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx +++ b/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx @@ -445,5 +445,5 @@ const mf = new ModuleFederation({ ] }); -host.loadRemote('@demo/sub1/util').then((m) => m.add(1, 2, 3)); +mf.loadRemote('@demo/sub1/util').then((m) => m.add(1, 2, 3)); ``` From de4c89291952262a38d0e00cb3208a8c874b5ace Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Thu, 3 Jul 2025 17:05:55 +0800 Subject: [PATCH 04/14] refactor: use new ModuleFederation instead of init --- .../webpack-host/runtimePlugin.ts | 4 +- .../webpack-host/src/runtimePlugin.ts | 4 +- apps/node-host/runtimePlugin.ts | 2 +- apps/router-demo/router-host-2000/src/App.tsx | 4 +- .../error-handling/lifecycle-based.ts | 4 +- .../runtime-plugin/error-handling/simple.ts | 4 +- .../src/runtime-plugin/fallback.ts | 4 +- .../src/runtime-plugin/shared-strategy.ts | 4 +- .../src/runtime-plugin/shared-strategy.ts | 4 +- .../3005-runtime-host/src/runtimePlugin.ts | 4 +- .../docs/en/blog/error-load-remote.mdx | 10 +- .../docs/en/configure/runtimeplugins.mdx | 4 +- .../en/guide/basic/runtime/runtime-api.mdx | 562 ++++++++------- .../en/guide/basic/runtime/runtime-hooks.mdx | 27 +- .../docs/en/guide/basic/runtime/runtime.mdx | 50 +- .../docs/en/guide/troubleshooting/other.mdx | 4 +- apps/website-new/docs/en/plugin/dev/index.mdx | 640 +----------------- .../docs/en/plugin/plugins/retry-plugin.mdx | 2 +- .../website-new/docs/zh/blog/announcement.mdx | 6 +- .../docs/zh/blog/error-load-remote.mdx | 31 +- .../docs/zh/configure/runtimeplugins.mdx | 4 +- .../zh/guide/basic/runtime/runtime-api.mdx | 563 ++++++++------- .../zh/guide/basic/runtime/runtime-hooks.mdx | 27 +- .../docs/zh/guide/basic/runtime/runtime.mdx | 54 +- .../docs/zh/guide/troubleshooting/other.mdx | 4 +- apps/website-new/docs/zh/plugin/dev/index.mdx | 640 +----------------- .../docs/zh/plugin/plugins/retry-plugin.mdx | 10 +- .../frameworks/next/dynamic-remotes.mdx | 6 +- .../bridge-react/src/provider/plugin.ts | 4 +- .../src/utils/chrome/fast-refresh.ts | 4 +- .../src/utils/chrome/post-message.ts | 4 +- .../src/utils/chrome/snapshot-plugin.ts | 4 +- packages/data-prefetch/src/plugin.ts | 4 +- .../dynamic-remote-type-hints-plugin.ts | 4 +- packages/enhanced/CHANGELOG.md | 4 +- packages/enhanced/README.md | 2 +- packages/enhanced/src/index.ts | 2 +- .../src/lib/container/ContainerPlugin.ts | 4 +- .../lib/container/ContainerReferencePlugin.ts | 4 +- .../lib/container/ModuleFederationPlugin.ts | 4 +- .../runtime/FederationRuntimePlugin.ts | 10 +- .../src/lib/sharing/ConsumeSharedPlugin.ts | 4 +- .../src/lib/sharing/ProvideSharedPlugin.ts | 4 +- .../src/wrapper/FederationRuntimePlugin.ts | 10 +- .../unit/container/ContainerPlugin.test.ts | 10 +- .../ContainerReferencePlugin.test.ts | 15 +- .../unit/sharing/ConsumeSharedPlugin.test.ts | 15 +- .../unit/sharing/ProvideSharedPlugin.test.ts | 13 +- packages/enhanced/test/unit/sharing/utils.ts | 2 +- packages/modernjs/src/cli/index.ts | 2 +- .../cli/mfRuntimePlugins/auto-fetch-data.ts | 4 +- .../cli/mfRuntimePlugins/inject-node-fetch.ts | 4 +- .../mfRuntimePlugins/resolve-entry-ipv4.ts | 4 +- .../cli/mfRuntimePlugins/shared-strategy.ts | 4 +- .../src/plugins/container/runtimePlugin.ts | 4 +- .../node/src/__tests__/runtimePlugin.test.ts | 4 +- .../src/recordDynamicRemoteEntryHashPlugin.ts | 4 +- packages/node/src/runtimePlugin.ts | 4 +- packages/retry-plugin/src/index.ts | 6 +- packages/runtime-core/__tests__/hooks.spec.ts | 10 +- packages/runtime-core/__tests__/mock/utils.ts | 4 +- packages/runtime-core/src/core.ts | 7 + packages/runtime-core/src/global.ts | 8 +- packages/runtime-core/src/index.ts | 2 +- .../src/plugins/generate-preload-assets.ts | 4 +- .../src/plugins/snapshot/index.ts | 4 +- packages/runtime-core/src/type/config.ts | 4 +- packages/runtime-core/src/type/plugin.ts | 2 +- .../src/index.ts | 4 +- packages/runtime/__tests__/hooks.spec.ts | 10 +- packages/runtime/__tests__/mock/utils.ts | 4 +- packages/runtime/src/index.ts | 10 +- 72 files changed, 947 insertions(+), 1975 deletions(-) diff --git a/apps/manifest-demo/webpack-host/runtimePlugin.ts b/apps/manifest-demo/webpack-host/runtimePlugin.ts index d318363e60..3b5a09cad0 100644 --- a/apps/manifest-demo/webpack-host/runtimePlugin.ts +++ b/apps/manifest-demo/webpack-host/runtimePlugin.ts @@ -1,6 +1,6 @@ -import { FederationRuntimePlugin } from '@module-federation/runtime/types'; +import { ModuleFederationRuntimePlugin } from '@module-federation/runtime/types'; -export default function (): FederationRuntimePlugin { +export default function (): ModuleFederationRuntimePlugin { return { name: 'custom-plugin-build', beforeInit(args) { diff --git a/apps/manifest-demo/webpack-host/src/runtimePlugin.ts b/apps/manifest-demo/webpack-host/src/runtimePlugin.ts index 5aad68d793..a4bbe28a2d 100644 --- a/apps/manifest-demo/webpack-host/src/runtimePlugin.ts +++ b/apps/manifest-demo/webpack-host/src/runtimePlugin.ts @@ -1,5 +1,5 @@ -import { FederationRuntimePlugin } from '@module-federation/runtime/types'; -export default function (): FederationRuntimePlugin { +import { ModuleFederationRuntimePlugin } from '@module-federation/runtime/types'; +export default function (): ModuleFederationRuntimePlugin { return { name: 'custom-plugin', beforeInit(args) { diff --git a/apps/node-host/runtimePlugin.ts b/apps/node-host/runtimePlugin.ts index 00d2bc60c1..644c388ce4 100644 --- a/apps/node-host/runtimePlugin.ts +++ b/apps/node-host/runtimePlugin.ts @@ -1,4 +1,4 @@ -import { FederationRuntimePlugin } from '@module-federation/runtime/types'; +import { ModuleFederationRuntimePlugin } from '@module-federation/runtime/types'; export default function () { return { diff --git a/apps/router-demo/router-host-2000/src/App.tsx b/apps/router-demo/router-host-2000/src/App.tsx index 5412248dd3..a1e795da8c 100644 --- a/apps/router-demo/router-host-2000/src/App.tsx +++ b/apps/router-demo/router-host-2000/src/App.tsx @@ -15,10 +15,10 @@ import './App.css'; import BridgeReactPlugin from '@module-federation/bridge-react/plugin'; import { ErrorBoundary } from 'react-error-boundary'; import Remote1AppNew from 'remote1/app'; -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; import { Spin } from 'antd'; -const fallbackPlugin: () => FederationRuntimePlugin = function () { +const fallbackPlugin: () => ModuleFederationRuntimePlugin = function () { return { name: 'fallback-plugin', errorLoadRemote(args) { diff --git a/apps/router-demo/router-host-2000/src/runtime-plugin/error-handling/lifecycle-based.ts b/apps/router-demo/router-host-2000/src/runtime-plugin/error-handling/lifecycle-based.ts index 60dad9e005..246a592764 100644 --- a/apps/router-demo/router-host-2000/src/runtime-plugin/error-handling/lifecycle-based.ts +++ b/apps/router-demo/router-host-2000/src/runtime-plugin/error-handling/lifecycle-based.ts @@ -9,7 +9,7 @@ * 2. Entry File Loading (afterResolve): Attempts to load from a backup service */ -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; interface LifecycleBasedConfig { backupEntryUrl?: string; @@ -18,7 +18,7 @@ interface LifecycleBasedConfig { export const createLifecycleBasedPlugin = ( config: LifecycleBasedConfig = {}, -): FederationRuntimePlugin => { +): ModuleFederationRuntimePlugin => { const { backupEntryUrl = 'http://localhost:2002/mf-manifest.json', errorMessage = 'Module loading failed, please try again later', diff --git a/apps/router-demo/router-host-2000/src/runtime-plugin/error-handling/simple.ts b/apps/router-demo/router-host-2000/src/runtime-plugin/error-handling/simple.ts index e20465a4bb..f5ab081cd8 100644 --- a/apps/router-demo/router-host-2000/src/runtime-plugin/error-handling/simple.ts +++ b/apps/router-demo/router-host-2000/src/runtime-plugin/error-handling/simple.ts @@ -12,7 +12,7 @@ * Use this when you don't need different handling strategies for different error types. */ -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; interface SimpleConfig { errorMessage?: string; @@ -20,7 +20,7 @@ interface SimpleConfig { export const createSimplePlugin = ( config: SimpleConfig = {}, -): FederationRuntimePlugin => { +): ModuleFederationRuntimePlugin => { const { errorMessage = 'Module loading failed, please try again later' } = config; diff --git a/apps/router-demo/router-host-2000/src/runtime-plugin/fallback.ts b/apps/router-demo/router-host-2000/src/runtime-plugin/fallback.ts index 25793eca07..927bfe48d3 100644 --- a/apps/router-demo/router-host-2000/src/runtime-plugin/fallback.ts +++ b/apps/router-demo/router-host-2000/src/runtime-plugin/fallback.ts @@ -1,4 +1,4 @@ -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; import { createLifecycleBasedPlugin, createSimplePlugin, @@ -15,7 +15,7 @@ interface FallbackConfig { const fallbackPlugin = ( config: FallbackConfig = {}, -): FederationRuntimePlugin => { +): ModuleFederationRuntimePlugin => { const { backupEntryUrl = 'http://localhost:2002/mf-manifest.json', errorMessage = 'Module loading failed, please try again later', diff --git a/apps/router-demo/router-host-2000/src/runtime-plugin/shared-strategy.ts b/apps/router-demo/router-host-2000/src/runtime-plugin/shared-strategy.ts index 678023199c..ad5dfda029 100644 --- a/apps/router-demo/router-host-2000/src/runtime-plugin/shared-strategy.ts +++ b/apps/router-demo/router-host-2000/src/runtime-plugin/shared-strategy.ts @@ -1,7 +1,7 @@ -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; // The default external policy is version-first: the version with a higher version is used preferentially, even if the version is not loaded. Internally, it must be compatible with the previous logic, mainly loading -const sharedStrategy: () => FederationRuntimePlugin = () => ({ +const sharedStrategy: () => ModuleFederationRuntimePlugin = () => ({ name: 'shared-strategy', beforeInit(args) { const { userOptions, shareInfo } = args; diff --git a/apps/router-demo/router-host-v5-2200/src/runtime-plugin/shared-strategy.ts b/apps/router-demo/router-host-v5-2200/src/runtime-plugin/shared-strategy.ts index 13b62e4273..f4890cb15f 100644 --- a/apps/router-demo/router-host-v5-2200/src/runtime-plugin/shared-strategy.ts +++ b/apps/router-demo/router-host-v5-2200/src/runtime-plugin/shared-strategy.ts @@ -1,7 +1,7 @@ -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; // The default external policy is version-first: the version with a higher version is used preferentially, even if the version is not loaded. Internally, it must be compatible with the previous logic, mainly loading -const sharedStrategy: () => FederationRuntimePlugin = () => ({ +const sharedStrategy: () => ModuleFederationRuntimePlugin = () => ({ name: 'shared-strategy', beforeInit(args) { const { userOptions, shareInfo } = args; diff --git a/apps/runtime-demo/3005-runtime-host/src/runtimePlugin.ts b/apps/runtime-demo/3005-runtime-host/src/runtimePlugin.ts index e45bce9631..9708bc92dc 100644 --- a/apps/runtime-demo/3005-runtime-host/src/runtimePlugin.ts +++ b/apps/runtime-demo/3005-runtime-host/src/runtimePlugin.ts @@ -1,5 +1,5 @@ -import { FederationRuntimePlugin } from '@module-federation/runtime/types'; -export default function (): FederationRuntimePlugin { +import { ModuleFederationRuntimePlugin } from '@module-federation/runtime/types'; +export default function (): ModuleFederationRuntimePlugin { return { name: 'custom-plugin', beforeInit(args) { diff --git a/apps/website-new/docs/en/blog/error-load-remote.mdx b/apps/website-new/docs/en/blog/error-load-remote.mdx index 9ac0242929..c3d8e6a0c1 100644 --- a/apps/website-new/docs/en/blog/error-load-remote.mdx +++ b/apps/website-new/docs/en/blog/error-load-remote.mdx @@ -172,7 +172,7 @@ import React from 'react'; import { init, loadRemote } from '@module-federation/enhanced/runtime'; import { RetryPlugin } from '@module-federation/retry-plugin'; -+ const fallbackPlugin: () => FederationRuntimePlugin = function () { ++ const fallbackPlugin: () => ModuleFederationRuntimePlugin = function () { + return { + name: 'fallback-plugin', + errorLoadRemote(args) { @@ -263,7 +263,7 @@ export default defineConfig({ ```tsx // src/runtime-plugin/fallback.ts -import type { FederationRuntimePlugin } from '@module-federation/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/runtime'; interface FallbackConfig { // Backup service address @@ -272,7 +272,7 @@ interface FallbackConfig { errorMessage?: string; } -const fallbackPlugin = (config: FallbackConfig = {}): FederationRuntimePlugin => { +const fallbackPlugin = (config: FallbackConfig = {}): ModuleFederationRuntimePlugin => { const { backupEntryUrl = 'http://localhost:2002/mf-manifest.json', errorMessage = 'Module loading failed, please try again later' @@ -435,9 +435,9 @@ return () => ({ If you don't need to distinguish between error types, you can use a generic error handling solution: ```ts - import type { FederationRuntimePlugin } from '@module-federation/runtime'; + import type { ModuleFederationRuntimePlugin } from '@module-federation/runtime'; - const fallbackPlugin = (errorMessage = 'Module loading failed, please try again later'): FederationRuntimePlugin => { + const fallbackPlugin = (errorMessage = 'Module loading failed, please try again later'): ModuleFederationRuntimePlugin => { return { name: 'fallback-plugin', async errorLoadRemote() { diff --git a/apps/website-new/docs/en/configure/runtimeplugins.mdx b/apps/website-new/docs/en/configure/runtimeplugins.mdx index 9235630ca6..b90e958dee 100644 --- a/apps/website-new/docs/en/configure/runtimeplugins.mdx +++ b/apps/website-new/docs/en/configure/runtimeplugins.mdx @@ -13,9 +13,9 @@ Once set, runtime plugins will be automatically injected and used during the bui To create a runtime plugin file, you can name it `custom-runtime-plugin.ts`: ```ts title="custom-runtime-plugin.ts" -import { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; -export default function (): FederationRuntimePlugin { +export default function (): ModuleFederationRuntimePlugin { return { name: 'custom-plugin-build', beforeInit(args) { diff --git a/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx b/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx index 059163b97d..e520efa524 100644 --- a/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx +++ b/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx @@ -1,11 +1,60 @@ # Runtime API -## init +import { Badge } from '@theme'; +import Collapse from '@components/Collapse' + +If the build plugin is used, an MF instance will be automatically created and stored in memory when the project starts. You can directly call methods of the MF instance via the API. + +If the build plugin is not used, you need to manually create an MF instance before calling the corresponding API. + +## ModuleFederation Class + +In addition to exposing APIs, the Runtime also provides the ModuleFederation class, which you can use to create ModuleFederation instance. + +* What is a `ModuleFederation` instance? + +A `ModuleFederation` instance is an instance of the `ModuleFederation` class, which contains all the functionality of the `ModuleFederation` runtime. + +> You can enter `__FEDERATION__.__INSTANCES__` in the console to view the created instances. + +* When to use `ModuleFederation` class? + +To ensure the uniqueness of the ModuleFederation instance, after the build plugin creates an instance, it will be stored in memory. The exported APIs all first obtain the ModuleFederation instance from memory and then call the APIs of the ModuleFederation instance. This is also why APIs like loadRemote can be used directly from the `@module-federation/enhanced/runtime` package and inherently understand what application container they are attached to. + +However, this singleton pattern also limits the ability to create multiple instances, as it assumes that there is only one instance per bundle. Therefore, if you need to create a new instance , you can use the ModuleFederation class to create a new one. + +```ts +import { ModuleFederation } from '@module-federation/enhanced/runtime'; + +const mf = new ModuleFederation({ + name: '@demo/host', + remotes: [ + { + name: '@demo/sub1', + entry: 'http://localhost:8080/vmok-manifest.json' + } + ] +}); + +mf.loadRemote('@demo/sub1/util').then((m) => m.add(1, 2, 3)); +``` + +## init API Deprecated + +:::warning Warning + +This API will be deprecated. If you need to get an existing instance, you can use [getInstance](#getinstance) to retrieve the created instance. + +If you need to create a new instance, please use the [ModuleFederation](#modulefederation) class. + +::: - Type: `init(options: InitOptions): void` - Used for dynamically registering `Vmok` modules at runtime. - InitOptions: + + ```ts type InitOptions { // Name of the current host @@ -67,9 +116,11 @@ type Share = { }; ``` -- Example + + + -```js +```ts import { init, loadRemote } from '@module-federation/enhanced/runtime'; init({ @@ -85,221 +136,27 @@ init({ ], }); ``` + -## loadRemote - -- Type: `loadRemote(id: string)` -- Loads a remote module. -- Example - -```javascript -import { init, loadRemote } from '@module-federation/enhanced/runtime'; - -init({ - name: "mf_host", - remotes: [ - { - name: "remote", - // Remote module alias. After configuring an alias, the module can be loaded through the alias. Please note: the prefixes of alias and name cannot be the same. - alias: "app1", - // Specify the remote module address ending with `mf-manifest.json`, which is generated by the mf plugin. - entry: "http://localhost:2001/vmok-manifest.json" - } - ] -}); - -// remoteName + expose -loadRemote("remote/util").then((m)=> m.add(1,2,3)); - -// alias + expose -loadRemote("app1/util").then((m)=> m.add(1,2,3)); -``` - -## loadShare - -- Type: `loadShare(pkgName: string, extraOptions?: { customShareInfo?: Partial;resolver?: (sharedOptions: ShareInfos[string]) => Shared;})` -- Gets a `share` dependency. When there is a `share` dependency in the global environment that meets the requirements of the current `host`, the existing dependency that meets the `share` conditions will be reused first. Otherwise, its own dependency will be loaded and stored in the global cache. -- This `API` is **generally not called directly by the user, but is used by the build plugin to transform its own dependencies**. - -- Example - -```js -import { init, loadRemote, loadShare } from '@module-federation/enhanced/runtime'; -import React from 'react'; -import ReactDom from 'react-dom'; - -init({ - name: "mf_host", - remotes: [], - shared: { - react: { - version: "17.0.0", - scope: "default", - lib: ()=> React, - shareConfig: { - singleton: true, - requiredVersion: "^17.0.0" - } - }, - "react-dom": { - version: "17.0.0", - scope: "default", - lib: ()=> ReactDom, - shareConfig: { - singleton: true, - requiredVersion: "^17.0.0" - } - } - } -}); - - -loadShare("react").then((reactFactory)=>{ - console.log(reactFactory()) -}); -``` - -If multiple versions of shared are set, the loaded shared with the highest version will be returned by default. This behavior can be changed by setting `extraOptions.resolver`: +## getInstance API -```js -import { init, loadRemote, loadShare } from '@module-federation/enhanced/runtime'; +- Type: `getInstance(): ModuleFederation` +- Retrieves the `ModuleFederation` instance created by the build plugin -init({ - name: 'mf_host', - remotes: [], - shared: { - react: [ - { - version: '17.0.0', - scope: 'default', - get: async ()=>() => ({ version: '17.0.0' }), - shareConfig: { - singleton: true, - requiredVersion: '^17.0.0', - }, - }, - { - version: '18.0.0', - scope: 'default', - // pass lib means the shared has loaded - lib: () => ({ version: '18.0.0' }), - shareConfig: { - singleton: true, - requiredVersion: '^18.0.0', - }, - }, - ], - }, -}); - -loadShare('react', { - resolver: (sharedOptions) => { - return ( - sharedOptions.find((i) => i.version === '17.0.0') ?? sharedOptions[0] - ); - }, - }).then((reactFactory) => { - console.log(reactFactory()); // { version: '17.0.0)' } -}); -``` - -## preloadRemote - -- type - -```typescript -async function preloadRemote(preloadOptions: Array){} - -type depsPreloadArg = Omit; -type PreloadRemoteArgs = { - // Name or alias of the remote to be preloaded - nameOrAlias: string; - // Whether to preload the module's interface, the default value is false. For details, please refer to the chapter in . The @vmok/kit version needs to be greater than 1.7.6. - prefetchInterface?: boolean; - // The exposes to be preloaded - // By default, all exposes are preloaded - // When exposes are provided, only the required exposes will be loaded - exposes?: Array; // Default request - // The default is sync, which only loads the synchronous code referenced in expose - // When set to all, both synchronous and asynchronous references will be loaded - resourceCategory?: 'all' | 'sync'; - // When no value is configured, all dependencies are loaded by default - // After configuring dependencies, only the configuration options will be loaded - depsRemote?: boolean | Array; - // When not configured, resources are not filtered - // After configuration, unnecessary resources will be filtered - filter?: (assetUrl: string) => boolean; -}; -``` - -- Details - -Through `preloadRemote`, you can start preloading module resources at an earlier stage to avoid waterfall requests. What can `preloadRemote` preload: - -* `remote`'s `remoteEntry` -* `remote`'s `expose` -* `remote`'s synchronous or asynchronous resources -* `remote`'s dependent `remote` resources - -- Example +When the build plugin is used, you can call `getInstance` to retrieve the `ModuleFederation` instance created by the build plugin. ```ts -import { init, preloadRemote } from '@module-federation/enhanced/runtime'; -init({ - name: 'mf_host', - remotes: [ - { - name: 'sub1', - entry: "http://localhost:2001/mf-manifest.json" - }, - { - name: 'sub2', - entry: "http://localhost:2002/mf-manifest.json" - }, - { - name: 'sub3', - entry: "http://localhost:2003/mf-manifest.json" - }, - ], -}); +import { getInstance } from '@module-federation/enhanced/runtime'; -// Preload the @demo/sub1 module -// Filter resource information that contains 'ignore' in the resource name -// Only preload the sub-dependency @demo/sub1-button module -preloadRemote([ - { - nameOrAlias: 'sub1', - filter(assetUrl) { - return assetUrl.indexOf('ignore') === -1; - }, - depsRemote: [{ nameOrAlias: 'sub1-button' }], - }, -]); - - -// Preload the @demo/sub2 module -// Preload all exposes under @demo/sub2 -// Preload the synchronous and asynchronous resources of @demo/sub2 -preloadRemote([ - { - nameOrAlias: 'sub2', - resourceCategory: 'all', - }, -]); - -// Preload the add expose of the @demo/sub3 module -preloadRemote([ - { - nameOrAlias: 'sub3', - resourceCategory: 'all', - exposes: ['add'], - }, -]); +const mfInstance = getInstance(); +mfInstance.loadRemote('remote/util'); ``` -## registerRemotes +If the build plugin is not used, calling `getInstance` will throw an exception. In this case, you need to use the [ModuleFederation](#modulefederation) class to create a new instance. -- type +## registerRemotes API + + ```typescript function registerRemotes(remotes: Remote[], options?: { force?: boolean }) {} @@ -324,6 +181,8 @@ interface RemoteWithVersion { } ``` + + - Details **info**: Please set `force:true` with caution! @@ -332,21 +191,45 @@ If `force: true` is set, it will overwrite the registered (and loaded) modules, * Example +If use build plugin: + ```ts -import { init, registerRemotes } from '@module-federation/enhanced/runtime'; +import { registerRemotes } from '@module-federation/enhanced/runtime'; -init({ +// Add a new remote @demo/sub2 +registerRemotes([ + { + name: 'sub2', + entry: 'http://localhost:2002/mf-manifest.json', + } +]); + +// Overwrite the previous remote @demo/sub1 +registerRemotes([ + { + name: 'sub1', + entry: 'http://localhost:2003/mf-manifest.json', + } +],{ force:true }); +``` +If not use build plugin: + + +```ts +import { ModuleFederation, registerRemotes } from '@module-federation/enhanced/runtime'; + +const mf = new ModuleFederation({ name: 'mf_host', remotes: [ { name: 'sub1', entry: 'http://localhost:2001/mf-manifest.json', } - ], + ] }); // Add a new remote @demo/sub2 -registerRemotes([ +mf.registerRemotes([ { name: 'sub2', entry: 'http://localhost:2002/mf-manifest.json', @@ -354,7 +237,7 @@ registerRemotes([ ]); // Overwrite the previous remote @demo/sub1 -registerRemotes([ +mf.registerRemotes([ { name: 'sub1', entry: 'http://localhost:2003/mf-manifest.json', @@ -362,30 +245,20 @@ registerRemotes([ ],{ force:true }); ``` -## registerPlugins +## registerPlugins API - type ```typescript -function registerPlugins(plugins: FederationRuntimePlugin[]) {} +function registerPlugins(plugins: ModuleFederationRuntimePlugin[]) {} ``` * Example ```ts -import { init, registerPlugins } from '@module-federation/enhanced/runtime'; +import { registerPlugins } from '@module-federation/enhanced/runtime'; import runtimePlugin from './custom-runtime-plugin'; -init({ - name: 'mf_host', - remotes: [ - { - name: 'sub1', - entry: 'http://localhost:2001/mf-manifest.json', - } - ], -}); - // Add a new runtime plugin registerPlugins([runtimePlugin()]); @@ -416,34 +289,233 @@ registerPlugins([ ]); ``` -## ModuleFederation +## registerShared API -In addition to exposing APIs, the Runtime also provides the ModuleFederation class, which you can use to create ModuleFederation instance. + -* What is a `ModuleFederation` instance? +```typescript +function registerShared(shared: Shared) {} -A `ModuleFederation` instance is an instance of the `ModuleFederation` class, which contains all the functionality of the `ModuleFederation` runtime. +type Shared = { + [pkgName: string]: ShareArgs | ShareArgs[]; +} +type ShareArgs = (SharedBaseArgs & { + get: SharedGetter; +}) | (SharedBaseArgs & { + lib: () => Module; +}) | SharedBaseArgs; +type SharedBaseArgs = { + version?: string; + shareConfig?: SharedConfig; + scope?: string | Array; + deps?: Array; + strategy?: 'version-first' | 'loaded-first'; + loaded?: boolean; +}; +interface SharedConfig { + singleton?: boolean; + requiredVersion: false | string; + eager?: boolean; + strictVersion?: boolean; + layer?: string | null; +} +type SharedGetter = (() => () => Module) | (() => Promise<() => Module>); +``` -> You can enter `__FEDERATION__.__INSTANCES__` in the console to view the created instances. + -* When to use `ModuleFederation` class? +* Example -To ensure the uniqueness of the ModuleFederation instance, after the build plugin creates an instance, it will be stored in memory. The exported APIs all first obtain the ModuleFederation instance from memory and then call the APIs of the ModuleFederation instance. This is also why APIs like loadRemote can be used directly from the `@module-federation/enhanced/runtime` package and inherently understand what application container they are attached to. +```ts +import { registerShared } from '@module-federation/enhanced/runtime'; +import React from 'react'; +import ReactDom from 'react-dom'; -However, this singleton pattern also limits the ability to create multiple instances, as it assumes that there is only one instance per bundle. Therefore, if you need to create a new instance , you can use the ModuleFederation class to create a new one. +registerShared({ + react: { + version: "18.0.0", + scope: "default", + lib: ()=> React, + shareConfig: { + singleton: true, + requiredVersion: "^18.0.0" + } + }, + "react-dom": { + version: "18.0.0", + scope: "default", + lib: ()=> ReactDom, + shareConfig: { + singleton: true, + requiredVersion: "^18.0.0" + } + } +}); +``` + +## loadRemote API + +- Type: `loadRemote(id: string)` +- Loads a remote module. +- Example ```ts -import { ModuleFederation } from '@module-federation/enhanced/runtime'; +import { loadRemote } from '@module-federation/enhanced/runtime'; -const mf = new ModuleFederation({ - name: '@demo/host', - remotes: [ - { - name: '@demo/sub1', - entry: 'http://localhost:8080/vmok-manifest.json' - } - ] +// remoteName + expose +loadRemote("remote/util").then((m)=> m.add(1,2,3)); + +// alias + expose +loadRemote("app1/util").then((m)=> m.add(1,2,3)); +``` + +## loadShare API + +- Type: `loadShare(pkgName: string, extraOptions?: { customShareInfo?: Partial;resolver?: (sharedOptions: ShareInfos[string]) => Shared;})` +- Gets a `share` dependency. When there is a `share` dependency in the global environment that meets the requirements of the current `host`, the existing dependency that meets the `share` conditions will be reused first. Otherwise, its own dependency will be loaded and stored in the global cache. +- This `API` is **generally not called directly by the user, but is used by the build plugin to transform its own dependencies**. + + + +```ts +import { registerShared, loadShare } from '@module-federation/enhanced/runtime'; +import React from 'react'; +import ReactDom from 'react-dom'; + +registerShared({ + react: { + version: "17.0.0", + scope: "default", + lib: ()=> React, + shareConfig: { + singleton: true, + requiredVersion: "^17.0.0" + } + }, + "react-dom": { + version: "17.0.0", + scope: "default", + lib: ()=> ReactDom, + shareConfig: { + singleton: true, + requiredVersion: "^17.0.0" + } + } }); -mf.loadRemote('@demo/sub1/util').then((m) => m.add(1, 2, 3)); +loadShare("react").then((reactFactory)=>{ + console.log(reactFactory()) +}); +``` + + + +If multiple versions of shared are set, the loaded shared with the highest version will be returned by default. This behavior can be changed by setting `extraOptions.resolver`: + +```ts +// ... + +loadShare('react', { + resolver: (sharedOptions) => { + return ( + sharedOptions.find((i) => i.version === '17.0.0') ?? sharedOptions[0] + ); + }, + }).then((reactFactory) => { + console.log(reactFactory()); // { version: '17.0.0)' } +}); +``` + +## preloadRemote API + + + +```typescript +async function preloadRemote(preloadOptions: Array){} + +type depsPreloadArg = Omit; +type PreloadRemoteArgs = { + // Name or alias of the remote to be preloaded + nameOrAlias: string; + // Whether to preload the module's interface, the default value is false. For details, please refer to the chapter in . The @vmok/kit version needs to be greater than 1.7.6. + prefetchInterface?: boolean; + // The exposes to be preloaded + // By default, all exposes are preloaded + // When exposes are provided, only the required exposes will be loaded + exposes?: Array; // Default request + // The default is sync, which only loads the synchronous code referenced in expose + // When set to all, both synchronous and asynchronous references will be loaded + resourceCategory?: 'all' | 'sync'; + // When no value is configured, all dependencies are loaded by default + // After configuring dependencies, only the configuration options will be loaded + depsRemote?: boolean | Array; + // When not configured, resources are not filtered + // After configuration, unnecessary resources will be filtered + filter?: (assetUrl: string) => boolean; +}; +``` + + + +- Details + +Through `preloadRemote`, you can start preloading module resources at an earlier stage to avoid waterfall requests. What can `preloadRemote` preload: + +* `remote`'s `remoteEntry` +* `remote`'s `expose` +* `remote`'s synchronous or asynchronous resources +* `remote`'s dependent `remote` resources + +- Example + +```ts +import { registerRemotes, preloadRemote } from '@module-federation/enhanced/runtime'; + +registerRemotes([ + { + name: 'sub1', + entry: "http://localhost:2001/mf-manifest.json" + }, + { + name: 'sub2', + entry: "http://localhost:2002/mf-manifest.json" + }, + { + name: 'sub3', + entry: "http://localhost:2003/mf-manifest.json" + }, +]); + +// Preload the @demo/sub1 module +// Filter resource information that contains 'ignore' in the resource name +// Only preload the sub-dependency @demo/sub1-button module +preloadRemote([ + { + nameOrAlias: 'sub1', + filter(assetUrl) { + return assetUrl.indexOf('ignore') === -1; + }, + depsRemote: [{ nameOrAlias: 'sub1-button' }], + }, +]); + + +// Preload the @demo/sub2 module +// Preload all exposes under @demo/sub2 +// Preload the synchronous and asynchronous resources of @demo/sub2 +preloadRemote([ + { + nameOrAlias: 'sub2', + resourceCategory: 'all', + }, +]); + +// Preload the add expose of the @demo/sub3 module +preloadRemote([ + { + nameOrAlias: 'sub3', + resourceCategory: 'all', + exposes: ['add'], + }, +]); ``` diff --git a/apps/website-new/docs/en/guide/basic/runtime/runtime-hooks.mdx b/apps/website-new/docs/en/guide/basic/runtime/runtime-hooks.mdx index c3f60af36e..a10e9042b6 100644 --- a/apps/website-new/docs/en/guide/basic/runtime/runtime-hooks.mdx +++ b/apps/website-new/docs/en/guide/basic/runtime/runtime-hooks.mdx @@ -24,7 +24,7 @@ interface ModuleFederationRuntimeOptions { version?: string; remotes: Array; shared: ShareInfos; - plugins: Array; + plugins: Array; inBrowser: boolean; } ``` @@ -166,11 +166,11 @@ type ErrorLoadRemoteOptions ={ * example ```ts -import { init, loadRemote } from '@module-federation/enhanced/runtime' +import { ModuleFederation, loadRemote } from '@module-federation/enhanced/runtime' -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; -const fallbackPlugin: () => FederationRuntimePlugin = +const fallbackPlugin: () => ModuleFederationRuntimePlugin = function () { return { name: 'fallback-plugin', @@ -182,7 +182,7 @@ const fallbackPlugin: () => FederationRuntimePlugin = }; -init({ +const mf = new ModuleFederation({ name: 'mf_host', remotes: [ { @@ -194,7 +194,7 @@ init({ plugins: [fallbackPlugin()] }); -loadRemote('app1/un-existed-module').then(mod=>{ +mf.loadRemote('app1/un-existed-module').then(mod=>{ expect(mod).toEqual('fallback'); }) ``` @@ -242,11 +242,11 @@ type ResolveShareOptions ={ * example ```ts -import { init, loadRemote } from '@module-federation/enhanced/runtime' +import { ModuleFederation, loadRemote } from '@module-federation/enhanced/runtime' -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; -const customSharedPlugin: () => FederationRuntimePlugin = +const customSharedPlugin: () => ModuleFederationRuntimePlugin = function () { return { name: 'custom-shared-plugin', @@ -269,7 +269,7 @@ const customSharedPlugin: () => FederationRuntimePlugin = }; -init({ +const mf = new ModuleFederation({ name: 'mf_host', shared: { react: { @@ -287,7 +287,7 @@ init({ window.React = ()=> 'Desired Shared'; -loadShare("react").then((reactFactory)=>{ +mf.loadShare("react").then((reactFactory)=>{ expect(reactFactory()).toEqual(window.React()); }); ``` @@ -358,10 +358,9 @@ type CreateScriptOptions ={ * example ```ts -import { init } from '@module-federation/enhanced/runtime' -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; -const changeScriptAttributePlugin: () => FederationRuntimePlugin = +const changeScriptAttributePlugin: () => ModuleFederationRuntimePlugin = function () { return { name: 'change-script-attribute', diff --git a/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx b/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx index b657224111..acc55f27b9 100644 --- a/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx +++ b/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx @@ -59,21 +59,49 @@ import { PackageManagerTabs } from '@theme'; ## Module Registration +If use build plugin, you can directly register modules using `registerRemotes`. + ```ts import { init, loadRemote } from '@module-federation/enhanced/runtime'; -init({ - // The name of the consumer module, required. If the ModuleFederationPlugin plugin is also registered, the `name` should be consistent with the `name`[/configure/name.html] configured in the plugin, - name: 'mf_host', - // Remote module registration information, including module name, alias, version, etc. - remotes: [ - { - name: "remote1", - alias: "remotes-1", - entry: "http://localhost:3001/mf-manifest.json" - } - ], +registerRemotes([ + { + name: 'remote1', + alias: 'remote-1', + entry: 'http://localhost:3001/mf-manifest.json', + } +]); + +``` + +If the build plugin is not used and no instance has been created, you can create a new instance and register the modules. + +```ts +import { ModuleFederation } from '@module-federation/enhanced/runtime'; + +const mf = new ModuleFederation({ + name: 'mf_host', + remotes: [ + { + name: 'remote1', + alias: 'remote-1', + entry: 'http://localhost:3001/mf-manifest.json', + } + ] }); + +``` + +If the build plugin is not used but an instance has already been created, you can call `instance.registerRemotes` to register modules. + +```ts +mf.registerRemotes([ + { + name: 'remote2', + alias: 'remote-2', + entry: 'http://localhost:3002/mf-manifest.json', + } +]); ``` ## Module Loading diff --git a/apps/website-new/docs/en/guide/troubleshooting/other.mdx b/apps/website-new/docs/en/guide/troubleshooting/other.mdx index 090f3992c4..3d60b59055 100644 --- a/apps/website-new/docs/en/guide/troubleshooting/other.mdx +++ b/apps/website-new/docs/en/guide/troubleshooting/other.mdx @@ -104,9 +104,9 @@ Add a runtime plugin via [runtimePlugins](../../configure/runtimeplugins) and co For example, to modify the crossorigin attribute of the preloaded link to `anonymous`: ```ts title="runtimePlugin.ts -import { FederationRuntimePlugin } from '@module-federation/runtime/types'; +import { ModuleFederationRuntimePlugin } from '@module-federation/runtime/types'; -export default function MFLinkPlugin(): FederationRuntimePlugin { +export default function MFLinkPlugin(): ModuleFederationRuntimePlugin { return { name: 'link-plugin', createLink({ url }) { diff --git a/apps/website-new/docs/en/plugin/dev/index.mdx b/apps/website-new/docs/en/plugin/dev/index.mdx index a9b1f5621f..3369b7ef96 100644 --- a/apps/website-new/docs/en/plugin/dev/index.mdx +++ b/apps/website-new/docs/en/plugin/dev/index.mdx @@ -11,14 +11,14 @@ Plugins developed by developers can modify the default behavior of `Module Feder ## Developing Plugins -Plugins are provided in the form of a function similar to `() => FederationRuntimePlugin`. +Plugins are provided in the form of a function similar to `() => ModuleFederationRuntimePlugin`. ### Plugin Example ```typescript title="custom-runtime-plugin.ts" -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; -const runtimePlugin: () => FederationRuntimePlugin = function () { +const runtimePlugin: () => ModuleFederationRuntimePlugin = function () { return { name: 'my-runtime-plugin', beforeInit(args) { @@ -93,8 +93,8 @@ The naming conventions for plugins are as follows: Here is an example: ```typescript -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; -const pluginFooBar = (): FederationRuntimePlugin => ({ +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +const pluginFooBar = (): ModuleFederationRuntimePlugin => ({ name: 'xxx-plugin', //... }); @@ -104,632 +104,4 @@ export default pluginFooBar; ## Hooks - -### beforeInit - -`SyncWaterfallHook` - -Updates the federated host configuration before the initialization process of the remote container. - -- Type - -```typescript -function beforeInit(args: BeforeInitOptions): BeforeInitOptions; - -type BeforeInitOptions = { - userOptions: UserOptions; - options: ModuleFederationRuntimeOptions; - origin: ModuleFederation; - shareInfo: ShareInfos; -}; - -interface ModuleFederationRuntimeOptions { - id?: string; - name: string; - version?: string; - remotes: Array; - shared: ShareInfos; - plugins: Array; - inBrowser: boolean; -} -``` - -### init - -`SyncHook` - -Called during the initialization of the remote container. - -- Type - -```typescript -function init(args: InitOptions): void; - -type InitOptions = { - options: ModuleFederationRuntimeOptions; - origin: ModuleFederation; -}; -``` - -### beforeRequest - -`AsyncWaterfallHook` - -Called before resolving the remote container, used to inject containers or update certain content before lookup. - -- Type - -```typescript -async function beforeRequest( - args: BeforeRequestOptions, -): Promise; - -type BeforeRequestOptions = { - id: string; - options: ModuleFederationRuntimeOptions; - origin: ModuleFederation; -}; -``` - -### afterResolve - -`AsyncWaterfallHook` - -Called after the container is resolved, allows for redirection or modification of the resolved information. - -- Type - -```typescript -async function afterResolve( - args: AfterResolveOptions, -): Promise; - -type AfterResolveOptions = { - id: string; - pkgNameOrAlias: string; - expose: string; - remote: Remote; - options: ModuleFederationRuntimeOptions; - origin: ModuleFederation; - remoteInfo: RemoteInfo; - remoteSnapshot?: ModuleInfo; -}; -``` - -### onLoad - -`AsyncHook` - -Triggered when the federated module is fully loaded, allowing access to and modification of the exported content of the loaded file. - -- Type - -```typescript -async function onLoad(args: OnLoadOptions): Promise; - -type OnLoadOptions = { - id: string; - expose: string; - pkgNameOrAlias: string; - remote: Remote; - options: ModuleOptions; - origin: ModuleFederation; - exposeModule: any; - exposeModuleFactory: any; - moduleInstance: Module; -}; - -type ModuleOptions = { - remoteInfo: RemoteInfo; - host: ModuleFederation; -}; - -interface RemoteInfo { - name: string; - version?: string; - buildVersion?: string; - entry: string; - type: RemoteEntryType; - entryGlobalName: string; - shareScope: string; -} -``` - -### handlePreloadModule - -`SyncHook` - -Handles the logic for preloading federated modules. - -- Type - -```typescript -function handlePreloadModule(args: HandlePreloadModuleOptions): void; - -type HandlePreloadModuleOptions = { - id: string; - name: string; - remoteSnapshot: ModuleInfo; - preloadConfig: PreloadRemoteArgs; -}; -``` - -### errorLoadRemote - -`AsyncHook` - -This hook is invoked when the loading of a federated module fails, enabling customized error handling strategies. - -It is designed to be triggered during various lifecycle stages of module loading in the event that any of the stages fail. - -Utilize `args.lifecycle` to identify the specific lifecycle stage that has initiated the call to `errorLoadRemote`, allowing for appropriate error handling or fallback mechanisms. - - -- Type - -```typescript -async function errorLoadRemote( - args: ErrorLoadRemoteOptions, -): Promise; - -type ErrorLoadRemoteOptions = { - id: string; - error: unknown; - options?: any; - from: 'build' | 'runtime'; - lifecycle: 'onLoad' | 'beforeRequest'; - origin: ModuleFederation; -}; -``` - -- Example - -```typescript -import { init, loadRemote } from '@module-federation/enhanced/runtime'; - -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; - -const fallbackPlugin: () => FederationRuntimePlugin = function () { - return { - name: 'fallback-plugin', - errorLoadRemote(args) { - if(args.lifecycle === 'onLoad') { - const fallback = 'fallback'; - return fallback; - } else if (args.lifecycle === 'beforeRequest') { - return args - } - } - }; -}; - -init({ - name: '@demo/app-main', - remotes: [ - { - name: '@demo/app2', - entry: 'http://localhost:3006/remoteEntry.js', - alias: 'app2' - } - ], - plugins: [fallbackPlugin()] -}); - -loadRemote('app2/un-existed-module').then((mod) => { - expect(mod).toEqual('fallback'); -}); -``` - -### beforeLoadShare - -`AsyncWaterfallHook` - -Called before attempting to load or negotiate shared modules between federated applications. - -- Type - -```typescript -async function beforeLoadShare( - args: BeforeLoadShareOptions, -): Promise; - -type BeforeLoadShareOptions = { - pkgName: string; - shareInfo?: Shared; - shared: Options['shared']; - origin: ModuleFederation; -}; -``` - -### resolveShare - -`SyncWaterfallHook` - -Allows for manual resolution of shared module requests. - -- Type - -```typescript -function resolveShare(args: ResolveShareOptions): ResolveShareOptions; - -type ResolveShareOptions = { - shareScopeMap: ShareScopeMap; - scope: string; - pkgName: string; - version: string; - GlobalFederation: Federation; - resolver: () => Shared | undefined; -}; -``` - -- Example - -```typescript -import { init, loadRemote } from '@module-federation/enhanced/runtime'; - -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; - -const customSharedPlugin: () => FederationRuntimePlugin = function () { - return { - name: 'custom-shared-plugin', - resolveShare(args) { - const { shareScopeMap, scope, pkgName, version, GlobalFederation } = args; - - if (pkgName !== 'react') { - return args; - } - - // set lib - args.resolver = function () { - shareScopeMap[scope][pkgName][version] = { - lib: ()=>window.React, - loaded:true, - loading: Promise.resolve(()=>window.React) - }; // Manually replace the local share scope with the desired module - return shareScopeMap[scope][pkgName][version]; - }; - - // set get - args.resolver = function () { - shareScopeMap[scope][pkgName][version] = { - get: async ()=>()=>window.React, - }; // Manually replace the local share scope with the desired module - return shareScopeMap[scope][pkgName][version]; - }; - return args; - }, - }; -}; - -init({ - name: '@demo/app-main', - shared: { - react: { - version: '17.0.0', - scope: 'default', - lib: () => React, - shareConfig: { - singleton: true, - requiredVersion: '^17.0.0', - }, - }, - }, - plugins: [customSharedPlugin()], -}); - -window.React = () => 'Desired Shared'; - -loadShare('react').then((reactFactory) => { - expect(reactFactory()).toEqual(window.React()); -}); -``` - -### beforePreloadRemote - -`AsyncHook` - -Called before the preload handler executes any preload logic. - -- Type - -```typescript -async function beforePreloadRemote( - args: BeforePreloadRemoteOptions, -): BeforePreloadRemoteOptions; - -type BeforePreloadRemoteOptions = { - preloadOps: Array; - options: Options; - origin: ModuleFederation; -}; -``` - -### generatePreloadAssets - -`AsyncHook` - -Generates preload assets based on configuration. - -- Type - -```typescript -async function generatePreloadAssets( - args: GeneratePreloadAssetsOptions, -): Promise; - -type GeneratePreloadAssetsOptions = { - origin: ModuleFederation; - preloadOptions: PreloadOptions[number]; - remote: Remote; - remoteInfo: RemoteInfo; - remoteSnapshot: ModuleInfo; - globalSnapshot: GlobalModuleInfo; -}; - -interface PreloadAssets { - cssAssets: Array; - jsAssetsWithoutEntry: Array; - entryAssets: Array; -} -``` - -### createScript - -`SyncHook` - -- Type - -```typescript -function createScript(args: CreateScriptOptions): HTMLScriptElement | {script?: HTMLScriptElement, timeout?: number } | void; - -type CreateScriptOptions = { - url: string; - attrs?: Record; -}; -``` - -- Example - -```typescript -import { init } from '@module-federation/enhanced/runtime'; -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; - -const changeScriptAttributePlugin: () => FederationRuntimePlugin = function () { - return { - name: 'change-script-attribute', - createScript({ url, attrs }) { - if (url === testRemoteEntry) { - let script = document.createElement('script'); - script.src = testRemoteEntry; - // can modify the attrs object - attrs['loader-hooks'] = 'isTrue'; - // or add them to the script - script.setAttribute('crossorigin', 'anonymous'); - return script; - } - }, - }; -}; -``` - -- Example with script timeout - -```typescript -import { init } from '@module-federation/enhanced/runtime'; -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; - -const changeScriptAttributePlugin: () => FederationRuntimePlugin = function () { - return { - name: 'change-script-attribute', - createScript({ url }) { - if (url === testRemoteEntry) { - let script = document.createElement('script'); - script.src = testRemoteEntry; - script.setAttribute('loader-hooks', 'isTrue'); - script.setAttribute('crossorigin', 'anonymous'); - return { script, timeout: 1000 } - } - }, - }; -}; -``` - - -### createLink - -`SyncHook` - -- **Type** - -```typescript -function createLink(args: CreateLinkOptions): HTMLLinkElement | void; - -type CreateScriptOptions = { - url: string; - attrs?: Record; -}; -``` - -- Example - -```typescript -import { init } from '@module-federation/enhanced/runtime'; -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; - -const changeLinkAttributePlugin: () => FederationRuntimePlugin = function () { - return { - name: 'change-link-attribute', - createLink({ url }) { - link.setAttribute('href', url); - link.setAttribute('rel', 'preload'); - link.setAttribute('as', 'script'); - link.setAttribute('crossorigin', 'anonymous'); - return link; - }, - }; -}; -``` - -### fetch -The `fetch` function allows customizing the request that fetches the manifest JSON. A successful `Response` must yield a valid JSON. - -`AsyncHook` - -- **Type** - -```typescript -function fetch(manifestUrl: string, requestInit: RequestInit): Promise | void | false; -``` - -- Example for including the credentials when fetching the manifest JSON: - -```typescript -// fetch-manifest-with-credentials-plugin.ts -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; - -export default function (): FederationRuntimePlugin { - return { - name: 'fetch-manifest-with-credentials-plugin', - fetch(manifestUrl, requestInit) { - return fetch(manifestUrl, { - ...requestInit, - credentials: 'include' - }); - }, - } -}; -``` - -### loadEntry -The `loadEntry` function allows for full customization of remotes, enabling you to extend and create new remote types. The following two simple examples demonstrate loading JSON data and module delegation. - -`asyncHook` - -- **Type** - -```typescript -function loadEntry(args: LoadEntryOptions): RemoteEntryExports | void; - -type LoadEntryOptions = { - createScriptHook: SyncHook, - remoteEntryExports?: RemoteEntryExports, - remoteInfo: RemoteInfo -}; -interface RemoteInfo { - name: string; - version?: string; - buildVersion?: string; - entry: string; - type: RemoteEntryType; - entryGlobalName: string; - shareScope: string; -} -export type RemoteEntryExports = { - get: (id: string) => () => Promise; - init: ( - shareScope: ShareScopeMap[string], - initScope?: InitScope, - remoteEntryInitOPtions?: RemoteEntryInitOptions, - ) => void | Promise; -}; -``` - -- Example Loading JSON Data - -```typescript -// load-json-data-plugin.ts -import { init } from '@module-federation/enhanced/runtime'; -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; - -const changeScriptAttributePlugin: () => FederationRuntimePlugin = function () { - return { - name: 'load-json-data-plugin', - loadEntry({ remoteInfo }) { - if (remoteInfo.jsonA === "jsonA") { - return { - init(shareScope, initScope, remoteEntryInitOPtions) {}, - async get(path) { - const json = await fetch(remoteInfo.entry + ".json").then(res => res.json()) - return () => ({ - path, - json - }) - } - } - } - }, - }; -}; -``` -```ts -// module-federation-config -{ - remotes: { - jsonA: "jsonA@https://cdn.jsdelivr.net/npm/@module-federation/runtime/package" - } -} -``` -```ts -// src/bootstrap.js -import jsonA from "jsonA" -jsonA // {...json data} -``` - -- Exmaple Delegate Modules - -```typescript -// delegate-modules-plugin.ts -import { init } from '@module-federation/enhanced/runtime'; -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; - -const changeScriptAttributePlugin: () => FederationRuntimePlugin = function () { - return { - name: 'delegate-modules-plugin', - loadEntry({ remoteInfo }) { - if (remoteInfo.name === "delegateModulesA") { - return { - init(shareScope, initScope, remoteEntryInitOPtions) {}, - async get(path) { - path = path.replace("./", "") - const {[path]: factory} = await import("./delegateModulesA.js") - const result = await factory() - return () => result - } - } - } - }, - }; -}; -``` -```ts -// ./src/delegateModulesA.js -export async function test1() { - return new Promise(resolve => { - setTimeout(() => { - resolve("test1 value") - }, 3000) - }) -} -export async function test2() { - return new Promise(resolve => { - setTimeout(() => { - resolve("test2 value") - }, 3000) - }) -} -``` -```ts -// module-federation-config -{ - remotes: { - delegateModulesA: "delegateModulesA@https://delegateModulesA.js" - } -} -``` -```ts -// src/bootstrap.js -import test1 from "delegateModulesA/test1" -import test2 from "delegateModulesA/test2" -test1 // "test1 value" -test2 // "test2 value" -``` +Refer to [Runtime Hooks](../../guide/basic/runtime/runtime-hooks) diff --git a/apps/website-new/docs/en/plugin/plugins/retry-plugin.mdx b/apps/website-new/docs/en/plugin/plugins/retry-plugin.mdx index 9e03a9af0a..f7d3f7e5a8 100644 --- a/apps/website-new/docs/en/plugin/plugins/retry-plugin.mdx +++ b/apps/website-new/docs/en/plugin/plugins/retry-plugin.mdx @@ -84,7 +84,7 @@ the resources that need to be retried are divided into two types: fetch type and ## Type ```ts -const RetryPlugin: (params: RetryPluginParams) => FederationRuntimePlugin; +const RetryPlugin: (params: RetryPluginParams) => ModuleFederationRuntimePlugin; type RetryPluginParams = { fetch?: FetchWithRetryOptions; // fetch retry options diff --git a/apps/website-new/docs/zh/blog/announcement.mdx b/apps/website-new/docs/zh/blog/announcement.mdx index b91cc23e56..6db335fbb3 100644 --- a/apps/website-new/docs/zh/blog/announcement.mdx +++ b/apps/website-new/docs/zh/blog/announcement.mdx @@ -36,9 +36,9 @@ Module Federation 作为 Webpack 5 推出的重磅功能,自发布以来已经 Module Federation 的新版本带来了显著的变化。在这个版本中,原本嵌入在 Webpack 中的 Module Federation Runtime 能力被提取出来,形成了一个独立的 SDK。这个变化意味着,现在我们可以不依赖于任何构建工具,就能够动态地注册和加载远程模块,以及注册共享模块。此外,模块的预加载和运行时插件的使用也得到了增强,从而为模块的加载过程提供了更为强大的控制能力。 ```ts -import { init, loadRemote } from '@module-federation/enhanced/runtime'; +import { ModuleFederation } from '@module-federation/enhanced/runtime'; -init({ +const mf = new ModuleFederation({ name: '@demo/app-main', remotes: [ { @@ -54,7 +54,7 @@ init({ ], }); -loadRemote("app2/util").then((md)=>{ +mf.loadRemote("app2/util").then((md)=>{ md.add(1,2,3); }); ``` diff --git a/apps/website-new/docs/zh/blog/error-load-remote.mdx b/apps/website-new/docs/zh/blog/error-load-remote.mdx index 822108eba5..ff6893af11 100644 --- a/apps/website-new/docs/zh/blog/error-load-remote.mdx +++ b/apps/website-new/docs/zh/blog/error-load-remote.mdx @@ -57,11 +57,11 @@ Module Federation 官方提供重试插件 [@module-federation/retry-plugin](htt ```diff import React from 'react'; -import { init, loadRemote } from '@module-federation/enhanced/runtime'; +import { ModuleFederation, loadRemote } from '@module-federation/enhanced/runtime'; + import { RetryPlugin } from '@module-federation/retry-plugin'; // 模块注册 -init({ +const mf = new ModuleFederation({ name: 'host', remotes: [ { @@ -79,7 +79,7 @@ init({ }); // 模块加载 -const Remote1Button = React.lazy(() => loadRemote('remote1/button')); +const Remote1Button = React.lazy(() => mf.loadRemote('remote1/button')); export default () => { return ( @@ -90,7 +90,7 @@ export default () => { } // 方法/函数加载 -loadRemote<{add: (...args: Array)=> number }>("remote1/util").then((md)=>{ +mf.loadRemote<{add: (...args: Array)=> number }>("remote1/util").then((md)=>{ md.add(1,2,3); }); @@ -171,15 +171,16 @@ export default retryPlugin; #### 纯运行时 + 动态 import + 纯运行时注册时,远程模块在注册后实际加载前才会请求资源。 ```tsx import React from 'react'; -import { init, loadRemote } from '@module-federation/enhanced/runtime'; +import { ModuleFederation, loadRemote } from '@module-federation/enhanced/runtime'; import { RetryPlugin } from '@module-federation/retry-plugin'; // 模块注册 -init({ +const mf = new ModuleFederation({ name: 'host', remotes: [ { @@ -218,10 +219,10 @@ loadRemote<{add: (...args: Array)=> number }>("remote1/util").then((md)= ```diff import React from 'react'; -import { init, loadRemote } from '@module-federation/enhanced/runtime'; +import { ModuleFederation, loadRemote } from '@module-federation/enhanced/runtime'; import { RetryPlugin } from '@module-federation/retry-plugin'; -+ const fallbackPlugin: () => FederationRuntimePlugin = function () { ++ const fallbackPlugin: () => ModuleFederationRuntimePlugin = function () { + return { + name: 'fallback-plugin', + errorLoadRemote(args) { @@ -231,7 +232,7 @@ import { RetryPlugin } from '@module-federation/retry-plugin'; + }; // 模块注册 -init({ +const mf = new ModuleFederation({ name: 'host', remotes: [ { @@ -250,7 +251,7 @@ init({ }); // 模块加载 -const Remote1Button = React.lazy(() => loadRemote('remote1/button')); +const Remote1Button = React.lazy(() => mf.loadRemote('remote1/button')); export default () => { return ( @@ -261,7 +262,7 @@ export default () => { } // 方法/函数加载 -loadRemote<{add: (...args: Array)=> number }>("remote1/util").then((md)=>{ +mf.loadRemote<{add: (...args: Array)=> number }>("remote1/util").then((md)=>{ md.add(1,2,3); }); @@ -320,7 +321,7 @@ export default defineConfig({ ```tsx // src/runtime-plugin/fallback.ts -import type { FederationRuntimePlugin } from '@module-federation/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/runtime'; interface FallbackConfig { // 备用服务地址 @@ -329,7 +330,7 @@ interface FallbackConfig { errorMessage?: string; } -const fallbackPlugin = (config: FallbackConfig = {}): FederationRuntimePlugin => { +const fallbackPlugin = (config: FallbackConfig = {}): ModuleFederationRuntimePlugin => { const { backupEntryUrl = 'http://localhost:2002/mf-manifest.json', errorMessage = '模块加载失败,请稍后重试' @@ -494,9 +495,9 @@ export default fallbackPlugin; 如果不需要区分错误类型,也可以使用一个通用的错误处理方案: ```ts - import type { FederationRuntimePlugin } from '@module-federation/runtime'; + import type { ModuleFederationRuntimePlugin } from '@module-federation/runtime'; - const fallbackPlugin = (errorMessage = '模块加载失败,请稍后重试'): FederationRuntimePlugin => { + const fallbackPlugin = (errorMessage = '模块加载失败,请稍后重试'): ModuleFederationRuntimePlugin => { return { name: 'fallback-plugin', async errorLoadRemote() { diff --git a/apps/website-new/docs/zh/configure/runtimeplugins.mdx b/apps/website-new/docs/zh/configure/runtimeplugins.mdx index a89c98a8c1..210828eb86 100644 --- a/apps/website-new/docs/zh/configure/runtimeplugins.mdx +++ b/apps/website-new/docs/zh/configure/runtimeplugins.mdx @@ -13,9 +13,9 @@ 创建运行时插件文件: `custom-runtime-plugin.ts` ```ts title="custom-runtime-plugin.ts" -import { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; -export default function (): FederationRuntimePlugin { +export default function (): ModuleFederationRuntimePlugin { return { name: 'custom-plugin-build', beforeInit(args) { diff --git a/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx b/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx index 0473cb1aa5..0b559f3261 100644 --- a/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx +++ b/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx @@ -1,11 +1,60 @@ # Runtime API -## init +import { Badge } from '@theme'; +import Collapse from '@components/Collapse' + +若使用构建插件,项目启动时将自动创建 MF 实例并存储于内存中。此时可直接通过 API 调用 MF 实例的方法。 + +若未使用构建插件,则需手动创建 MF 实例,之后调用相应 API。 + +## ModuleFederation Class + +`Runtime` 除了暴露出 API 外,还提供了 `ModuleFederation` 类,你可以使用它来创建一个 `ModuleFederation` 实例。 + +* 什么是 `ModuleFederation` 实例 ? + +`ModuleFederation` 实例是 `ModuleFederation` 类的实例,它包含了 `ModuleFederation` 运行时的所有功能。 + +> 你可以在控制台输入 `__FEDERATION__.__INSTANCES__` 来查看已经创建好的实例。 + +* 什么时候使用 `ModuleFederation` class? + +为了保证 `ModuleFederation` 实例的唯一性,我们在构建插件创建实例后,会将其存储到内存中,导出的 API 都是先从内存中获取 `ModuleFederation` 实例,然后再调用 `ModuleFederation` 实例的 API。这也是为什么 `loadRemote` 等 API 可以直接使用的原因。 + +但是这种单例模式同时也限制无法创建多份实例,因此如果你需要**创建新的 ModuleFederation 实例**,那么你可以使用 `ModuleFederation` 类来创建一个新的实例。 + +```ts +import { ModuleFederation } from '@module-federation/enhanced/runtime'; + +const mf = new ModuleFederation({ + name: 'host', + remotes: [ + { + name: 'sub1', + entry: 'http://localhost:8080/mf-manifest.json' + } + ] +}); + +mf.loadRemote('@demo/sub1/util').then((m) => m.add(1, 2, 3)); +``` + +## init API 废弃 + +:::warning Warning + +此 API 将被废弃。如果需要获取已创建的实例,可以使用 [getInstance](#getinstance) 获取已创建的实例。 + +如果需要创建新的实例,请使用 [ModuleFederation](#modulefederation) class。 + +::: - Type: `init(options: InitOptions): void` - 用于运行时动态注册 `Vmok` 模块 - InitOptions: + + ```ts type InitOptions { // 当前 host 的名称 @@ -67,9 +116,11 @@ type Share = { }; ``` -- 示例 + + + -```js +```ts import { init, loadRemote } from '@module-federation/enhanced/runtime'; init({ @@ -85,221 +136,27 @@ init({ ], }); ``` + -## loadRemote - -- Type: `loadRemote(id: string)` -- 加载远程模块 -- 示例 - -```javascript -import { init, loadRemote } from '@module-federation/enhanced/runtime'; - -init({ - name: "mf_host", - remotes: [ - { - name: "remote", - // 远程模块别名,配置别名后可通过别名加载模块。请注意:alias 和 name 的前缀不能相等 - alias: "app1", - // 指定以 `mf-manifest.json` 结尾的远程模块地址,该文件由 mf 插件生成 - entry: "http://localhost:2001/vmok-manifest.json" - } - ] -}); - -// remoteName + expose -loadRemote("remote/util").then((m)=> m.add(1,2,3)); - -// alias + expose -loadRemote("app1/util").then((m)=> m.add(1,2,3)); -``` - -## loadShare - -- Type: `loadShare(pkgName: string, extraOptions?: { customShareInfo?: Partial;resolver?: (sharedOptions: ShareInfos[string]) => Shared;})` -- 获取 `share` 依赖,当全局环境有符合当前 `host` 的 `share` 依赖时,将优先复用当前已存在且满足 `share` 条件的依赖,否则将加载自身的依赖并存入全局缓存 -- 该 `API` **一般不由用户直接调用,用于构建插件转换自身依赖时使用** - -- 示例 - -```js -import { init, loadRemote, loadShare } from '@module-federation/enhanced/runtime'; -import React from 'react'; -import ReactDom from 'react-dom'; - -init({ - name: "mf_host", - remotes: [], - shared: { - react: { - version: "17.0.0", - scope: "default", - lib: ()=> React, - shareConfig: { - singleton: true, - requiredVersion: "^17.0.0" - } - }, - "react-dom": { - version: "17.0.0", - scope: "default", - lib: ()=> ReactDom, - shareConfig: { - singleton: true, - requiredVersion: "^17.0.0" - } - } - } -}); - - -loadShare("react").then((reactFactory)=>{ - console.log(reactFactory()) -}); -``` - -如果设置了多个版本 shared,默认会返回已加载且最高版本的 shared 。可以通过设置 `extraOptions.resolver` 来改变这个行为: - -```js -import { init, loadRemote, loadShare } from '@module-federation/enhanced/runtime'; - -init({ - name: 'mf_host', - remotes: [], - shared: { - react: [ - { - version: '17.0.0', - scope: 'default', - get: async ()=>() => ({ version: '17.0.0' }), - shareConfig: { - singleton: true, - requiredVersion: '^17.0.0', - }, - }, - { - version: '18.0.0', - scope: 'default', - // pass lib means the shared has loaded - lib: () => ({ version: '18.0.0' }), - shareConfig: { - singleton: true, - requiredVersion: '^18.0.0', - }, - }, - ], - }, -}); - -loadShare('react', { - resolver: (sharedOptions) => { - return ( - sharedOptions.find((i) => i.version === '17.0.0') ?? sharedOptions[0] - ); - }, - }).then((reactFactory) => { - console.log(reactFactory()); // { version: '17.0.0)' } -}); -``` - -## preloadRemote - -- type - -```typescript -async function preloadRemote(preloadOptions: Array){} - -type depsPreloadArg = Omit; -type PreloadRemoteArgs = { - // 预加载 remote 的名称和别名 - nameOrAlias: string; - // 是否需要预加载模块的接口,默认值为 false,具体参考<性能优化>中章节,@vmok/kit 版本需要大于 1.7.6 - prefetchInterface?: boolean; - // 需要预加载的 expose - // 默认预加载所有的 expose - // 提供了 expose 时只会加载所需要的 expose - exposes?: Array; // 默认请求 - // 默认为 sync,只会加载 expose 中引用的同步代码 - // 设置为 all 时将加载同步引用和异步引用 - resourceCategory?: 'all' | 'sync'; - // 未配置值时默认加载所有的依赖 - // 配置了依赖后仅会加载配置选项 - depsRemote?: boolean | Array; - // 未配置时不过滤资源 - // 配置了后将会过滤不需要的资源 - filter?: (assetUrl: string) => boolean; -}; -``` - -- Details - -通过 `preloadRemote` 可以在更早的阶段开始预加载模块资源,避免出现瀑布请求,`preloadRemote` 可以预加载哪些内容: +## getInstance API -* `remote` 的 `remoteEntry` -* `remote` 的 `expose` -* `remote` 的同步资源还是异步资源 -* `remote` 依赖的 `remote` 资源 +- Type: `getInstance(): ModuleFederation` +- 获取构建插件创建的 `ModuleFederation` 实例 -- Example +当使用了构建插件后,可以调用 `getInstance` 获取构建插件创建的 `ModuleFederation` 实例。 ```ts -import { init, preloadRemote } from '@module-federation/enhanced/runtime'; -init({ - name: 'mf_host', - remotes: [ - { - name: 'sub1', - entry: "http://localhost:2001/mf-manifest.json" - }, - { - name: 'sub2', - entry: "http://localhost:2002/mf-manifest.json" - }, - { - name: 'sub3', - entry: "http://localhost:2003/mf-manifest.json" - }, - ], -}); - -// 预加载 @demo/sub1 模块 -// 过滤资源名称中携带 ignore 的资源信息 -// 只预加载子依赖的 @demo/sub1-button 模块 -preloadRemote([ - { - nameOrAlias: 'sub1', - filter(assetUrl) { - return assetUrl.indexOf('ignore') === -1; - }, - depsRemote: [{ nameOrAlias: 'sub1-button' }], - }, -]); - - -// 预加载 @demo/sub2 模块 -// 预加载 @demo/sub2 下的所有 expose -// 预加载 @demo/sub2 的同步资源和异步资源 -preloadRemote([ - { - nameOrAlias: 'sub2', - resourceCategory: 'all', - }, -]); +import { getInstance } from '@module-federation/enhanced/runtime'; -// 预加载 @demo/sub3 模块的 add expose -preloadRemote([ - { - nameOrAlias: 'sub3', - resourceCategory: 'all', - exposes: ['add'], - }, -]); +const mfInstance = getInstance(); +mfInstance.loadRemote('remote/util'); ``` -## registerRemotes +如果没有使用构建插件,调用 `getInstance` 会抛出异常,此时你需要使用 [ModuleFederation](#modulefederation) class 来创建一个新的实例。 -- type +## registerRemotes API + + ```typescript function registerRemotes(remotes: Remote[], options?: { force?: boolean }) {} @@ -324,6 +181,8 @@ interface RemoteWithVersion { } ``` + + - Details **info**: 请谨慎设置 `force:true` ! @@ -332,21 +191,45 @@ interface RemoteWithVersion { * Example +使用了构建插件 + ```ts -import { init, registerRemotes } from '@module-federation/enhanced/runtime'; +import { registerRemotes } from '@module-federation/enhanced/runtime'; -init({ +// 增加新的 remote @demo/sub2 +registerRemotes([ + { + name: 'sub2', + entry: 'http://localhost:2002/mf-manifest.json', + } +]); + +// 覆盖之前的 remote @demo/sub1 +registerRemotes([ + { + name: 'sub1', + entry: 'http://localhost:2003/mf-manifest.json', + } +],{ force:true }); +``` + +没使用构建插件 + +```ts +import { ModuleFederation, registerRemotes } from '@module-federation/enhanced/runtime'; + +const mf = new ModuleFederation({ name: 'mf_host', remotes: [ { name: 'sub1', entry: 'http://localhost:2001/mf-manifest.json', } - ], + ] }); // 增加新的 remote @demo/sub2 -registerRemotes([ +mf.registerRemotes([ { name: 'sub2', entry: 'http://localhost:2002/mf-manifest.json', @@ -354,7 +237,7 @@ registerRemotes([ ]); // 覆盖之前的 remote @demo/sub1 -registerRemotes([ +mf.registerRemotes([ { name: 'sub1', entry: 'http://localhost:2003/mf-manifest.json', @@ -362,30 +245,20 @@ registerRemotes([ ],{ force:true }); ``` -## registerPlugins +## registerPlugins API - type ```typescript -function registerPlugins(plugins: FederationRuntimePlugin[]) {} +function registerPlugins(plugins: ModuleFederationRuntimePlugin[]) {} ``` * Example ```ts -import { init, registerPlugins } from '@module-federation/enhanced/runtime'; +import { registerPlugins } from '@module-federation/enhanced/runtime'; import runtimePlugin from './custom-runtime-plugin'; -init({ - name: 'mf_host', - remotes: [ - { - name: 'sub1', - entry: 'http://localhost:2001/mf-manifest.json', - } - ], -}); - // 增加新的运行时插件 registerPlugins([runtimePlugin()]); @@ -416,34 +289,234 @@ registerPlugins([ ]); ``` -## ModuleFederation +## registerShared API -`Runtime` 除了暴露出 API 外,还提供了 `ModuleFederation` 类,你可以使用它来创建一个 `ModuleFederation` 实例。 + -* 什么是 `ModuleFederation` 实例 ? +```typescript +function registerShared(shared: Shared) {} -`ModuleFederation` 实例是 `ModuleFederation` 类的实例,它包含了 `ModuleFederation` 运行时的所有功能。 +type Shared = { + [pkgName: string]: ShareArgs | ShareArgs[]; +} +type ShareArgs = (SharedBaseArgs & { + get: SharedGetter; +}) | (SharedBaseArgs & { + lib: () => Module; +}) | SharedBaseArgs; +type SharedBaseArgs = { + version?: string; + shareConfig?: SharedConfig; + scope?: string | Array; + deps?: Array; + strategy?: 'version-first' | 'loaded-first'; + loaded?: boolean; +}; +interface SharedConfig { + singleton?: boolean; + requiredVersion: false | string; + eager?: boolean; + strictVersion?: boolean; + layer?: string | null; +} +type SharedGetter = (() => () => Module) | (() => Promise<() => Module>); +``` -> 你可以在控制台输入 `__FEDERATION__.__INSTANCES__` 来查看已经创建好的实例。 + -* 什么时候使用 `ModuleFederation` class? +* Example -为了保证 `ModuleFederation` 实例的唯一性,我们在构建插件创建实例后,会将其存储到内存中,导出的 API 都是先从内存中获取 `ModuleFederation` 实例,然后再调用 `ModuleFederation` 实例的 API。这也是为什么 `loadRemote` 等 API 可以直接使用的原因。 +```ts +import { registerShared } from '@module-federation/enhanced/runtime'; +import React from 'react'; +import ReactDom from 'react-dom'; -但是这种单例模式同时也限制无法创建多份实例,因此如果你需要**创建新的 ModuleFederation 实例**,那么你可以使用 `ModuleFederation` 类来创建一个新的实例。 +registerShared({ + react: { + version: "18.0.0", + scope: "default", + lib: ()=> React, + shareConfig: { + singleton: true, + requiredVersion: "^18.0.0" + } + }, + "react-dom": { + version: "18.0.0", + scope: "default", + lib: ()=> ReactDom, + shareConfig: { + singleton: true, + requiredVersion: "^18.0.0" + } + } +}); +``` + +## loadRemote API + +- Type: `loadRemote(id: string)` +- 加载远程模块 +- 示例 ```ts -import { ModuleFederation } from '@module-federation/enhanced/runtime'; +import { getInstance, loadRemote } from '@module-federation/enhanced/runtime'; -const mf = new ModuleFederation({ - name: 'host', - remotes: [ - { - name: 'sub1', - entry: 'http://localhost:8080/mf-manifest.json' - } - ] +// remoteName + expose +loadRemote("remote/util").then((m)=> m.add(1,2,3)); + +// alias + expose +loadRemote("app1/util").then((m)=> m.add(1,2,3)); +``` + +## loadShare API + +- Type: `loadShare(pkgName: string, extraOptions?: { customShareInfo?: Partial;resolver?: (sharedOptions: ShareInfos[string]) => Shared;})` +- 获取 `share` 依赖,当全局环境有符合当前 `host` 的 `share` 依赖时,将优先复用当前已存在且满足 `share` 条件的依赖,否则将加载自身的依赖并存入全局缓存 +- 该 `API` **一般不由用户直接调用,用于构建插件转换自身依赖时使用** + + + +```ts +import { registerShared, loadShare } from '@module-federation/enhanced/runtime'; +import React from 'react'; +import ReactDom from 'react-dom'; + +registerShared({ + react: { + version: "17.0.0", + scope: "default", + lib: ()=> React, + shareConfig: { + singleton: true, + requiredVersion: "^17.0.0" + } + }, + "react-dom": { + version: "17.0.0", + scope: "default", + lib: ()=> ReactDom, + shareConfig: { + singleton: true, + requiredVersion: "^17.0.0" + } + } }); -mf.loadRemote('@demo/sub1/util').then((m) => m.add(1, 2, 3)); + +loadShare("react").then((reactFactory)=>{ + console.log(reactFactory()) +}); +``` + + + +如果设置了多个版本 shared,默认会返回已加载且最高版本的 shared 。可以通过设置 `extraOptions.resolver` 来改变这个行为: + +```ts +// ... + +loadShare('react', { + resolver: (sharedOptions) => { + return ( + sharedOptions.find((i) => i.version === '17.0.0') ?? sharedOptions[0] + ); + }, + }).then((reactFactory) => { + console.log(reactFactory()); // { version: '17.0.0)' } +}); +``` + +## preloadRemote API + + + +```typescript +async function preloadRemote(preloadOptions: Array){} + +type depsPreloadArg = Omit; +type PreloadRemoteArgs = { + // 预加载 remote 的名称和别名 + nameOrAlias: string; + // 是否需要预加载模块的接口,默认值为 false,具体参考<性能优化>中章节,@vmok/kit 版本需要大于 1.7.6 + prefetchInterface?: boolean; + // 需要预加载的 expose + // 默认预加载所有的 expose + // 提供了 expose 时只会加载所需要的 expose + exposes?: Array; // 默认请求 + // 默认为 sync,只会加载 expose 中引用的同步代码 + // 设置为 all 时将加载同步引用和异步引用 + resourceCategory?: 'all' | 'sync'; + // 未配置值时默认加载所有的依赖 + // 配置了依赖后仅会加载配置选项 + depsRemote?: boolean | Array; + // 未配置时不过滤资源 + // 配置了后将会过滤不需要的资源 + filter?: (assetUrl: string) => boolean; +}; +``` + + + +- Details + +通过 `preloadRemote` 可以在更早的阶段开始预加载模块资源,避免出现瀑布请求,`preloadRemote` 可以预加载哪些内容: + +* `remote` 的 `remoteEntry` +* `remote` 的 `expose` +* `remote` 的同步资源还是异步资源 +* `remote` 依赖的 `remote` 资源 + +- Example + +```ts +import { registerRemotes, preloadRemote } from '@module-federation/enhanced/runtime'; + +registerRemotes([ + { + name: 'sub1', + entry: "http://localhost:2001/mf-manifest.json" + }, + { + name: 'sub2', + entry: "http://localhost:2002/mf-manifest.json" + }, + { + name: 'sub3', + entry: "http://localhost:2003/mf-manifest.json" + }, +]); + +// 预加载 @demo/sub1 模块 +// 过滤资源名称中携带 ignore 的资源信息 +// 只预加载子依赖的 @demo/sub1-button 模块 +preloadRemote([ + { + nameOrAlias: 'sub1', + filter(assetUrl) { + return assetUrl.indexOf('ignore') === -1; + }, + depsRemote: [{ nameOrAlias: 'sub1-button' }], + }, +]); + + +// 预加载 @demo/sub2 模块 +// 预加载 @demo/sub2 下的所有 expose +// 预加载 @demo/sub2 的同步资源和异步资源 +preloadRemote([ + { + nameOrAlias: 'sub2', + resourceCategory: 'all', + }, +]); + +// 预加载 @demo/sub3 模块的 add expose +preloadRemote([ + { + nameOrAlias: 'sub3', + resourceCategory: 'all', + exposes: ['add'], + }, +]); ``` diff --git a/apps/website-new/docs/zh/guide/basic/runtime/runtime-hooks.mdx b/apps/website-new/docs/zh/guide/basic/runtime/runtime-hooks.mdx index 2745a082c7..2ecd6204a3 100644 --- a/apps/website-new/docs/zh/guide/basic/runtime/runtime-hooks.mdx +++ b/apps/website-new/docs/zh/guide/basic/runtime/runtime-hooks.mdx @@ -24,7 +24,7 @@ interface ModuleFederationRuntimeOptions { version?: string; remotes: Array; shared: ShareInfos; - plugins: Array; + plugins: Array; inBrowser: boolean; } ``` @@ -168,11 +168,11 @@ type ErrorLoadRemoteOptions ={ * example ```ts -import { init, loadRemote } from '@module-federation/enhanced/runtime' +import { ModuleFederation } from '@module-federation/enhanced/runtime' -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; -const fallbackPlugin: () => FederationRuntimePlugin = +const fallbackPlugin: () => ModuleFederationRuntimePlugin = function () { return { name: 'fallback-plugin', @@ -184,7 +184,7 @@ const fallbackPlugin: () => FederationRuntimePlugin = }; -init({ +const mf = new ModuleFederation({ name: 'mf_host', remotes: [ { @@ -196,7 +196,7 @@ init({ plugins: [fallbackPlugin()] }); -loadRemote('app1/un-existed-module').then(mod=>{ +mf.loadRemote('app1/un-existed-module').then(mod=>{ expect(mod).toEqual('fallback'); }) ``` @@ -244,11 +244,11 @@ type ResolveShareOptions ={ * example ```ts -import { init, loadRemote } from '@module-federation/enhanced/runtime' +import { ModuleFederation, loadRemote } from '@module-federation/enhanced/runtime' -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; -const customSharedPlugin: () => FederationRuntimePlugin = +const customSharedPlugin: () => ModuleFederationRuntimePlugin = function () { return { name: 'custom-shared-plugin', @@ -271,7 +271,7 @@ const customSharedPlugin: () => FederationRuntimePlugin = }; -init({ +const mf = new ModuleFederation({ name: 'mf_host', shared: { react: { @@ -289,7 +289,7 @@ init({ window.React = ()=> 'Desired Shared'; -loadShare("react").then((reactFactory)=>{ +mf.loadShare("react").then((reactFactory)=>{ expect(reactFactory()).toEqual(window.React()); }); ``` @@ -360,10 +360,9 @@ type CreateScriptOptions ={ * example ```ts -import { init } from '@module-federation/enhanced/runtime' -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; -const changeScriptAttributePlugin: () => FederationRuntimePlugin = +const changeScriptAttributePlugin: () => ModuleFederationRuntimePlugin = function () { return { name: 'change-script-attribute', diff --git a/apps/website-new/docs/zh/guide/basic/runtime/runtime.mdx b/apps/website-new/docs/zh/guide/basic/runtime/runtime.mdx index 47b3ee7ab1..5e40b24062 100644 --- a/apps/website-new/docs/zh/guide/basic/runtime/runtime.mdx +++ b/apps/website-new/docs/zh/guide/basic/runtime/runtime.mdx @@ -58,21 +58,49 @@ import { PackageManagerTabs } from '@theme'; ## 模块注册 +如果使用了构建插件,那么可以直接使用 `registerRemotes` 注册模块。 + +```ts +import { registerRemotes } from '@module-federation/enhanced/runtime'; + +registerRemotes([ + { + name: 'remote1', + alias: 'remote-1', + entry: 'http://localhost:3001/mf-manifest.json', + } +]); + +``` + +如果没有使用构建插件,并且没有创建实例,那么可以创建新的实例,并注册模块。 + ```ts -import { init, loadRemote } from '@module-federation/enhanced/runtime'; - -init({ - // 消费者模块名称,必填。如同时注册了 ModuleFederationPlugin 插件,则 `name` 应与插件中配置的 `name`[/configure/name.html] 保持一致, - name: 'mf_host', - // 远程模块注册信息,包含模块名称、别名、版本等信息。 - remotes: [ - { - name: "remote1", - alias: "remotes-1", - entry: "http://localhost:3001/mf-manifest.json" - } - ], +import { ModuleFederation } from '@module-federation/enhanced/runtime'; + +const mf = new ModuleFederation({ + name: 'mf_host', + remotes: [ + { + name: 'remote1', + alias: 'remote-1', + entry: 'http://localhost:3001/mf-manifest.json', + } + ] }); + +``` + +如果没有使用构建插件,但已经创建了实例,那么可以调用 `instance.registerRemotes` 进行模块注册。 + +```ts +mf.registerRemotes([ + { + name: 'remote2', + alias: 'remote-2', + entry: 'http://localhost:3002/mf-manifest.json', + } +]); ``` ## 模块加载 diff --git a/apps/website-new/docs/zh/guide/troubleshooting/other.mdx b/apps/website-new/docs/zh/guide/troubleshooting/other.mdx index dfb0f5fe6c..bdac2e0332 100644 --- a/apps/website-new/docs/zh/guide/troubleshooting/other.mdx +++ b/apps/website-new/docs/zh/guide/troubleshooting/other.mdx @@ -78,9 +78,9 @@ Uncaught TypeError: Cannot read properties on null (reading `useState`) 例如需要修改预加载 link 的 crossorigin 属性为 `anonymous`: ```ts title="runtimePlugin.ts -import { FederationRuntimePlugin } from '@module-federation/runtime/types'; +import { ModuleFederationRuntimePlugin } from '@module-federation/runtime/types'; -export default function MFLinkPlugin(): FederationRuntimePlugin { +export default function MFLinkPlugin(): ModuleFederationRuntimePlugin { return { name: 'link-plugin', createLink({ url }) { diff --git a/apps/website-new/docs/zh/plugin/dev/index.mdx b/apps/website-new/docs/zh/plugin/dev/index.mdx index e806542643..65924f8928 100644 --- a/apps/website-new/docs/zh/plugin/dev/index.mdx +++ b/apps/website-new/docs/zh/plugin/dev/index.mdx @@ -11,14 +11,14 @@ Module Federation 提供了一套轻量的运行时插件系统,用以实现 ## 开发插件 -插件提供类似 `() => FederationRuntimePlugin` 的函数。 +插件提供类似 `() => ModuleFederationRuntimePlugin` 的函数。 ### 插件示例 ```ts title="custom-runtime-plugin.ts" -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; -const runtimePlugin: () => FederationRuntimePlugin = function () { +const runtimePlugin: () => ModuleFederationRuntimePlugin = function () { return { name: 'my-runtime-plugin', beforeInit(args) { @@ -93,8 +93,8 @@ registerPlugins([runtimePlugin()]); 下面是一个例子: ```ts -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; -const pluginFooBar = (): FederationRuntimePlugin => ({ +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +const pluginFooBar = (): ModuleFederationRuntimePlugin => ({ name: 'xxx-plugin', //... }); @@ -104,632 +104,4 @@ export default pluginFooBar; ## hooks -当然,这里是上述内容的中文翻译: - -### beforeInit - -`SyncWaterfallHook` - -在远程容器的初始化过程之前更新 Federation 实例配置。 - -- 类型 - -```ts -function beforeInit(args: BeforeInitOptions): BeforeInitOptions; - -type BeforeInitOptions = { - userOptions: UserOptions; - options: ModuleFederationRuntimeOptions; - origin: ModuleFederation; - shareInfo: ShareInfos; -}; - -interface ModuleFederationRuntimeOptions { - id?: string; - name: string; - version?: string; - remotes: Array; - shared: ShareInfos; - plugins: Array; - inBrowser: boolean; -} -``` - -### init - -`SyncHook` - -在远程容器初始化期间调用。 - -- 类型 - -```ts -function init(args: InitOptions): void; - -type InitOptions = { - options: ModuleFederationRuntimeOptions; - origin: ModuleFederation; -}; -``` - -### beforeRequest - -`AsyncWaterfallHook` - -在解析远程容器之前调用,用于注入容器或在查找之前更新某些内容。 - -- 类型 - -```ts -async function beforeRequest( - args: BeforeRequestOptions, -): Promise; - -type BeforeRequestOptions = { - id: string; - options: ModuleFederationRuntimeOptions; - origin: ModuleFederation; -}; -``` - -### afterResolve - -`AsyncWaterfallHook` - -在解析容器后调用,允许重定向或修改已解析的信息。 - -- 类型 - -```ts -async function afterResolve( - args: AfterResolveOptions, -): Promise; - -type AfterResolveOptions = { - id: string; - pkgNameOrAlias: string; - expose: string; - remote: Remote; - options: ModuleFederationRuntimeOptions; - origin: ModuleFederation; - remoteInfo: RemoteInfo; - remoteSnapshot?: ModuleInfo; -}; -``` - -### onLoad - -`AsyncHook` - -远程模块加载完毕时触发,允许访问和修改已加载文件的导出内容。 - -- 类型 - -```ts -async function onLoad(args: OnLoadOptions): Promise; - -type OnLoadOptions = { - id: string; - expose: string; - pkgNameOrAlias: string; - remote: Remote; - options: ModuleOptions; - origin: ModuleFederation; - exposeModule: any; - exposeModuleFactory: any; - moduleInstance: Module; -}; - -type ModuleOptions = { - remoteInfo: RemoteInfo; - host: ModuleFederation; -}; - -interface RemoteInfo { - name: string; - version?: string; - buildVersion?: string; - entry: string; - type: RemoteEntryType; - entryGlobalName: string; - shareScope: string; -} -``` - -### handlePreloadModule - -`SyncHook` - -处理远程模块预加载逻辑。 - -- 类型 - -```ts -function handlePreloadModule(args: HandlePreloadModuleOptions): void; - -type HandlePreloadModuleOptions = { - id: string; - name: string; - remoteSnapshot: ModuleInfo; - preloadConfig: PreloadRemoteArgs; -}; -``` - -### errorLoadRemote - -`AsyncHook` - -当远程模块加载失败时,此钩子将被触发,允许自定义错误处理策略。 - -它被设计为在模块加载的各个生命周期阶段失败时触发。 - -利用 `args.lifecycle` 来识别调用 `errorLoadRemote` 的具体生命周期阶段,从而实现适当的错误处理或回退机制。 - - -- 类型 - -```typescript -async function errorLoadRemote( - args: ErrorLoadRemoteOptions, -): Promise; - -type ErrorLoadRemoteOptions = { - id: string; - error: unknown; - options?: any; - from: 'build' | 'runtime'; - lifecycle: 'onLoad' | 'beforeRequest'; - origin: ModuleFederation; -}; -``` - -- 示例 - -```typescript -import { init, loadRemote } from '@module-federation/enhanced/runtime'; - -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; - -const fallbackPlugin: () => FederationRuntimePlugin = function () { - return { - name: 'fallback-plugin', - errorLoadRemote(args) { - if(args.lifecycle === 'onLoad') { - const fallback = 'fallback'; - return fallback; - } else if (args.lifecycle === 'beforeRequest') { - return args - } - } - }; -}; - -init({ - name: '@demo/app-main', - remotes: [ - { - name: '@demo/app2', - entry: 'http://localhost:3006/remoteEntry.js', - alias: 'app2' - } - ], - plugins: [fallbackPlugin()] -}); - -loadRemote('app2/un-existed-module').then((mod) => { - expect(mod).toEqual('fallback'); -}); -``` -### beforeLoadShare - -`AsyncWaterfallHook` - -在尝试加载或协商联合应用之间的共享模块之前调用。 - -- 类型 - -```ts -async function beforeLoadShare( - args: BeforeLoadShareOptions, -): Promise; - -type BeforeLoadShareOptions = { - pkgName: string; - shareInfo?: Shared; - shared: Options['shared']; - origin: ModuleFederation; -}; -``` - -### resolveShare - -`SyncWaterfallHook` - -允许手动解析共享模块请求。 - -- 类型 - -```ts -function resolveShare(args: ResolveShareOptions): ResolveShareOptions; - -type ResolveShareOptions = { - shareScopeMap: ShareScopeMap; - scope: string; - pkgName: string; - version: string; - GlobalFederation: Federation; - resolver: () => Shared | undefined; -}; -``` - -- 示例 - -```ts -import { init, loadRemote } from '@module-federation/enhanced/runtime'; - -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; - -const customSharedPlugin: () => FederationRuntimePlugin = function () { - return { - name: 'custom-shared-plugin', - resolveShare(args) { - const { shareScopeMap, scope, pkgName, version, GlobalFederation } = args; - - if (pkgName !== 'react') { - return args; - } - - // set lib - args.resolver = function () { - shareScopeMap[scope][pkgName][version] = { - lib: ()=>window.React, - loaded:true, - loading: Promise.resolve(()=>window.React) - }; // Manually replace the local share scope with the desired module - return shareScopeMap[scope][pkgName][version]; - }; - - // set get - args.resolver = function () { - shareScopeMap[scope][pkgName][version] = { - get: async ()=>()=>window.React, - }; // Manually replace the local share scope with the desired module - return shareScopeMap[scope][pkgName][version]; - }; - return args; - }, - }; -}; - -init({ - name: '@demo/app-main', - shared: { - react: { - version: '17.0.0', - scope: 'default', - lib: () => React, - shareConfig: { - singleton: true, - requiredVersion: '^17.0.0', - }, - }, - }, - plugins: [customSharedPlugin()], -}); - -window.React = () => 'Desired Shared'; - -loadShare('react').then((reactFactory) => { - expect(reactFactory()).toEqual(window.React()); -}); -``` - -### beforePreloadRemote - -`AsyncHook` - -在预加载处理程序执行任何预加载逻辑之前调用。 - -- 类型 - -```ts -async function beforePreloadRemote( - args: BeforePreloadRemoteOptions, -): BeforePreloadRemoteOptions; - -type BeforePreloadRemoteOptions = { - preloadOps: Array; - options: Options; - origin: ModuleFederation; -}; -``` - -### generatePreloadAssets - -`AsyncHook` - -根据配置生成预加载资产。 - -- 类型 - -```ts -async function generatePreloadAssets( - args: GeneratePreloadAssetsOptions, -): Promise; - -type GeneratePreloadAssetsOptions = { - origin: ModuleFederation; - preloadOptions: PreloadOptions[number]; - remote: Remote; - remoteInfo: RemoteInfo; - remoteSnapshot: ModuleInfo; - globalSnapshot: GlobalModuleInfo; -}; - -interface PreloadAssets { - cssAssets: Array; - jsAssetsWithoutEntry: Array; - entryAssets: Array; -} -``` - -### createScript - -`SyncHook` - -- 类型 - -```typescript -function createScript(args: CreateScriptOptions): HTMLScriptElement | {script?: HTMLScriptElement, timeout?: number } | void; - -type CreateScriptOptions = { - url: string; - attrs?: Record; -}; -``` - -- 示例 - -```typescript -import { init } from '@module-federation/enhanced/runtime'; -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; - -const changeScriptAttributePlugin: () => FederationRuntimePlugin = function () { - return { - name: 'change-script-attribute', - createScript({ url }) { - if (url === testRemoteEntry) { - let script = document.createElement('script'); - script.src = testRemoteEntry; - script.setAttribute('loader-hooks', 'isTrue'); - script.setAttribute('crossorigin', 'anonymous'); - return script; - } - }, - }; -}; -``` - -- 示例带有脚本超时 - -```typescript -import { init } from '@module-federation/enhanced/runtime'; -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; - -const changeScriptAttributePlugin: () => FederationRuntimePlugin = function () { - return { - name: 'change-script-attribute', - createScript({ url }) { - if (url === testRemoteEntry) { - let script = document.createElement('script'); - script.src = testRemoteEntry; - script.setAttribute('loader-hooks', 'isTrue'); - script.setAttribute('crossorigin', 'anonymous'); - return { script, timeout: 1000 } - } - }, - }; -}; -``` - - -### createLink - -`SyncHook` - -- 类型 - -```typescript -function createLink(args: CreateLinkOptions): HTMLLinkElement | void; - -type CreateScriptOptions = { - url: string; - attrs?: Record; -}; -``` - -- 示例 - -```typescript -import { init } from '@module-federation/enhanced/runtime'; -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; - -const changeLinkAttributePlugin: () => FederationRuntimePlugin = function () { - return { - name: 'change-link-attribute', - createLink({ url }) { - link.setAttribute('href', url); - link.setAttribute('rel', 'preload'); - link.setAttribute('as', 'script'); - link.setAttribute('crossorigin', 'anonymous'); - return link; - }, - }; -}; -``` - -### fetch - -`fetch` 函数用于自定义获取 Manifest 的请求。 - -`AsyncHook` - -- **类型** - -```typescript -function fetch(manifestUrl: string, requestInit: RequestInit): Promise | void | false; -``` - -- 示例:获取 manifest 时增加 credentials: - -```typescript -// fetch-manifest-with-credentials-plugin.ts -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; - -export default function (): FederationRuntimePlugin { - return { - name: 'fetch-manifest-with-credentials-plugin', - fetch(manifestUrl, requestInit) { - return fetch(manifestUrl, { - ...requestInit, - credentials: 'include' - }); - }, - } -}; -``` - -### loadEntry -可以完全自定义remote, 可以扩展新的remote类型。 -下面两个简单的例子分别实现了加载json数据和模块代理 - -`asyncHook` - -- 类型 - -```typescript -function createScript(args: LoadEntryOptions): HTMLScriptElement | {script?: HTMLScriptElement, timeout?: number } | void; - -type LoadEntryOptions = { - createScriptHook: SyncHook, - remoteEntryExports?: RemoteEntryExports, - remoteInfo: RemoteInfo -}; -interface RemoteInfo { - name: string; - version?: string; - buildVersion?: string; - entry: string; - type: RemoteEntryType; - entryGlobalName: string; - shareScope: string; -} -export type RemoteEntryExports = { - get: (id: string) => () => Promise; - init: ( - shareScope: ShareScopeMap[string], - initScope?: InitScope, - remoteEntryInitOPtions?: RemoteEntryInitOptions, - ) => void | Promise; -}; -``` - -- 示例(加载json数据) - -```typescript -// load-json-data-plugin.ts -import { init } from '@module-federation/enhanced/runtime'; -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; - -const changeScriptAttributePlugin: () => FederationRuntimePlugin = function () { - return { - name: 'load-json-data-plugin', - loadEntry({ remoteInfo }) { - if (remoteInfo.jsonA === "jsonA") { - return { - init(shareScope, initScope, remoteEntryInitOPtions) {}, - async get(path) { - const json = await fetch(remoteInfo.entry + ".json").then(res => res.json()) - return () => ({ - path, - json - }) - } - } - } - }, - }; -}; -``` -```ts -// module-federation-config -{ - remotes: { - jsonA: "jsonA@https://cdn.jsdelivr.net/npm/@module-federation/runtime/package" - } -} -``` -```ts -// src/bootstrap.js -import jsonA from "jsonA" -jsonA // {...json data} -``` - -- 示例(模块代理) - -```typescript -// delegate-modules-plugin.ts -import { init } from '@module-federation/enhanced/runtime'; -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; - -const changeScriptAttributePlugin: () => FederationRuntimePlugin = function () { - return { - name: 'delegate-modules-plugin', - loadEntry({ remoteInfo }) { - if (remoteInfo.name === "delegateModulesA") { - return { - init(shareScope, initScope, remoteEntryInitOPtions) {}, - async get(path) { - path = path.replace("./", "") - const {[path]: factory} = await import("./delegateModulesA.js") - const result = await factory() - return () => result - } - } - } - }, - }; -}; -``` -```ts -// ./src/delegateModulesA.js -export async function test1() { - return new Promise(resolve => { - setTimeout(() => { - resolve("test1 value") - }, 3000) - }) -} -export async function test2() { - return new Promise(resolve => { - setTimeout(() => { - resolve("test2 value") - }, 3000) - }) -} -``` -```ts -// module-federation-config -{ - remotes: { - delegateModulesA: "delegateModulesA@https://delegateModulesA.js" - } -} -``` -```ts -// src/bootstrap.js -import test1 from "delegateModulesA/test1" -import test2 from "delegateModulesA/test2" -test1 // "test1 value" -test2 // "test2 value" -``` +参考 [Runtime Hooks](../../guide/basic/runtime/runtime-hooks) diff --git a/apps/website-new/docs/zh/plugin/plugins/retry-plugin.mdx b/apps/website-new/docs/zh/plugin/plugins/retry-plugin.mdx index 8ec322c700..f73e363cdf 100644 --- a/apps/website-new/docs/zh/plugin/plugins/retry-plugin.mdx +++ b/apps/website-new/docs/zh/plugin/plugins/retry-plugin.mdx @@ -63,10 +63,10 @@ export default retryPlugin; ### 方式二:运行时注册 ```ts -import { init, loadRemote } from '@module-federation/enhanced/runtime'; +import { ModuleFederation, loadRemote } from '@module-federation/enhanced/runtime'; import { RetryPlugin } from '@module-federation/retry-plugin'; -init({ +const mf = new ModuleFederation({ name: 'federation_consumer', remotes: [], plugins: [ @@ -83,7 +83,7 @@ init({ ## Type ```ts -const RetryPlugin: (params: RetryPluginParams) => FederationRuntimePlugin; +const RetryPlugin: (params: RetryPluginParams) => ModuleFederationRuntimePlugin; type RetryPluginParams = { fetch?: FetchWithRetryOptions; // fetch retry options @@ -173,10 +173,10 @@ type ScriptWithRetryOptions = { ```ts -import { init, loadRemote } from '@module-federation/enhanced/runtime'; +import { ModuleFederation, loadRemote } from '@module-federation/enhanced/runtime'; import { RetryPlugin } from '@module-federation/retry-plugin'; -init({ +const mf = new ModuleFederation({ name: 'federation_consumer', remotes: [], plugins: [ diff --git a/apps/website-new/docs/zh/practice/frameworks/next/dynamic-remotes.mdx b/apps/website-new/docs/zh/practice/frameworks/next/dynamic-remotes.mdx index 9a49b6b1cb..96b3de1758 100644 --- a/apps/website-new/docs/zh/practice/frameworks/next/dynamic-remotes.mdx +++ b/apps/website-new/docs/zh/practice/frameworks/next/dynamic-remotes.mdx @@ -1,11 +1,11 @@ # 动态远程模块 ```js -import { loadRemote, init } from '@module-federation/runtime'; +import { loadRemote, ModuleFederation } from '@module-federation/runtime'; // if i have remotes in my federation plugin, i can pass the name of the remote loadRemote('home/exposedModule') // if i want to load a custom remote not known at build time. -init({ +const mf = new ModuleFederation({ name: 'hostname', remotes: [ { @@ -15,5 +15,5 @@ init({ ], force: true // may be needed to sideload remotes after the fact. }) -loadRemote('home/exposedModule') +mf.loadRemote('home/exposedModule') ``` diff --git a/packages/bridge/bridge-react/src/provider/plugin.ts b/packages/bridge/bridge-react/src/provider/plugin.ts index dea20e6bac..e58646e673 100644 --- a/packages/bridge/bridge-react/src/provider/plugin.ts +++ b/packages/bridge/bridge-react/src/provider/plugin.ts @@ -1,5 +1,5 @@ import type { - FederationRuntimePlugin, + ModuleFederationRuntimePlugin, ModuleFederation, } from '@module-federation/runtime'; @@ -9,7 +9,7 @@ export type FederationRuntimeType = { export const federationRuntime: FederationRuntimeType = { instance: null }; -function BridgeReactPlugin(): FederationRuntimePlugin { +function BridgeReactPlugin(): ModuleFederationRuntimePlugin { return { name: 'bridge-react-plugin', beforeInit(args) { diff --git a/packages/chrome-devtools/src/utils/chrome/fast-refresh.ts b/packages/chrome-devtools/src/utils/chrome/fast-refresh.ts index 598b509a2d..df17cdccc8 100644 --- a/packages/chrome-devtools/src/utils/chrome/fast-refresh.ts +++ b/packages/chrome-devtools/src/utils/chrome/fast-refresh.ts @@ -1,5 +1,5 @@ import type { - FederationRuntimePlugin, + ModuleFederationRuntimePlugin, Shared, } from '@module-federation/runtime/types'; import { loadScript } from '@module-federation/sdk'; @@ -8,7 +8,7 @@ import { isObject, getUnpkgUrl } from '../index'; import { definePropertyGlobalVal } from '../sdk'; import { __FEDERATION_DEVTOOLS__ } from '../../template'; -const fastRefreshPlugin = (): FederationRuntimePlugin => { +const fastRefreshPlugin = (): ModuleFederationRuntimePlugin => { return { name: 'mf-fast-refresh-plugin', beforeInit({ userOptions, ...args }) { diff --git a/packages/chrome-devtools/src/utils/chrome/post-message.ts b/packages/chrome-devtools/src/utils/chrome/post-message.ts index 18b7a8fce6..b2a3be3427 100644 --- a/packages/chrome-devtools/src/utils/chrome/post-message.ts +++ b/packages/chrome-devtools/src/utils/chrome/post-message.ts @@ -1,9 +1,9 @@ import helpers from '@module-federation/runtime/helpers'; -import type { FederationRuntimePlugin } from '@module-federation/runtime/types'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/runtime/types'; import { definePropertyGlobalVal } from '../sdk'; -const getModuleInfo = (): FederationRuntimePlugin => { +const getModuleInfo = (): ModuleFederationRuntimePlugin => { return { name: 'mf-devtool-getModuleInfo-plugin', loadRemoteSnapshot({ options, moduleInfo, remoteSnapshot, ...res }) { diff --git a/packages/chrome-devtools/src/utils/chrome/snapshot-plugin.ts b/packages/chrome-devtools/src/utils/chrome/snapshot-plugin.ts index a8dc24ab87..6084b9e30a 100644 --- a/packages/chrome-devtools/src/utils/chrome/snapshot-plugin.ts +++ b/packages/chrome-devtools/src/utils/chrome/snapshot-plugin.ts @@ -1,7 +1,7 @@ import { MODULE_DEVTOOL_IDENTIFIER } from '@module-federation/sdk'; import runtimeHelpers from '@module-federation/runtime/helpers'; -import type { FederationRuntimePlugin } from '@module-federation/runtime/types'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/runtime/types'; import { definePropertyGlobalVal } from '../sdk'; @@ -10,7 +10,7 @@ declare global { var __INIT_VMOK_CHROME_DEVTOOL_PLUGIN__: boolean | undefined; } -const chromeDevtoolsPlugin: () => FederationRuntimePlugin = function () { +const chromeDevtoolsPlugin: () => ModuleFederationRuntimePlugin = function () { return { name: 'mf-chrome-devtools-inject-snapshot-plugin', beforeLoadRemoteSnapshot({ options }) { diff --git a/packages/data-prefetch/src/plugin.ts b/packages/data-prefetch/src/plugin.ts index 6dfd3014a5..102ba1f6b7 100644 --- a/packages/data-prefetch/src/plugin.ts +++ b/packages/data-prefetch/src/plugin.ts @@ -1,4 +1,4 @@ -import type { FederationRuntimePlugin } from '@module-federation/runtime/types'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/runtime/types'; import { ModuleInfo, getResourceUrl } from '@module-federation/sdk'; import { getSignalFromManifest } from './common/runtime-utils'; @@ -18,7 +18,7 @@ interface Loading { const loadingArray: Array = []; let sharedFlag = SHARED_STRATEGY; // eslint-disable-next-line max-lines-per-function -export const prefetchPlugin = (): FederationRuntimePlugin => ({ +export const prefetchPlugin = (): ModuleFederationRuntimePlugin => ({ name: 'data-prefetch-runtime-plugin', initContainer(options) { const { remoteSnapshot, remoteInfo, id, origin } = options; diff --git a/packages/dts-plugin/src/runtime-plugins/dynamic-remote-type-hints-plugin.ts b/packages/dts-plugin/src/runtime-plugins/dynamic-remote-type-hints-plugin.ts index 67f189ddeb..543d8e5aa9 100644 --- a/packages/dts-plugin/src/runtime-plugins/dynamic-remote-type-hints-plugin.ts +++ b/packages/dts-plugin/src/runtime-plugins/dynamic-remote-type-hints-plugin.ts @@ -1,4 +1,4 @@ -import type { FederationRuntimePlugin } from '@module-federation/runtime/types'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/runtime/types'; import { createWebsocket } from '../server/createWebsocket'; import { AddDynamicRemoteAction, @@ -9,7 +9,7 @@ import { getIpFromEntry } from '../dev-worker/utils'; declare const FEDERATION_IPV4: string | undefined; const PLUGIN_NAME = 'dynamic-remote-type-hints-plugin'; -function dynamicRemoteTypeHintsPlugin(): FederationRuntimePlugin { +function dynamicRemoteTypeHintsPlugin(): ModuleFederationRuntimePlugin { let ws = createWebsocket(); let isConnected = false; ws.onopen = () => { diff --git a/packages/enhanced/CHANGELOG.md b/packages/enhanced/CHANGELOG.md index b65505a66c..3a1b661b9b 100644 --- a/packages/enhanced/CHANGELOG.md +++ b/packages/enhanced/CHANGELOG.md @@ -764,7 +764,7 @@ - 6b02145: Added a check to skip processing when virtualRuntimeEntry is present. - - Added an early return in `FederationRuntimePlugin` to skip processing if `options.virtualRuntimeEntry` is defined. + - Added an early return in `ModuleFederationRuntimePlugin` to skip processing if `options.virtualRuntimeEntry` is defined. - 22a3b83: fix(data-prefetch): apply DataPrefetchPlugin on demand - Updated dependencies [22a3b83] @@ -784,7 +784,7 @@ - 70a1708: Added a check to skip processing when virtualRuntimeEntry is present. - - Added an early return in `FederationRuntimePlugin` to skip processing if `options.virtualRuntimeEntry` is defined. + - Added an early return in `ModuleFederationRuntimePlugin` to skip processing if `options.virtualRuntimeEntry` is defined. - @module-federation/rspack@0.6.9 - @module-federation/sdk@0.6.9 - @module-federation/runtime-tools@0.6.9 diff --git a/packages/enhanced/README.md b/packages/enhanced/README.md index 78874298f2..5c51089ba5 100644 --- a/packages/enhanced/README.md +++ b/packages/enhanced/README.md @@ -10,7 +10,7 @@ The following items are exported: - SharePlugin - ConsumeSharedPlugin - ProvideSharedPlugin -- FederationRuntimePlugin +- ModuleFederationRuntimePlugin - AsyncBoundaryPlugin - HoistContainerReferencesPlugin diff --git a/packages/enhanced/src/index.ts b/packages/enhanced/src/index.ts index 9ff68acb16..30520be312 100644 --- a/packages/enhanced/src/index.ts +++ b/packages/enhanced/src/index.ts @@ -9,7 +9,7 @@ export { default as ContainerPlugin } from './wrapper/ContainerPlugin'; export { default as ConsumeSharedPlugin } from './wrapper/ConsumeSharedPlugin'; export { default as ProvideSharedPlugin } from './wrapper/ProvideSharedPlugin'; export { default as FederationModulesPlugin } from './wrapper/FederationModulesPlugin'; -export { default as FederationRuntimePlugin } from './wrapper/FederationRuntimePlugin'; +export { default as ModuleFederationRuntimePlugin } from './wrapper/ModuleFederationRuntimePlugin'; export { default as AsyncBoundaryPlugin } from './wrapper/AsyncBoundaryPlugin'; export { default as HoistContainerReferencesPlugin } from './wrapper/HoistContainerReferencesPlugin'; diff --git a/packages/enhanced/src/lib/container/ContainerPlugin.ts b/packages/enhanced/src/lib/container/ContainerPlugin.ts index 4fa8ab3f79..c14e798341 100644 --- a/packages/enhanced/src/lib/container/ContainerPlugin.ts +++ b/packages/enhanced/src/lib/container/ContainerPlugin.ts @@ -15,7 +15,7 @@ import type { WebpackPluginFunction, } from 'webpack'; import type { containerPlugin } from '@module-federation/sdk'; -import FederationRuntimePlugin from './runtime/FederationRuntimePlugin'; +import ModuleFederationRuntimePlugin from './runtime/ModuleFederationRuntimePlugin'; import FederationModulesPlugin from './runtime/FederationModulesPlugin'; import checkOptions from '../../schemas/container/ContainerPlugin.check'; import schema from '../../schemas/container/ContainerPlugin'; @@ -172,7 +172,7 @@ class ContainerPlugin { ContainerPlugin.patchChunkSplit(compiler, this._options.name); } - const federationRuntimePluginInstance = new FederationRuntimePlugin(); + const federationRuntimePluginInstance = new ModuleFederationRuntimePlugin(); federationRuntimePluginInstance.apply(compiler); const { name, exposes, shareScope, filename, library, runtime } = diff --git a/packages/enhanced/src/lib/container/ContainerReferencePlugin.ts b/packages/enhanced/src/lib/container/ContainerReferencePlugin.ts index d160a13e67..f620799a3d 100644 --- a/packages/enhanced/src/lib/container/ContainerReferencePlugin.ts +++ b/packages/enhanced/src/lib/container/ContainerReferencePlugin.ts @@ -14,7 +14,7 @@ import RemoteRuntimeModule from './RemoteRuntimeModule'; import RemoteToExternalDependency from './RemoteToExternalDependency'; import { parseOptions } from './options'; import { containerReferencePlugin } from '@module-federation/sdk'; -import FederationRuntimePlugin from './runtime/FederationRuntimePlugin'; +import ModuleFederationRuntimePlugin from './runtime/ModuleFederationRuntimePlugin'; import schema from '../../schemas/container/ContainerReferencePlugin'; import checkOptions from '../../schemas/container/ContainerReferencePlugin.check'; @@ -70,7 +70,7 @@ class ContainerReferencePlugin { */ apply(compiler: Compiler): void { const { _remotes: remotes, _remoteType: remoteType } = this; - new FederationRuntimePlugin().apply(compiler); + new ModuleFederationRuntimePlugin().apply(compiler); /** @type {Record} */ const remoteExternals: Record = {}; diff --git a/packages/enhanced/src/lib/container/ModuleFederationPlugin.ts b/packages/enhanced/src/lib/container/ModuleFederationPlugin.ts index 172e780c56..243a66c0b3 100644 --- a/packages/enhanced/src/lib/container/ModuleFederationPlugin.ts +++ b/packages/enhanced/src/lib/container/ModuleFederationPlugin.ts @@ -18,7 +18,7 @@ import type { Compiler, WebpackPluginInstance } from 'webpack'; import SharePlugin from '../sharing/SharePlugin'; import ContainerPlugin from './ContainerPlugin'; import ContainerReferencePlugin from './ContainerReferencePlugin'; -import FederationRuntimePlugin from './runtime/FederationRuntimePlugin'; +import ModuleFederationRuntimePlugin from './runtime/ModuleFederationRuntimePlugin'; import { RemoteEntryPlugin } from '@module-federation/rspack/remote-entry-plugin'; import { ExternalsType } from 'webpack/declarations/WebpackOptions'; import StartupChunkDependenciesPlugin from '../startup/MfStartupChunkDependenciesPlugin'; @@ -147,7 +147,7 @@ class ModuleFederationPlugin implements WebpackPluginInstance { new PrefetchPlugin(options).apply(compiler); } - new FederationRuntimePlugin(options).apply(compiler); + new ModuleFederationRuntimePlugin(options).apply(compiler); const library = options.library || { type: 'var', name: options.name }; const remoteType = diff --git a/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts b/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts index 076b937d82..6e3693d4b5 100644 --- a/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts +++ b/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts @@ -56,7 +56,7 @@ const federationGlobal = getFederationGlobalScope(RuntimeGlobals); const onceForCompiler = new WeakSet(); const onceForCompilerEntryMap = new WeakMap(); -class FederationRuntimePlugin { +class ModuleFederationRuntimePlugin { options?: moduleFederationPlugin.ModuleFederationPluginOptions; entryFilePath: string; bundlerRuntimePath: string; @@ -163,7 +163,7 @@ class FederationRuntimePlugin { if (!this.options?.virtualRuntimeEntry) { const containerName = this.options.name; const hash = createHash( - `${containerName} ${FederationRuntimePlugin.getTemplate( + `${containerName} ${ModuleFederationRuntimePlugin.getTemplate( compiler, this.options, this.bundlerRuntimePath, @@ -173,7 +173,7 @@ class FederationRuntimePlugin { entryFilePath = path.join(TEMP_DIR, `entry.${hash}.js`); } else { entryFilePath = `data:text/javascript;charset=utf-8;base64,${pBtoa( - FederationRuntimePlugin.getTemplate( + ModuleFederationRuntimePlugin.getTemplate( compiler, this.options, this.bundlerRuntimePath, @@ -202,7 +202,7 @@ class FederationRuntimePlugin { mkdirpSync(fs, TEMP_DIR); fs.writeFileSync( filePath, - FederationRuntimePlugin.getTemplate( + ModuleFederationRuntimePlugin.getTemplate( compiler, this.options, this.bundlerRuntimePath, @@ -429,4 +429,4 @@ class FederationRuntimePlugin { } } -export default FederationRuntimePlugin; +export default ModuleFederationRuntimePlugin; diff --git a/packages/enhanced/src/lib/sharing/ConsumeSharedPlugin.ts b/packages/enhanced/src/lib/sharing/ConsumeSharedPlugin.ts index 6cb15ee0a4..1f6abc7ca3 100644 --- a/packages/enhanced/src/lib/sharing/ConsumeSharedPlugin.ts +++ b/packages/enhanced/src/lib/sharing/ConsumeSharedPlugin.ts @@ -25,7 +25,7 @@ import ConsumeSharedFallbackDependency from './ConsumeSharedFallbackDependency'; import ConsumeSharedModule from './ConsumeSharedModule'; import ConsumeSharedRuntimeModule from './ConsumeSharedRuntimeModule'; import ProvideForSharedDependency from './ProvideForSharedDependency'; -import FederationRuntimePlugin from '../container/runtime/FederationRuntimePlugin'; +import ModuleFederationRuntimePlugin from '../container/runtime/ModuleFederationRuntimePlugin'; import ShareRuntimeModule from './ShareRuntimeModule'; import type { SemVerRange } from 'webpack/lib/util/semver'; import type { ResolveData } from 'webpack/lib/NormalModuleFactory'; @@ -145,7 +145,7 @@ class ConsumeSharedPlugin { } apply(compiler: Compiler): void { - new FederationRuntimePlugin().apply(compiler); + new ModuleFederationRuntimePlugin().apply(compiler); process.env['FEDERATION_WEBPACK_PATH'] = process.env['FEDERATION_WEBPACK_PATH'] || getWebpackPath(compiler); diff --git a/packages/enhanced/src/lib/sharing/ProvideSharedPlugin.ts b/packages/enhanced/src/lib/sharing/ProvideSharedPlugin.ts index aceacb0d52..734ed87169 100644 --- a/packages/enhanced/src/lib/sharing/ProvideSharedPlugin.ts +++ b/packages/enhanced/src/lib/sharing/ProvideSharedPlugin.ts @@ -21,7 +21,7 @@ import type { ProvideSharedPluginOptions, ProvidesConfig, } from '../../declarations/plugins/sharing/ProvideSharedPlugin'; -import FederationRuntimePlugin from '../container/runtime/FederationRuntimePlugin'; +import ModuleFederationRuntimePlugin from '../container/runtime/ModuleFederationRuntimePlugin'; import { createSchemaValidation } from '../../utils'; const WebpackError = require( normalizeWebpackPath('webpack/lib/WebpackError'), @@ -124,7 +124,7 @@ class ProvideSharedPlugin { * @returns {void} */ apply(compiler: Compiler): void { - new FederationRuntimePlugin().apply(compiler); + new ModuleFederationRuntimePlugin().apply(compiler); process.env['FEDERATION_WEBPACK_PATH'] = process.env['FEDERATION_WEBPACK_PATH'] || getWebpackPath(compiler); diff --git a/packages/enhanced/src/wrapper/FederationRuntimePlugin.ts b/packages/enhanced/src/wrapper/FederationRuntimePlugin.ts index e2390f7964..16f07e50a0 100644 --- a/packages/enhanced/src/wrapper/FederationRuntimePlugin.ts +++ b/packages/enhanced/src/wrapper/FederationRuntimePlugin.ts @@ -3,9 +3,11 @@ import type { moduleFederationPlugin } from '@module-federation/sdk'; import { getWebpackPath } from '@module-federation/sdk/normalize-webpack-path'; -const PLUGIN_NAME = 'FederationRuntimePlugin'; +const PLUGIN_NAME = 'ModuleFederationRuntimePlugin'; -export default class FederationRuntimePlugin implements WebpackPluginInstance { +export default class ModuleFederationRuntimePlugin + implements WebpackPluginInstance +{ private _options?: moduleFederationPlugin.ModuleFederationPluginOptions; name: string; entryFilePath: string; @@ -20,8 +22,8 @@ export default class FederationRuntimePlugin implements WebpackPluginInstance { process.env['FEDERATION_WEBPACK_PATH'] = process.env['FEDERATION_WEBPACK_PATH'] || getWebpackPath(compiler); const CoreFederationRuntimePlugin = - require('../lib/container/runtime/FederationRuntimePlugin') - .default as typeof import('../lib/container/runtime/FederationRuntimePlugin').default; + require('../lib/container/runtime/ModuleFederationRuntimePlugin') + .default as typeof import('../lib/container/runtime/ModuleFederationRuntimePlugin').default; const pluginInstance = new CoreFederationRuntimePlugin(this._options); pluginInstance.apply(compiler); diff --git a/packages/enhanced/test/unit/container/ContainerPlugin.test.ts b/packages/enhanced/test/unit/container/ContainerPlugin.test.ts index 689c6d1c36..d5d8855947 100644 --- a/packages/enhanced/test/unit/container/ContainerPlugin.test.ts +++ b/packages/enhanced/test/unit/container/ContainerPlugin.test.ts @@ -80,7 +80,7 @@ jest.mock( ); jest.mock( - '../../../src/lib/container/runtime/FederationRuntimePlugin', + '../../../src/lib/container/runtime/ModuleFederationRuntimePlugin', () => { return jest.fn().mockImplementation(() => ({ apply: jest.fn(), @@ -290,7 +290,7 @@ describe('ContainerPlugin', () => { expect(true).toBe(true); }); - it('should register FederationRuntimePlugin', () => { + it('should register ModuleFederationRuntimePlugin', () => { const options = { name: 'test-container', exposes: { @@ -301,10 +301,10 @@ describe('ContainerPlugin', () => { const plugin = new ContainerPlugin(options); plugin.apply(mockCompiler); - const FederationRuntimePlugin = require('../../../src/lib/container/runtime/FederationRuntimePlugin'); - expect(FederationRuntimePlugin).toHaveBeenCalled(); + const ModuleFederationRuntimePlugin = require('../../../src/lib/container/runtime/ModuleFederationRuntimePlugin'); + expect(ModuleFederationRuntimePlugin).toHaveBeenCalled(); expect( - FederationRuntimePlugin.mock.results[0].value.apply, + ModuleFederationRuntimePlugin.mock.results[0].value.apply, ).toHaveBeenCalledWith(mockCompiler); }); diff --git a/packages/enhanced/test/unit/container/ContainerReferencePlugin.test.ts b/packages/enhanced/test/unit/container/ContainerReferencePlugin.test.ts index c767a916c6..47f3e92e27 100644 --- a/packages/enhanced/test/unit/container/ContainerReferencePlugin.test.ts +++ b/packages/enhanced/test/unit/container/ContainerReferencePlugin.test.ts @@ -49,14 +49,17 @@ jest.mock('../../../src/lib/container/RemoteModule', () => MockRemoteModule, { virtual: true, }); -// Mock FederationRuntimePlugin +// Mock ModuleFederationRuntimePlugin const mockApply = jest.fn(); const mockFederationRuntimePlugin = jest.fn().mockImplementation(() => ({ apply: mockApply, })); -jest.mock('../../../src/lib/container/runtime/FederationRuntimePlugin', () => { - return mockFederationRuntimePlugin; -}); +jest.mock( + '../../../src/lib/container/runtime/ModuleFederationRuntimePlugin', + () => { + return mockFederationRuntimePlugin; + }, +); // Mock FallbackModuleFactory jest.mock( @@ -301,7 +304,7 @@ describe('ContainerReferencePlugin', () => { expect(mockCompilation.dependencyFactories.size).toBeGreaterThan(0); }); - it('should apply FederationRuntimePlugin', () => { + it('should apply ModuleFederationRuntimePlugin', () => { const options = { remotes: { 'remote-app': 'remote-app@http://localhost:3001/remoteEntry.js', @@ -316,7 +319,7 @@ describe('ContainerReferencePlugin', () => { const plugin = new ContainerReferencePlugin(options); plugin.apply(mockCompiler); - // Verify FederationRuntimePlugin was created and applied + // Verify ModuleFederationRuntimePlugin was created and applied expect(mockFederationRuntimePlugin).toHaveBeenCalled(); expect(mockApply).toHaveBeenCalledWith(mockCompiler); }); diff --git a/packages/enhanced/test/unit/sharing/ConsumeSharedPlugin.test.ts b/packages/enhanced/test/unit/sharing/ConsumeSharedPlugin.test.ts index 1e68199ab5..040615feb9 100644 --- a/packages/enhanced/test/unit/sharing/ConsumeSharedPlugin.test.ts +++ b/packages/enhanced/test/unit/sharing/ConsumeSharedPlugin.test.ts @@ -39,12 +39,15 @@ jest.mock('@module-federation/sdk/normalize-webpack-path', () => ({ getWebpackPath: jest.fn(() => 'mocked-webpack-path'), })); -// Mock FederationRuntimePlugin -jest.mock('../../../src/lib/container/runtime/FederationRuntimePlugin', () => { - return jest.fn().mockImplementation(() => ({ - apply: jest.fn(), - })); -}); +// Mock ModuleFederationRuntimePlugin +jest.mock( + '../../../src/lib/container/runtime/ModuleFederationRuntimePlugin', + () => { + return jest.fn().mockImplementation(() => ({ + apply: jest.fn(), + })); + }, +); // Mock ConsumeSharedModule jest.mock('../../../src/lib/sharing/ConsumeSharedModule', () => { diff --git a/packages/enhanced/test/unit/sharing/ProvideSharedPlugin.test.ts b/packages/enhanced/test/unit/sharing/ProvideSharedPlugin.test.ts index d278af00a5..9cc4f382cf 100644 --- a/packages/enhanced/test/unit/sharing/ProvideSharedPlugin.test.ts +++ b/packages/enhanced/test/unit/sharing/ProvideSharedPlugin.test.ts @@ -26,11 +26,14 @@ jest.mock('@module-federation/sdk/normalize-webpack-path', () => ({ getWebpackPath: jest.fn(() => 'mocked-webpack-path'), })); -jest.mock('../../../src/lib/container/runtime/FederationRuntimePlugin', () => { - return jest.fn().mockImplementation(() => ({ - apply: jest.fn(), - })); -}); +jest.mock( + '../../../src/lib/container/runtime/ModuleFederationRuntimePlugin', + () => { + return jest.fn().mockImplementation(() => ({ + apply: jest.fn(), + })); + }, +); // Mock ProvideSharedDependency class MockProvideSharedDependency { diff --git a/packages/enhanced/test/unit/sharing/utils.ts b/packages/enhanced/test/unit/sharing/utils.ts index 660ebb3930..6e1d78f464 100644 --- a/packages/enhanced/test/unit/sharing/utils.ts +++ b/packages/enhanced/test/unit/sharing/utils.ts @@ -447,7 +447,7 @@ export const createSharingTestEnvironment = () => { * Mock classes for federation runtime plugins */ export const createMockFederationRuntime = () => { - // Mock FederationRuntimePlugin + // Mock ModuleFederationRuntimePlugin const MockFederationRuntimePlugin = jest.fn().mockImplementation(() => ({ apply: jest.fn(), })); diff --git a/packages/modernjs/src/cli/index.ts b/packages/modernjs/src/cli/index.ts index 62de6e9cf2..06fad84b5f 100644 --- a/packages/modernjs/src/cli/index.ts +++ b/packages/modernjs/src/cli/index.ts @@ -88,4 +88,4 @@ export default moduleFederationPlugin; export { createModuleFederationConfig } from '@module-federation/enhanced'; -export type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +export type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; diff --git a/packages/modernjs/src/cli/mfRuntimePlugins/auto-fetch-data.ts b/packages/modernjs/src/cli/mfRuntimePlugins/auto-fetch-data.ts index 259ee4c914..b768365937 100644 --- a/packages/modernjs/src/cli/mfRuntimePlugins/auto-fetch-data.ts +++ b/packages/modernjs/src/cli/mfRuntimePlugins/auto-fetch-data.ts @@ -15,9 +15,9 @@ import { MF_DATA_FETCH_TYPE, MF_DATA_FETCH_STATUS } from '../../constant'; import { DATA_FETCH_CLIENT_SUFFIX } from '@module-federation/rsbuild-plugin/constant'; import type { MF_DATA_FETCH_MAP_VALUE } from '../../interfaces/global'; -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; -const autoFetchData: () => FederationRuntimePlugin = () => ({ +const autoFetchData: () => ModuleFederationRuntimePlugin = () => ({ name: 'auto-fetch-data-plugin', beforeInit(args) { initDataFetchMap(); diff --git a/packages/modernjs/src/cli/mfRuntimePlugins/inject-node-fetch.ts b/packages/modernjs/src/cli/mfRuntimePlugins/inject-node-fetch.ts index 630228e7d6..7ea5c64bf3 100644 --- a/packages/modernjs/src/cli/mfRuntimePlugins/inject-node-fetch.ts +++ b/packages/modernjs/src/cli/mfRuntimePlugins/inject-node-fetch.ts @@ -1,7 +1,7 @@ -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; import nodeFetch from 'node-fetch'; -const injectNodeFetchPlugin: () => FederationRuntimePlugin = () => ({ +const injectNodeFetchPlugin: () => ModuleFederationRuntimePlugin = () => ({ name: 'inject-node-fetch-plugin', beforeInit(args) { if (!globalThis.fetch) { diff --git a/packages/modernjs/src/cli/mfRuntimePlugins/resolve-entry-ipv4.ts b/packages/modernjs/src/cli/mfRuntimePlugins/resolve-entry-ipv4.ts index 554b09b6aa..252eb5b6fb 100644 --- a/packages/modernjs/src/cli/mfRuntimePlugins/resolve-entry-ipv4.ts +++ b/packages/modernjs/src/cli/mfRuntimePlugins/resolve-entry-ipv4.ts @@ -1,4 +1,4 @@ -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; import { LOCALHOST } from '../../constant'; declare const FEDERATION_IPV4: string | undefined; @@ -26,7 +26,7 @@ function replaceLocalhost(url: string): string { return url.replace(LOCALHOST, ipv4); } -const resolveEntryIpv4Plugin: () => FederationRuntimePlugin = () => ({ +const resolveEntryIpv4Plugin: () => ModuleFederationRuntimePlugin = () => ({ name: 'resolve-entry-ipv4', beforeRegisterRemote(args) { diff --git a/packages/modernjs/src/cli/mfRuntimePlugins/shared-strategy.ts b/packages/modernjs/src/cli/mfRuntimePlugins/shared-strategy.ts index 9207b57a3b..d7365727fc 100644 --- a/packages/modernjs/src/cli/mfRuntimePlugins/shared-strategy.ts +++ b/packages/modernjs/src/cli/mfRuntimePlugins/shared-strategy.ts @@ -1,6 +1,6 @@ -import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; -const sharedStrategy: () => FederationRuntimePlugin = () => ({ +const sharedStrategy: () => ModuleFederationRuntimePlugin = () => ({ name: 'shared-strategy-plugin', beforeInit(args) { const { userOptions } = args; diff --git a/packages/nextjs-mf/src/plugins/container/runtimePlugin.ts b/packages/nextjs-mf/src/plugins/container/runtimePlugin.ts index 558dbcc0bb..960770725c 100644 --- a/packages/nextjs-mf/src/plugins/container/runtimePlugin.ts +++ b/packages/nextjs-mf/src/plugins/container/runtimePlugin.ts @@ -1,6 +1,6 @@ -import { FederationRuntimePlugin } from '@module-federation/runtime/types'; +import { ModuleFederationRuntimePlugin } from '@module-federation/runtime/types'; -export default function (): FederationRuntimePlugin { +export default function (): ModuleFederationRuntimePlugin { return { name: 'next-internal-plugin', createScript: function (args: { diff --git a/packages/node/src/__tests__/runtimePlugin.test.ts b/packages/node/src/__tests__/runtimePlugin.test.ts index 9d5f638bdb..1af6962b39 100644 --- a/packages/node/src/__tests__/runtimePlugin.test.ts +++ b/packages/node/src/__tests__/runtimePlugin.test.ts @@ -13,7 +13,7 @@ import runtimePlugin, { setupWebpackRequirePatching, } from '../runtimePlugin'; import type { - FederationRuntimePlugin, + ModuleFederationRuntimePlugin, ModuleFederation, Federation, } from '@module-federation/runtime'; @@ -125,7 +125,7 @@ const mockModule = { } as any; describe('runtimePlugin', () => { - let plugin: FederationRuntimePlugin; + let plugin: ModuleFederationRuntimePlugin; beforeEach(() => { jest.clearAllMocks(); diff --git a/packages/node/src/recordDynamicRemoteEntryHashPlugin.ts b/packages/node/src/recordDynamicRemoteEntryHashPlugin.ts index 2edba24d7e..0b0f9fe53f 100644 --- a/packages/node/src/recordDynamicRemoteEntryHashPlugin.ts +++ b/packages/node/src/recordDynamicRemoteEntryHashPlugin.ts @@ -1,6 +1,6 @@ -import type { FederationRuntimePlugin } from '@module-federation/runtime'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/runtime'; -const recordDynamicRemoteEntryHashPlugin: () => FederationRuntimePlugin = +const recordDynamicRemoteEntryHashPlugin: () => ModuleFederationRuntimePlugin = () => ({ name: 'record-dynamic-remote-entry-hash-plugin', beforeInit(args) { diff --git a/packages/node/src/runtimePlugin.ts b/packages/node/src/runtimePlugin.ts index 1905596b54..8c65c98ec3 100644 --- a/packages/node/src/runtimePlugin.ts +++ b/packages/node/src/runtimePlugin.ts @@ -1,5 +1,5 @@ import type { - FederationRuntimePlugin, + ModuleFederationRuntimePlugin, ModuleFederation, } from '@module-federation/runtime'; type WebpackRequire = { @@ -360,7 +360,7 @@ export const setupWebpackRequirePatching = ( } }; -export default function (): FederationRuntimePlugin { +export default function (): ModuleFederationRuntimePlugin { return { name: 'node-federation-plugin', beforeInit(args) { diff --git a/packages/retry-plugin/src/index.ts b/packages/retry-plugin/src/index.ts index c1bef77f65..ab36f045f1 100644 --- a/packages/retry-plugin/src/index.ts +++ b/packages/retry-plugin/src/index.ts @@ -1,9 +1,11 @@ -import { FederationRuntimePlugin } from '@module-federation/runtime/types'; +import { ModuleFederationRuntimePlugin } from '@module-federation/runtime/types'; import { fetchWithRetry } from './fetch-retry'; import type { RetryPluginParams } from './types'; import { scriptCommonRetry } from './util'; -const RetryPlugin: (params: RetryPluginParams) => FederationRuntimePlugin = ({ +const RetryPlugin: ( + params: RetryPluginParams, +) => ModuleFederationRuntimePlugin = ({ fetch: fetchOption, script: scriptOption, }) => ({ diff --git a/packages/runtime-core/__tests__/hooks.spec.ts b/packages/runtime-core/__tests__/hooks.spec.ts index 87e7da7079..23952bb175 100644 --- a/packages/runtime-core/__tests__/hooks.spec.ts +++ b/packages/runtime-core/__tests__/hooks.spec.ts @@ -1,6 +1,6 @@ import { assert, describe, test, it } from 'vitest'; import { ModuleFederation } from '../src/core'; -import { FederationRuntimePlugin } from '../src/type/plugin'; +import { ModuleFederationRuntimePlugin } from '../src/type/plugin'; import { mockStaticServer, removeScriptTags } from './mock/utils'; import { addGlobalSnapshot } from '../src/global'; @@ -20,7 +20,7 @@ describe('hooks', () => { initArgs: any, beforeLoadRemoteArgs, loadRemoteArgs; - const testPlugin: () => FederationRuntimePlugin = () => ({ + const testPlugin: () => ModuleFederationRuntimePlugin = () => ({ name: 'testPlugin', beforeInit(args) { beforeInitArgs = args; @@ -234,7 +234,7 @@ describe('hooks', () => { statusText: 'OK', headers: { 'Content-Type': 'application/json' }, }); - const fetchPlugin: () => FederationRuntimePlugin = () => ({ + const fetchPlugin: () => ModuleFederationRuntimePlugin = () => ({ name: 'fetch-plugin', fetch(url, options) { if (url === 'http://mockxxx.com/loader-fetch-hooks-mf-manifest.json') { @@ -292,7 +292,7 @@ describe('hooks', () => { headers: { 'Content-Type': 'application/json' }, }); - const fetchPlugin: () => FederationRuntimePlugin = function () { + const fetchPlugin: () => ModuleFederationRuntimePlugin = function () { return { name: 'fetch-plugin', fetch(url, options) { @@ -304,7 +304,7 @@ describe('hooks', () => { }, }; }; - const loadEntryPlugin = function (): FederationRuntimePlugin { + const loadEntryPlugin = function (): ModuleFederationRuntimePlugin { return { name: 'load-entry-plugin', loadEntry({ remoteInfo }) { diff --git a/packages/runtime-core/__tests__/mock/utils.ts b/packages/runtime-core/__tests__/mock/utils.ts index 4ad4f3eafa..7cc926bfd9 100644 --- a/packages/runtime-core/__tests__/mock/utils.ts +++ b/packages/runtime-core/__tests__/mock/utils.ts @@ -90,7 +90,7 @@ export function mockStaticServer({ }); } -import { FederationRuntimePlugin } from '../../src/type'; +import { ModuleFederationRuntimePlugin } from '../../src/type'; import { ProviderModuleInfo } from '@module-federation/sdk'; export const mockRemoteSnapshot: ( @@ -98,7 +98,7 @@ export const mockRemoteSnapshot: ( remoteSnapshots: { [name: string]: ProviderModuleInfo; }, -) => FederationRuntimePlugin = function (uniqueId, remoteSnapshots) { +) => ModuleFederationRuntimePlugin = function (uniqueId, remoteSnapshots) { return { name: `mock-snapshot-${uniqueId}`, loadSnapshot({ moduleInfo, ...info }) { diff --git a/packages/runtime-core/src/core.ts b/packages/runtime-core/src/core.ts index 15369242c0..1915ba0dbf 100644 --- a/packages/runtime-core/src/core.ts +++ b/packages/runtime-core/src/core.ts @@ -343,4 +343,11 @@ export class ModuleFederation { registerRemotes(remotes: Remote[], options?: { force?: boolean }): void { return this.remoteHandler.registerRemotes(remotes, options); } + + registerShared(shared: UserOptions['shared']) { + this.sharedHandler.registerShared(this.options, { + ...this.options, + shared, + }); + } } diff --git a/packages/runtime-core/src/global.ts b/packages/runtime-core/src/global.ts index 6fa56d1717..d55534192f 100644 --- a/packages/runtime-core/src/global.ts +++ b/packages/runtime-core/src/global.ts @@ -12,10 +12,10 @@ import { isDebugMode, } from '@module-federation/sdk'; import { warn } from './utils/logger'; -import { FederationRuntimePlugin } from './type/plugin'; +import { ModuleFederationRuntimePlugin } from './type/plugin'; export interface Federation { - __GLOBAL_PLUGIN__: Array; + __GLOBAL_PLUGIN__: Array; __DEBUG_CONSTRUCTOR_VERSION__?: string; moduleInfo: GlobalModuleInfo; __DEBUG_CONSTRUCTOR__?: typeof ModuleFederation; @@ -269,7 +269,7 @@ export const getRemoteEntryExports = ( // If a plugin is not registered, it is added to the global plugins. // If a plugin is already registered, a warning message is logged. export const registerGlobalPlugins = ( - plugins: Array, + plugins: Array, ): void => { const { __GLOBAL_PLUGIN__ } = nativeGlobal.__FEDERATION__; @@ -282,7 +282,7 @@ export const registerGlobalPlugins = ( }); }; -export const getGlobalHostPlugins = (): Array => +export const getGlobalHostPlugins = (): Array => nativeGlobal.__FEDERATION__.__GLOBAL_PLUGIN__; export const getPreloaded = (id: string) => diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 603ffb421e..9345ce4eef 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -12,7 +12,7 @@ export { getGlobalSnapshot, getInfoWithoutType, } from './global'; -export type { UserOptions, FederationRuntimePlugin } from './type'; +export type { UserOptions, ModuleFederationRuntimePlugin } from './type'; export { assert } from './utils/logger'; export { registerGlobalPlugins } from './global'; export { diff --git a/packages/runtime-core/src/plugins/generate-preload-assets.ts b/packages/runtime-core/src/plugins/generate-preload-assets.ts index fdaf295081..8f98a03de5 100644 --- a/packages/runtime-core/src/plugins/generate-preload-assets.ts +++ b/packages/runtime-core/src/plugins/generate-preload-assets.ts @@ -8,7 +8,7 @@ import { } from '@module-federation/sdk'; import { EntryAssets, - FederationRuntimePlugin, + ModuleFederationRuntimePlugin, PreloadAssets, PreloadConfig, PreloadOptions, @@ -310,7 +310,7 @@ export function generatePreloadAssets( }; } -export const generatePreloadAssetsPlugin: () => FederationRuntimePlugin = +export const generatePreloadAssetsPlugin: () => ModuleFederationRuntimePlugin = function () { return { name: 'generate-preload-assets-plugin', diff --git a/packages/runtime-core/src/plugins/snapshot/index.ts b/packages/runtime-core/src/plugins/snapshot/index.ts index b40be0b1f6..fde0f97357 100644 --- a/packages/runtime-core/src/plugins/snapshot/index.ts +++ b/packages/runtime-core/src/plugins/snapshot/index.ts @@ -3,7 +3,7 @@ import { getResourceUrl, isBrowserEnv, } from '@module-federation/sdk'; -import { FederationRuntimePlugin } from '../../type/plugin'; +import { ModuleFederationRuntimePlugin } from '../../type/plugin'; import { error, isPureRemoteEntry, @@ -37,7 +37,7 @@ export function assignRemoteInfo( remoteInfo.buildVersion = remoteSnapshot.buildVersion; } -export function snapshotPlugin(): FederationRuntimePlugin { +export function snapshotPlugin(): ModuleFederationRuntimePlugin { return { name: 'snapshot-plugin', async afterResolve(args) { diff --git a/packages/runtime-core/src/type/config.ts b/packages/runtime-core/src/type/config.ts index 7e24d614dc..cf2ccec40c 100644 --- a/packages/runtime-core/src/type/config.ts +++ b/packages/runtime-core/src/type/config.ts @@ -4,7 +4,7 @@ import type { Module, RemoteEntryType, } from '@module-federation/sdk'; -import { FederationRuntimePlugin } from './plugin'; +import { ModuleFederationRuntimePlugin } from './plugin'; export type Optional = Omit & Partial; export type PartialOptional = Omit & { @@ -111,7 +111,7 @@ export interface Options { version?: string; remotes: Array; shared: ShareInfos; - plugins: Array; + plugins: Array; inBrowser: boolean; shareStrategy?: ShareStrategy; } diff --git a/packages/runtime-core/src/type/plugin.ts b/packages/runtime-core/src/type/plugin.ts index 1da9b0829f..bba4e421f7 100644 --- a/packages/runtime-core/src/type/plugin.ts +++ b/packages/runtime-core/src/type/plugin.ts @@ -36,7 +36,7 @@ type RemoteLifeCycleCyclePartial = Partial<{ [k in keyof RemoteLifeCycle]: Parameters[0]; }>; -export type FederationRuntimePlugin = CoreLifeCyclePartial & +export type ModuleFederationRuntimePlugin = CoreLifeCyclePartial & SnapshotLifeCycleCyclePartial & SharedLifeCycleCyclePartial & RemoteLifeCycleCyclePartial & diff --git a/packages/runtime-plugins/inject-external-runtime-core-plugin/src/index.ts b/packages/runtime-plugins/inject-external-runtime-core-plugin/src/index.ts index 995b238bf2..e574325d0f 100644 --- a/packages/runtime-plugins/inject-external-runtime-core-plugin/src/index.ts +++ b/packages/runtime-plugins/inject-external-runtime-core-plugin/src/index.ts @@ -1,6 +1,6 @@ import * as runtimeCore from '@module-federation/runtime-tools/runtime-core'; -import type { FederationRuntimePlugin } from '@module-federation/runtime-tools/runtime-core'; +import type { ModuleFederationRuntimePlugin } from '@module-federation/runtime-tools/runtime-core'; declare global { var __VERSION__: string; var _FEDERATION_RUNTIME_CORE: typeof runtimeCore; @@ -10,7 +10,7 @@ declare global { }; } -function injectExternalRuntimeCorePlugin(): FederationRuntimePlugin { +function injectExternalRuntimeCorePlugin(): ModuleFederationRuntimePlugin { return { name: 'inject-external-runtime-core-plugin', version: __VERSION__, diff --git a/packages/runtime/__tests__/hooks.spec.ts b/packages/runtime/__tests__/hooks.spec.ts index 4f43797276..03034d36a3 100644 --- a/packages/runtime/__tests__/hooks.spec.ts +++ b/packages/runtime/__tests__/hooks.spec.ts @@ -3,7 +3,7 @@ import { ModuleFederation, addGlobalSnapshot, } from '@module-federation/runtime-core'; -import { FederationRuntimePlugin } from '@module-federation/runtime-core/types'; +import { ModuleFederationRuntimePlugin } from '@module-federation/runtime-core/types'; import { mockStaticServer, removeScriptTags } from './mock/utils'; // eslint-disable-next-line max-lines-per-function @@ -22,7 +22,7 @@ describe('hooks', () => { initArgs: any, beforeLoadRemoteArgs, loadRemoteArgs; - const testPlugin: () => FederationRuntimePlugin = () => ({ + const testPlugin: () => ModuleFederationRuntimePlugin = () => ({ name: 'testPlugin', beforeInit(args) { beforeInitArgs = args; @@ -236,7 +236,7 @@ describe('hooks', () => { statusText: 'OK', headers: { 'Content-Type': 'application/json' }, }); - const fetchPlugin: () => FederationRuntimePlugin = () => ({ + const fetchPlugin: () => ModuleFederationRuntimePlugin = () => ({ name: 'fetch-plugin', fetch(url, options) { if (url === 'http://mockxxx.com/loader-fetch-hooks-mf-manifest.json') { @@ -294,7 +294,7 @@ describe('hooks', () => { headers: { 'Content-Type': 'application/json' }, }); - const fetchPlugin: () => FederationRuntimePlugin = function () { + const fetchPlugin: () => ModuleFederationRuntimePlugin = function () { return { name: 'fetch-plugin', fetch(url, options) { @@ -306,7 +306,7 @@ describe('hooks', () => { }, }; }; - const loadEntryPlugin = function (): FederationRuntimePlugin { + const loadEntryPlugin = function (): ModuleFederationRuntimePlugin { return { name: 'load-entry-plugin', loadEntry({ remoteInfo }) { diff --git a/packages/runtime/__tests__/mock/utils.ts b/packages/runtime/__tests__/mock/utils.ts index 4ad4f3eafa..7cc926bfd9 100644 --- a/packages/runtime/__tests__/mock/utils.ts +++ b/packages/runtime/__tests__/mock/utils.ts @@ -90,7 +90,7 @@ export function mockStaticServer({ }); } -import { FederationRuntimePlugin } from '../../src/type'; +import { ModuleFederationRuntimePlugin } from '../../src/type'; import { ProviderModuleInfo } from '@module-federation/sdk'; export const mockRemoteSnapshot: ( @@ -98,7 +98,7 @@ export const mockRemoteSnapshot: ( remoteSnapshots: { [name: string]: ProviderModuleInfo; }, -) => FederationRuntimePlugin = function (uniqueId, remoteSnapshots) { +) => ModuleFederationRuntimePlugin = function (uniqueId, remoteSnapshots) { return { name: `mock-snapshot-${uniqueId}`, loadSnapshot({ moduleInfo, ...info }) { diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index dda18c669e..343e3e56d0 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -15,7 +15,7 @@ export { getRemoteEntry, getRemoteInfo, registerGlobalPlugins, - type FederationRuntimePlugin, + type ModuleFederationRuntimePlugin, type Federation, } from '@module-federation/runtime-core'; @@ -100,5 +100,13 @@ export function getInstance() { return FederationInstance; } +export function registerShared( + ...args: Parameters +): ReturnType { + assert(FederationInstance, 'Please call init first'); + // eslint-disable-next-line prefer-spread + return FederationInstance.registerShared.apply(FederationInstance, args); +} + // Inject for debug setGlobalFederationConstructor(ModuleFederation); From 6bb06014251abd35297cfc5fb3e17829e8716ee1 Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Thu, 3 Jul 2025 17:58:29 +0800 Subject: [PATCH 05/14] feat: add createInstance api --- .../docs/en/configure/experiments.mdx | 2 +- .../website-new/docs/en/configure/exposes.mdx | 2 +- .../docs/en/configure/getpublicpath.mdx | 2 +- .../docs/en/configure/implementation.mdx | 2 +- .../website-new/docs/en/configure/remotes.mdx | 2 +- .../docs/en/configure/runtimeplugins.mdx | 2 +- .../en/guide/basic/runtime/runtime-api.mdx | 8 ++-- .../en/guide/basic/runtime/runtime-hooks.mdx | 6 +-- .../docs/en/guide/basic/runtime/runtime.mdx | 4 +- apps/website-new/docs/en/plugin/dev/index.mdx | 2 +- .../website-new/docs/zh/blog/announcement.mdx | 4 +- .../docs/zh/blog/error-load-remote.mdx | 12 +++--- .../website-new/docs/zh/configure/exposes.mdx | 2 +- .../docs/zh/configure/getpublicpath.mdx | 2 +- .../docs/zh/configure/implementation.mdx | 2 +- .../website-new/docs/zh/configure/remotes.mdx | 2 +- .../docs/zh/configure/runtimeplugins.mdx | 2 +- .../zh/guide/basic/runtime/runtime-api.mdx | 42 +++++++++---------- .../zh/guide/basic/runtime/runtime-hooks.mdx | 8 ++-- .../docs/zh/guide/basic/runtime/runtime.mdx | 4 +- apps/website-new/docs/zh/plugin/dev/index.mdx | 2 +- .../docs/zh/plugin/plugins/retry-plugin.mdx | 8 ++-- .../frameworks/next/dynamic-remotes.mdx | 4 +- packages/runtime/src/index.ts | 15 +++++-- 24 files changed, 74 insertions(+), 67 deletions(-) diff --git a/apps/website-new/docs/en/configure/experiments.mdx b/apps/website-new/docs/en/configure/experiments.mdx index 8927f1cfc2..2eca65496b 100644 --- a/apps/website-new/docs/en/configure/experiments.mdx +++ b/apps/website-new/docs/en/configure/experiments.mdx @@ -74,7 +74,7 @@ This object contains flags related to build-time optimizations that can affect t - **Required:** No - **Default:** `false` -When set to `true`, this option defines the `FEDERATION_OPTIMIZE_NO_SNAPSHOT_PLUGIN` global constant as `true` during the build. In the `@module-federation/runtime-core`, this prevents the `snapshotPlugin()` and `generatePreloadAssetsPlugin()` from being included and initialized within the FederationHost. +When set to `true`, this option defines the `FEDERATION_OPTIMIZE_NO_SNAPSHOT_PLUGIN` global constant as `true` during the build. In the `@module-federation/runtime-core`, this prevents the `snapshotPlugin()` and `generatePreloadAssetsPlugin()` from being included and initialized within the Module Federation instance. **Impact:** * **Benefit:** Can reduce the overall bundle size of the Module Federation runtime by excluding the code for these two plugins. diff --git a/apps/website-new/docs/en/configure/exposes.mdx b/apps/website-new/docs/en/configure/exposes.mdx index 3864929c17..96e82462b3 100644 --- a/apps/website-new/docs/en/configure/exposes.mdx +++ b/apps/website-new/docs/en/configure/exposes.mdx @@ -32,7 +32,7 @@ Example: ```jsx module.exports = { plugins: [ - new ModuleFederation({ + new ModuleFederationPlugin({ name: 'provider', exposes: { // Note: "./" is not supported. Exporting as `.` indicates a default export diff --git a/apps/website-new/docs/en/configure/getpublicpath.mdx b/apps/website-new/docs/en/configure/getpublicpath.mdx index c8431db1be..ea9cb58db5 100644 --- a/apps/website-new/docs/en/configure/getpublicpath.mdx +++ b/apps/website-new/docs/en/configure/getpublicpath.mdx @@ -20,7 +20,7 @@ If you're using module federation webpack plugin and want to set a dynamic publi ```ts title="rspack.config.ts" module.exports = { plugins: [ - new ModuleFederation({ + new ModuleFederationPlugin({ name: 'provider', exposes: { './Button': './src/components/Button.tsx', diff --git a/apps/website-new/docs/en/configure/implementation.mdx b/apps/website-new/docs/en/configure/implementation.mdx index 1ba27a7ed8..c9e1a4244b 100644 --- a/apps/website-new/docs/en/configure/implementation.mdx +++ b/apps/website-new/docs/en/configure/implementation.mdx @@ -16,7 +16,7 @@ For example, if you want to use a specific version of `@module-federation/runtim module.exports = { // ...other configurations plugins: [ - new ModuleFederation({ + new ModuleFederationPlugin({ // ...other Module Federation options implementation: require.resolve('@module-federation/runtime-tools'), }), diff --git a/apps/website-new/docs/en/configure/remotes.mdx b/apps/website-new/docs/en/configure/remotes.mdx index 6728109cbb..da6e216316 100644 --- a/apps/website-new/docs/en/configure/remotes.mdx +++ b/apps/website-new/docs/en/configure/remotes.mdx @@ -31,7 +31,7 @@ interface PluginRemoteOptions { ```js module.exports = { plugins: [ - new ModuleFederation({ + new ModuleFederationPlugin({ name: 'host', // The `remotes` below defines two remotes, named `manifest-provider` for the project started on port 3011 and `js-entry-provider` for the project started on port 3012 remotes: { diff --git a/apps/website-new/docs/en/configure/runtimeplugins.mdx b/apps/website-new/docs/en/configure/runtimeplugins.mdx index b90e958dee..a005ed1e7f 100644 --- a/apps/website-new/docs/en/configure/runtimeplugins.mdx +++ b/apps/website-new/docs/en/configure/runtimeplugins.mdx @@ -36,7 +36,7 @@ Then, apply this plugin in your build configuration: const path = require('path'); module.exports = { plugins: [ - new ModuleFederation({ + new ModuleFederationPlugin({ name: 'host', remotes: { 'manifest-provider': diff --git a/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx b/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx index e520efa524..a136edf3dc 100644 --- a/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx +++ b/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx @@ -24,9 +24,9 @@ To ensure the uniqueness of the ModuleFederation instance, after the build plugi However, this singleton pattern also limits the ability to create multiple instances, as it assumes that there is only one instance per bundle. Therefore, if you need to create a new instance , you can use the ModuleFederation class to create a new one. ```ts -import { ModuleFederation } from '@module-federation/enhanced/runtime'; +import { createInstance } from '@module-federation/enhanced/runtime'; -const mf = new ModuleFederation({ +const mf = createInstance({ name: '@demo/host', remotes: [ { @@ -216,9 +216,9 @@ If not use build plugin: ```ts -import { ModuleFederation, registerRemotes } from '@module-federation/enhanced/runtime'; +import { createInstance, registerRemotes } from '@module-federation/enhanced/runtime'; -const mf = new ModuleFederation({ +const mf = createInstance({ name: 'mf_host', remotes: [ { diff --git a/apps/website-new/docs/en/guide/basic/runtime/runtime-hooks.mdx b/apps/website-new/docs/en/guide/basic/runtime/runtime-hooks.mdx index a10e9042b6..7eba04ad6e 100644 --- a/apps/website-new/docs/en/guide/basic/runtime/runtime-hooks.mdx +++ b/apps/website-new/docs/en/guide/basic/runtime/runtime-hooks.mdx @@ -166,7 +166,7 @@ type ErrorLoadRemoteOptions ={ * example ```ts -import { ModuleFederation, loadRemote } from '@module-federation/enhanced/runtime' +import { createInstance, loadRemote } from '@module-federation/enhanced/runtime' import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; @@ -182,7 +182,7 @@ const fallbackPlugin: () => ModuleFederationRuntimePlugin = }; -const mf = new ModuleFederation({ +const mf = createInstance({ name: 'mf_host', remotes: [ { @@ -269,7 +269,7 @@ const customSharedPlugin: () => ModuleFederationRuntimePlugin = }; -const mf = new ModuleFederation({ +const mf = createInstance({ name: 'mf_host', shared: { react: { diff --git a/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx b/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx index acc55f27b9..1b85f70d0f 100644 --- a/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx +++ b/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx @@ -77,9 +77,9 @@ registerRemotes([ If the build plugin is not used and no instance has been created, you can create a new instance and register the modules. ```ts -import { ModuleFederation } from '@module-federation/enhanced/runtime'; +import { createInstance } from '@module-federation/enhanced/runtime'; -const mf = new ModuleFederation({ +const mf = createInstance({ name: 'mf_host', remotes: [ { diff --git a/apps/website-new/docs/en/plugin/dev/index.mdx b/apps/website-new/docs/en/plugin/dev/index.mdx index 3369b7ef96..eab3ce871c 100644 --- a/apps/website-new/docs/en/plugin/dev/index.mdx +++ b/apps/website-new/docs/en/plugin/dev/index.mdx @@ -57,7 +57,7 @@ Registering plugins (either method is acceptable): const path = require('path'); module.exports = { plugins: [ - new ModuleFederation({ + new ModuleFederationPlugin({ // ... runtimePlugins: [path.resolve(__dirname, './custom-runtime-plugin.ts')], }), diff --git a/apps/website-new/docs/zh/blog/announcement.mdx b/apps/website-new/docs/zh/blog/announcement.mdx index 6db335fbb3..17edc720f2 100644 --- a/apps/website-new/docs/zh/blog/announcement.mdx +++ b/apps/website-new/docs/zh/blog/announcement.mdx @@ -36,9 +36,9 @@ Module Federation 作为 Webpack 5 推出的重磅功能,自发布以来已经 Module Federation 的新版本带来了显著的变化。在这个版本中,原本嵌入在 Webpack 中的 Module Federation Runtime 能力被提取出来,形成了一个独立的 SDK。这个变化意味着,现在我们可以不依赖于任何构建工具,就能够动态地注册和加载远程模块,以及注册共享模块。此外,模块的预加载和运行时插件的使用也得到了增强,从而为模块的加载过程提供了更为强大的控制能力。 ```ts -import { ModuleFederation } from '@module-federation/enhanced/runtime'; +import { createInstance } from '@module-federation/enhanced/runtime'; -const mf = new ModuleFederation({ +const mf = createInstance({ name: '@demo/app-main', remotes: [ { diff --git a/apps/website-new/docs/zh/blog/error-load-remote.mdx b/apps/website-new/docs/zh/blog/error-load-remote.mdx index ff6893af11..a31b2ef7ae 100644 --- a/apps/website-new/docs/zh/blog/error-load-remote.mdx +++ b/apps/website-new/docs/zh/blog/error-load-remote.mdx @@ -57,11 +57,11 @@ Module Federation 官方提供重试插件 [@module-federation/retry-plugin](htt ```diff import React from 'react'; -import { ModuleFederation, loadRemote } from '@module-federation/enhanced/runtime'; +import { createInstance, loadRemote } from '@module-federation/enhanced/runtime'; + import { RetryPlugin } from '@module-federation/retry-plugin'; // 模块注册 -const mf = new ModuleFederation({ +const mf = createInstance({ name: 'host', remotes: [ { @@ -176,11 +176,11 @@ export default retryPlugin; ```tsx import React from 'react'; -import { ModuleFederation, loadRemote } from '@module-federation/enhanced/runtime'; +import { createInstance, loadRemote } from '@module-federation/enhanced/runtime'; import { RetryPlugin } from '@module-federation/retry-plugin'; // 模块注册 -const mf = new ModuleFederation({ +const mf = createInstance({ name: 'host', remotes: [ { @@ -219,7 +219,7 @@ loadRemote<{add: (...args: Array)=> number }>("remote1/util").then((md)= ```diff import React from 'react'; -import { ModuleFederation, loadRemote } from '@module-federation/enhanced/runtime'; +import { createInstance, loadRemote } from '@module-federation/enhanced/runtime'; import { RetryPlugin } from '@module-federation/retry-plugin'; + const fallbackPlugin: () => ModuleFederationRuntimePlugin = function () { @@ -232,7 +232,7 @@ import { RetryPlugin } from '@module-federation/retry-plugin'; + }; // 模块注册 -const mf = new ModuleFederation({ +const mf = createInstance({ name: 'host', remotes: [ { diff --git a/apps/website-new/docs/zh/configure/exposes.mdx b/apps/website-new/docs/zh/configure/exposes.mdx index 17f572a8ff..0315bc6be3 100644 --- a/apps/website-new/docs/zh/configure/exposes.mdx +++ b/apps/website-new/docs/zh/configure/exposes.mdx @@ -33,7 +33,7 @@ interface ExposesConfig { ```jsx module.exports = { plugins: [ - new ModuleFederation({ + new ModuleFederationPlugin({ name: 'provider', exposes: { // 注意: 不支持 "./",为 . 导出是表示为 default 默认导出 diff --git a/apps/website-new/docs/zh/configure/getpublicpath.mdx b/apps/website-new/docs/zh/configure/getpublicpath.mdx index a4b7b09070..23c3e469bc 100644 --- a/apps/website-new/docs/zh/configure/getpublicpath.mdx +++ b/apps/website-new/docs/zh/configure/getpublicpath.mdx @@ -12,7 +12,7 @@ ```ts title="rspack.config.ts" module.exports = { plugins: [ - new ModuleFederation({ + new ModuleFederationPlugin({ name: 'provider', exposes: { './Button': './src/components/Button.tsx', diff --git a/apps/website-new/docs/zh/configure/implementation.mdx b/apps/website-new/docs/zh/configure/implementation.mdx index e8cd2a0a77..c3c7606199 100644 --- a/apps/website-new/docs/zh/configure/implementation.mdx +++ b/apps/website-new/docs/zh/configure/implementation.mdx @@ -10,7 +10,7 @@ module.exports = { // ...other configurations plugins: [ - new ModuleFederation({ + new ModuleFederationPlugin({ // ...other Module Federation options implementation: require.resolve('@module-federation/runtime-tools'), }), diff --git a/apps/website-new/docs/zh/configure/remotes.mdx b/apps/website-new/docs/zh/configure/remotes.mdx index ab8301b025..380ab997a8 100644 --- a/apps/website-new/docs/zh/configure/remotes.mdx +++ b/apps/website-new/docs/zh/configure/remotes.mdx @@ -31,7 +31,7 @@ interface PluginRemoteOptions { ```js module.exports = { plugins: [ - new ModuleFederation({ + new ModuleFederationPlugin({ name: 'host', // 下面的 remotes 中定义了两个 remote,分别是名称为:manifest_provider 在 3011 端口启动的项目、js_entry_provider 在 3012 端口启动的项目 remotes: { diff --git a/apps/website-new/docs/zh/configure/runtimeplugins.mdx b/apps/website-new/docs/zh/configure/runtimeplugins.mdx index 210828eb86..fc6fddcc80 100644 --- a/apps/website-new/docs/zh/configure/runtimeplugins.mdx +++ b/apps/website-new/docs/zh/configure/runtimeplugins.mdx @@ -37,7 +37,7 @@ export default function (): ModuleFederationRuntimePlugin { const path = require('path'); module.exports = { plugins: [ - new ModuleFederation({ + new ModuleFederationPlugin({ name: 'host', remotes: { 'manifest-provider': diff --git a/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx b/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx index 0b559f3261..8ce73cee68 100644 --- a/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx +++ b/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx @@ -3,13 +3,9 @@ import { Badge } from '@theme'; import Collapse from '@components/Collapse' -若使用构建插件,项目启动时将自动创建 MF 实例并存储于内存中。此时可直接通过 API 调用 MF 实例的方法。 +若使用构建插件,项目启动时将自动创建 `ModuleFederation` 实例并存储于内存中。此时可直接调用 API,API 会自动从内存中获取构建运行时创建的 `ModuleFederation` 实例。 -若未使用构建插件,则需手动创建 MF 实例,之后调用相应 API。 - -## ModuleFederation Class - -`Runtime` 除了暴露出 API 外,还提供了 `ModuleFederation` 类,你可以使用它来创建一个 `ModuleFederation` 实例。 +若未使用构建插件,则需手动创建 `ModuleFederation` 实例,之后调用相应 API。 * 什么是 `ModuleFederation` 实例 ? @@ -17,16 +13,20 @@ import Collapse from '@components/Collapse' > 你可以在控制台输入 `__FEDERATION__.__INSTANCES__` 来查看已经创建好的实例。 -* 什么时候使用 `ModuleFederation` class? +## createInstance + +`Runtime` 除了暴露出 API 外,还提供了 `ModuleFederation` 类,你可以使用它来创建一个 `ModuleFederation` 实例。 + +* 什么时候使用 `createInstance` ? 为了保证 `ModuleFederation` 实例的唯一性,我们在构建插件创建实例后,会将其存储到内存中,导出的 API 都是先从内存中获取 `ModuleFederation` 实例,然后再调用 `ModuleFederation` 实例的 API。这也是为什么 `loadRemote` 等 API 可以直接使用的原因。 -但是这种单例模式同时也限制无法创建多份实例,因此如果你需要**创建新的 ModuleFederation 实例**,那么你可以使用 `ModuleFederation` 类来创建一个新的实例。 +但是这种单例模式同时也限制无法创建多份实例,因此如果你需要**创建新的 ModuleFederation 实例**,那么你可以使用 `createInstance` 来创建一个新的实例。 ```ts -import { ModuleFederation } from '@module-federation/enhanced/runtime'; +import { createInstance } from '@module-federation/enhanced/runtime'; -const mf = new ModuleFederation({ +const mf = createInstance({ name: 'host', remotes: [ { @@ -39,13 +39,13 @@ const mf = new ModuleFederation({ mf.loadRemote('@demo/sub1/util').then((m) => m.add(1, 2, 3)); ``` -## init API 废弃 +## init 废弃 :::warning Warning 此 API 将被废弃。如果需要获取已创建的实例,可以使用 [getInstance](#getinstance) 获取已创建的实例。 -如果需要创建新的实例,请使用 [ModuleFederation](#modulefederation) class。 +如果需要创建新的实例,请使用 [createInstance](#createinstance) class。 ::: @@ -138,7 +138,7 @@ init({ ``` -## getInstance API +## getInstance - Type: `getInstance(): ModuleFederation` - 获取构建插件创建的 `ModuleFederation` 实例 @@ -154,7 +154,7 @@ mfInstance.loadRemote('remote/util'); 如果没有使用构建插件,调用 `getInstance` 会抛出异常,此时你需要使用 [ModuleFederation](#modulefederation) class 来创建一个新的实例。 -## registerRemotes API +## registerRemotes @@ -216,9 +216,9 @@ registerRemotes([ 没使用构建插件 ```ts -import { ModuleFederation, registerRemotes } from '@module-federation/enhanced/runtime'; +import { createInstance, registerRemotes } from '@module-federation/enhanced/runtime'; -const mf = new ModuleFederation({ +const mf = createInstance({ name: 'mf_host', remotes: [ { @@ -245,7 +245,7 @@ mf.registerRemotes([ ],{ force:true }); ``` -## registerPlugins API +## registerPlugins - type @@ -289,7 +289,7 @@ registerPlugins([ ]); ``` -## registerShared API +## registerShared @@ -353,7 +353,7 @@ registerShared({ }); ``` -## loadRemote API +## loadRemote - Type: `loadRemote(id: string)` - 加载远程模块 @@ -369,7 +369,7 @@ loadRemote("remote/util").then((m)=> m.add(1,2,3)); loadRemote("app1/util").then((m)=> m.add(1,2,3)); ``` -## loadShare API +## loadShare - Type: `loadShare(pkgName: string, extraOptions?: { customShareInfo?: Partial;resolver?: (sharedOptions: ShareInfos[string]) => Shared;})` - 获取 `share` 依赖,当全局环境有符合当前 `host` 的 `share` 依赖时,将优先复用当前已存在且满足 `share` 条件的依赖,否则将加载自身的依赖并存入全局缓存 @@ -427,7 +427,7 @@ loadShare('react', { }); ``` -## preloadRemote API +## preloadRemote diff --git a/apps/website-new/docs/zh/guide/basic/runtime/runtime-hooks.mdx b/apps/website-new/docs/zh/guide/basic/runtime/runtime-hooks.mdx index 2ecd6204a3..796feb020f 100644 --- a/apps/website-new/docs/zh/guide/basic/runtime/runtime-hooks.mdx +++ b/apps/website-new/docs/zh/guide/basic/runtime/runtime-hooks.mdx @@ -168,7 +168,7 @@ type ErrorLoadRemoteOptions ={ * example ```ts -import { ModuleFederation } from '@module-federation/enhanced/runtime' +import { createInstance } from '@module-federation/enhanced/runtime' import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; @@ -184,7 +184,7 @@ const fallbackPlugin: () => ModuleFederationRuntimePlugin = }; -const mf = new ModuleFederation({ +const mf = createInstance({ name: 'mf_host', remotes: [ { @@ -244,7 +244,7 @@ type ResolveShareOptions ={ * example ```ts -import { ModuleFederation, loadRemote } from '@module-federation/enhanced/runtime' +import { createInstance, loadRemote } from '@module-federation/enhanced/runtime' import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime'; @@ -271,7 +271,7 @@ const customSharedPlugin: () => ModuleFederationRuntimePlugin = }; -const mf = new ModuleFederation({ +const mf = createInstance({ name: 'mf_host', shared: { react: { diff --git a/apps/website-new/docs/zh/guide/basic/runtime/runtime.mdx b/apps/website-new/docs/zh/guide/basic/runtime/runtime.mdx index 5e40b24062..d245a25524 100644 --- a/apps/website-new/docs/zh/guide/basic/runtime/runtime.mdx +++ b/apps/website-new/docs/zh/guide/basic/runtime/runtime.mdx @@ -76,9 +76,9 @@ registerRemotes([ 如果没有使用构建插件,并且没有创建实例,那么可以创建新的实例,并注册模块。 ```ts -import { ModuleFederation } from '@module-federation/enhanced/runtime'; +import { createInstance } from '@module-federation/enhanced/runtime'; -const mf = new ModuleFederation({ +const mf = createInstance({ name: 'mf_host', remotes: [ { diff --git a/apps/website-new/docs/zh/plugin/dev/index.mdx b/apps/website-new/docs/zh/plugin/dev/index.mdx index 65924f8928..1ba2d9b65f 100644 --- a/apps/website-new/docs/zh/plugin/dev/index.mdx +++ b/apps/website-new/docs/zh/plugin/dev/index.mdx @@ -57,7 +57,7 @@ export default runtimePlugin; const path = require('path'); module.exports = { plugins: [ - new ModuleFederation({ + new ModuleFederationPlugin({ // ... runtimePlugins: [path.resolve(__dirname, './custom-runtime-plugin.ts')], }), diff --git a/apps/website-new/docs/zh/plugin/plugins/retry-plugin.mdx b/apps/website-new/docs/zh/plugin/plugins/retry-plugin.mdx index f73e363cdf..3cd5a9e79c 100644 --- a/apps/website-new/docs/zh/plugin/plugins/retry-plugin.mdx +++ b/apps/website-new/docs/zh/plugin/plugins/retry-plugin.mdx @@ -63,10 +63,10 @@ export default retryPlugin; ### 方式二:运行时注册 ```ts -import { ModuleFederation, loadRemote } from '@module-federation/enhanced/runtime'; +import { createInstance, loadRemote } from '@module-federation/enhanced/runtime'; import { RetryPlugin } from '@module-federation/retry-plugin'; -const mf = new ModuleFederation({ +const mf = createInstance({ name: 'federation_consumer', remotes: [], plugins: [ @@ -173,10 +173,10 @@ type ScriptWithRetryOptions = { ```ts -import { ModuleFederation, loadRemote } from '@module-federation/enhanced/runtime'; +import { createInstance, loadRemote } from '@module-federation/enhanced/runtime'; import { RetryPlugin } from '@module-federation/retry-plugin'; -const mf = new ModuleFederation({ +const mf = createInstance({ name: 'federation_consumer', remotes: [], plugins: [ diff --git a/apps/website-new/docs/zh/practice/frameworks/next/dynamic-remotes.mdx b/apps/website-new/docs/zh/practice/frameworks/next/dynamic-remotes.mdx index 96b3de1758..086506c4d5 100644 --- a/apps/website-new/docs/zh/practice/frameworks/next/dynamic-remotes.mdx +++ b/apps/website-new/docs/zh/practice/frameworks/next/dynamic-remotes.mdx @@ -1,11 +1,11 @@ # 动态远程模块 ```js -import { loadRemote, ModuleFederation } from '@module-federation/runtime'; +import { loadRemote, createInstance } from '@module-federation/runtime'; // if i have remotes in my federation plugin, i can pass the name of the remote loadRemote('home/exposedModule') // if i want to load a custom remote not known at build time. -const mf = new ModuleFederation({ +const mf = createInstance({ name: 'hostname', remotes: [ { diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 343e3e56d0..2d8b2535fe 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -21,15 +21,22 @@ export { export { ModuleFederation }; +export function createInstance(options: UserOptions) { + // Retrieve debug constructor + const ModuleFederationConstructor = + getGlobalFederationConstructor() || ModuleFederation; + return new ModuleFederationConstructor(options); +} + let FederationInstance: ModuleFederation | null = null; +/** + * @deprecated Use createInstance or getInstance instead + */ export function init(options: UserOptions): ModuleFederation { // Retrieve the same instance with the same name const instance = getGlobalFederationInstance(options.name, options.version); if (!instance) { - // Retrieve debug constructor - const FederationConstructor = - getGlobalFederationConstructor() || ModuleFederation; - FederationInstance = new FederationConstructor(options); + FederationInstance = createInstance(options); setGlobalFederationInstance(FederationInstance); return FederationInstance; } else { From 513009cd019c30d08245dd8f09193e08e8a1b095 Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Thu, 3 Jul 2025 18:08:55 +0800 Subject: [PATCH 06/14] chore: correct import --- packages/enhanced/CHANGELOG.md | 4 ++-- packages/enhanced/README.md | 2 +- packages/enhanced/src/index.ts | 2 +- .../enhanced/src/lib/container/ContainerPlugin.ts | 4 ++-- .../src/lib/container/ContainerReferencePlugin.ts | 4 ++-- .../src/lib/container/ModuleFederationPlugin.ts | 4 ++-- .../container/runtime/FederationRuntimePlugin.ts | 10 +++++----- .../src/lib/sharing/ConsumeSharedPlugin.ts | 4 ++-- .../src/lib/sharing/ProvideSharedPlugin.ts | 4 ++-- .../src/wrapper/FederationRuntimePlugin.ts | 10 ++++------ .../test/unit/container/ContainerPlugin.test.ts | 10 +++++----- .../container/ContainerReferencePlugin.test.ts | 15 ++++++--------- .../test/unit/sharing/ConsumeSharedPlugin.test.ts | 15 ++++++--------- .../test/unit/sharing/ProvideSharedPlugin.test.ts | 13 +++++-------- packages/enhanced/test/unit/sharing/utils.ts | 2 +- 15 files changed, 46 insertions(+), 57 deletions(-) diff --git a/packages/enhanced/CHANGELOG.md b/packages/enhanced/CHANGELOG.md index 3a1b661b9b..b65505a66c 100644 --- a/packages/enhanced/CHANGELOG.md +++ b/packages/enhanced/CHANGELOG.md @@ -764,7 +764,7 @@ - 6b02145: Added a check to skip processing when virtualRuntimeEntry is present. - - Added an early return in `ModuleFederationRuntimePlugin` to skip processing if `options.virtualRuntimeEntry` is defined. + - Added an early return in `FederationRuntimePlugin` to skip processing if `options.virtualRuntimeEntry` is defined. - 22a3b83: fix(data-prefetch): apply DataPrefetchPlugin on demand - Updated dependencies [22a3b83] @@ -784,7 +784,7 @@ - 70a1708: Added a check to skip processing when virtualRuntimeEntry is present. - - Added an early return in `ModuleFederationRuntimePlugin` to skip processing if `options.virtualRuntimeEntry` is defined. + - Added an early return in `FederationRuntimePlugin` to skip processing if `options.virtualRuntimeEntry` is defined. - @module-federation/rspack@0.6.9 - @module-federation/sdk@0.6.9 - @module-federation/runtime-tools@0.6.9 diff --git a/packages/enhanced/README.md b/packages/enhanced/README.md index 5c51089ba5..78874298f2 100644 --- a/packages/enhanced/README.md +++ b/packages/enhanced/README.md @@ -10,7 +10,7 @@ The following items are exported: - SharePlugin - ConsumeSharedPlugin - ProvideSharedPlugin -- ModuleFederationRuntimePlugin +- FederationRuntimePlugin - AsyncBoundaryPlugin - HoistContainerReferencesPlugin diff --git a/packages/enhanced/src/index.ts b/packages/enhanced/src/index.ts index 30520be312..9ff68acb16 100644 --- a/packages/enhanced/src/index.ts +++ b/packages/enhanced/src/index.ts @@ -9,7 +9,7 @@ export { default as ContainerPlugin } from './wrapper/ContainerPlugin'; export { default as ConsumeSharedPlugin } from './wrapper/ConsumeSharedPlugin'; export { default as ProvideSharedPlugin } from './wrapper/ProvideSharedPlugin'; export { default as FederationModulesPlugin } from './wrapper/FederationModulesPlugin'; -export { default as ModuleFederationRuntimePlugin } from './wrapper/ModuleFederationRuntimePlugin'; +export { default as FederationRuntimePlugin } from './wrapper/FederationRuntimePlugin'; export { default as AsyncBoundaryPlugin } from './wrapper/AsyncBoundaryPlugin'; export { default as HoistContainerReferencesPlugin } from './wrapper/HoistContainerReferencesPlugin'; diff --git a/packages/enhanced/src/lib/container/ContainerPlugin.ts b/packages/enhanced/src/lib/container/ContainerPlugin.ts index c14e798341..4fa8ab3f79 100644 --- a/packages/enhanced/src/lib/container/ContainerPlugin.ts +++ b/packages/enhanced/src/lib/container/ContainerPlugin.ts @@ -15,7 +15,7 @@ import type { WebpackPluginFunction, } from 'webpack'; import type { containerPlugin } from '@module-federation/sdk'; -import ModuleFederationRuntimePlugin from './runtime/ModuleFederationRuntimePlugin'; +import FederationRuntimePlugin from './runtime/FederationRuntimePlugin'; import FederationModulesPlugin from './runtime/FederationModulesPlugin'; import checkOptions from '../../schemas/container/ContainerPlugin.check'; import schema from '../../schemas/container/ContainerPlugin'; @@ -172,7 +172,7 @@ class ContainerPlugin { ContainerPlugin.patchChunkSplit(compiler, this._options.name); } - const federationRuntimePluginInstance = new ModuleFederationRuntimePlugin(); + const federationRuntimePluginInstance = new FederationRuntimePlugin(); federationRuntimePluginInstance.apply(compiler); const { name, exposes, shareScope, filename, library, runtime } = diff --git a/packages/enhanced/src/lib/container/ContainerReferencePlugin.ts b/packages/enhanced/src/lib/container/ContainerReferencePlugin.ts index f620799a3d..d160a13e67 100644 --- a/packages/enhanced/src/lib/container/ContainerReferencePlugin.ts +++ b/packages/enhanced/src/lib/container/ContainerReferencePlugin.ts @@ -14,7 +14,7 @@ import RemoteRuntimeModule from './RemoteRuntimeModule'; import RemoteToExternalDependency from './RemoteToExternalDependency'; import { parseOptions } from './options'; import { containerReferencePlugin } from '@module-federation/sdk'; -import ModuleFederationRuntimePlugin from './runtime/ModuleFederationRuntimePlugin'; +import FederationRuntimePlugin from './runtime/FederationRuntimePlugin'; import schema from '../../schemas/container/ContainerReferencePlugin'; import checkOptions from '../../schemas/container/ContainerReferencePlugin.check'; @@ -70,7 +70,7 @@ class ContainerReferencePlugin { */ apply(compiler: Compiler): void { const { _remotes: remotes, _remoteType: remoteType } = this; - new ModuleFederationRuntimePlugin().apply(compiler); + new FederationRuntimePlugin().apply(compiler); /** @type {Record} */ const remoteExternals: Record = {}; diff --git a/packages/enhanced/src/lib/container/ModuleFederationPlugin.ts b/packages/enhanced/src/lib/container/ModuleFederationPlugin.ts index 243a66c0b3..66d847d69f 100644 --- a/packages/enhanced/src/lib/container/ModuleFederationPlugin.ts +++ b/packages/enhanced/src/lib/container/ModuleFederationPlugin.ts @@ -18,7 +18,7 @@ import type { Compiler, WebpackPluginInstance } from 'webpack'; import SharePlugin from '../sharing/SharePlugin'; import ContainerPlugin from './ContainerPlugin'; import ContainerReferencePlugin from './ContainerReferencePlugin'; -import ModuleFederationRuntimePlugin from './runtime/ModuleFederationRuntimePlugin'; +import FederationRuntimePlugin from './runtime/FederationModulesPlugin'; import { RemoteEntryPlugin } from '@module-federation/rspack/remote-entry-plugin'; import { ExternalsType } from 'webpack/declarations/WebpackOptions'; import StartupChunkDependenciesPlugin from '../startup/MfStartupChunkDependenciesPlugin'; @@ -147,7 +147,7 @@ class ModuleFederationPlugin implements WebpackPluginInstance { new PrefetchPlugin(options).apply(compiler); } - new ModuleFederationRuntimePlugin(options).apply(compiler); + new FederationRuntimePlugin(options).apply(compiler); const library = options.library || { type: 'var', name: options.name }; const remoteType = diff --git a/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts b/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts index 6e3693d4b5..076b937d82 100644 --- a/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts +++ b/packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts @@ -56,7 +56,7 @@ const federationGlobal = getFederationGlobalScope(RuntimeGlobals); const onceForCompiler = new WeakSet(); const onceForCompilerEntryMap = new WeakMap(); -class ModuleFederationRuntimePlugin { +class FederationRuntimePlugin { options?: moduleFederationPlugin.ModuleFederationPluginOptions; entryFilePath: string; bundlerRuntimePath: string; @@ -163,7 +163,7 @@ class ModuleFederationRuntimePlugin { if (!this.options?.virtualRuntimeEntry) { const containerName = this.options.name; const hash = createHash( - `${containerName} ${ModuleFederationRuntimePlugin.getTemplate( + `${containerName} ${FederationRuntimePlugin.getTemplate( compiler, this.options, this.bundlerRuntimePath, @@ -173,7 +173,7 @@ class ModuleFederationRuntimePlugin { entryFilePath = path.join(TEMP_DIR, `entry.${hash}.js`); } else { entryFilePath = `data:text/javascript;charset=utf-8;base64,${pBtoa( - ModuleFederationRuntimePlugin.getTemplate( + FederationRuntimePlugin.getTemplate( compiler, this.options, this.bundlerRuntimePath, @@ -202,7 +202,7 @@ class ModuleFederationRuntimePlugin { mkdirpSync(fs, TEMP_DIR); fs.writeFileSync( filePath, - ModuleFederationRuntimePlugin.getTemplate( + FederationRuntimePlugin.getTemplate( compiler, this.options, this.bundlerRuntimePath, @@ -429,4 +429,4 @@ class ModuleFederationRuntimePlugin { } } -export default ModuleFederationRuntimePlugin; +export default FederationRuntimePlugin; diff --git a/packages/enhanced/src/lib/sharing/ConsumeSharedPlugin.ts b/packages/enhanced/src/lib/sharing/ConsumeSharedPlugin.ts index 1f6abc7ca3..6cb15ee0a4 100644 --- a/packages/enhanced/src/lib/sharing/ConsumeSharedPlugin.ts +++ b/packages/enhanced/src/lib/sharing/ConsumeSharedPlugin.ts @@ -25,7 +25,7 @@ import ConsumeSharedFallbackDependency from './ConsumeSharedFallbackDependency'; import ConsumeSharedModule from './ConsumeSharedModule'; import ConsumeSharedRuntimeModule from './ConsumeSharedRuntimeModule'; import ProvideForSharedDependency from './ProvideForSharedDependency'; -import ModuleFederationRuntimePlugin from '../container/runtime/ModuleFederationRuntimePlugin'; +import FederationRuntimePlugin from '../container/runtime/FederationRuntimePlugin'; import ShareRuntimeModule from './ShareRuntimeModule'; import type { SemVerRange } from 'webpack/lib/util/semver'; import type { ResolveData } from 'webpack/lib/NormalModuleFactory'; @@ -145,7 +145,7 @@ class ConsumeSharedPlugin { } apply(compiler: Compiler): void { - new ModuleFederationRuntimePlugin().apply(compiler); + new FederationRuntimePlugin().apply(compiler); process.env['FEDERATION_WEBPACK_PATH'] = process.env['FEDERATION_WEBPACK_PATH'] || getWebpackPath(compiler); diff --git a/packages/enhanced/src/lib/sharing/ProvideSharedPlugin.ts b/packages/enhanced/src/lib/sharing/ProvideSharedPlugin.ts index 734ed87169..aceacb0d52 100644 --- a/packages/enhanced/src/lib/sharing/ProvideSharedPlugin.ts +++ b/packages/enhanced/src/lib/sharing/ProvideSharedPlugin.ts @@ -21,7 +21,7 @@ import type { ProvideSharedPluginOptions, ProvidesConfig, } from '../../declarations/plugins/sharing/ProvideSharedPlugin'; -import ModuleFederationRuntimePlugin from '../container/runtime/ModuleFederationRuntimePlugin'; +import FederationRuntimePlugin from '../container/runtime/FederationRuntimePlugin'; import { createSchemaValidation } from '../../utils'; const WebpackError = require( normalizeWebpackPath('webpack/lib/WebpackError'), @@ -124,7 +124,7 @@ class ProvideSharedPlugin { * @returns {void} */ apply(compiler: Compiler): void { - new ModuleFederationRuntimePlugin().apply(compiler); + new FederationRuntimePlugin().apply(compiler); process.env['FEDERATION_WEBPACK_PATH'] = process.env['FEDERATION_WEBPACK_PATH'] || getWebpackPath(compiler); diff --git a/packages/enhanced/src/wrapper/FederationRuntimePlugin.ts b/packages/enhanced/src/wrapper/FederationRuntimePlugin.ts index 16f07e50a0..e2390f7964 100644 --- a/packages/enhanced/src/wrapper/FederationRuntimePlugin.ts +++ b/packages/enhanced/src/wrapper/FederationRuntimePlugin.ts @@ -3,11 +3,9 @@ import type { moduleFederationPlugin } from '@module-federation/sdk'; import { getWebpackPath } from '@module-federation/sdk/normalize-webpack-path'; -const PLUGIN_NAME = 'ModuleFederationRuntimePlugin'; +const PLUGIN_NAME = 'FederationRuntimePlugin'; -export default class ModuleFederationRuntimePlugin - implements WebpackPluginInstance -{ +export default class FederationRuntimePlugin implements WebpackPluginInstance { private _options?: moduleFederationPlugin.ModuleFederationPluginOptions; name: string; entryFilePath: string; @@ -22,8 +20,8 @@ export default class ModuleFederationRuntimePlugin process.env['FEDERATION_WEBPACK_PATH'] = process.env['FEDERATION_WEBPACK_PATH'] || getWebpackPath(compiler); const CoreFederationRuntimePlugin = - require('../lib/container/runtime/ModuleFederationRuntimePlugin') - .default as typeof import('../lib/container/runtime/ModuleFederationRuntimePlugin').default; + require('../lib/container/runtime/FederationRuntimePlugin') + .default as typeof import('../lib/container/runtime/FederationRuntimePlugin').default; const pluginInstance = new CoreFederationRuntimePlugin(this._options); pluginInstance.apply(compiler); diff --git a/packages/enhanced/test/unit/container/ContainerPlugin.test.ts b/packages/enhanced/test/unit/container/ContainerPlugin.test.ts index d5d8855947..689c6d1c36 100644 --- a/packages/enhanced/test/unit/container/ContainerPlugin.test.ts +++ b/packages/enhanced/test/unit/container/ContainerPlugin.test.ts @@ -80,7 +80,7 @@ jest.mock( ); jest.mock( - '../../../src/lib/container/runtime/ModuleFederationRuntimePlugin', + '../../../src/lib/container/runtime/FederationRuntimePlugin', () => { return jest.fn().mockImplementation(() => ({ apply: jest.fn(), @@ -290,7 +290,7 @@ describe('ContainerPlugin', () => { expect(true).toBe(true); }); - it('should register ModuleFederationRuntimePlugin', () => { + it('should register FederationRuntimePlugin', () => { const options = { name: 'test-container', exposes: { @@ -301,10 +301,10 @@ describe('ContainerPlugin', () => { const plugin = new ContainerPlugin(options); plugin.apply(mockCompiler); - const ModuleFederationRuntimePlugin = require('../../../src/lib/container/runtime/ModuleFederationRuntimePlugin'); - expect(ModuleFederationRuntimePlugin).toHaveBeenCalled(); + const FederationRuntimePlugin = require('../../../src/lib/container/runtime/FederationRuntimePlugin'); + expect(FederationRuntimePlugin).toHaveBeenCalled(); expect( - ModuleFederationRuntimePlugin.mock.results[0].value.apply, + FederationRuntimePlugin.mock.results[0].value.apply, ).toHaveBeenCalledWith(mockCompiler); }); diff --git a/packages/enhanced/test/unit/container/ContainerReferencePlugin.test.ts b/packages/enhanced/test/unit/container/ContainerReferencePlugin.test.ts index 47f3e92e27..c767a916c6 100644 --- a/packages/enhanced/test/unit/container/ContainerReferencePlugin.test.ts +++ b/packages/enhanced/test/unit/container/ContainerReferencePlugin.test.ts @@ -49,17 +49,14 @@ jest.mock('../../../src/lib/container/RemoteModule', () => MockRemoteModule, { virtual: true, }); -// Mock ModuleFederationRuntimePlugin +// Mock FederationRuntimePlugin const mockApply = jest.fn(); const mockFederationRuntimePlugin = jest.fn().mockImplementation(() => ({ apply: mockApply, })); -jest.mock( - '../../../src/lib/container/runtime/ModuleFederationRuntimePlugin', - () => { - return mockFederationRuntimePlugin; - }, -); +jest.mock('../../../src/lib/container/runtime/FederationRuntimePlugin', () => { + return mockFederationRuntimePlugin; +}); // Mock FallbackModuleFactory jest.mock( @@ -304,7 +301,7 @@ describe('ContainerReferencePlugin', () => { expect(mockCompilation.dependencyFactories.size).toBeGreaterThan(0); }); - it('should apply ModuleFederationRuntimePlugin', () => { + it('should apply FederationRuntimePlugin', () => { const options = { remotes: { 'remote-app': 'remote-app@http://localhost:3001/remoteEntry.js', @@ -319,7 +316,7 @@ describe('ContainerReferencePlugin', () => { const plugin = new ContainerReferencePlugin(options); plugin.apply(mockCompiler); - // Verify ModuleFederationRuntimePlugin was created and applied + // Verify FederationRuntimePlugin was created and applied expect(mockFederationRuntimePlugin).toHaveBeenCalled(); expect(mockApply).toHaveBeenCalledWith(mockCompiler); }); diff --git a/packages/enhanced/test/unit/sharing/ConsumeSharedPlugin.test.ts b/packages/enhanced/test/unit/sharing/ConsumeSharedPlugin.test.ts index 040615feb9..1e68199ab5 100644 --- a/packages/enhanced/test/unit/sharing/ConsumeSharedPlugin.test.ts +++ b/packages/enhanced/test/unit/sharing/ConsumeSharedPlugin.test.ts @@ -39,15 +39,12 @@ jest.mock('@module-federation/sdk/normalize-webpack-path', () => ({ getWebpackPath: jest.fn(() => 'mocked-webpack-path'), })); -// Mock ModuleFederationRuntimePlugin -jest.mock( - '../../../src/lib/container/runtime/ModuleFederationRuntimePlugin', - () => { - return jest.fn().mockImplementation(() => ({ - apply: jest.fn(), - })); - }, -); +// Mock FederationRuntimePlugin +jest.mock('../../../src/lib/container/runtime/FederationRuntimePlugin', () => { + return jest.fn().mockImplementation(() => ({ + apply: jest.fn(), + })); +}); // Mock ConsumeSharedModule jest.mock('../../../src/lib/sharing/ConsumeSharedModule', () => { diff --git a/packages/enhanced/test/unit/sharing/ProvideSharedPlugin.test.ts b/packages/enhanced/test/unit/sharing/ProvideSharedPlugin.test.ts index 9cc4f382cf..d278af00a5 100644 --- a/packages/enhanced/test/unit/sharing/ProvideSharedPlugin.test.ts +++ b/packages/enhanced/test/unit/sharing/ProvideSharedPlugin.test.ts @@ -26,14 +26,11 @@ jest.mock('@module-federation/sdk/normalize-webpack-path', () => ({ getWebpackPath: jest.fn(() => 'mocked-webpack-path'), })); -jest.mock( - '../../../src/lib/container/runtime/ModuleFederationRuntimePlugin', - () => { - return jest.fn().mockImplementation(() => ({ - apply: jest.fn(), - })); - }, -); +jest.mock('../../../src/lib/container/runtime/FederationRuntimePlugin', () => { + return jest.fn().mockImplementation(() => ({ + apply: jest.fn(), + })); +}); // Mock ProvideSharedDependency class MockProvideSharedDependency { diff --git a/packages/enhanced/test/unit/sharing/utils.ts b/packages/enhanced/test/unit/sharing/utils.ts index 6e1d78f464..660ebb3930 100644 --- a/packages/enhanced/test/unit/sharing/utils.ts +++ b/packages/enhanced/test/unit/sharing/utils.ts @@ -447,7 +447,7 @@ export const createSharingTestEnvironment = () => { * Mock classes for federation runtime plugins */ export const createMockFederationRuntime = () => { - // Mock ModuleFederationRuntimePlugin + // Mock FederationRuntimePlugin const MockFederationRuntimePlugin = jest.fn().mockImplementation(() => ({ apply: jest.fn(), })); From 5072973805a61fa36495876ded642639954874b4 Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Thu, 3 Jul 2025 20:15:44 +0800 Subject: [PATCH 07/14] docs: typo --- .../en/guide/basic/runtime/runtime-api.mdx | 63 ++++++++++--------- .../docs/en/guide/basic/runtime/runtime.mdx | 2 +- .../zh/guide/basic/runtime/runtime-api.mdx | 26 ++++---- 3 files changed, 46 insertions(+), 45 deletions(-) diff --git a/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx b/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx index a136edf3dc..eb12f737a7 100644 --- a/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx +++ b/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx @@ -3,49 +3,49 @@ import { Badge } from '@theme'; import Collapse from '@components/Collapse' -If the build plugin is used, an MF instance will be automatically created and stored in memory when the project starts. You can directly call methods of the MF instance via the API. +If the build plugin is used, an MF instance will be automatically created and stored in memory when the project starts. You can directly call methods of the MF instance via the . If the build plugin is not used, you need to manually create an MF instance before calling the corresponding API. -## ModuleFederation Class - -In addition to exposing APIs, the Runtime also provides the ModuleFederation class, which you can use to create ModuleFederation instance. - * What is a `ModuleFederation` instance? A `ModuleFederation` instance is an instance of the `ModuleFederation` class, which contains all the functionality of the `ModuleFederation` runtime. > You can enter `__FEDERATION__.__INSTANCES__` in the console to view the created instances. -* When to use `ModuleFederation` class? +## createInstance + +In addition to exposing APIs, the Runtime also provides the `createInstance`, which you can use to create ModuleFederation instance. + +* When to use `createInstance`? To ensure the uniqueness of the ModuleFederation instance, after the build plugin creates an instance, it will be stored in memory. The exported APIs all first obtain the ModuleFederation instance from memory and then call the APIs of the ModuleFederation instance. This is also why APIs like loadRemote can be used directly from the `@module-federation/enhanced/runtime` package and inherently understand what application container they are attached to. -However, this singleton pattern also limits the ability to create multiple instances, as it assumes that there is only one instance per bundle. Therefore, if you need to create a new instance , you can use the ModuleFederation class to create a new one. +However, this singleton pattern also limits the ability to create multiple instances, as it assumes that there is only one instance per bundle. Therefore, if you need to create a new instance , you can use the [createInstance](#createinstance) to create a new one. ```ts import { createInstance } from '@module-federation/enhanced/runtime'; const mf = createInstance({ - name: '@demo/host', + name: 'host', remotes: [ { - name: '@demo/sub1', - entry: 'http://localhost:8080/vmok-manifest.json' + name: 'sub1', + entry: 'http://localhost:8080/mf-manifest.json' } ] }); -mf.loadRemote('@demo/sub1/util').then((m) => m.add(1, 2, 3)); +mf.loadRemote('sub1/util').then((m) => m.add(1, 2, 3)); ``` -## init API Deprecated +## init Deprecated :::warning Warning This API will be deprecated. If you need to get an existing instance, you can use [getInstance](#getinstance) to retrieve the created instance. -If you need to create a new instance, please use the [ModuleFederation](#modulefederation) class. +If you need to create a new instance, please use the [createInstance](#createinstance). ::: @@ -138,7 +138,7 @@ init({ ``` -## getInstance API +## getInstance - Type: `getInstance(): ModuleFederation` - Retrieves the `ModuleFederation` instance created by the build plugin @@ -152,9 +152,9 @@ const mfInstance = getInstance(); mfInstance.loadRemote('remote/util'); ``` -If the build plugin is not used, calling `getInstance` will throw an exception. In this case, you need to use the [ModuleFederation](#modulefederation) class to create a new instance. +If the build plugin is not used, calling `getInstance` will throw an exception. In this case, you need to use the [createInstance](#createinstance) to create a new instance. -## registerRemotes API +## registerRemotes @@ -196,7 +196,7 @@ If use build plugin: ```ts import { registerRemotes } from '@module-federation/enhanced/runtime'; -// Add a new remote @demo/sub2 +// Add a new remote sub2 registerRemotes([ { name: 'sub2', @@ -204,7 +204,7 @@ registerRemotes([ } ]); -// Overwrite the previous remote @demo/sub1 +// Overwrite the previous remote sub1 registerRemotes([ { name: 'sub1', @@ -212,6 +212,7 @@ registerRemotes([ } ],{ force:true }); ``` + If not use build plugin: @@ -228,7 +229,7 @@ const mf = createInstance({ ] }); -// Add a new remote @demo/sub2 +// Add a new remote sub2 mf.registerRemotes([ { name: 'sub2', @@ -236,7 +237,7 @@ mf.registerRemotes([ } ]); -// Overwrite the previous remote @demo/sub1 +// Overwrite the previous remote sub1 mf.registerRemotes([ { name: 'sub1', @@ -245,7 +246,7 @@ mf.registerRemotes([ ],{ force:true }); ``` -## registerPlugins API +## registerPlugins - type @@ -289,7 +290,7 @@ registerPlugins([ ]); ``` -## registerShared API +## registerShared @@ -353,7 +354,7 @@ registerShared({ }); ``` -## loadRemote API +## loadRemote - Type: `loadRemote(id: string)` - Loads a remote module. @@ -369,7 +370,7 @@ loadRemote("remote/util").then((m)=> m.add(1,2,3)); loadRemote("app1/util").then((m)=> m.add(1,2,3)); ``` -## loadShare API +## loadShare - Type: `loadShare(pkgName: string, extraOptions?: { customShareInfo?: Partial;resolver?: (sharedOptions: ShareInfos[string]) => Shared;})` - Gets a `share` dependency. When there is a `share` dependency in the global environment that meets the requirements of the current `host`, the existing dependency that meets the `share` conditions will be reused first. Otherwise, its own dependency will be loaded and stored in the global cache. @@ -426,7 +427,7 @@ loadShare('react', { }); ``` -## preloadRemote API +## preloadRemote @@ -486,9 +487,9 @@ registerRemotes([ }, ]); -// Preload the @demo/sub1 module +// Preload the sub1 module // Filter resource information that contains 'ignore' in the resource name -// Only preload the sub-dependency @demo/sub1-button module +// Only preload the sub-dependency sub1-button module preloadRemote([ { nameOrAlias: 'sub1', @@ -500,9 +501,9 @@ preloadRemote([ ]); -// Preload the @demo/sub2 module -// Preload all exposes under @demo/sub2 -// Preload the synchronous and asynchronous resources of @demo/sub2 +// Preload the sub2 module +// Preload all exposes under sub2 +// Preload the synchronous and asynchronous resources of sub2 preloadRemote([ { nameOrAlias: 'sub2', @@ -510,7 +511,7 @@ preloadRemote([ }, ]); -// Preload the add expose of the @demo/sub3 module +// Preload the add expose of the sub3 module preloadRemote([ { nameOrAlias: 'sub3', diff --git a/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx b/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx index 1b85f70d0f..6f3c4ff9b0 100644 --- a/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx +++ b/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx @@ -62,7 +62,7 @@ import { PackageManagerTabs } from '@theme'; If use build plugin, you can directly register modules using `registerRemotes`. ```ts -import { init, loadRemote } from '@module-federation/enhanced/runtime'; +import { registerRemotes } from '@module-federation/enhanced/runtime'; registerRemotes([ { diff --git a/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx b/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx index 8ce73cee68..fe359658f9 100644 --- a/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx +++ b/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx @@ -36,7 +36,7 @@ const mf = createInstance({ ] }); -mf.loadRemote('@demo/sub1/util').then((m) => m.add(1, 2, 3)); +mf.loadRemote('sub1/util').then((m) => m.add(1, 2, 3)); ``` ## init 废弃 @@ -45,7 +45,7 @@ mf.loadRemote('@demo/sub1/util').then((m) => m.add(1, 2, 3)); 此 API 将被废弃。如果需要获取已创建的实例,可以使用 [getInstance](#getinstance) 获取已创建的实例。 -如果需要创建新的实例,请使用 [createInstance](#createinstance) class。 +如果需要创建新的实例,请使用 [createInstance](#createinstance)。 ::: @@ -152,7 +152,7 @@ const mfInstance = getInstance(); mfInstance.loadRemote('remote/util'); ``` -如果没有使用构建插件,调用 `getInstance` 会抛出异常,此时你需要使用 [ModuleFederation](#modulefederation) class 来创建一个新的实例。 +如果没有使用构建插件,调用 `getInstance` 会抛出异常,此时你需要使用 [createInstance](#createinstance) 来创建一个新的实例。 ## registerRemotes @@ -196,7 +196,7 @@ interface RemoteWithVersion { ```ts import { registerRemotes } from '@module-federation/enhanced/runtime'; -// 增加新的 remote @demo/sub2 +// 增加新的 remote sub2 registerRemotes([ { name: 'sub2', @@ -204,7 +204,7 @@ registerRemotes([ } ]); -// 覆盖之前的 remote @demo/sub1 +// 覆盖之前的 remote sub1 registerRemotes([ { name: 'sub1', @@ -228,7 +228,7 @@ const mf = createInstance({ ] }); -// 增加新的 remote @demo/sub2 +// 增加新的 remote sub2 mf.registerRemotes([ { name: 'sub2', @@ -236,7 +236,7 @@ mf.registerRemotes([ } ]); -// 覆盖之前的 remote @demo/sub1 +// 覆盖之前的 remote sub1 mf.registerRemotes([ { name: 'sub1', @@ -487,9 +487,9 @@ registerRemotes([ }, ]); -// 预加载 @demo/sub1 模块 +// 预加载 sub1 模块 // 过滤资源名称中携带 ignore 的资源信息 -// 只预加载子依赖的 @demo/sub1-button 模块 +// 只预加载子依赖的 sub1-button 模块 preloadRemote([ { nameOrAlias: 'sub1', @@ -501,9 +501,9 @@ preloadRemote([ ]); -// 预加载 @demo/sub2 模块 -// 预加载 @demo/sub2 下的所有 expose -// 预加载 @demo/sub2 的同步资源和异步资源 +// 预加载 sub2 模块 +// 预加载 sub2 下的所有 expose +// 预加载 sub2 的同步资源和异步资源 preloadRemote([ { nameOrAlias: 'sub2', @@ -511,7 +511,7 @@ preloadRemote([ }, ]); -// 预加载 @demo/sub3 模块的 add expose +// 预加载 sub3 模块的 add expose preloadRemote([ { nameOrAlias: 'sub3', From 24c2b98bff624774984a9c625d9c9011154f207e Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Fri, 4 Jul 2025 10:49:38 +0800 Subject: [PATCH 08/14] docs: add createInstance desc --- .../en/guide/basic/runtime/runtime-api.mdx | 31 +++++++++++++++++-- .../zh/guide/basic/runtime/runtime-api.mdx | 31 +++++++++++++++++-- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx b/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx index eb12f737a7..ac62c0a22a 100644 --- a/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx +++ b/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx @@ -5,8 +5,31 @@ import Collapse from '@components/Collapse' If the build plugin is used, an MF instance will be automatically created and stored in memory when the project starts. You can directly call methods of the MF instance via the . +```ts +import { loadRemote } from '@module-federation/enhanced/runtime'; + +loadRemote('remote1'); +``` + If the build plugin is not used, you need to manually create an MF instance before calling the corresponding API. +```ts +import { createInstance } from '@module-federation/enhanced/runtime'; + +const mf = createInstance({ + name: 'host', + remotes: [ + { + name: 'remote1', + entry: 'http://localhost:2001/vmok-manifest.json' + } + ] +}); + + +mf.loadRemote('remote1'); +``` + * What is a `ModuleFederation` instance? A `ModuleFederation` instance is an instance of the `ModuleFederation` class, which contains all the functionality of the `ModuleFederation` runtime. @@ -15,13 +38,17 @@ A `ModuleFederation` instance is an instance of the `ModuleFederation` class, wh ## createInstance -In addition to exposing APIs, the Runtime also provides the `createInstance`, which you can use to create ModuleFederation instance. +Used to create ModuleFederation instance. * When to use `createInstance`? To ensure the uniqueness of the ModuleFederation instance, after the build plugin creates an instance, it will be stored in memory. The exported APIs all first obtain the ModuleFederation instance from memory and then call the APIs of the ModuleFederation instance. This is also why APIs like loadRemote can be used directly from the `@module-federation/enhanced/runtime` package and inherently understand what application container they are attached to. -However, this singleton pattern also limits the ability to create multiple instances, as it assumes that there is only one instance per bundle. Therefore, if you need to create a new instance , you can use the [createInstance](#createinstance) to create a new one. +This singleton pattern works for most scenarios, but in the following cases, you need to use `createInstance`: + +- No build plugin is used (Only use runtime) +- Multiple ModuleFederation instances need to be created with different configurations for each instance +- You want to use ModuleFederation's partitioning feature to encapsulate corresponding APIs for use in other projects ```ts import { createInstance } from '@module-federation/enhanced/runtime'; diff --git a/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx b/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx index fe359658f9..1a44367b60 100644 --- a/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx +++ b/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx @@ -5,8 +5,31 @@ import Collapse from '@components/Collapse' 若使用构建插件,项目启动时将自动创建 `ModuleFederation` 实例并存储于内存中。此时可直接调用 API,API 会自动从内存中获取构建运行时创建的 `ModuleFederation` 实例。 +```ts +import { loadRemote } from '@module-federation/enhanced/runtime'; + +loadRemote('remote1'); +``` + 若未使用构建插件,则需手动创建 `ModuleFederation` 实例,之后调用相应 API。 +```ts +import { createInstance } from '@module-federation/enhanced/runtime'; + +const mf = createInstance({ + name: 'host', + remotes: [ + { + name: 'remote1', + entry: 'http://localhost:2001/vmok-manifest.json' + } + ] +}); + + +mf.loadRemote('remote1'); +``` + * 什么是 `ModuleFederation` 实例 ? `ModuleFederation` 实例是 `ModuleFederation` 类的实例,它包含了 `ModuleFederation` 运行时的所有功能。 @@ -15,13 +38,17 @@ import Collapse from '@components/Collapse' ## createInstance -`Runtime` 除了暴露出 API 外,还提供了 `ModuleFederation` 类,你可以使用它来创建一个 `ModuleFederation` 实例。 +用于创建 ModuleFederation 实例。 * 什么时候使用 `createInstance` ? 为了保证 `ModuleFederation` 实例的唯一性,我们在构建插件创建实例后,会将其存储到内存中,导出的 API 都是先从内存中获取 `ModuleFederation` 实例,然后再调用 `ModuleFederation` 实例的 API。这也是为什么 `loadRemote` 等 API 可以直接使用的原因。 -但是这种单例模式同时也限制无法创建多份实例,因此如果你需要**创建新的 ModuleFederation 实例**,那么你可以使用 `createInstance` 来创建一个新的实例。 +这种单例模式适用于大多数场景,但如果在以下场景,你需要使用 `createInstance`: + +- 没有使用构建插件(纯运行时场景) +- 需要创建多个 ModuleFederation 实例,每个实例的配置不同 +- 希望使用 ModuleFederation 分治特性,封装相应的 API 提供给其他项目使用 ```ts import { createInstance } from '@module-federation/enhanced/runtime'; From f11dbcbe90a58cc0537c013df2b5ddc9c213d934 Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Fri, 4 Jul 2025 15:28:46 +0800 Subject: [PATCH 09/14] fix: correct FederationRuntimePlugin --- packages/enhanced/src/lib/container/ModuleFederationPlugin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/enhanced/src/lib/container/ModuleFederationPlugin.ts b/packages/enhanced/src/lib/container/ModuleFederationPlugin.ts index 66d847d69f..172e780c56 100644 --- a/packages/enhanced/src/lib/container/ModuleFederationPlugin.ts +++ b/packages/enhanced/src/lib/container/ModuleFederationPlugin.ts @@ -18,7 +18,7 @@ import type { Compiler, WebpackPluginInstance } from 'webpack'; import SharePlugin from '../sharing/SharePlugin'; import ContainerPlugin from './ContainerPlugin'; import ContainerReferencePlugin from './ContainerReferencePlugin'; -import FederationRuntimePlugin from './runtime/FederationModulesPlugin'; +import FederationRuntimePlugin from './runtime/FederationRuntimePlugin'; import { RemoteEntryPlugin } from '@module-federation/rspack/remote-entry-plugin'; import { ExternalsType } from 'webpack/declarations/WebpackOptions'; import StartupChunkDependenciesPlugin from '../startup/MfStartupChunkDependenciesPlugin'; From 46e3112e90a64331e094fe7a9e8d4b17d90af3a2 Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Fri, 4 Jul 2025 17:19:10 +0800 Subject: [PATCH 10/14] docs: add migrate help doc --- .../docs/en/guide/basic/runtime/runtime-api.mdx | 15 +++++++++++++++ .../docs/zh/guide/basic/runtime/runtime-api.mdx | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx b/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx index ac62c0a22a..20c7fe5379 100644 --- a/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx +++ b/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx @@ -165,6 +165,21 @@ init({ ``` +### How to Migrate + +**Using Build Plugin** + +1. Remove calls to the `init` API +2. Use [registerShared](#registershared) instead of the `shared` configuration in `init` +3. Use [registerRemotes](#registerremotes) instead of the `remotes` configuration in `init` +4. Use [registerPlugins](#registerplugins) instead of the `plugins` configuration in `init` +5. Use [getInstance](#getinstance) to obtain the `ModuleFederation` instance created by the build plugin + + +**Not Using Build Plugin** + +If you are not using the build plugin, you can replace `init` with [createInstance](#createinstance), which has exactly the same parameters. + ## getInstance - Type: `getInstance(): ModuleFederation` diff --git a/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx b/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx index 1a44367b60..0669564cf7 100644 --- a/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx +++ b/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx @@ -165,6 +165,21 @@ init({ ``` +### 如何迁移 + +**使用了构建插件** + +1. 移除 `init` API 的调用 +2. 使用 [registerShared](#registershared) 代替 `init` 中的 `shared` 配置 +3. 使用 [registerRemotes](#registerremotes) 代替 `init` 中的 `remotes` 配置 +4. 使用 [registerPlugins](#registerplugins) 代替 `init` 中的 `plugins` 配置 +5. 使用 [getInstance](#getinstance) 获取构建插件创建的 `ModuleFederation` 实例 + + +**没有使用构建插件** + +如果没有使用构建插件,你可以使用 [createInstance](#createinstance)替换 `init` ,其参数完全一致。 + ## getInstance - Type: `getInstance(): ModuleFederation` From 3606064b51081cdb0ad8a738f4cfe82a49f3dcc2 Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Fri, 4 Jul 2025 17:20:19 +0800 Subject: [PATCH 11/14] feat(runtime): add createInstance api and deprecate init --- .changeset/good-apricots-own.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/good-apricots-own.md diff --git a/.changeset/good-apricots-own.md b/.changeset/good-apricots-own.md new file mode 100644 index 0000000000..6f356a3c5b --- /dev/null +++ b/.changeset/good-apricots-own.md @@ -0,0 +1,5 @@ +--- +'@module-federation/runtime': minor +--- + +feat(runtime): add createInstance api and deprecate init From 41b124019aec9bdeb74f35bd4d8d35bd08220630 Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Fri, 4 Jul 2025 17:22:21 +0800 Subject: [PATCH 12/14] refactor(runtime): rename FederationRuntimePlugin to ModuleFederationRuntimePlugin --- .changeset/few-games-obey.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/few-games-obey.md diff --git a/.changeset/few-games-obey.md b/.changeset/few-games-obey.md new file mode 100644 index 0000000000..06a98bf608 --- /dev/null +++ b/.changeset/few-games-obey.md @@ -0,0 +1,5 @@ +--- +'@module-federation/runtime': patch +--- + +refactor(runtime): rename FederationRuntimePlugin to ModuleFederationRuntimePlugin From 1107f88d44805956f491d45c2f19fe6899cd70a1 Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Mon, 7 Jul 2025 14:31:13 +0800 Subject: [PATCH 13/14] chore(runtime): add RUNTIME-009 error code --- .changeset/cuddly-spoons-grin.md | 5 + .../en/guide/basic/runtime/runtime-api.mdx | 746 ++++++++++++------ .../docs/en/guide/basic/runtime/runtime.mdx | 355 ++++++--- .../troubleshooting/runtime/RUNTIME-009.mdx | 13 + .../zh/guide/basic/runtime/runtime-api.mdx | 744 +++++++++++------ .../docs/zh/guide/basic/runtime/runtime.mdx | 357 ++++++--- .../troubleshooting/runtime/RUNTIME-009.mdx | 13 + .../website-new/src/components/en/runtime.mdx | 31 + .../website-new/src/components/zh/runtime.mdx | 31 + packages/error-codes/src/desc.ts | 2 + packages/error-codes/src/error-codes.ts | 1 + packages/runtime/src/index.ts | 19 +- 12 files changed, 1615 insertions(+), 702 deletions(-) create mode 100644 .changeset/cuddly-spoons-grin.md create mode 100644 apps/website-new/docs/en/guide/troubleshooting/runtime/RUNTIME-009.mdx create mode 100644 apps/website-new/docs/zh/guide/troubleshooting/runtime/RUNTIME-009.mdx create mode 100644 apps/website-new/src/components/en/runtime.mdx create mode 100644 apps/website-new/src/components/zh/runtime.mdx diff --git a/.changeset/cuddly-spoons-grin.md b/.changeset/cuddly-spoons-grin.md new file mode 100644 index 0000000000..277006d025 --- /dev/null +++ b/.changeset/cuddly-spoons-grin.md @@ -0,0 +1,5 @@ +--- +'@module-federation/runtime': patch +--- + +chore(runtime): add RUNTIME-009 error code diff --git a/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx b/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx index 20c7fe5379..52654f9424 100644 --- a/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx +++ b/apps/website-new/docs/en/guide/basic/runtime/runtime-api.mdx @@ -1,40 +1,10 @@ # Runtime API -import { Badge } from '@theme'; +import { Steps, Tab, Tabs, Badge, Aside } from '@theme'; import Collapse from '@components/Collapse' +import Runtime from '@components/en/runtime'; -If the build plugin is used, an MF instance will be automatically created and stored in memory when the project starts. You can directly call methods of the MF instance via the . - -```ts -import { loadRemote } from '@module-federation/enhanced/runtime'; - -loadRemote('remote1'); -``` - -If the build plugin is not used, you need to manually create an MF instance before calling the corresponding API. - -```ts -import { createInstance } from '@module-federation/enhanced/runtime'; - -const mf = createInstance({ - name: 'host', - remotes: [ - { - name: 'remote1', - entry: 'http://localhost:2001/vmok-manifest.json' - } - ] -}); - - -mf.loadRemote('remote1'); -``` - -* What is a `ModuleFederation` instance? - -A `ModuleFederation` instance is an instance of the `ModuleFederation` class, which contains all the functionality of the `ModuleFederation` runtime. - -> You can enter `__FEDERATION__.__INSTANCES__` in the console to view the created instances. + ## createInstance @@ -167,7 +137,7 @@ init({ ### How to Migrate -**Using Build Plugin** +#### Build Plugin(Use build plugin) 1. Remove calls to the `init` API 2. Use [registerShared](#registershared) instead of the `shared` configuration in `init` @@ -175,8 +145,84 @@ init({ 4. Use [registerPlugins](#registerplugins) instead of the `plugins` configuration in `init` 5. Use [getInstance](#getinstance) to obtain the `ModuleFederation` instance created by the build plugin +```diff +- import { init } from '@module-federation/enhanced/runtime'; ++ import { registerShared, registerRemotes, registerPlugins, getInstance } from '@module-federation/enhanced/runtime'; +import React from 'react'; +import mfRuntimePlugin from 'mf-runtime-plugin'; + +- const instance = init({ ++ const instance = getInstance(); +- name: 'mf_host', +- remotes: [ +- { +- name: 'remote', +- entry: 'http://localhost:2001/mf-manifest.json', +- } +- ], ++ registerRemotes([ ++ { ++ name: 'remote', ++ entry: 'http://localhost:2001/mf-manifest.json', ++ } ++ ]); +- shared: { +- react: { +- version: "18.0.0", +- scope: "default", +- lib: ()=> React, +- shareConfig: { +- singleton: true, +- requiredVersion: "^18.0.0" +- } +- }, +- }, ++ registerShared({ ++ react: { ++ version: "18.0.0", ++ scope: "default", ++ lib: ()=> React, ++ shareConfig: { ++ singleton: true, ++ requiredVersion: "^18.0.0" ++ } ++ } ++ }); +- plugins: [mfRuntimePlugin()] ++ registerPlugins([mfRuntimePlugin()]); +-}); + +``` -**Not Using Build Plugin** +#### Pure Runtime(Not Use Build Plugin) + +```diff +- import { init } from '@module-federation/enhanced/runtime'; ++ import { createInstance } from '@module-federation/enhanced/runtime'; + +-const instance = init({ ++ const instance = createInstance({ +name: 'mf_host', +remotes: [ + { + name: 'remote', + entry: 'http://localhost:2001/mf-manifest.json', + } +], +shared: { + react: { + version: "18.0.0", + scope: "default", + lib: ()=> React, + shareConfig: { + singleton: true, + requiredVersion: "^18.0.0" + } + }, +}, +plugins: [mfRuntimePlugin()] +}); +``` If you are not using the build plugin, you can replace `init` with [createInstance](#createinstance), which has exactly the same parameters. @@ -231,62 +277,60 @@ interface RemoteWithVersion { If `force: true` is set, it will overwrite the registered (and loaded) modules, and automatically delete the cache of the loaded modules (if they have been loaded), and output a warning in the console to inform that this operation is risky. -* Example - -If use build plugin: + + + ```tsx + import { registerRemotes } from '@module-federation/enhanced/runtime'; -```ts -import { registerRemotes } from '@module-federation/enhanced/runtime'; - -// Add a new remote sub2 -registerRemotes([ - { - name: 'sub2', - entry: 'http://localhost:2002/mf-manifest.json', - } -]); - -// Overwrite the previous remote sub1 -registerRemotes([ - { - name: 'sub1', - entry: 'http://localhost:2003/mf-manifest.json', - } -],{ force:true }); -``` - -If not use build plugin: - - -```ts -import { createInstance, registerRemotes } from '@module-federation/enhanced/runtime'; - -const mf = createInstance({ - name: 'mf_host', - remotes: [ - { - name: 'sub1', - entry: 'http://localhost:2001/mf-manifest.json', - } - ] -}); + // register new remote sub2 + registerRemotes([ + { + name: 'sub2', + entry: 'http://localhost:2002/mf-manifest.json', + } + ]); -// Add a new remote sub2 -mf.registerRemotes([ - { - name: 'sub2', - entry: 'http://localhost:2002/mf-manifest.json', - } -]); + // override remote sub1 + registerRemotes([ + { + name: 'sub1', + entry: 'http://localhost:2003/mf-manifest.json', + } + ],{ force:true }); + ``` + + + ```ts + import { createInstance } from '@module-federation/enhanced/runtime'; + + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'sub1', + entry: 'http://localhost:2001/mf-manifest.json', + } + ] + }); + + // register new remote sub2 + mf.registerRemotes([ + { + name: 'sub2', + entry: 'http://localhost:2002/mf-manifest.json', + } + ]); -// Overwrite the previous remote sub1 -mf.registerRemotes([ - { - name: 'sub1', - entry: 'http://localhost:2003/mf-manifest.json', - } -],{ force:true }); -``` + // override remote sub1 + mf.registerRemotes([ + { + name: 'sub1', + entry: 'http://localhost:2003/mf-manifest.json', + } + ],{ force:true }); + ``` + + ## registerPlugins @@ -296,41 +340,88 @@ mf.registerRemotes([ function registerPlugins(plugins: ModuleFederationRuntimePlugin[]) {} ``` -* Example - -```ts -import { registerPlugins } from '@module-federation/enhanced/runtime'; -import runtimePlugin from './custom-runtime-plugin'; - -// Add a new runtime plugin -registerPlugins([runtimePlugin()]); - -registerPlugins([ - { - name: 'custom-plugin-runtime', - beforeInit(args) { - const { userOptions, origin } = args; - if (origin.options.name && origin.options.name !== userOptions.name) { - userOptions.name = origin.options.name; + + + ```tsx + import { registerPlugins } from '@module-federation/enhanced/runtime'; + import runtimePlugin from './custom-runtime-plugin'; + + // add new runtime plugin + registerPlugins([runtimePlugin()]); + + registerPlugins([ + { + name: 'custom-plugin-runtime', + beforeInit(args) { + const { userOptions, origin } = args; + if (origin.options.name && origin.options.name !== userOptions.name) { + userOptions.name = origin.options.name; + } + console.log('[build time inject] beforeInit: ', args); + return args; + }, + beforeLoadShare(args) { + console.log('[build time inject] beforeLoadShare: ', args); + return args; + }, + createLink({ url }) { + const link = document.createElement('link'); + link.setAttribute('href', url); + link.setAttribute('rel', 'preload'); + link.setAttribute('as', 'script'); + link.setAttribute('crossorigin', 'anonymous'); + return link; + }, } - console.log('[build time inject] beforeInit: ', args); - return args; - }, - beforeLoadShare(args) { - console.log('[build time inject] beforeLoadShare: ', args); - return args; - }, - createLink({ url }) { - const link = document.createElement('link'); - link.setAttribute('href', url); - link.setAttribute('rel', 'preload'); - link.setAttribute('as', 'script'); - link.setAttribute('crossorigin', 'anonymous'); - return link; - }, - } -]); -``` + ]); + ``` + + + ```ts + import { createInstance } from '@module-federation/enhanced/runtime'; + import runtimePlugin from './custom-runtime-plugin'; + + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'sub1', + entry: 'http://localhost:2001/mf-manifest.json', + } + ] + }); + + // add new runtime plugin + mf.registerPlugins([runtimePlugin()]); + + mf.registerPlugins([ + { + name: 'custom-plugin-runtime', + beforeInit(args) { + const { userOptions, origin } = args; + if (origin.options.name && origin.options.name !== userOptions.name) { + userOptions.name = origin.options.name; + } + console.log('[build time inject] beforeInit: ', args); + return args; + }, + beforeLoadShare(args) { + console.log('[build time inject] beforeLoadShare: ', args); + return args; + }, + createLink({ url }) { + const link = document.createElement('link'); + link.setAttribute('href', url); + link.setAttribute('rel', 'preload'); + link.setAttribute('as', 'script'); + link.setAttribute('crossorigin', 'anonymous'); + return link; + }, + } + ]); + ``` + + ## registerShared @@ -367,50 +458,74 @@ type SharedGetter = (() => () => Module) | (() => Promise<() => Module>); -* Example - -```ts -import { registerShared } from '@module-federation/enhanced/runtime'; -import React from 'react'; -import ReactDom from 'react-dom'; - -registerShared({ - react: { - version: "18.0.0", - scope: "default", - lib: ()=> React, - shareConfig: { - singleton: true, - requiredVersion: "^18.0.0" + + + ```tsx + import { registerShared } from '@module-federation/enhanced/runtime'; + import React from 'react'; + import ReactDom from 'react-dom'; + + registerShared({ + react: { + version: "18.0.0", + scope: "default", + lib: ()=> React, + shareConfig: { + singleton: true, + requiredVersion: "^18.0.0" + } + }, + "react-dom": { + version: "18.0.0", + scope: "default", + lib: ()=> ReactDom, + shareConfig: { + singleton: true, + requiredVersion: "^18.0.0" + } } - }, - "react-dom": { - version: "18.0.0", - scope: "default", - lib: ()=> ReactDom, - shareConfig: { - singleton: true, - requiredVersion: "^18.0.0" + }); + ``` + + + ```ts + import { createInstance } from '@module-federation/enhanced/runtime'; + import React from 'react'; + import ReactDom from 'react-dom'; + + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'sub1', + entry: 'http://localhost:2001/mf-manifest.json', } - } -}); -``` - -## loadRemote - -- Type: `loadRemote(id: string)` -- Loads a remote module. -- Example - -```ts -import { loadRemote } from '@module-federation/enhanced/runtime'; - -// remoteName + expose -loadRemote("remote/util").then((m)=> m.add(1,2,3)); - -// alias + expose -loadRemote("app1/util").then((m)=> m.add(1,2,3)); -``` + ] + }); + + registerShared({ + react: { + version: "18.0.0", + scope: "default", + lib: ()=> React, + shareConfig: { + singleton: true, + requiredVersion: "^18.0.0" + } + }, + "react-dom": { + version: "18.0.0", + scope: "default", + lib: ()=> ReactDom, + shareConfig: { + singleton: true, + requiredVersion: "^18.0.0" + } + } + }); + ``` + + ## loadShare @@ -418,40 +533,85 @@ loadRemote("app1/util").then((m)=> m.add(1,2,3)); - Gets a `share` dependency. When there is a `share` dependency in the global environment that meets the requirements of the current `host`, the existing dependency that meets the `share` conditions will be reused first. Otherwise, its own dependency will be loaded and stored in the global cache. - This `API` is **generally not called directly by the user, but is used by the build plugin to transform its own dependencies**. - - -```ts -import { registerShared, loadShare } from '@module-federation/enhanced/runtime'; -import React from 'react'; -import ReactDom from 'react-dom'; - -registerShared({ - react: { - version: "17.0.0", - scope: "default", - lib: ()=> React, - shareConfig: { - singleton: true, - requiredVersion: "^17.0.0" + + + ```tsx + import { registerShared, loadShare } from '@module-federation/enhanced/runtime'; + import React from 'react'; + import ReactDom from 'react-dom'; + + registerShared({ + react: { + version: "17.0.0", + scope: "default", + lib: ()=> React, + shareConfig: { + singleton: true, + requiredVersion: "^17.0.0" + } + }, + "react-dom": { + version: "17.0.0", + scope: "default", + lib: ()=> ReactDom, + shareConfig: { + singleton: true, + requiredVersion: "^17.0.0" + } } - }, - "react-dom": { - version: "17.0.0", - scope: "default", - lib: ()=> ReactDom, - shareConfig: { - singleton: true, - requiredVersion: "^17.0.0" + }); + + + loadShare("react").then((reactFactory)=>{ + console.log(reactFactory()) + }); + ``` + + + ```ts + import { createInstance } from '@module-federation/enhanced/runtime'; + import React from 'react'; + import ReactDom from 'react-dom'; + + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'remote', + entry: 'http://localhost:2001/mf-manifest.json', + alias: 'app1' + } + ] + }); + + mf.registerShared({ + react: { + version: "17.0.0", + scope: "default", + lib: ()=> React, + shareConfig: { + singleton: true, + requiredVersion: "^17.0.0" + } + }, + "react-dom": { + version: "17.0.0", + scope: "default", + lib: ()=> ReactDom, + shareConfig: { + singleton: true, + requiredVersion: "^17.0.0" + } } - } -}); + }); -loadShare("react").then((reactFactory)=>{ - console.log(reactFactory()) -}); -``` - + mf.loadShare("react").then((reactFactory)=>{ + console.log(reactFactory()) + }); + ``` + + If multiple versions of shared are set, the loaded shared with the highest version will be returned by default. This behavior can be changed by setting `extraOptions.resolver`: @@ -469,6 +629,48 @@ loadShare('react', { }); ``` +## loadRemote + +- Type: `loadRemote(id: string)` +- Loads a remote module. +- Example + + + + ```tsx + import { loadRemote } from '@module-federation/enhanced/runtime'; + + // remoteName + expose + loadRemote("remote/util").then((m)=> m.add(1,2,3)); + + // alias + expose + loadRemote("app1/util").then((m)=> m.add(1,2,3)); + ``` + + + ```ts + import { createInstance } from '@module-federation/enhanced/runtime'; + + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'remote', + entry: 'http://localhost:2001/mf-manifest.json', + alias: 'app1' + } + ] + }); + + // remoteName + expose + mf.loadRemote("remote/util").then((m)=> m.add(1,2,3)); + + // alias + expose + mf.loadRemote("app1/util").then((m)=> m.add(1,2,3)); + ``` + + + ## preloadRemote @@ -508,57 +710,119 @@ Through `preloadRemote`, you can start preloading module resources at an earlier * `remote`'s `expose` * `remote`'s synchronous or asynchronous resources * `remote`'s dependent `remote` resources + + + ```tsx + import { registerRemotes, preloadRemote } from '@module-federation/enhanced/runtime'; + + registerRemotes([ + { + name: 'sub1', + entry: "http://localhost:2001/mf-manifest.json" + }, + { + name: 'sub2', + entry: "http://localhost:2002/mf-manifest.json" + }, + { + name: 'sub3', + entry: "http://localhost:2003/mf-manifest.json" + }, + ]); + + // 预加载 sub1 模块 + // 过滤资源名称中携带 ignore 的资源信息 + // 只预加载子依赖的 sub1-button 模块 + preloadRemote([ + { + nameOrAlias: 'sub1', + filter(assetUrl) { + return assetUrl.indexOf('ignore') === -1; + }, + depsRemote: [{ nameOrAlias: 'sub1-button' }], + }, + ]); -- Example - -```ts -import { registerRemotes, preloadRemote } from '@module-federation/enhanced/runtime'; -registerRemotes([ - { - name: 'sub1', - entry: "http://localhost:2001/mf-manifest.json" - }, - { - name: 'sub2', - entry: "http://localhost:2002/mf-manifest.json" - }, - { - name: 'sub3', - entry: "http://localhost:2003/mf-manifest.json" - }, -]); + // 预加载 sub2 模块 + // 预加载 sub2 下的所有 expose + // 预加载 sub2 的同步资源和异步资源 + preloadRemote([ + { + nameOrAlias: 'sub2', + resourceCategory: 'all', + }, + ]); -// Preload the sub1 module -// Filter resource information that contains 'ignore' in the resource name -// Only preload the sub-dependency sub1-button module -preloadRemote([ - { - nameOrAlias: 'sub1', - filter(assetUrl) { - return assetUrl.indexOf('ignore') === -1; + // 预加载 sub3 模块的 add expose + preloadRemote([ + { + nameOrAlias: 'sub3', + resourceCategory: 'all', + exposes: ['add'], }, - depsRemote: [{ nameOrAlias: 'sub1-button' }], - }, -]); + ]); + ``` + + + ```ts + import { createInstance } from '@module-federation/enhanced/runtime'; + + const mf = createInstance({ + name: 'mf_host', + remotes: [] + }); + + mf.registerRemotes([ + { + name: 'sub1', + entry: "http://localhost:2001/mf-manifest.json" + }, + { + name: 'sub2', + entry: "http://localhost:2002/mf-manifest.json" + }, + { + name: 'sub3', + entry: "http://localhost:2003/mf-manifest.json" + }, + ]); + + // preload sub1 + // Filter resource information with ignore in resource names + // Preload only sub-dependency sub1-button module + mf.preloadRemote([ + { + nameOrAlias: 'sub1', + filter(assetUrl) { + return assetUrl.indexOf('ignore') === -1; + }, + depsRemote: [{ nameOrAlias: 'sub1-button' }], + }, + ]); -// Preload the sub2 module -// Preload all exposes under sub2 -// Preload the synchronous and asynchronous resources of sub2 -preloadRemote([ - { - nameOrAlias: 'sub2', - resourceCategory: 'all', - }, -]); + // preload sub2 + // Preload all exposes in sub2 + // Preload sub2's sync and async resources + mf.preloadRemote([ + { + nameOrAlias: 'sub2', + resourceCategory: 'all', + }, + ]); + + // preload sub3 + // Preload all exposes in sub3 + // Preload sub3's sync and async resources + mf.preloadRemote([ + { + nameOrAlias: 'sub3', + resourceCategory: 'all', + exposes: ['add'], + }, + ]); + ``` + + -// Preload the add expose of the sub3 module -preloadRemote([ - { - nameOrAlias: 'sub3', - resourceCategory: 'all', - exposes: ['add'], - }, -]); -``` diff --git a/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx b/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx index 6f3c4ff9b0..4298839512 100644 --- a/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx +++ b/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx @@ -33,6 +33,10 @@ The two modes are not conflicting and can be used together. You can flexibly cho |The loading process can be affected through the `runtime`'s `plugin` mechanism|Currently does not support providing `plugin` to affect the loading process| |Does not support remote type hints|Supports remote type hints| +import Runtime from '@components/en/runtime'; + + + ## Installation import { PackageManagerTabs } from '@theme'; @@ -59,126 +63,265 @@ import { PackageManagerTabs } from '@theme'; ## Module Registration -If use build plugin, you can directly register modules using `registerRemotes`. - -```ts -import { registerRemotes } from '@module-federation/enhanced/runtime'; - -registerRemotes([ - { - name: 'remote1', - alias: 'remote-1', - entry: 'http://localhost:3001/mf-manifest.json', - } -]); - -``` +import { Steps, Tab, Tabs } from '@theme'; -If the build plugin is not used and no instance has been created, you can create a new instance and register the modules. + + + ```tsx + // If use build plugin, you can use `registerRemotes` directly. + import { registerRemotes } from '@module-federation/enhanced/runtime'; -```ts -import { createInstance } from '@module-federation/enhanced/runtime'; - -const mf = createInstance({ - name: 'mf_host', - remotes: [ + registerRemotes([ + { + name: 'remote1', + alias: 'remote-1', + entry: 'http://localhost:3001/mf-manifest.json', + } + ]); + ``` + + + ```ts + // If not use build plugin, you can create new instance and register remote. + import { createInstance } from '@module-federation/enhanced/runtime'; + + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'remote1', + alias: 'remote-1', + entry: 'http://localhost:3001/mf-manifest.json', + } + ] + }); + + mf.registerRemotes([ { - name: 'remote1', - alias: 'remote-1', - entry: 'http://localhost:3001/mf-manifest.json', + name: 'remote1', + alias: 'remote-1', + entry: 'http://localhost:3001/mf-manifest.json', } - ] -}); + ]); + ``` + + -``` +## Module Loading -If the build plugin is not used but an instance has already been created, you can call `instance.registerRemotes` to register modules. -```ts -mf.registerRemotes([ - { - name: 'remote2', - alias: 'remote-2', - entry: 'http://localhost:3002/mf-manifest.json', + + + ```tsx + // If use build plugin, you can use `loadRemote` directly. + import { loadRemote } from '@module-federation/enhanced/runtime'; + + export default () => { + const MyButton = React.lazy(() => + loadRemote('remote1').then(({ MyButton }) => { + return { + default: MyButton + }; + }), + ); + + return ( + + + + ); } -]); -``` - -## Module Loading - -```tsx -import { loadRemote } from '@module-federation/enhanced/runtime'; - -export default () => { - const MyButton = React.lazy(() => - loadRemote('remote1').then(({ MyButton }) => { - return { - default: MyButton - }; - }), - ); - - return ( - - - - ); -} -``` + ``` + + + ```ts + // If not use build plugin, you can create new instance and load remote. + import { createInstance } from '@module-federation/enhanced/runtime'; + + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'remote1', + alias: 'remote-1', + entry: 'http://localhost:3001/mf-manifest.json', + } + ] + }); + + export default () => { + const MyButton = React.lazy(() => + mf.loadRemote('remote1').then(({ MyButton }) => { + return { + default: MyButton + }; + }), + ); + + return ( + + + + ); + } + ``` + + ### Loading Anonymous Modules -```tsx -import React from 'react'; -import { loadRemote } from '@module-federation/enhanced/runtime'; - -const RemoteButton = React.lazy(() => loadRemote('provider/button')); -// Can also be loaded by module alias: -// const RemoteButton = React.lazy(() => loadRemote('remotes-1/button')); - -export default () => { - return ( - - - - ); -} -``` + + + ```tsx + // If use build plugin, you can use `loadRemote` directly. + import React from 'react'; + import { loadRemote } from '@module-federation/enhanced/runtime'; + + const RemoteButton = React.lazy(() => loadRemote('provider/button')); + // 也可通过模块别名加载: + // const RemoteButton = React.lazy(() => loadRemote('remotes-1/button')); + + export default () => { + return ( + + + + ); + } + ``` + + + ```tsx + // If not use build plugin, you can create new instance and load remote. + import { createInstance } from '@module-federation/enhanced/runtime'; + import React from 'react'; + + // 创建实例 + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'remote1', + alias: 'remote-1', + entry: 'http://localhost:3001/mf-manifest.json', + } + ] + }); + + // Use instance api load remote. + const RemoteButton = React.lazy(() => mf.loadRemote('provider/button')); + // Also support use alias to load + // const RemoteButton = React.lazy(() => mf.loadRemote('remotes-1/button')); + + export default () => { + return ( + + + + ); + } + ``` + + ### Loading Named Modules -```tsx -import React from 'react'; -import { loadRemote } from '@module-federation/enhanced/runtime'; - -export default () => { - const RemoteButton = React.lazy(() => - loadRemote('remote1/button').then(({ RemoteButton }) => { - return { - default: RemoteButton - }; - }), - ); - return ( - - - - ); -} -``` + + + ```tsx + // If use build plugin, you can use `loadRemote` directly. + import React from 'react'; + import { loadRemote } from '@module-federation/enhanced/runtime'; + + export default () => { + const RemoteButton = React.lazy(() => + loadRemote('remote1/button').then(({ RemoteButton }) => { + return { + default: RemoteButton + }; + }), + ); + return ( + + + + ); + } + ``` + + + ```tsx + // If not use build plugin, you can create new instance and load remote. + import { createInstance } from '@module-federation/enhanced/runtime'; + import React from 'react'; + + // 创建实例 + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'remote1', + alias: 'remote-1', + entry: 'http://localhost:3001/mf-manifest.json', + } + ] + }); + + export default () => { + const RemoteButton = React.lazy(() => + // Use instance api load remote. + mf.loadRemote('remote1/button').then(({ RemoteButton }) => { + return { + default: RemoteButton + }; + }), + ); + return ( + + + + ); + } + ``` + + ### Loading Utility Functions -```ts -import { loadRemote } from '@module-federation/enhanced/runtime'; - -// Load the util exposed by app2 -loadRemote<{add: (...args: Array)=> number }>("@demo/app2/util").then((md)=>{ - md.add(1,2); -}); - -// Load by alias: -// loadRemote<{add: (...args: Array)=> number }>("app3/util").then((md)=>{ -// md.add(1,2); -// }); -``` + + + ```tsx + // If use build plugin, you can use `loadRemote` directly. + import React from 'react'; + import { loadRemote } from '@module-federation/enhanced/runtime'; + + // 加载 remote1 expose 的 util + loadRemote<{add: (...args: Array)=> number }>("remote1/util").then((md)=>{ + md.add(1,2); + }); + ``` + + + ```tsx + // If not use build plugin, you can create new instance and load remote. + import { createInstance, loadRemote } from '@module-federation/enhanced/runtime'; + import React from 'react'; + + // create instance + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'remote1', + alias: 'remote-1', + entry: 'http://localhost:3001/mf-manifest.json', + } + ] + }); + + mf.loadRemote<{add: (...args: Array)=> number }>("remote1/util").then((md)=>{ + md.add(1,2); + }); + ``` + + diff --git a/apps/website-new/docs/en/guide/troubleshooting/runtime/RUNTIME-009.mdx b/apps/website-new/docs/en/guide/troubleshooting/runtime/RUNTIME-009.mdx new file mode 100644 index 0000000000..4edea660f7 --- /dev/null +++ b/apps/website-new/docs/en/guide/troubleshooting/runtime/RUNTIME-009.mdx @@ -0,0 +1,13 @@ +import ErrorCodeTitle from '@components/ErrorCodeTitle'; + + + +## Reason + +Not use build plugin, but directly call runtime api. + +## Solution + +import Runtime from '@components/en/runtime'; + + diff --git a/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx b/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx index 0669564cf7..32c17b8b3f 100644 --- a/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx +++ b/apps/website-new/docs/zh/guide/basic/runtime/runtime-api.mdx @@ -1,40 +1,10 @@ # Runtime API -import { Badge } from '@theme'; +import { Steps, Tab, Tabs, Badge, Aside } from '@theme'; import Collapse from '@components/Collapse' +import Runtime from '@components/zh/runtime'; -若使用构建插件,项目启动时将自动创建 `ModuleFederation` 实例并存储于内存中。此时可直接调用 API,API 会自动从内存中获取构建运行时创建的 `ModuleFederation` 实例。 - -```ts -import { loadRemote } from '@module-federation/enhanced/runtime'; - -loadRemote('remote1'); -``` - -若未使用构建插件,则需手动创建 `ModuleFederation` 实例,之后调用相应 API。 - -```ts -import { createInstance } from '@module-federation/enhanced/runtime'; - -const mf = createInstance({ - name: 'host', - remotes: [ - { - name: 'remote1', - entry: 'http://localhost:2001/vmok-manifest.json' - } - ] -}); - - -mf.loadRemote('remote1'); -``` - -* 什么是 `ModuleFederation` 实例 ? - -`ModuleFederation` 实例是 `ModuleFederation` 类的实例,它包含了 `ModuleFederation` 运行时的所有功能。 - -> 你可以在控制台输入 `__FEDERATION__.__INSTANCES__` 来查看已经创建好的实例。 + ## createInstance @@ -167,7 +137,7 @@ init({ ### 如何迁移 -**使用了构建插件** +#### Build Plugin(使用构建插件) 1. 移除 `init` API 的调用 2. 使用 [registerShared](#registershared) 代替 `init` 中的 `shared` 配置 @@ -175,8 +145,86 @@ init({ 4. 使用 [registerPlugins](#registerplugins) 代替 `init` 中的 `plugins` 配置 5. 使用 [getInstance](#getinstance) 获取构建插件创建的 `ModuleFederation` 实例 +```diff +- import { init } from '@module-federation/enhanced/runtime'; ++ import { registerShared, registerRemotes, registerPlugins, getInstance } from '@module-federation/enhanced/runtime'; +import React from 'react'; +import mfRuntimePlugin from 'mf-runtime-plugin'; + +- const instance = init({ ++ const instance = getInstance(); +- name: 'mf_host', +- remotes: [ +- { +- name: 'remote', +- entry: 'http://localhost:2001/mf-manifest.json', +- } +- ], ++ registerRemotes([ ++ { ++ name: 'remote', ++ entry: 'http://localhost:2001/mf-manifest.json', ++ } ++ ]); +- shared: { +- react: { +- version: "18.0.0", +- scope: "default", +- lib: ()=> React, +- shareConfig: { +- singleton: true, +- requiredVersion: "^18.0.0" +- } +- }, +- }, ++ registerShared({ ++ react: { ++ version: "18.0.0", ++ scope: "default", ++ lib: ()=> React, ++ shareConfig: { ++ singleton: true, ++ requiredVersion: "^18.0.0" ++ } ++ } ++ }); +- plugins: [mfRuntimePlugin()] ++ registerPlugins([mfRuntimePlugin()]); +-}); + +``` + +#### Pure Runtime(未使用构建插件) + + +```diff +- import { init } from '@module-federation/enhanced/runtime'; ++ import { createInstance } from '@module-federation/enhanced/runtime'; + +-const instance = init({ ++ const instance = createInstance({ +name: 'mf_host', +remotes: [ + { + name: 'remote', + entry: 'http://localhost:2001/mf-manifest.json', + } +], +shared: { + react: { + version: "18.0.0", + scope: "default", + lib: ()=> React, + shareConfig: { + singleton: true, + requiredVersion: "^18.0.0" + } + }, +}, +plugins: [mfRuntimePlugin()] +}); +``` -**没有使用构建插件** 如果没有使用构建插件,你可以使用 [createInstance](#createinstance)替换 `init` ,其参数完全一致。 @@ -231,61 +279,60 @@ interface RemoteWithVersion { 如果设置 `force: true`, 这会覆盖已经注册(且加载的模块, 并且自动删除已经加载过的模块缓存(如果已经加载过),同时在控制台输出警告,告知这操作存在风险性。 -* Example - -使用了构建插件 + + + ```tsx + import { registerRemotes } from '@module-federation/enhanced/runtime'; -```ts -import { registerRemotes } from '@module-federation/enhanced/runtime'; - -// 增加新的 remote sub2 -registerRemotes([ - { - name: 'sub2', - entry: 'http://localhost:2002/mf-manifest.json', - } -]); - -// 覆盖之前的 remote sub1 -registerRemotes([ - { - name: 'sub1', - entry: 'http://localhost:2003/mf-manifest.json', - } -],{ force:true }); -``` - -没使用构建插件 - -```ts -import { createInstance, registerRemotes } from '@module-federation/enhanced/runtime'; - -const mf = createInstance({ - name: 'mf_host', - remotes: [ - { - name: 'sub1', - entry: 'http://localhost:2001/mf-manifest.json', - } - ] -}); + // 增加新的 remote sub2 + registerRemotes([ + { + name: 'sub2', + entry: 'http://localhost:2002/mf-manifest.json', + } + ]); -// 增加新的 remote sub2 -mf.registerRemotes([ - { - name: 'sub2', - entry: 'http://localhost:2002/mf-manifest.json', - } -]); + // 覆盖之前的 remote sub1 + registerRemotes([ + { + name: 'sub1', + entry: 'http://localhost:2003/mf-manifest.json', + } + ],{ force:true }); + ``` + + + ```ts + import { createInstance } from '@module-federation/enhanced/runtime'; + + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'sub1', + entry: 'http://localhost:2001/mf-manifest.json', + } + ] + }); + + // 增加新的 remote sub2 + mf.registerRemotes([ + { + name: 'sub2', + entry: 'http://localhost:2002/mf-manifest.json', + } + ]); -// 覆盖之前的 remote sub1 -mf.registerRemotes([ - { - name: 'sub1', - entry: 'http://localhost:2003/mf-manifest.json', - } -],{ force:true }); -``` + // 覆盖之前的 remote sub1 + mf.registerRemotes([ + { + name: 'sub1', + entry: 'http://localhost:2003/mf-manifest.json', + } + ],{ force:true }); + ``` + + ## registerPlugins @@ -295,41 +342,88 @@ mf.registerRemotes([ function registerPlugins(plugins: ModuleFederationRuntimePlugin[]) {} ``` -* Example - -```ts -import { registerPlugins } from '@module-federation/enhanced/runtime'; -import runtimePlugin from './custom-runtime-plugin'; - -// 增加新的运行时插件 -registerPlugins([runtimePlugin()]); - -registerPlugins([ - { - name: 'custom-plugin-runtime', - beforeInit(args) { - const { userOptions, origin } = args; - if (origin.options.name && origin.options.name !== userOptions.name) { - userOptions.name = origin.options.name; + + + ```tsx + import { registerPlugins } from '@module-federation/enhanced/runtime'; + import runtimePlugin from './custom-runtime-plugin'; + + // 增加新的运行时插件 + registerPlugins([runtimePlugin()]); + + registerPlugins([ + { + name: 'custom-plugin-runtime', + beforeInit(args) { + const { userOptions, origin } = args; + if (origin.options.name && origin.options.name !== userOptions.name) { + userOptions.name = origin.options.name; + } + console.log('[build time inject] beforeInit: ', args); + return args; + }, + beforeLoadShare(args) { + console.log('[build time inject] beforeLoadShare: ', args); + return args; + }, + createLink({ url }) { + const link = document.createElement('link'); + link.setAttribute('href', url); + link.setAttribute('rel', 'preload'); + link.setAttribute('as', 'script'); + link.setAttribute('crossorigin', 'anonymous'); + return link; + }, } - console.log('[build time inject] beforeInit: ', args); - return args; - }, - beforeLoadShare(args) { - console.log('[build time inject] beforeLoadShare: ', args); - return args; - }, - createLink({ url }) { - const link = document.createElement('link'); - link.setAttribute('href', url); - link.setAttribute('rel', 'preload'); - link.setAttribute('as', 'script'); - link.setAttribute('crossorigin', 'anonymous'); - return link; - }, - } -]); -``` + ]); + ``` + + + ```ts + import { createInstance } from '@module-federation/enhanced/runtime'; + import runtimePlugin from './custom-runtime-plugin'; + + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'sub1', + entry: 'http://localhost:2001/mf-manifest.json', + } + ] + }); + + // 增加新的运行时插件 + mf.registerPlugins([runtimePlugin()]); + + mf.registerPlugins([ + { + name: 'custom-plugin-runtime', + beforeInit(args) { + const { userOptions, origin } = args; + if (origin.options.name && origin.options.name !== userOptions.name) { + userOptions.name = origin.options.name; + } + console.log('[build time inject] beforeInit: ', args); + return args; + }, + beforeLoadShare(args) { + console.log('[build time inject] beforeLoadShare: ', args); + return args; + }, + createLink({ url }) { + const link = document.createElement('link'); + link.setAttribute('href', url); + link.setAttribute('rel', 'preload'); + link.setAttribute('as', 'script'); + link.setAttribute('crossorigin', 'anonymous'); + return link; + }, + } + ]); + ``` + + ## registerShared @@ -366,50 +460,74 @@ type SharedGetter = (() => () => Module) | (() => Promise<() => Module>); -* Example - -```ts -import { registerShared } from '@module-federation/enhanced/runtime'; -import React from 'react'; -import ReactDom from 'react-dom'; - -registerShared({ - react: { - version: "18.0.0", - scope: "default", - lib: ()=> React, - shareConfig: { - singleton: true, - requiredVersion: "^18.0.0" + + + ```tsx + import { registerShared } from '@module-federation/enhanced/runtime'; + import React from 'react'; + import ReactDom from 'react-dom'; + + registerShared({ + react: { + version: "18.0.0", + scope: "default", + lib: ()=> React, + shareConfig: { + singleton: true, + requiredVersion: "^18.0.0" + } + }, + "react-dom": { + version: "18.0.0", + scope: "default", + lib: ()=> ReactDom, + shareConfig: { + singleton: true, + requiredVersion: "^18.0.0" + } } - }, - "react-dom": { - version: "18.0.0", - scope: "default", - lib: ()=> ReactDom, - shareConfig: { - singleton: true, - requiredVersion: "^18.0.0" + }); + ``` + + + ```ts + import { createInstance } from '@module-federation/enhanced/runtime'; + import React from 'react'; + import ReactDom from 'react-dom'; + + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'sub1', + entry: 'http://localhost:2001/mf-manifest.json', } - } -}); -``` - -## loadRemote - -- Type: `loadRemote(id: string)` -- 加载远程模块 -- 示例 - -```ts -import { getInstance, loadRemote } from '@module-federation/enhanced/runtime'; - -// remoteName + expose -loadRemote("remote/util").then((m)=> m.add(1,2,3)); - -// alias + expose -loadRemote("app1/util").then((m)=> m.add(1,2,3)); -``` + ] + }); + + registerShared({ + react: { + version: "18.0.0", + scope: "default", + lib: ()=> React, + shareConfig: { + singleton: true, + requiredVersion: "^18.0.0" + } + }, + "react-dom": { + version: "18.0.0", + scope: "default", + lib: ()=> ReactDom, + shareConfig: { + singleton: true, + requiredVersion: "^18.0.0" + } + } + }); + ``` + + ## loadShare @@ -417,41 +535,85 @@ loadRemote("app1/util").then((m)=> m.add(1,2,3)); - 获取 `share` 依赖,当全局环境有符合当前 `host` 的 `share` 依赖时,将优先复用当前已存在且满足 `share` 条件的依赖,否则将加载自身的依赖并存入全局缓存 - 该 `API` **一般不由用户直接调用,用于构建插件转换自身依赖时使用** - - -```ts -import { registerShared, loadShare } from '@module-federation/enhanced/runtime'; -import React from 'react'; -import ReactDom from 'react-dom'; - -registerShared({ - react: { - version: "17.0.0", - scope: "default", - lib: ()=> React, - shareConfig: { - singleton: true, - requiredVersion: "^17.0.0" + + + ```tsx + import { registerShared, loadShare } from '@module-federation/enhanced/runtime'; + import React from 'react'; + import ReactDom from 'react-dom'; + + registerShared({ + react: { + version: "17.0.0", + scope: "default", + lib: ()=> React, + shareConfig: { + singleton: true, + requiredVersion: "^17.0.0" + } + }, + "react-dom": { + version: "17.0.0", + scope: "default", + lib: ()=> ReactDom, + shareConfig: { + singleton: true, + requiredVersion: "^17.0.0" + } } - }, - "react-dom": { - version: "17.0.0", - scope: "default", - lib: ()=> ReactDom, - shareConfig: { - singleton: true, - requiredVersion: "^17.0.0" + }); + + + loadShare("react").then((reactFactory)=>{ + console.log(reactFactory()) + }); + ``` + + + ```ts + import { createInstance } from '@module-federation/enhanced/runtime'; + import React from 'react'; + import ReactDom from 'react-dom'; + + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'remote', + entry: 'http://localhost:2001/mf-manifest.json', + alias: 'app1' + } + ] + }); + + mf.registerShared({ + react: { + version: "17.0.0", + scope: "default", + lib: ()=> React, + shareConfig: { + singleton: true, + requiredVersion: "^17.0.0" + } + }, + "react-dom": { + version: "17.0.0", + scope: "default", + lib: ()=> ReactDom, + shareConfig: { + singleton: true, + requiredVersion: "^17.0.0" + } } - } -}); + }); -loadShare("react").then((reactFactory)=>{ - console.log(reactFactory()) -}); -``` - - + mf.loadShare("react").then((reactFactory)=>{ + console.log(reactFactory()) + }); + ``` + + 如果设置了多个版本 shared,默认会返回已加载且最高版本的 shared 。可以通过设置 `extraOptions.resolver` 来改变这个行为: @@ -469,6 +631,47 @@ loadShare('react', { }); ``` +## loadRemote + +- Type: `loadRemote(id: string)` +- 加载远程模块 + + + + ```tsx + import { loadRemote } from '@module-federation/enhanced/runtime'; + + // remoteName + expose + loadRemote("remote/util").then((m)=> m.add(1,2,3)); + + // alias + expose + loadRemote("app1/util").then((m)=> m.add(1,2,3)); + ``` + + + ```ts + import { createInstance } from '@module-federation/enhanced/runtime'; + + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'remote', + entry: 'http://localhost:2001/mf-manifest.json', + alias: 'app1' + } + ] + }); + + // remoteName + expose + mf.loadRemote("remote/util").then((m)=> m.add(1,2,3)); + + // alias + expose + mf.loadRemote("app1/util").then((m)=> m.add(1,2,3)); + ``` + + + ## preloadRemote @@ -509,56 +712,117 @@ type PreloadRemoteArgs = { * `remote` 的同步资源还是异步资源 * `remote` 依赖的 `remote` 资源 -- Example + + + ```tsx + import { registerRemotes, preloadRemote } from '@module-federation/enhanced/runtime'; + + registerRemotes([ + { + name: 'sub1', + entry: "http://localhost:2001/mf-manifest.json" + }, + { + name: 'sub2', + entry: "http://localhost:2002/mf-manifest.json" + }, + { + name: 'sub3', + entry: "http://localhost:2003/mf-manifest.json" + }, + ]); + + // 预加载 sub1 模块 + // 过滤资源名称中携带 ignore 的资源信息 + // 只预加载子依赖的 sub1-button 模块 + preloadRemote([ + { + nameOrAlias: 'sub1', + filter(assetUrl) { + return assetUrl.indexOf('ignore') === -1; + }, + depsRemote: [{ nameOrAlias: 'sub1-button' }], + }, + ]); -```ts -import { registerRemotes, preloadRemote } from '@module-federation/enhanced/runtime'; -registerRemotes([ - { - name: 'sub1', - entry: "http://localhost:2001/mf-manifest.json" - }, - { - name: 'sub2', - entry: "http://localhost:2002/mf-manifest.json" - }, - { - name: 'sub3', - entry: "http://localhost:2003/mf-manifest.json" - }, -]); + // 预加载 sub2 模块 + // 预加载 sub2 下的所有 expose + // 预加载 sub2 的同步资源和异步资源 + preloadRemote([ + { + nameOrAlias: 'sub2', + resourceCategory: 'all', + }, + ]); -// 预加载 sub1 模块 -// 过滤资源名称中携带 ignore 的资源信息 -// 只预加载子依赖的 sub1-button 模块 -preloadRemote([ - { - nameOrAlias: 'sub1', - filter(assetUrl) { - return assetUrl.indexOf('ignore') === -1; + // 预加载 sub3 模块的 add expose + preloadRemote([ + { + nameOrAlias: 'sub3', + resourceCategory: 'all', + exposes: ['add'], }, - depsRemote: [{ nameOrAlias: 'sub1-button' }], - }, -]); + ]); + ``` + + + ```ts + import { createInstance } from '@module-federation/enhanced/runtime'; + + const mf = createInstance({ + name: 'mf_host', + remotes: [] + }); + + mf.registerRemotes([ + { + name: 'sub1', + entry: "http://localhost:2001/mf-manifest.json" + }, + { + name: 'sub2', + entry: "http://localhost:2002/mf-manifest.json" + }, + { + name: 'sub3', + entry: "http://localhost:2003/mf-manifest.json" + }, + ]); + + // 预加载 sub1 模块 + // 过滤资源名称中携带 ignore 的资源信息 + // 只预加载子依赖的 sub1-button 模块 + mf.preloadRemote([ + { + nameOrAlias: 'sub1', + filter(assetUrl) { + return assetUrl.indexOf('ignore') === -1; + }, + depsRemote: [{ nameOrAlias: 'sub1-button' }], + }, + ]); -// 预加载 sub2 模块 -// 预加载 sub2 下的所有 expose -// 预加载 sub2 的同步资源和异步资源 -preloadRemote([ - { - nameOrAlias: 'sub2', - resourceCategory: 'all', - }, -]); + // 预加载 sub2 模块 + // 预加载 sub2 下的所有 expose + // 预加载 sub2 的同步资源和异步资源 + mf.preloadRemote([ + { + nameOrAlias: 'sub2', + resourceCategory: 'all', + }, + ]); + + // 预加载 sub3 模块的 add expose + mf.preloadRemote([ + { + nameOrAlias: 'sub3', + resourceCategory: 'all', + exposes: ['add'], + }, + ]); + ``` + + -// 预加载 sub3 模块的 add expose -preloadRemote([ - { - nameOrAlias: 'sub3', - resourceCategory: 'all', - exposes: ['add'], - }, -]); -``` diff --git a/apps/website-new/docs/zh/guide/basic/runtime/runtime.mdx b/apps/website-new/docs/zh/guide/basic/runtime/runtime.mdx index d245a25524..d1710f0835 100644 --- a/apps/website-new/docs/zh/guide/basic/runtime/runtime.mdx +++ b/apps/website-new/docs/zh/guide/basic/runtime/runtime.mdx @@ -9,7 +9,6 @@ ::: - 目前 `Module Federation` 提供了两种注册模块和加载模块的方式: - 一种是在构建插件中声明(一般是在 `module-federation.config.ts` 文件中声明) @@ -32,6 +31,10 @@ |可以通过 `runtime` 的 `plugin` 机制影响加载流程|目前不支持提供 `plugin` 影响加载流程| |不支持远程类型提示|支持远程类型提示| +import Runtime from '@components/zh/runtime'; + + + ## 安装 import { PackageManagerTabs } from '@theme'; @@ -55,129 +58,267 @@ import { PackageManagerTabs } from '@theme'; ::: - ## 模块注册 -如果使用了构建插件,那么可以直接使用 `registerRemotes` 注册模块。 - -```ts -import { registerRemotes } from '@module-federation/enhanced/runtime'; - -registerRemotes([ - { - name: 'remote1', - alias: 'remote-1', - entry: 'http://localhost:3001/mf-manifest.json', - } -]); - -``` - -如果没有使用构建插件,并且没有创建实例,那么可以创建新的实例,并注册模块。 +import { Steps, Tab, Tabs } from '@theme'; -```ts -import { createInstance } from '@module-federation/enhanced/runtime'; + + + ```tsx + // 如果使用了构建插件,那么可以直接使用 `registerRemotes` 注册模块。 + import { registerRemotes } from '@module-federation/enhanced/runtime'; -const mf = createInstance({ - name: 'mf_host', - remotes: [ + registerRemotes([ { - name: 'remote1', - alias: 'remote-1', - entry: 'http://localhost:3001/mf-manifest.json', + name: 'remote1', + alias: 'remote-1', + entry: 'http://localhost:3001/mf-manifest.json', } - ] -}); - -``` - -如果没有使用构建插件,但已经创建了实例,那么可以调用 `instance.registerRemotes` 进行模块注册。 - -```ts -mf.registerRemotes([ - { - name: 'remote2', - alias: 'remote-2', - entry: 'http://localhost:3002/mf-manifest.json', - } -]); -``` + ]); + ``` + + + ```ts + // 如果没有使用构建插件,那么可以创建新的实例,并注册模块 + import { createInstance } from '@module-federation/enhanced/runtime'; + + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'remote1', + alias: 'remote-1', + entry: 'http://localhost:3001/mf-manifest.json', + } + ] + }); + + mf.registerRemotes([ + { + name: 'remote1', + alias: 'remote-1', + entry: 'http://localhost:3001/mf-manifest.json', + } + ]); + ``` + + ## 模块加载 -```tsx -import { loadRemote } from '@module-federation/enhanced/runtime'; - -export default () => { - const MyButton = React.lazy(() => - loadRemote('remote1').then(({ MyButton }) => { - return { - default: MyButton - }; - }), - ); - - return ( - - - - ); -} -``` + + + ```tsx + // 如果使用了构建插件,那么可以直接使用 `loadRemote` 加载模块。 + import { loadRemote } from '@module-federation/enhanced/runtime'; + + export default () => { + const MyButton = React.lazy(() => + loadRemote('remote1').then(({ MyButton }) => { + return { + default: MyButton + }; + }), + ); + + return ( + + + + ); + } + ``` + + + ```ts + // 如果没有使用构建插件,那么可以创建新的实例,并注册模块 + import { createInstance } from '@module-federation/enhanced/runtime'; + + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'remote1', + alias: 'remote-1', + entry: 'http://localhost:3001/mf-manifest.json', + } + ] + }); + + export default () => { + const MyButton = React.lazy(() => + mf.loadRemote('remote1').then(({ MyButton }) => { + return { + default: MyButton + }; + }), + ); + + return ( + + + + ); + } + ``` + + ### 加载匿名模块 -```tsx -import React from 'react'; -import { loadRemote } from '@module-federation/enhanced/runtime'; - -const RemoteButton = React.lazy(() => loadRemote('provider/button')); -// 也可通过模块别名加载: -// const RemoteButton = React.lazy(() => loadRemote('remotes-1/button')); + + + ```tsx + // 如果使用了构建插件,那么可以直接使用 `loadRemote` 加载模块。 + import React from 'react'; + import { loadRemote } from '@module-federation/enhanced/runtime'; + + const RemoteButton = React.lazy(() => loadRemote('provider/button')); + // 也可通过模块别名加载: + // const RemoteButton = React.lazy(() => loadRemote('remotes-1/button')); + + export default () => { + return ( + + + + ); + } + ``` + + + ```tsx + // 如果没有使用构建插件,那么可以创建新的实例,并注册模块 + import { createInstance } from '@module-federation/enhanced/runtime'; + import React from 'react'; + + // 创建实例 + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'remote1', + alias: 'remote-1', + entry: 'http://localhost:3001/mf-manifest.json', + } + ] + }); + + // 使用 实例 loadRemote API 加载模块 + const RemoteButton = React.lazy(() => mf.loadRemote('provider/button')); + // 也可通过模块别名加载: + // const RemoteButton = React.lazy(() => mf.loadRemote('remotes-1/button')); + + export default () => { + return ( + + + + ); + } + ``` + + -export default () => { - return ( - - - - ); -} -``` ### 加载具名模块 -```tsx -import React from 'react'; -import { loadRemote } from '@module-federation/enhanced/runtime'; - -export default () => { - const RemoteButton = React.lazy(() => - loadRemote('remote1/button').then(({ RemoteButton }) => { - return { - default: RemoteButton - }; - }), - ); - return ( - - - - ); -} -``` + + + ```tsx + // 如果使用了构建插件,那么可以直接使用 `loadRemote` 加载模块。 + import React from 'react'; + import { loadRemote } from '@module-federation/enhanced/runtime'; + + export default () => { + const RemoteButton = React.lazy(() => + loadRemote('remote1/button').then(({ RemoteButton }) => { + return { + default: RemoteButton + }; + }), + ); + return ( + + + + ); + } + ``` + + + ```tsx + // 如果没有使用构建插件,那么可以创建新的实例,并注册模块 + import { createInstance } from '@module-federation/enhanced/runtime'; + import React from 'react'; + + // 创建实例 + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'remote1', + alias: 'remote-1', + entry: 'http://localhost:3001/mf-manifest.json', + } + ] + }); + + export default () => { + const RemoteButton = React.lazy(() => + // 使用 实例 loadRemote API 加载模块 + mf.loadRemote('remote1/button').then(({ RemoteButton }) => { + return { + default: RemoteButton + }; + }), + ); + return ( + + + + ); + } + ``` + + ### 加载工具函数 -```ts -import { loadRemote } from '@module-federation/enhanced/runtime'; - -// 加载 app2 expose 的 util -loadRemote<{add: (...args: Array)=> number }>("@demo/app2/util").then((md)=>{ - md.add(1,2); -}); - -// 通过别名加载: -// loadRemote<{add: (...args: Array)=> number }>("app3/util").then((md)=>{ -// md.add(1,2); -// }); -``` + + + ```tsx + // 如果使用了构建插件,那么可以直接使用 `loadRemote` 加载模块。 + import React from 'react'; + import { loadRemote } from '@module-federation/enhanced/runtime'; + + // 加载 remote1 expose 的 util + loadRemote<{add: (...args: Array)=> number }>("remote1/util").then((md)=>{ + md.add(1,2); + }); + ``` + + + ```tsx + // 如果没有使用构建插件,那么可以创建新的实例,并注册模块 + import { createInstance, loadRemote } from '@module-federation/enhanced/runtime'; + import React from 'react'; + + // 创建实例 + const mf = createInstance({ + name: 'mf_host', + remotes: [ + { + name: 'remote1', + alias: 'remote-1', + entry: 'http://localhost:3001/mf-manifest.json', + } + ] + }); + + mf.loadRemote<{add: (...args: Array)=> number }>("remote1/util").then((md)=>{ + md.add(1,2); + }); + ``` + + diff --git a/apps/website-new/docs/zh/guide/troubleshooting/runtime/RUNTIME-009.mdx b/apps/website-new/docs/zh/guide/troubleshooting/runtime/RUNTIME-009.mdx new file mode 100644 index 0000000000..10f77d002b --- /dev/null +++ b/apps/website-new/docs/zh/guide/troubleshooting/runtime/RUNTIME-009.mdx @@ -0,0 +1,13 @@ +import ErrorCodeTitle from '@components/ErrorCodeTitle'; + + + +## 原因 + +未使用构建插件,但直接调用了 runtime api 。 + +## 解决方法 + +import Runtime from '@components/zh/runtime'; + + diff --git a/apps/website-new/src/components/en/runtime.mdx b/apps/website-new/src/components/en/runtime.mdx new file mode 100644 index 0000000000..acfc89f7f9 --- /dev/null +++ b/apps/website-new/src/components/en/runtime.mdx @@ -0,0 +1,31 @@ +If the build plugin is used, an MF instance will be automatically created and stored in memory when the project starts. You can directly call methods of the MF instance via the . + +```ts +import { loadRemote } from '@module-federation/enhanced/runtime'; + +loadRemote('remote1'); +``` + +If the build plugin is not used, you need to manually create an MF instance before calling the corresponding API. + +```ts +import { createInstance } from '@module-federation/enhanced/runtime'; + +const mf = createInstance({ + name: 'host', + remotes: [ + { + name: 'remote1', + entry: 'http://localhost:2001/vmok-manifest.json', + }, + ], +}); + +mf.loadRemote('remote1'); +``` + +- What is a `ModuleFederation` instance? + +A `ModuleFederation` instance is an instance of the `ModuleFederation` class, which contains all the functionality of the `ModuleFederation` runtime. + +> You can enter `__FEDERATION__.__INSTANCES__` in the console to view the created instances. diff --git a/apps/website-new/src/components/zh/runtime.mdx b/apps/website-new/src/components/zh/runtime.mdx new file mode 100644 index 0000000000..1abf83edb8 --- /dev/null +++ b/apps/website-new/src/components/zh/runtime.mdx @@ -0,0 +1,31 @@ +若使用构建插件,项目启动时将自动创建 `ModuleFederation` 实例并存储于内存中。此时可直接调用 API,API 会自动从内存中获取构建运行时创建的 `ModuleFederation` 实例。 + +```ts +import { loadRemote } from '@module-federation/enhanced/runtime'; + +loadRemote('remote1'); +``` + +若未使用构建插件,则需手动创建 `ModuleFederation` 实例,之后调用相应 API。 + +```ts +import { createInstance } from '@module-federation/enhanced/runtime'; + +const mf = createInstance({ + name: 'host', + remotes: [ + { + name: 'remote1', + entry: 'http://localhost:2001/vmok-manifest.json', + }, + ], +}); + +mf.loadRemote('remote1'); +``` + +- 什么是 `ModuleFederation` 实例 ? + +`ModuleFederation` 实例是 `ModuleFederation` 类的实例,它包含了 `ModuleFederation` 运行时的所有功能。 + +> 你可以在控制台输入 `__FEDERATION__.__INSTANCES__` 来查看已经创建好的实例。 diff --git a/packages/error-codes/src/desc.ts b/packages/error-codes/src/desc.ts index 9116f82d41..2d63679f5d 100644 --- a/packages/error-codes/src/desc.ts +++ b/packages/error-codes/src/desc.ts @@ -7,6 +7,7 @@ import { RUNTIME_006, RUNTIME_007, RUNTIME_008, + RUNTIME_009, TYPE_001, BUILD_001, BUILD_002, @@ -21,6 +22,7 @@ export const runtimeDescMap = { [RUNTIME_006]: 'Invalid loadShareSync function call from runtime', [RUNTIME_007]: 'Failed to get remote snapshot.', [RUNTIME_008]: 'Failed to load script resources.', + [RUNTIME_009]: 'Please call createInstance first.', }; export const typeDescMap = { diff --git a/packages/error-codes/src/error-codes.ts b/packages/error-codes/src/error-codes.ts index aa2d5850b7..321f9e5ee6 100644 --- a/packages/error-codes/src/error-codes.ts +++ b/packages/error-codes/src/error-codes.ts @@ -6,6 +6,7 @@ export const RUNTIME_005 = 'RUNTIME-005'; export const RUNTIME_006 = 'RUNTIME-006'; export const RUNTIME_007 = 'RUNTIME-007'; export const RUNTIME_008 = 'RUNTIME-008'; +export const RUNTIME_009 = 'RUNTIME-009'; export const TYPE_001 = 'TYPE-001'; export const BUILD_001 = 'BUILD-001'; diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 2d8b2535fe..f9e473c534 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -6,6 +6,11 @@ import { assert, setGlobalFederationConstructor, } from '@module-federation/runtime-core'; +import { + runtimeDescMap, + getShortErrorMsg, + RUNTIME_009, +} from '@module-federation/error-codes'; import { getGlobalFederationInstance } from './utils'; export { @@ -52,7 +57,7 @@ export function init(options: UserOptions): ModuleFederation { export function loadRemote( ...args: Parameters ): Promise { - assert(FederationInstance, 'Please call init first'); + assert(FederationInstance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); const loadRemote: typeof FederationInstance.loadRemote = FederationInstance.loadRemote; // eslint-disable-next-line prefer-spread @@ -62,7 +67,7 @@ export function loadRemote( export function loadShare( ...args: Parameters ): Promise T | undefined)> { - assert(FederationInstance, 'Please call init first'); + assert(FederationInstance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); // eslint-disable-next-line prefer-spread const loadShare: typeof FederationInstance.loadShare = FederationInstance.loadShare; @@ -72,7 +77,7 @@ export function loadShare( export function loadShareSync( ...args: Parameters ): () => T | never { - assert(FederationInstance, 'Please call init first'); + assert(FederationInstance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); const loadShareSync: typeof FederationInstance.loadShareSync = FederationInstance.loadShareSync; // eslint-disable-next-line prefer-spread @@ -82,7 +87,7 @@ export function loadShareSync( export function preloadRemote( ...args: Parameters ): ReturnType { - assert(FederationInstance, 'Please call init first'); + assert(FederationInstance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); // eslint-disable-next-line prefer-spread return FederationInstance.preloadRemote.apply(FederationInstance, args); } @@ -90,7 +95,7 @@ export function preloadRemote( export function registerRemotes( ...args: Parameters ): ReturnType { - assert(FederationInstance, 'Please call init first'); + assert(FederationInstance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); // eslint-disable-next-line prefer-spread return FederationInstance.registerRemotes.apply(FederationInstance, args); } @@ -98,7 +103,7 @@ export function registerRemotes( export function registerPlugins( ...args: Parameters ): ReturnType { - assert(FederationInstance, 'Please call init first'); + assert(FederationInstance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); // eslint-disable-next-line prefer-spread return FederationInstance.registerPlugins.apply(FederationInstance, args); } @@ -110,7 +115,7 @@ export function getInstance() { export function registerShared( ...args: Parameters ): ReturnType { - assert(FederationInstance, 'Please call init first'); + assert(FederationInstance, getShortErrorMsg(RUNTIME_009, runtimeDescMap)); // eslint-disable-next-line prefer-spread return FederationInstance.registerShared.apply(FederationInstance, args); } From d66cd8fa6725c996d71afec991aa2a6e25aa31dd Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Mon, 7 Jul 2025 14:50:39 +0800 Subject: [PATCH 14/14] docs: typo --- apps/website-new/docs/en/guide/basic/runtime/runtime.mdx | 2 ++ apps/website-new/docs/zh/guide/basic/runtime/runtime.mdx | 2 ++ 2 files changed, 4 insertions(+) diff --git a/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx b/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx index 4298839512..b8c4b73f0f 100644 --- a/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx +++ b/apps/website-new/docs/en/guide/basic/runtime/runtime.mdx @@ -115,6 +115,7 @@ import { Steps, Tab, Tabs } from '@theme'; ```tsx // If use build plugin, you can use `loadRemote` directly. import { loadRemote } from '@module-federation/enhanced/runtime'; + import React from 'react'; export default () => { const MyButton = React.lazy(() => @@ -137,6 +138,7 @@ import { Steps, Tab, Tabs } from '@theme'; ```ts // If not use build plugin, you can create new instance and load remote. import { createInstance } from '@module-federation/enhanced/runtime'; + import React from 'react'; const mf = createInstance({ name: 'mf_host', diff --git a/apps/website-new/docs/zh/guide/basic/runtime/runtime.mdx b/apps/website-new/docs/zh/guide/basic/runtime/runtime.mdx index d1710f0835..59db264a71 100644 --- a/apps/website-new/docs/zh/guide/basic/runtime/runtime.mdx +++ b/apps/website-new/docs/zh/guide/basic/runtime/runtime.mdx @@ -111,6 +111,7 @@ import { Steps, Tab, Tabs } from '@theme'; ```tsx // 如果使用了构建插件,那么可以直接使用 `loadRemote` 加载模块。 import { loadRemote } from '@module-federation/enhanced/runtime'; + import React from 'react'; export default () => { const MyButton = React.lazy(() => @@ -133,6 +134,7 @@ import { Steps, Tab, Tabs } from '@theme'; ```ts // 如果没有使用构建插件,那么可以创建新的实例,并注册模块 import { createInstance } from '@module-federation/enhanced/runtime'; + import React from 'react'; const mf = createInstance({ name: 'mf_host',