Hooks de runtime

El runtime de Module Federation expone hooks en las etapas clave: la carga de módulos remotos, la inicialización de contenedores, el registro y la resolución de dependencias compartidas, entre otras. Son útiles para diagnóstico y trazado, fallbacks ante errores, reescritura de dependencias compartidas, personalización de la carga de recursos y escenarios similares.

Valores de retorno de los hooks

En SyncHook y AsyncHook, devolver undefined significa que el plugin solo observa el evento. No borra un valor devuelto por un plugin anterior. Un plugin posterior aún puede sustituir ese valor devolviendo otro valor distinto de undefined.

En SyncWaterfallHook y AsyncWaterfallHook, devuelve el objeto de argumentos completo y actualizado cuando necesites cambiar el payload. Devolver undefined conserva los argumentos actuales y los pasa al siguiente plugin.

beforeInit

SyncWaterfallHook

Actualiza la configuración de inicialización correspondiente antes de que se inicialice la instancia de MF.

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

Se llama después de que se inicializa la instancia de MF.

function init(args: InitOptions): void;

type InitOptions = {
  options: ModuleFederationRuntimeOptions;
  origin: ModuleFederation;
};

beforeRequest

AsyncWaterfallHook

Se llama antes de resolver la ruta del remote; es útil para actualizar algo antes de la búsqueda.

async function beforeRequest(
  args: BeforeRequestOptions,
): Promise<BeforeRequestOptions>;

type BeforeRequestOptions = {
  id: string;
  options: ModuleFederationRuntimeOptions;
  origin: ModuleFederation;
};

afterMatchRemote

AsyncHook

Se llama después de que el runtime hace coincidir una petición de loadRemote con un remote configurado. Si la coincidencia falla, el hook se sigue llamando con error. Es útil para diagnóstico, trazado de peticiones y para comprobar si un fallo ocurrió antes de cargar el manifest o el remoteEntry.

async function afterMatchRemote(args: AfterMatchRemoteOptions): Promise<void>;

type AfterMatchRemoteOptions = {
  id: string;
  options: ModuleFederationRuntimeOptions;
  remote?: Remote;
  expose?: string;
  remoteInfo?: RemoteInfo;
  error?: unknown;
  origin: ModuleFederation;
};

afterResolve

AsyncWaterfallHook

Se llama después de resolver la ruta del remote y permite modificar el contenido resuelto.

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

Se dispara después de cargar un remote y permite acceder a los exports expuestos del archivo cargado y modificarlos.

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;
}

afterLoadRemote

AsyncHook

Se llama cuando termina una petición de loadRemote. Se ejecuta en las cargas correctas, en las fallidas y en las recuperadas por errorLoadRemote. Úsalo para registrar el estado final de la carga del remote.

En una carga correcta, onLoad se ejecuta antes que afterLoadRemote. Si una carga falla y errorLoadRemote devuelve un fallback, afterLoadRemote recibe el error original y recovered: true.

async function afterLoadRemote(args: AfterLoadRemoteOptions): Promise<void>;

type AfterLoadRemoteOptions = {
  id: string;
  expose?: string;
  remote?: RemoteInfo;
  options?: {
    loadFactory?: boolean;
    from?: 'build' | 'runtime';
  };
  error?: unknown;
  recovered?: boolean;
  origin: ModuleFederation;
};

beforeInitContainer

AsyncWaterfallHook

Se dispara antes de que el host llame a remoteEntry.init(...). Es útil para reescribir dinámicamente el shareScope / initScope que se usa en esta inicialización y las remoteEntryInitOptions que se pasan al remote.

async function beforeInitContainer(
  args: BeforeInitContainerOptions,
): Promise<BeforeInitContainerOptions>;

type BeforeInitContainerOptions = {
  shareScope: ShareScopeMap[string];
  initScope: InitScope;
  remoteEntryInitOptions: RemoteEntryInitOptions;
  remoteInfo: RemoteInfo;
  origin: ModuleFederation;
};

initContainer

AsyncWaterfallHook

Se dispara cuando remoteEntry.init(...) se completa correctamente. Es útil para métricas, observabilidad o personalizaciones posteriores a la inicialización.

async function initContainer(
  args: InitContainerOptions,
): Promise<InitContainerOptions>;

type InitContainerOptions = {
  shareScope: ShareScopeMap[string];
  initScope: InitScope;
  remoteEntryInitOptions: RemoteEntryInitOptions;
  remoteInfo: RemoteInfo;
  remoteEntryExports: RemoteEntryExports;
  origin: ModuleFederation;
  id?: string;
  remoteSnapshot?: ModuleInfo;
};

handlePreloadModule

SyncHook

Gestiona la lógica de precarga de los remotes.

function handlePreloadModule(args: HandlePreloadModuleOptions): void;

type HandlePreloadModuleOptions = {
  id: string;
  name: string;
  remote: Remote;
  remoteSnapshot: ModuleInfo;
  preloadConfig: PreloadRemoteArgs;
  origin: ModuleFederation;
};

errorLoadRemote

AsyncHook

Se llama si falla la carga de remotes y permite gestionar el error de forma personalizada. Puede devolver una lógica de fallback personalizada.

async function errorLoadRemote(
  args: ErrorLoadRemoteOptions,
): Promise<void | unknown>;

type ErrorLoadRemoteOptions = {
  id: string;
  error: unknown;
  options?: any;
  from: 'build' | 'runtime';
  lifecycle: 'beforeRequest' | 'beforeLoadShare' | 'afterResolve' | 'onLoad';
  remote?: RemoteInfo;
  expose?: string;
  origin: ModuleFederation;
};

El parámetro lifecycle indica la etapa en la que se produjo el error:

  • beforeRequest: error durante el procesamiento inicial de la petición
  • afterResolve: error durante la carga del manifest (el caso más habitual en fallos de red)
  • onLoad: error durante la carga y ejecución del módulo
  • beforeLoadShare: error al cargar un remoteEntry durante la inicialización de las dependencias compartidas

Si este hook devuelve un módulo de fallback, el runtime usa ese valor para continuar. Si un plugin de diagnóstico o de logging devuelve undefined, no borra el fallback devuelto por otro plugin.

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');
});

afterRegisterShare

SyncHook

Se llama después de que el runtime intenta registrar una versión de una dependencia compartida en un share scope. Este hook informa tanto del candidato como del registro que realmente queda registrado, así que también puede observar conflictos en los que gana un provider ya existente.

function afterRegisterShare(args: AfterRegisterShareOptions): void;

type AfterRegisterShareOptions = {
  pkgName: string;
  scope: string;
  shared: Shared;
  previousShared?: Shared;
  registeredShared?: Shared;
  shareScopeMap: ShareScopeMap;
  trigger: 'build' | 'runtime' | 'container-init';
  origin: ModuleFederation;
};
  • shared es el candidato que el runtime intentó registrar.
  • previousShared es el registro con el mismo scope, paquete y versión antes de este intento.
  • registeredShared es el registro que queda tras resolver el conflicto.
  • trigger identifica si el registro procede del código de runtime generado en el build, de una llamada a la API de runtime o de la inicialización de un contenedor remoto.

beforeLoadShare

AsyncWaterfallHook

Se llama antes de cargar shared; se puede usar para modificar la configuración de shared correspondiente.

async function beforeLoadShare(
  args: BeforeLoadShareOptions,
): Promise<BeforeLoadShareOptions>;

type BeforeLoadShareOptions = {
  pkgName: string;
  shareInfo?: Shared;
  shared: Options['shared'];
  origin: ModuleFederation;
  loadContext?: SharedLoadContext;
};

loadContext contiene el consumer que disparó la resolución de shared cuando esa información está disponible:

type SharedLoadContext = {
  trigger?: 'build' | 'runtime' | 'container-init';
  moduleId?: string | number;
  chunkId?: string | number;
};

Como beforeLoadShare es un hook waterfall, un plugin puede devolver un loadContext actualizado; ese mismo valor se reenvía después a resolveShare, afterLoadShare y errorLoadShare.

afterLoadShare

SyncHook

Se llama después de que loadShare o loadShareSync resuelven correctamente una dependencia compartida. Úsalo para observar qué provider y qué versión se seleccionaron.

function afterLoadShare(args: AfterLoadShareOptions): void;

type AfterLoadShareOptions = {
  pkgName: string;
  shareInfo?: Partial<Shared>;
  selectedShared?: Partial<Shared>;
  shared: Options['shared'];
  shareScopeMap: ShareScopeMap;
  lifecycle: 'loadShare' | 'loadShareSync';
  loadContext?: SharedLoadContext;
  origin: ModuleFederation;
};

errorLoadShare

SyncHook

Se llama cuando falla la resolución de una dependencia compartida o no se puede seleccionar una dependencia compartida válida. Es útil para diagnosticar dependencias compartidas ausentes, discrepancias de versión y errores de configuración de eager.

function errorLoadShare(args: ErrorLoadShareOptions): void;

type ErrorLoadShareOptions = {
  pkgName: string;
  shareInfo?: Partial<Shared>;
  shared: Options['shared'];
  shareScopeMap: ShareScopeMap;
  lifecycle: 'loadShare' | 'loadShareSync';
  origin: ModuleFederation;
  error?: unknown;
  recovered?: boolean;
  loadContext?: SharedLoadContext;
};

initContainerShareScopeMap

SyncWaterfallHook

Se dispara cuando el host alinea el mapeo de share scopes de un remote. Es útil para redirigir un scope a otro (por ejemplo, crear un alias de scope1 a default para que compartan el mismo pool).

function initContainerShareScopeMap(
  args: InitContainerShareScopeMapOptions,
): InitContainerShareScopeMapOptions;

type InitContainerShareScopeMapOptions = {
  shareScope: ShareScopeMap[string];
  options: Options;
  origin: ModuleFederation;
  scopeName: string;
  hostShareScopeMap?: ShareScopeMap;
};

resolveShare

SyncWaterfallHook

Permite sobrescribir el resultado final de la selección del módulo compartido.

resolveShare se ejecuta después de que el runtime ya haya elegido el scope y la versión candidatos. En este punto, cambiar campos como scope o version en args no cambia por sí solo el resultado final. Para cambiar a otro módulo compartido, sustituye args.resolver y devuelve el registro de shared que quieras usar.

function resolveShare(args: ResolveShareOptions): ResolveShareOptions;

type ResolveShareOptions = {
  shareScopeMap: ShareScopeMap;
  scope: string;
  pkgName: string;
  version: string;
  shareInfo: Shared;
  GlobalFederation: Federation;
  resolver: () =>
    | {
        shared: Shared;
        useTreesShaking: boolean;
      }
    | undefined;
  loadContext?: SharedLoadContext;
};
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 { pkgName, shareScopeMap } = args;

      if (pkgName !== 'react') {
        return args;
      }

      const fallbackShared = shareScopeMap.default?.react?.['17.0.0'];
      if (!fallbackShared) {
        return args;
      }

      args.resolver = function () {
        return {
          shared: fallbackShared,
          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()],
});

mf.loadShare('react').then((reactFactory) => {
  expect(reactFactory()).toEqual(React);
});

beforePreloadRemote

AsyncHook

Se llama antes de que el handler de precarga ejecute cualquier lógica de precarga.

Se ejecuta tanto en las llamadas explícitas a preloadRemote como en la precarga automática basada en el snapshot durante loadRemote. Modifica args.preloadOps directamente para ajustar la configuración que se usa para generar los recursos de precarga.

async function beforePreloadRemote(
  args: BeforePreloadRemoteOptions,
): Promise<void>;

type BeforePreloadRemoteOptions = {
  preloadOps: Array<PreloadRemoteArgs>;
  options: Options;
  origin: ModuleFederation;
};

generatePreloadAssets

AsyncHook

Se usa para controlar la generación de los recursos que deben precargarse.

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>;
}

beforeLoadManifest

AsyncHook

Se llama justo antes de que el runtime solicite y analice el manifest de un remote. No se llama cuando el manifest ya está en la caché del runtime.

async function beforeLoadManifest(
  args: BeforeLoadManifestOptions,
): Promise<void>;

type BeforeLoadManifestOptions = {
  manifestUrl: string;
  moduleInfo: Remote;
  resourceOptions?: {
    initiator: 'loadRemote' | 'preloadRemote' | 'loadShare';
    id: string;
  };
  origin: ModuleFederation;
};

afterLoadManifest

AsyncHook

Se llama cuando termina la carga del manifest. Se ejecuta en caso de éxito de red, aciertos de caché, fallos de validación y valores recuperados mediante errorLoadRemote.

async function afterLoadManifest(
  args: AfterLoadManifestOptions,
): Promise<void>;

type AfterLoadManifestOptions = {
  manifestUrl: string;
  moduleInfo: Remote;
  resourceOptions?: {
    initiator: 'loadRemote' | 'preloadRemote' | 'loadShare';
    id: string;
  };
  manifestJson?: Manifest;
  response?: Response;
  error?: unknown;
  cached?: boolean;
  recovered?: boolean;
  origin: ModuleFederation;
};
  • cached es true cuando no fue necesaria ninguna petición de red.
  • error conserva el error original de carga o de validación.
  • recovered es true cuando errorLoadRemote proporcionó un manifest utilizable.

loaderHook

loaderHook se usa para interceptar la carga de recursos y la resolución de las factorías de módulos.

beforeInitRemote

AsyncHook

Se llama antes de inicializar el contenedor remoto con remoteEntry.init(...).

async function beforeInitRemote(args: BeforeInitRemoteOptions): Promise<void>;

type BeforeInitRemoteOptions = {
  id?: string;
  remoteInfo: RemoteInfo;
  remoteSnapshot?: ModuleInfo;
  origin: ModuleFederation;
};

afterInitRemote

AsyncHook

Se llama después de que la inicialización del contenedor remoto se complete correctamente o falle. Cuando el remote ya estaba inicializado, cached se establece en true.

async function afterInitRemote(args: AfterInitRemoteOptions): Promise<void>;

type AfterInitRemoteOptions = {
  id?: string;
  remoteInfo: RemoteInfo;
  remoteSnapshot?: ModuleInfo;
  remoteEntryExports?: RemoteEntryExports;
  error?: unknown;
  cached?: boolean;
  origin: ModuleFederation;
};

beforeGetExpose

AsyncHook

Se llama antes de remoteEntry.get(expose).

async function beforeGetExpose(args: BeforeGetExposeOptions): Promise<void>;

type BeforeGetExposeOptions = {
  id: string;
  expose: string;
  moduleInfo: RemoteInfo;
  remoteEntryExports: RemoteEntryExports;
  origin: ModuleFederation;
};

afterGetExpose

AsyncHook

Se llama después de que remoteEntry.get(expose) se complete correctamente o falle.

async function afterGetExpose(args: AfterGetExposeOptions): Promise<void>;

type AfterGetExposeOptions = {
  id: string;
  expose: string;
  moduleInfo: RemoteInfo;
  remoteEntryExports: RemoteEntryExports;
  moduleFactory?: () => unknown | Promise<unknown>;
  error?: unknown;
  origin: ModuleFederation;
};

beforeExecuteFactory

AsyncHook

Se llama antes de ejecutar la factoría del módulo expuesto. Se omite cuando se llama a loadRemote con loadFactory: false.

async function beforeExecuteFactory(
  args: BeforeExecuteFactoryOptions,
): Promise<void>;

type BeforeExecuteFactoryOptions = {
  id: string;
  expose: string;
  moduleInfo: RemoteInfo;
  loadFactory: boolean;
  origin: ModuleFederation;
};

afterExecuteFactory

AsyncHook

Se llama después de que la factoría del módulo expuesto se complete correctamente o falle. Se omite cuando se llama a loadRemote con loadFactory: false.

async function afterExecuteFactory(
  args: AfterExecuteFactoryOptions,
): Promise<void>;

type AfterExecuteFactoryOptions = {
  id: string;
  expose: string;
  moduleInfo: RemoteInfo;
  loadFactory: boolean;
  exposeModule?: unknown;
  error?: unknown;
  origin: ModuleFederation;
};

createScript

SyncHook

Se usa para modificar el script al cargar recursos.

function createScript(args: CreateScriptOptions): CreateScriptHookReturn;

type CreateScriptOptions = {
  url: string;
  attrs?: Record<string, any>;
  remoteInfo?: RemoteInfo;
  resourceContext?: ResourceLoadContext;
};

type CreateScriptHookReturn =
  | HTMLScriptElement
  | { script?: HTMLScriptElement; timeout?: number }
  | void;

type ResourceLoadContext = {
  initiator: 'loadRemote' | 'preloadRemote' | 'loadShare';
  id: string;
  resourceType: 'manifest' | 'remoteEntry' | 'js' | 'css';
  url?: string;
  expose?: string;
};

timeout se mide en milisegundos y controla el tiempo de espera de la carga del script. El valor por defecto es 20000. resourceContext indica si la carga de este recurso procede de loadRemote, preloadRemote o de la inicialización de shared, además del tipo de recurso, el id y el módulo expuesto cuando corresponde. Puedes combinar timeout y resourceContext para establecer tiempos de espera distintos para las cargas de remoteEntry y las cargas de precarga.

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,
            timeout: 30000,
          };
        }
      },
    };
  };

fetch

AsyncHook

La función fetch permite personalizar la petición que obtiene el JSON del manifest. Una Response correcta debe devolver un JSON válido.

function fetch(
  manifestUrl: string,
  requestInit: RequestInit,
  remoteInfo?: RemoteInfo,
  resourceContext?: ResourceLoadContext,
): Promise<Response> | void | false;

resourceContext permite saber si esta petición del manifest procede de loadRemote, preloadRemote o de la inicialización de shared.

  • Ejemplo para incluir las credenciales al obtener el JSON del manifest:
import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';

// fetch-manifest-with-credentials-plugin.ts
export default function (): FederationRuntimePlugin {
  return {
    name: 'fetch-manifest-with-credentials-plugin',
    fetch(manifestUrl, requestInit) {
      return fetch(manifestUrl, {
        ...requestInit,
        credentials: 'include',
      });
    },
  };
}

SyncHook

Se usa para personalizar el elemento link que se crea para la carga de precargas y estilos.

function createLink(args: CreateLinkOptions): HTMLLinkElement | void;

type CreateLinkOptions = {
  url: string;
  attrs?: Record<string, any>;
  remoteInfo?: RemoteInfo;
  resourceContext?: ResourceLoadContext;
};

type CreateLinkHookReturn =
  | HTMLLinkElement
  | { link?: HTMLLinkElement; timeout?: number }
  | void;

resourceContext tiene la misma forma que en createScript. Permite distinguir JS/CSS precargado, la carga real del remoteEntry y el id del recurso relacionado. timeout se mide en milisegundos y controla el tiempo de espera de la carga del link. El valor por defecto es 20000.

Ejemplo: usar un tiempo de espera más corto para los recursos de precarga de baja prioridad y, a la vez, mantener la carga normal de remotes más tolerante.

import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime';

const resourceTimeoutPlugin = (): ModuleFederationRuntimePlugin => ({
  name: 'resource-timeout-plugin',
  createScript({ resourceContext }) {
    if (
      resourceContext?.initiator === 'loadRemote' &&
      resourceContext.resourceType === 'remoteEntry'
    ) {
      return {
        timeout: 30000,
      };
    }
  },
  createLink({ resourceContext }) {
    if (resourceContext?.initiator === 'preloadRemote') {
      return {
        timeout: 5000,
      };
    }
  },
});

loadEntryError

AsyncHook

Se dispara cuando falla la carga del remoteEntry (normalmente por errores de carga del script). Es útil para reintentos o estrategias de fallback personalizadas.

async function loadEntryError(
  args: LoadEntryErrorOptions,
): Promise<Promise<RemoteEntryExports | undefined> | undefined>;

type LoadEntryErrorOptions = {
  getRemoteEntry: typeof getRemoteEntry;
  origin: ModuleFederation;
  remoteInfo: RemoteInfo;
  remoteEntryExports?: RemoteEntryExports;
  globalLoading: Record<string, Promise<void | RemoteEntryExports> | undefined>;
  uniqueKey: string;
};

afterLoadEntry

AsyncHook

Se llama cuando la carga de un remoteEntry se completa correctamente, falla o es recuperada por loadEntryError.

async function afterLoadEntry(args: AfterLoadEntryOptions): Promise<void>;

type AfterLoadEntryOptions = {
  origin: ModuleFederation;
  remoteInfo: RemoteInfo;
  remoteEntryExports?: RemoteEntryExports | false | void;
  resourceContext?: ResourceLoadContext;
  cached?: boolean;
  error?: unknown;
  recovered?: boolean;
};

resourceContext identifica la petición que necesitaba el remoteEntry. cached es true cuando los exports ya estaban disponibles, mientras que recovered significa que loadEntryError devolvió exports de sustitución tras un error de carga.

getModuleFactory

AsyncHook

Se dispara antes de llamar a remoteEntry.get(expose) y permite personalizar la resolución de la factoría del módulo.

async function getModuleFactory(
  args: GetModuleFactoryOptions,
): Promise<(() => Promise<Module>) | undefined>;

type GetModuleFactoryOptions = {
  remoteEntryExports: RemoteEntryExports;
  expose: string;
  moduleInfo: RemoteInfo;
};

loadEntry

asyncHook

La función loadEntry permite personalizar por completo los remotes, de modo que puedes extender y crear nuevos tipos de remote. Los dos ejemplos sencillos siguientes muestran cómo cargar datos JSON y cómo delegar módulos.

function loadEntry(
  args: LoadEntryOptions,
): Promise<RemoteEntryExports | void> | RemoteEntryExports | void;

type LoadEntryOptions = {
  origin: ModuleFederation;
  loaderHook: ModuleFederation['loaderHook'];
  remoteEntryExports?: RemoteEntryExports;
  remoteInfo: RemoteInfo;
  resourceContext?: ResourceLoadContext;
};
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>;
};
  • Ejemplo de carga de datos JSON
import { init } from '@module-federation/enhanced/runtime';

import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';

// load-json-data-plugin.ts
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,
            });
          },
        };
      }
    },
  };
};
// module-federation-config
{
  remotes: {
    jsonA: "jsonA@https://cdn.jsdelivr.net/npm/@module-federation/runtime/package";
  }
}
// src/bootstrap.js
import jsonA from 'jsonA';
jsonA; // {...json data}
  • Ejemplo de delegación de módulos
import { init } from '@module-federation/enhanced/runtime';

import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';

// delegate-modules-plugin.ts
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;
          },
        };
      }
    },
  };
};
// ./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);
  });
}
// module-federation-config
{
  remotes: {
    delegateModulesA: 'delegateModulesA@https://delegateModulesA.js';
  }
}
// src/bootstrap.js
import test1 from 'delegateModulesA/test1';
import test2 from 'delegateModulesA/test2';
test1; // "test1 value"
test2; // "test2 value"

bridgeHook

bridgeHook observa las operaciones de renderizado, destrucción y sincronización de rutas de React Bridge y Vue Bridge. Los paquetes de Bridge pasan un contexto semántico de la operación como segundo argumento, a la vez que mantienen el primer argumento info compatible con los plugins existentes.

import type {
  BridgeOperationContext,
  BridgeOperationResult,
} from '@module-federation/bridge-shared';

type BridgeOperationContext = {
  side: 'consumer' | 'producer';
  framework: 'react' | 'vue';
  operation: 'render' | 'update' | 'destroy' | 'route-sync';
  moduleName?: string;
  route?: {
    action: 'memory-route-init' | 'host-to-remote';
    from?: string;
    to?: string;
    basename?: string;
    mechanism?: 'popstate';
  };
  reason?: 'unmount' | 'direct';
};

type BridgeOperationResult = {
  context: BridgeOperationContext;
  result?: unknown;
};

beforeBridgeRender

SyncHook

Se dispara antes de que un producer o consumer de Bridge se renderice o se actualice. Puedes devolver un objeto para ampliar los argumentos de renderizado (por ejemplo, extraProps).

function beforeBridgeRender(
  info: Record<string, any>,
  context?: BridgeOperationContext,
): void | Record<string, any>;

afterBridgeRender

SyncHook

Se dispara después del renderizado de Bridge.

function afterBridgeRender(
  info: Record<string, any>,
  operation?: BridgeOperationResult,
): void | Record<string, any>;

beforeBridgeDestroy

SyncHook

Se dispara antes del desmontaje de Bridge.

function beforeBridgeDestroy(
  info: Record<string, any>,
  context?: BridgeOperationContext,
): void | Record<string, any>;

afterBridgeDestroy

SyncHook

Se dispara después del desmontaje de Bridge.

function afterBridgeDestroy(
  info: Record<string, any>,
  operation?: BridgeOperationResult,
): void | Record<string, any>;

afterBridgeRouteSync

SyncHook

Se dispara después de que Bridge sincroniza una ruta. Los consumers de React y Vue informan de la sincronización de popstate del host al remote, mientras que un producer de Vue también informa de la navegación inicial de la ruta en memoria. operation.result contiene el resultado del router cuando el framework lo proporciona.

function afterBridgeRouteSync(operation: BridgeOperationResult): void;
import type { ModuleFederationRuntimePlugin } from '@module-federation/enhanced/runtime';

const bridgeTracePlugin = (): ModuleFederationRuntimePlugin => ({
  name: 'bridge-trace-plugin',
  afterBridgeRender(_info, operation) {
    console.log('bridge render', operation?.context);
  },
  afterBridgeRouteSync(operation) {
    console.log('bridge route', operation.context.route);
  },
});