diff --git a/package.json b/package.json index 2850697e3e..6b603f3f6f 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "nx": "nx", "commit": "cz", "docs": "typedoc", + "f": "nx format:write", "lint": "nx run-many --target=lint", "test": "nx run-many --target=test", "build": "nx run-many --target=build --parallel=5 --projects=tag:type:pkg", diff --git a/packages/runtime/src/plugins/generate-preload-assets.ts b/packages/runtime/src/plugins/generate-preload-assets.ts index 6cae50e4f5..4ec7b2bfcc 100644 --- a/packages/runtime/src/plugins/generate-preload-assets.ts +++ b/packages/runtime/src/plugins/generate-preload-assets.ts @@ -328,7 +328,7 @@ export const generatePreloadAssetsPlugin: () => FederationRuntimePlugin = moduleInfo: { name: remoteInfo.name, entry: remote.entry, - type: 'global', + type: remoteInfo.type || 'global', entryGlobalName: '', shareScope: '', }, diff --git a/packages/runtime/src/utils/load.ts b/packages/runtime/src/utils/load.ts index 0b3aaaa3d3..3f6cbb779a 100644 --- a/packages/runtime/src/utils/load.ts +++ b/packages/runtime/src/utils/load.ts @@ -147,7 +147,7 @@ async function loadEntryNode({ remoteInfo: RemoteInfo; createScriptHook: FederationHost['loaderHook']['lifecycle']['createScript']; }) { - const { entry, entryGlobalName: globalName, name } = remoteInfo; + const { entry, entryGlobalName: globalName, name, type } = remoteInfo; const { entryExports: remoteEntryExports } = getRemoteEntryExports( name, globalName, @@ -158,7 +158,7 @@ async function loadEntryNode({ } return loadScriptNode(entry, { - attrs: { name, globalName }, + attrs: { name, globalName, type }, createScriptHook: (url, attrs) => { const res = createScriptHook.emit({ url, attrs }); diff --git a/packages/sdk/src/dom.ts b/packages/sdk/src/dom.ts index a8f4da4d1b..1eedbe810b 100644 --- a/packages/sdk/src/dom.ts +++ b/packages/sdk/src/dom.ts @@ -47,8 +47,9 @@ export function createScript(info: { } if (!script) { + const attrs = info.attrs; script = document.createElement('script'); - script.type = 'text/javascript'; + script.type = attrs?.['type'] === 'module' ? 'module' : 'text/javascript'; script.src = info.url; let createScriptRes: CreateScriptHookReturnDom = undefined; if (info.createScriptHook) { @@ -65,7 +66,6 @@ export function createScript(info: { } } } - const attrs = info.attrs; if (attrs && !createScriptRes) { Object.keys(attrs).forEach((name) => { if (script) { diff --git a/packages/sdk/src/generateSnapshotFromManifest.ts b/packages/sdk/src/generateSnapshotFromManifest.ts index c9d5f585cc..7400c89ccc 100644 --- a/packages/sdk/src/generateSnapshotFromManifest.ts +++ b/packages/sdk/src/generateSnapshotFromManifest.ts @@ -190,7 +190,8 @@ export function generateSnapshotFromManifest( ssrRemoteEntry.name, ); remoteSnapshot.ssrRemoteEntry = fullSSRRemoteEntry; - remoteSnapshot.ssrRemoteEntryType = 'commonjs-module'; + remoteSnapshot.ssrRemoteEntryType = + ssrRemoteEntry.type || 'commonjs-module'; } return remoteSnapshot; diff --git a/packages/sdk/src/node.ts b/packages/sdk/src/node.ts index 2757427d09..8abccf2d31 100644 --- a/packages/sdk/src/node.ts +++ b/packages/sdk/src/node.ts @@ -132,7 +132,26 @@ export function createScriptNode( }; getFetch() - .then((f) => handleScriptFetch(f, urlObj)) + .then(async (f) => { + if (attrs?.['type'] === 'esm' || attrs?.['type'] === 'module') { + return loadModule(urlObj.href, { + fetch: f, + vm: await importNodeModule('vm'), + }) + .then(async (module) => { + await module.evaluate(); + cb(undefined, module.namespace); + }) + .catch((e) => { + cb( + e instanceof Error + ? e + : new Error(`Script execution error: ${e}`), + ); + }); + } + handleScriptFetch(f, urlObj); + }) .catch((err) => { cb(err); }); @@ -165,3 +184,31 @@ export function loadScriptNode( ); }); } + +async function loadModule( + url: string, + options: { + vm: any; + fetch: any; + } +) { + const { fetch, vm } = options; + const response = await fetch(url); + const code = await response.text(); + + const module: any = new vm.SourceTextModule(code, { + // @ts-ignore + importModuleDynamically: async (specifier, script) => { + const resolvedUrl = new URL(specifier, url).href; + return loadModule(resolvedUrl, options); + }, + }); + + await module.link(async (specifier: string) => { + const resolvedUrl = new URL(specifier, url).href; + const module = await loadModule(resolvedUrl, options); + return module; + }); + + return module; +}