Skip to content

Commit a9c10e4

Browse files
authored
docs: re-add documentation for the manifest fetch and loadEntry hook (#4131)
1 parent e35923c commit a9c10e4

File tree

2 files changed

+336
-7
lines changed

2 files changed

+336
-7
lines changed

apps/website-new/docs/en/guide/basic/runtime/runtime-hooks.mdx

Lines changed: 171 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ The `lifecycle` parameter indicates the stage where the error occurred:
172172
- `afterResolve`: Error during manifest loading (most common for network failures)
173173
- `onLoad`: Error during module loading and execution
174174
- `beforeLoadShare`: Error during shared dependency loading
175-
175+
176176
* example
177177
178178
```ts
@@ -185,12 +185,12 @@ const fallbackPlugin: () => ModuleFederationRuntimePlugin =
185185
name: 'fallback-plugin',
186186
errorLoadRemote(args) {
187187
const { lifecycle, id, error } = args;
188-
188+
189189
// Log error details safely
190190
if (error) {
191191
console.warn(`Failed to load remote ${id} at ${lifecycle}:`, error?.message || error);
192192
}
193-
193+
194194
// Handle different error types based on lifecycle
195195
switch (lifecycle) {
196196
case 'afterResolve':
@@ -203,27 +203,27 @@ const fallbackPlugin: () => ModuleFederationRuntimePlugin =
203203
remotes: [],
204204
exposes: []
205205
};
206-
206+
207207
case 'beforeRequest':
208208
// Request processing failed - can return modified args or void
209209
console.warn(`Request processing failed for ${id}`);
210210
return void 0;
211-
211+
212212
case 'onLoad':
213213
// Module loading failed - provide fallback component
214214
return () => ({
215215
__esModule: true,
216216
default: () => 'Fallback Component'
217217
});
218-
218+
219219
case 'beforeLoadShare':
220220
// Shared dependency loading failed - return fallback shared module
221221
console.warn(`Shared dependency loading failed for ${id}`);
222222
return () => ({
223223
__esModule: true,
224224
default: {}
225225
});
226-
226+
227227
default:
228228
// Unknown lifecycle - log and return void
229229
console.warn(`Unknown lifecycle ${lifecycle} for ${id}`);
@@ -425,3 +425,167 @@ const changeScriptAttributePlugin: () => ModuleFederationRuntimePlugin =
425425
};
426426
};
427427
```
428+
429+
## fetch
430+
The `fetch` function allows customizing the request that fetches the manifest JSON. A successful `Response` must yield a valid JSON.
431+
432+
`AsyncHook`
433+
434+
- **Type**
435+
436+
```typescript
437+
function fetch(manifestUrl: string, requestInit: RequestInit): Promise<Response> | void | false;
438+
```
439+
440+
- Example for including the credentials when fetching the manifest JSON:
441+
442+
```typescript
443+
// fetch-manifest-with-credentials-plugin.ts
444+
import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';
445+
446+
export default function (): FederationRuntimePlugin {
447+
return {
448+
name: 'fetch-manifest-with-credentials-plugin',
449+
fetch(manifestUrl, requestInit) {
450+
return fetch(manifestUrl, {
451+
...requestInit,
452+
credentials: 'include'
453+
});
454+
},
455+
}
456+
};
457+
```
458+
459+
## loadEntry
460+
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.
461+
462+
`asyncHook`
463+
464+
- **Type**
465+
466+
```typescript
467+
function loadEntry(args: LoadEntryOptions): RemoteEntryExports | void;
468+
469+
type LoadEntryOptions = {
470+
createScriptHook: SyncHook,
471+
remoteEntryExports?: RemoteEntryExports,
472+
remoteInfo: RemoteInfo
473+
};
474+
interface RemoteInfo {
475+
name: string;
476+
version?: string;
477+
buildVersion?: string;
478+
entry: string;
479+
type: RemoteEntryType;
480+
entryGlobalName: string;
481+
shareScope: string;
482+
}
483+
export type RemoteEntryExports = {
484+
get: (id: string) => () => Promise<Module>;
485+
init: (
486+
shareScope: ShareScopeMap[string],
487+
initScope?: InitScope,
488+
remoteEntryInitOPtions?: RemoteEntryInitOptions,
489+
) => void | Promise<void>;
490+
};
491+
```
492+
493+
- Example Loading JSON Data
494+
495+
```typescript
496+
// load-json-data-plugin.ts
497+
import { init } from '@module-federation/enhanced/runtime';
498+
import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';
499+
500+
const changeScriptAttributePlugin: () => FederationRuntimePlugin = function () {
501+
return {
502+
name: 'load-json-data-plugin',
503+
loadEntry({ remoteInfo }) {
504+
if (remoteInfo.jsonA === "jsonA") {
505+
return {
506+
init(shareScope, initScope, remoteEntryInitOPtions) {},
507+
async get(path) {
508+
const json = await fetch(remoteInfo.entry + ".json").then(res => res.json())
509+
return () => ({
510+
path,
511+
json
512+
})
513+
}
514+
}
515+
}
516+
},
517+
};
518+
};
519+
```
520+
```ts
521+
// module-federation-config
522+
{
523+
remotes: {
524+
jsonA: "jsonA@https://cdn.jsdelivr.net/npm/@module-federation/runtime/package"
525+
}
526+
}
527+
```
528+
```ts
529+
// src/bootstrap.js
530+
import jsonA from "jsonA"
531+
jsonA // {...json data}
532+
```
533+
534+
- Example Delegate Modules
535+
536+
```typescript
537+
// delegate-modules-plugin.ts
538+
import { init } from '@module-federation/enhanced/runtime';
539+
import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';
540+
541+
const changeScriptAttributePlugin: () => FederationRuntimePlugin = function () {
542+
return {
543+
name: 'delegate-modules-plugin',
544+
loadEntry({ remoteInfo }) {
545+
if (remoteInfo.name === "delegateModulesA") {
546+
return {
547+
init(shareScope, initScope, remoteEntryInitOPtions) {},
548+
async get(path) {
549+
path = path.replace("./", "")
550+
const {[path]: factory} = await import("./delegateModulesA.js")
551+
const result = await factory()
552+
return () => result
553+
}
554+
}
555+
}
556+
},
557+
};
558+
};
559+
```
560+
```ts
561+
// ./src/delegateModulesA.js
562+
export async function test1() {
563+
return new Promise(resolve => {
564+
setTimeout(() => {
565+
resolve("test1 value")
566+
}, 3000)
567+
})
568+
}
569+
export async function test2() {
570+
return new Promise(resolve => {
571+
setTimeout(() => {
572+
resolve("test2 value")
573+
}, 3000)
574+
})
575+
}
576+
```
577+
```ts
578+
// module-federation-config
579+
{
580+
remotes: {
581+
delegateModulesA: "delegateModulesA@https://delegateModulesA.js"
582+
}
583+
}
584+
```
585+
```ts
586+
// src/bootstrap.js
587+
import test1 from "delegateModulesA/test1"
588+
import test2 from "delegateModulesA/test2"
589+
test1 // "test1 value"
590+
test2 // "test2 value"
591+
```

apps/website-new/docs/zh/guide/basic/runtime/runtime-hooks.mdx

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,3 +378,168 @@ const changeScriptAttributePlugin: () => ModuleFederationRuntimePlugin =
378378
};
379379
};
380380
```
381+
382+
## fetch
383+
`fetch` 函数允许自定义获取清单(manifest)JSON 的请求。成功的 `Response` 必须返回一个有效的 JSON。
384+
385+
`AsyncHook`
386+
387+
- **Type**
388+
389+
```typescript
390+
function fetch(manifestUrl: string, requestInit: RequestInit): Promise<Response> | void | false;
391+
```
392+
393+
- 示例:在获取清单(manifest)JSON 时包含凭证:
394+
395+
```typescript
396+
// fetch-manifest-with-credentials-plugin.ts
397+
import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';
398+
399+
export default function (): FederationRuntimePlugin {
400+
return {
401+
name: 'fetch-manifest-with-credentials-plugin',
402+
fetch(manifestUrl, requestInit) {
403+
return fetch(manifestUrl, {
404+
...requestInit,
405+
credentials: 'include'
406+
});
407+
},
408+
}
409+
};
410+
```
411+
412+
## loadEntry
413+
`loadEntry` 函数允许对 remotes 进行完全自定义,从而可以扩展并创建新的 remote 类型。以下两个简单示例分别演示了如何加载 JSON 数据以及模块代理(module delegation)。
414+
415+
`asyncHook`
416+
417+
- **Type**
418+
419+
```typescript
420+
function loadEntry(args: LoadEntryOptions): RemoteEntryExports | void;
421+
422+
type LoadEntryOptions = {
423+
createScriptHook: SyncHook,
424+
remoteEntryExports?: RemoteEntryExports,
425+
remoteInfo: RemoteInfo
426+
};
427+
interface RemoteInfo {
428+
name: string;
429+
version?: string;
430+
buildVersion?: string;
431+
entry: string;
432+
type: RemoteEntryType;
433+
entryGlobalName: string;
434+
shareScope: string;
435+
}
436+
export type RemoteEntryExports = {
437+
get: (id: string) => () => Promise<Module>;
438+
init: (
439+
shareScope: ShareScopeMap[string],
440+
initScope?: InitScope,
441+
remoteEntryInitOPtions?: RemoteEntryInitOptions,
442+
) => void | Promise<void>;
443+
};
444+
```
445+
446+
- 示例:加载 JSON 数据
447+
448+
```typescript
449+
// load-json-data-plugin.ts
450+
import { init } from '@module-federation/enhanced/runtime';
451+
import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';
452+
453+
const changeScriptAttributePlugin: () => FederationRuntimePlugin = function () {
454+
return {
455+
name: 'load-json-data-plugin',
456+
loadEntry({ remoteInfo }) {
457+
if (remoteInfo.jsonA === "jsonA") {
458+
return {
459+
init(shareScope, initScope, remoteEntryInitOPtions) {},
460+
async get(path) {
461+
const json = await fetch(remoteInfo.entry + ".json").then(res => res.json())
462+
return () => ({
463+
path,
464+
json
465+
})
466+
}
467+
}
468+
}
469+
},
470+
};
471+
};
472+
```
473+
```ts
474+
// module-federation-config
475+
{
476+
remotes: {
477+
jsonA: "jsonA@https://cdn.jsdelivr.net/npm/@module-federation/runtime/package"
478+
}
479+
}
480+
```
481+
```ts
482+
// src/bootstrap.js
483+
import jsonA from "jsonA"
484+
jsonA // {...json data}
485+
```
486+
487+
- 示例:模块代理(Delegate Modules)
488+
489+
```typescript
490+
// delegate-modules-plugin.ts
491+
import { init } from '@module-federation/enhanced/runtime';
492+
import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';
493+
494+
const changeScriptAttributePlugin: () => FederationRuntimePlugin = function () {
495+
return {
496+
name: 'delegate-modules-plugin',
497+
loadEntry({ remoteInfo }) {
498+
if (remoteInfo.name === "delegateModulesA") {
499+
return {
500+
init(shareScope, initScope, remoteEntryInitOPtions) {},
501+
async get(path) {
502+
path = path.replace("./", "")
503+
const {[path]: factory} = await import("./delegateModulesA.js")
504+
const result = await factory()
505+
return () => result
506+
}
507+
}
508+
}
509+
},
510+
};
511+
};
512+
```
513+
```ts
514+
// ./src/delegateModulesA.js
515+
export async function test1() {
516+
return new Promise(resolve => {
517+
setTimeout(() => {
518+
resolve("test1 value")
519+
}, 3000)
520+
})
521+
}
522+
export async function test2() {
523+
return new Promise(resolve => {
524+
setTimeout(() => {
525+
resolve("test2 value")
526+
}, 3000)
527+
})
528+
}
529+
```
530+
```ts
531+
// module-federation-config
532+
{
533+
remotes: {
534+
delegateModulesA: "delegateModulesA@https://delegateModulesA.js"
535+
}
536+
}
537+
```
538+
```ts
539+
// src/bootstrap.js
540+
import test1 from "delegateModulesA/test1"
541+
import test2 from "delegateModulesA/test2"
542+
test1 // "test1 value"
543+
test2 // "test2 value"
544+
```
545+

0 commit comments

Comments
 (0)