diff --git a/packages/angular/mfe/index.ts b/packages/angular/mfe/index.ts new file mode 100644 index 0000000000000..e4e04b459af09 --- /dev/null +++ b/packages/angular/mfe/index.ts @@ -0,0 +1 @@ +export { setRemoteUrlResolver, loadRemoteModule } from './mfe'; diff --git a/packages/angular/mfe/mfe.ts b/packages/angular/mfe/mfe.ts new file mode 100644 index 0000000000000..b045eeef70a20 --- /dev/null +++ b/packages/angular/mfe/mfe.ts @@ -0,0 +1,70 @@ +export type ResolveRemoteUrlFunction = ( + remoteName: string +) => string | Promise; + +declare const __webpack_init_sharing__: (scope: 'default') => Promise; +declare const __webpack_share_scopes__: { default: unknown }; + +let resolveRemoteUrl: ResolveRemoteUrlFunction; +export function setRemoteUrlResolver( + _resolveRemoteUrl: ResolveRemoteUrlFunction +) { + resolveRemoteUrl = _resolveRemoteUrl; +} + +let remoteUrlDefinitions: Record; +export function setRemoteDefinitions(definitions: Record) { + remoteUrlDefinitions = definitions; +} + +let remoteModuleMap = new Map(); +let remoteContainerMap = new Map(); +export async function loadRemoteModule(remoteName: string, moduleName: string) { + const remoteModuleKey = `${remoteName}:${moduleName}`; + if (remoteModuleMap.has(remoteModuleKey)) { + return remoteModuleMap.get(remoteModuleKey); + } + + const container = remoteContainerMap.has(remoteName) + ? remoteContainerMap.get(remoteName) + : await loadRemoteContainer(remoteName); + + const factory = await container.get(moduleName); + const Module = factory(); + + remoteModuleMap.set(remoteModuleKey, Module); + + return Module; +} + +function loadModule(url: string) { + return import(/* webpackIgnore:true */ url); +} + +let initialSharingScopeCreated = false; +async function loadRemoteContainer(remoteName: string) { + if (!resolveRemoteUrl && !remoteUrlDefinitions) { + throw new Error( + 'Call setRemoteDefinitions or setRemoteUrlResolver to allow Dynamic Federation to find the remote apps correctly.' + ); + } + + if (!initialSharingScopeCreated) { + initialSharingScopeCreated = true; + await __webpack_init_sharing__('default'); + } + + const remoteUrl = remoteUrlDefinitions + ? remoteUrlDefinitions[remoteName] + : await resolveRemoteUrl(remoteName); + + const containerUrl = `${remoteUrl}${ + remoteUrl.endsWith('/') ? '' : '/' + }remoteEntry.mjs`; + + const container = await loadModule(containerUrl); + await container.init(__webpack_share_scopes__.default); + + remoteContainerMap.set(remoteName, container); + return container; +} diff --git a/packages/angular/mfe/ng-package.json b/packages/angular/mfe/ng-package.json new file mode 100644 index 0000000000000..bb6786ef97ad7 --- /dev/null +++ b/packages/angular/mfe/ng-package.json @@ -0,0 +1,6 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "lib": { + "entryFile": "index.ts" + } +} diff --git a/packages/angular/module-federation/index.ts b/packages/angular/module-federation/index.ts new file mode 100644 index 0000000000000..fbc9790dd7608 --- /dev/null +++ b/packages/angular/module-federation/index.ts @@ -0,0 +1 @@ +export { withModuleFederation } from '../src/utils/mfe/with-module-federation'; diff --git a/packages/angular/package.json b/packages/angular/package.json index 9f7d6162b8dae..04e5d71d55d1f 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -22,7 +22,7 @@ "./executors": "./executors.js", "./tailwind": "./tailwind.js", "./src/generators/utils": "./src/generators/utils/index.js", - "./module-federation": "./src/utils/mfe/with-module-federation.js" + "./module-federation": "./module-federation/index.js" }, "author": "Victor Savkin", "license": "MIT",