diff --git a/.changeset/thick-cups-hope.md b/.changeset/thick-cups-hope.md new file mode 100644 index 00000000000..2b800725a4a --- /dev/null +++ b/.changeset/thick-cups-hope.md @@ -0,0 +1,5 @@ +--- +'@module-federation/runtime': patch +--- + +fix fetch hook types on runtime plugin interfaces diff --git a/.changeset/warm-forks-collect.md b/.changeset/warm-forks-collect.md new file mode 100644 index 00000000000..bb1d5187fe8 --- /dev/null +++ b/.changeset/warm-forks-collect.md @@ -0,0 +1,5 @@ +--- +'@module-federation/node': minor +--- + +fetch hook supported in node runtime plugin diff --git a/.changeset/wild-pillows-help.md b/.changeset/wild-pillows-help.md new file mode 100644 index 00000000000..7888c9e66f7 --- /dev/null +++ b/.changeset/wild-pillows-help.md @@ -0,0 +1,5 @@ +--- +'@module-federation/sdk': patch +--- + +refactor node fetch functions to call fetch hook from runtime plugin diff --git a/packages/node/src/runtimePlugin.ts b/packages/node/src/runtimePlugin.ts index ecabea278c1..58382a5ccaa 100644 --- a/packages/node/src/runtimePlugin.ts +++ b/packages/node/src/runtimePlugin.ts @@ -42,7 +42,8 @@ declare const __non_webpack_require__: (id: string) => any; export default function (): FederationRuntimePlugin { return { name: 'node-federation-plugin', - beforeInit: function (args: any) { + beforeInit(args) { + //patch webpack chunk loading handlers (() => { function resolveFile(rootOutputDir: string, chunkId: string): string { const path = __non_webpack_require__('path'); @@ -182,11 +183,17 @@ export default function (): FederationRuntimePlugin { args.origin.loaderHook.lifecycle.fetch .emit(url.href, {}) - .then(function (res: Response) { - return res.text(); + .then(function (res: Response | false | void | Promise) { + if (res instanceof Response) { + return res.text(); + } else if (typeof res === 'string') { + return res; + } else { + throw new Error('Invalid response type:' + res); + } }) .then(function (data: string) { - const chunk: any = {}; + const chunk: Record = {}; try { const urlDirname = url.pathname .split('/') @@ -201,9 +208,9 @@ export default function (): FederationRuntimePlugin { } catch (e) { callback(e as Error, null); } - }); + }) + .catch((err: Error) => callback(err, null)); } - function httpVmStrategy( chunkName: string, remoteName: string, @@ -227,7 +234,7 @@ export default function (): FederationRuntimePlugin { ); fetchHook - .then((res: Response) => res.text()) + .then((res: any) => res.text()) .then((data: string) => { try { const chunk: any = {}; diff --git a/packages/runtime/src/core.ts b/packages/runtime/src/core.ts index 8614dc8bc2c..5c232314912 100644 --- a/packages/runtime/src/core.ts +++ b/packages/runtime/src/core.ts @@ -105,7 +105,7 @@ export class FederationHost { fetch: new AsyncHook< [string, RequestInit], Promise | void | false - >('fetch'), + >(), }); constructor(userOptions: UserOptions) { diff --git a/packages/sdk/src/node.ts b/packages/sdk/src/node.ts index 6e2735e6f24..a981e687110 100644 --- a/packages/sdk/src/node.ts +++ b/packages/sdk/src/node.ts @@ -32,20 +32,49 @@ export function createScriptNode( cb(new Error(`Invalid URL: ${e}`)); return; } - const getFetch = async () => { - //@ts-ignore - if (typeof globalThis['webpackChunkLoad'] !== 'undefined') { - //@ts-ignore - return globalThis['webpackChunkLoad']; - } else if (typeof fetch === 'undefined') { - const fetchModule = await importNodeModule('node-fetch'); - //@ts-ignore - return fetchModule?.default || fetchModule; - } else { - return fetch; - } + const getFetch = (): Promise => { + return new Promise((resolve, reject) => { + // @ts-expect-error + if (typeof __webpack_require__ !== 'undefined') { + try { + if ( + // @ts-expect-error + __webpack_require__.federation.instance.loaderHook.lifecycle.fetch + .emit + ) { + return resolve(function ( + input: RequestInfo | URL, + init?: RequestInit, + ): Promise { + // @ts-expect-error + return __webpack_require__.federation.instance.loaderHook.lifecycle.fetch.emit( + input, + init || {}, + ); + }); + } + } catch (e) { + console.warn( + 'federation.instance.loaderHook.lifecycle.fetch failed:', + e, + ); + // fall through + } + } + + if (typeof fetch === 'undefined') { + importNodeModule('node-fetch') + .then((fetchModule) => { + const nodeFetch = fetchModule.default || fetchModule; + resolve(nodeFetch as unknown as typeof fetch); // Ensure compatibility + }) + .catch(reject); + } else { + resolve(fetch); + } + }); }; - console.log('fetching', urlObj.href); + getFetch().then((f) => { f(urlObj.href) .then((res: Response) => res.text()) @@ -115,7 +144,6 @@ export function loadScriptNode( `__FEDERATION_${info?.attrs?.['name']}:custom__`; const entryExports = ((globalThis as any)[remoteEntryKey] = scriptContext); - debugger; resolve(entryExports); } }, diff --git a/packages/webpack-bundler-runtime/src/initializeSharing.ts b/packages/webpack-bundler-runtime/src/initializeSharing.ts index cdaa817f757..110a1c6ee8e 100644 --- a/packages/webpack-bundler-runtime/src/initializeSharing.ts +++ b/packages/webpack-bundler-runtime/src/initializeSharing.ts @@ -22,10 +22,8 @@ export function initializeSharing({ typeof console !== 'undefined' && console.warn && console.warn(msg); var initExternal = (id: string | number) => { - var handleError = (err: any) => { + var handleError = (err: any) => warn('Initialization of sharing external failed: ' + err); - debugger; - }; try { var module = webpackRequire(id); if (!module) return;