diff --git a/packages/utilities/README.md b/packages/utilities/README.md
index 06b84cdc728..8898c298532 100644
--- a/packages/utilities/README.md
+++ b/packages/utilities/README.md
@@ -50,7 +50,9 @@ Usage looks something like this:
```js
import { importRemote } from '@module-federation/utilities';
+// --
// If it's a regular js module:
+// --
importRemote({
url: 'http://localhost:3001',
scope: 'Foo',
@@ -65,7 +67,20 @@ importRemote({
},
);
+// --
+// If it's a ESM module (this is currently the default for NX):
+// --
+const Bar = lazy(() => importRemote({ url: 'http://localhost:3001', scope: 'Foo', module: 'Bar', esm: true }));
+
+return (
+ Loading Bar...}>
+
+
+);
+
+// --
// If Bar is a React component you can use it with lazy and Suspense just like a dynamic import:
+// --
const Bar = lazy(() => importRemote({ url: 'http://localhost:3001', scope: 'Foo', module: 'Bar' }));
return (
diff --git a/packages/utilities/project.json b/packages/utilities/project.json
index dc230717c22..7d2a6acea1b 100644
--- a/packages/utilities/project.json
+++ b/packages/utilities/project.json
@@ -13,7 +13,7 @@
"tsConfig": "packages/utilities/tsconfig.lib.json",
"assets": ["packages/utilities/*.md"],
"buildableProjectDepsInPackageJsonType": "dependencies",
- "updateBuildableProjectDepsInPackageJson": true,
+ "updateBuildableProjectDepsInPackageJson": true
}
},
"publish": {
diff --git a/packages/utilities/src/utils/importRemote.ts b/packages/utilities/src/utils/importRemote.ts
index fab01bfd3cf..4b2a90101de 100644
--- a/packages/utilities/src/utils/importRemote.ts
+++ b/packages/utilities/src/utils/importRemote.ts
@@ -26,6 +26,7 @@ export interface ImportRemoteOptions {
module: string;
remoteEntryFileName?: string;
bustRemoteEntryCache?: boolean;
+ esm?: boolean;
}
/**
@@ -43,7 +44,7 @@ const REMOTE_ENTRY_FILE = 'remoteEntry.js';
* @returns {Promise} A promise that resolves when the remote is loaded
*/
const loadRemote = (
- url: ImportRemoteOptions['url'],
+ url: RemoteData['url'],
scope: ImportRemoteOptions['scope'],
bustRemoteEntryCache: ImportRemoteOptions['bustRemoteEntryCache'],
) =>
@@ -67,6 +68,25 @@ const loadRemote = (
);
});
+const loadEsmRemote = async (
+ url: RemoteData['url'],
+ scope: ImportRemoteOptions['scope'],
+) => {
+ const module = await import(/* webpackIgnore: true */ url);
+
+ if (!module) {
+ throw new Error(
+ `Unable to load requested remote from ${url} with scope ${scope}`,
+ );
+ }
+
+ window[scope] = {
+ ...module,
+ __initializing: false,
+ __initialized: false,
+ } satisfies WebpackRemoteContainer;
+};
+
/**
* Function to initialize sharing
* @async
@@ -114,6 +134,7 @@ export const importRemote = async ({
module,
remoteEntryFileName = REMOTE_ENTRY_FILE,
bustRemoteEntryCache = true,
+ esm = false,
}: ImportRemoteOptions): Promise => {
const remoteScope = scope as unknown as number;
if (!window[remoteScope]) {
@@ -125,15 +146,14 @@ export const importRemote = async ({
remoteUrl = await url();
}
+ const remoteUrlWithEntryFile = `${remoteUrl}/${remoteEntryFileName}`;
+
+ const asyncContainer = !esm
+ ? loadRemote(remoteUrlWithEntryFile, scope, bustRemoteEntryCache)
+ : loadEsmRemote(remoteUrlWithEntryFile, scope);
+
// Load the remote and initialize the share scope if it's empty
- await Promise.all([
- loadRemote(
- `${remoteUrl}/${remoteEntryFileName}`,
- scope,
- bustRemoteEntryCache,
- ),
- initSharing(),
- ]);
+ await Promise.all([asyncContainer, initSharing()]);
if (!window[remoteScope]) {
throw new Error(
`Remote loaded successfully but ${scope} could not be found! Verify that the name is correct in the Webpack configuration!`,
diff --git a/packages/utilities/tsconfig.json b/packages/utilities/tsconfig.json
index 828a2b0b65c..963cd7748c9 100644
--- a/packages/utilities/tsconfig.json
+++ b/packages/utilities/tsconfig.json
@@ -2,13 +2,15 @@
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"target": "ES2015",
- "module": "commonjs",
+ "module": "Node16",
+ "moduleResolution": "Node16",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
- "noFallthroughCasesInSwitch": true
+ "noFallthroughCasesInSwitch": true,
+ "resolveJsonModule": true
},
"files": [],
"include": [],
diff --git a/packages/utilities/tsconfig.lib.json b/packages/utilities/tsconfig.lib.json
index d1989338eb1..ad80c6dc113 100644
--- a/packages/utilities/tsconfig.lib.json
+++ b/packages/utilities/tsconfig.lib.json
@@ -4,7 +4,7 @@
"jsx": "react",
"outDir": "../../dist/out-tsc",
"declaration": true,
- "types": ["node"]
+ "types": ["node"],
},
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts", "**/*.test.js"]