# Runtime Hooks
## beforeInit
`SyncWaterfallHook`
Updates the corresponding init configuration before the MF instance is initialized.
- type
```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<Remote>;
shared: ShareInfos;
plugins: Array<ModuleFederationRuntimePlugin>;
inBrowser: boolean;
}
```
## init
`SyncHook`
Called after the MF instance is initialized.
- type
```ts
function init(args: InitOptions): void
type InitOptions ={
options: ModuleFederationRuntimeOptions;
origin: ModuleFederation;
}
```
## beforeRequest
`AsyncWaterfallHook`
Called before resolving the remote path, useful for updating something before lookup.
- type
```ts
async function beforeRequest(args: BeforeRequestOptions): Promise<BeforeRequestOptions>
type BeforeRequestOptions ={
id: string;
options: ModuleFederationRuntimeOptions;
origin: ModuleFederation;
}
```
## afterResolve
`AsyncWaterfallHook`
Called after resolving the remote path, allowing modification of the resolved content.
- type
```ts
async function afterResolve(args: AfterResolveOptions): Promise<AfterResolveOptions>
type AfterResolveOptions ={
id: string;
pkgNameOrAlias: string;
expose: string;
remote: Remote;
options: ModuleFederationRuntimeOptions;
origin: ModuleFederation;
remoteInfo: RemoteInfo;
remoteSnapshot?: ModuleInfo;
}
```
## onLoad
`AsyncHook`
Triggered once a federated module is loaded, allowing access and modification to the exports of the loaded file.
- type
```ts
async function onLoad(args: OnLoadOptions): Promise<void>
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 preloading logic for remotes.
- type
```ts
function handlePreloadModule(args: HandlePreloadModuleOptions): void
type HandlePreloadModuleOptions ={
id: string;
name: string;
remoteSnapshot: ModuleInfo;
preloadConfig: PreloadRemoteArgs;
}
```
## errorLoadRemote
`AsyncHook`
Called if loading remotes fails, enabling custom error handling. Can return a custom fallback logic.
- type
```ts
async function errorLoadRemote(args: ErrorLoadRemoteOptions): Promise<void | unknown>
type ErrorLoadRemoteOptions ={
id: string;
error: unknown;
options?: any;
from: 'build' | 'runtime';
lifecycle: 'beforeRequest' | 'beforeLoadShare' | 'afterResolve' | 'onLoad';
origin: ModuleFederation;
}
```
The `lifecycle` parameter indicates the stage where the error occurred:
- `beforeRequest`: Error during initial request processing
- `afterResolve`: Error during manifest loading (most common for network failures)
- `onLoad`: Error during module loading and execution
- `beforeLoadShare`: Error during shared dependency loading
- example
```ts
import { createInstance, loadRemote } from '@module-federation/enhanced/runtime'
import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime';
const fallbackPlugin: () => ModuleFederationRuntimePlugin =
function () {
return {
name: 'fallback-plugin',
errorLoadRemote(args) {
const { lifecycle, id, error } = args;
if (error) {
console.warn(`Failed to load remote ${id} at ${lifecycle}:`, error?.message || error);
}
switch (lifecycle) {
case 'afterResolve':
return {
id: id || 'fallback',
name: id || 'fallback',
metaData: { /* fallback manifest */ },
shared: [],
remotes: [],
exposes: []
};
case 'beforeRequest':
console.warn(`Request processing failed for ${id}`);
return void 0;
case 'onLoad':
return () => ({
__esModule: true,
default: () => 'Fallback Component'
});
case 'beforeLoadShare':
console.warn(`Shared dependency loading failed for ${id}`);
return () => ({
__esModule: true,
default: {}
});
default:
console.warn(`Unknown lifecycle ${lifecycle} for ${id}`);
return void 0;
}
},
};
};
const mf = createInstance({
name: 'mf_host',
remotes: [
{
name: "remote",
alias: "app1",
entry: "http://localhost:2001/mf-manifest.json"
}
],
plugins: [fallbackPlugin()]
});
mf.loadRemote('app1/un-existed-module').then(mod=>{
expect(mod).toEqual('fallback');
});
```
## beforeLoadShare
`AsyncWaterfallHook`
Called before loading shared, can be used to modify the corresponding shared configuration.
- type
```ts
async function beforeLoadShare(args: BeforeLoadShareOptions): Promise<BeforeLoadShareOptions>
type BeforeLoadShareOptions ={
pkgName: string;
shareInfo?: Shared;
shared: Options['shared'];
origin: ModuleFederation;
}
```
## resolveShare
`SyncWaterfallHook`
Allows manual setting of the actual shared module to be used.
- type
```ts
function resolveShare(args: ResolveShareOptions): ResolveShareOptions
type ResolveShareOptions ={
shareScopeMap: ShareScopeMap;
scope: string;
pkgName: string;
version: string;
GlobalFederation: Federation;
resolver: () => {
shared: Shared;
useTreesShaking: boolean;
};
}
```
- example
```ts
import { createInstance, loadRemote } from '@module-federation/enhanced/runtime'
import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime';
const customSharedPlugin: () => ModuleFederationRuntimePlugin =
function () {
return {
name: 'custom-shared-plugin',
resolveShare(args) {
const { shareScopeMap, scope, pkgName, version, GlobalFederation } = args;
if (
pkgName !== 'react'
) {
return args;
}
args.resolver = function () {
shareScopeMap[scope][pkgName][version] = window.React; // replace local share scope manually with desired module
return {
shared:shareScopeMap[scope][pkgName][version],
useTreesShaking:false
};
};
return args;
},
};
};
const mf = createInstance({
name: 'mf_host',
shared: {
react: {
version: '17.0.0',
scope: 'default',
lib: () => React,
shareConfig: {
singleton: true,
requiredVersion: '^17.0.0',
},
},
},
plugins: [customSharedPlugin()]
});
window.React = ()=> 'Desired Shared';
mf.loadShare("react").then((reactFactory)=>{
expect(reactFactory()).toEqual(window.React());
});
```
## beforePreloadRemote
`AsyncHook`
Called before the preload handler executes any preload logic.
- type
```ts
async function beforePreloadRemote(args: BeforePreloadRemoteOptions): BeforePreloadRemoteOptions
type BeforePreloadRemoteOptions ={
preloadOps: Array<PreloadRemoteArgs>;
options: Options;
origin: ModuleFederation;
}
```
## generatePreloadAssets
`AsyncHook`
Used to control the generation of resources that need to be preloaded.
- type
```ts
async function generatePreloadAssets(args: GeneratePreloadAssetsOptions): Promise<PreloadAssets>
type GeneratePreloadAssetsOptions ={
origin: ModuleFederation;
preloadOptions: PreloadOptions[number];
remote: Remote;
remoteInfo: RemoteInfo;
remoteSnapshot: ModuleInfo;
globalSnapshot: GlobalModuleInfo;
}
interface PreloadAssets {
cssAssets: Array<string>;
jsAssetsWithoutEntry: Array<string>;
entryAssets: Array<EntryAssets>;
}
```
## createScript
`SyncHook`
Used to modify the script when loading resources.
- type
```ts
function createScript(args: CreateScriptOptions): HTMLScriptElement | void
type CreateScriptOptions ={
url: string;
}
```
- example
```ts
import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime';
const changeScriptAttributePlugin: () => ModuleFederationRuntimePlugin =
function () {
return {
name: 'change-script-attribute',
createScript({ url }) {
if (url === 'http://localhost:3001/remoteEntry.js') {
let script = document.createElement('script');
script.src = url;
script.setAttribute('loader-hooks', 'isTrue');
script.setAttribute('crossorigin', 'anonymous');
return script;
}
}
};
};
```
## 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<Response> | 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<Module>;
init: (
shareScope: ShareScopeMap[string],
initScope?: InitScope,
remoteEntryInitOPtions?: RemoteEntryInitOptions,
) => void | Promise<void>;
};
```
- 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}
```
- Example 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"
```