Skip to content

Commit 56c938c

Browse files
Feature/support esm import remote (#1469)
* Support ESM with ImportRemote * Switch to eval, revert module TSC type * Convert to TS module Node16 * Update ReadMe
1 parent 69c20c0 commit 56c938c

File tree

5 files changed

+50
-13
lines changed

5 files changed

+50
-13
lines changed

packages/utilities/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ Usage looks something like this:
5050
```js
5151
import { importRemote } from '@module-federation/utilities';
5252

53+
// --
5354
// If it's a regular js module:
55+
// --
5456
importRemote({
5557
url: 'http://localhost:3001',
5658
scope: 'Foo',
@@ -65,7 +67,20 @@ importRemote({
6567
},
6668
);
6769

70+
// --
71+
// If it's a ESM module (this is currently the default for NX):
72+
// --
73+
const Bar = lazy(() => importRemote({ url: 'http://localhost:3001', scope: 'Foo', module: 'Bar', esm: true }));
74+
75+
return (
76+
<Suspense fallback={<div>Loading Bar...</div>}>
77+
<Bar />
78+
</Suspense>
79+
);
80+
81+
// --
6882
// If Bar is a React component you can use it with lazy and Suspense just like a dynamic import:
83+
// --
6984
const Bar = lazy(() => importRemote({ url: 'http://localhost:3001', scope: 'Foo', module: 'Bar' }));
7085

7186
return (

packages/utilities/project.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"tsConfig": "packages/utilities/tsconfig.lib.json",
1414
"assets": ["packages/utilities/*.md"],
1515
"buildableProjectDepsInPackageJsonType": "dependencies",
16-
"updateBuildableProjectDepsInPackageJson": true,
16+
"updateBuildableProjectDepsInPackageJson": true
1717
}
1818
},
1919
"publish": {

packages/utilities/src/utils/importRemote.ts

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface ImportRemoteOptions {
2626
module: string;
2727
remoteEntryFileName?: string;
2828
bustRemoteEntryCache?: boolean;
29+
esm?: boolean;
2930
}
3031

3132
/**
@@ -43,7 +44,7 @@ const REMOTE_ENTRY_FILE = 'remoteEntry.js';
4344
* @returns {Promise<void>} A promise that resolves when the remote is loaded
4445
*/
4546
const loadRemote = (
46-
url: ImportRemoteOptions['url'],
47+
url: RemoteData['url'],
4748
scope: ImportRemoteOptions['scope'],
4849
bustRemoteEntryCache: ImportRemoteOptions['bustRemoteEntryCache'],
4950
) =>
@@ -67,6 +68,25 @@ const loadRemote = (
6768
);
6869
});
6970

71+
const loadEsmRemote = async (
72+
url: RemoteData['url'],
73+
scope: ImportRemoteOptions['scope'],
74+
) => {
75+
const module = await import(/* webpackIgnore: true */ url);
76+
77+
if (!module) {
78+
throw new Error(
79+
`Unable to load requested remote from ${url} with scope ${scope}`,
80+
);
81+
}
82+
83+
window[scope] = {
84+
...module,
85+
__initializing: false,
86+
__initialized: false,
87+
} satisfies WebpackRemoteContainer;
88+
};
89+
7090
/**
7191
* Function to initialize sharing
7292
* @async
@@ -114,6 +134,7 @@ export const importRemote = async <T>({
114134
module,
115135
remoteEntryFileName = REMOTE_ENTRY_FILE,
116136
bustRemoteEntryCache = true,
137+
esm = false,
117138
}: ImportRemoteOptions): Promise<T> => {
118139
const remoteScope = scope as unknown as number;
119140
if (!window[remoteScope]) {
@@ -125,15 +146,14 @@ export const importRemote = async <T>({
125146
remoteUrl = await url();
126147
}
127148

149+
const remoteUrlWithEntryFile = `${remoteUrl}/${remoteEntryFileName}`;
150+
151+
const asyncContainer = !esm
152+
? loadRemote(remoteUrlWithEntryFile, scope, bustRemoteEntryCache)
153+
: loadEsmRemote(remoteUrlWithEntryFile, scope);
154+
128155
// Load the remote and initialize the share scope if it's empty
129-
await Promise.all([
130-
loadRemote(
131-
`${remoteUrl}/${remoteEntryFileName}`,
132-
scope,
133-
bustRemoteEntryCache,
134-
),
135-
initSharing(),
136-
]);
156+
await Promise.all([asyncContainer, initSharing()]);
137157
if (!window[remoteScope]) {
138158
throw new Error(
139159
`Remote loaded successfully but ${scope} could not be found! Verify that the name is correct in the Webpack configuration!`,

packages/utilities/tsconfig.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
"extends": "../../tsconfig.base.json",
33
"compilerOptions": {
44
"target": "ES2015",
5-
"module": "commonjs",
5+
"module": "Node16",
6+
"moduleResolution": "Node16",
67
"forceConsistentCasingInFileNames": true,
78
"strict": true,
89
"noImplicitOverride": true,
910
"noPropertyAccessFromIndexSignature": true,
1011
"noImplicitReturns": true,
11-
"noFallthroughCasesInSwitch": true
12+
"noFallthroughCasesInSwitch": true,
13+
"resolveJsonModule": true
1214
},
1315
"files": [],
1416
"include": [],

packages/utilities/tsconfig.lib.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"jsx": "react",
55
"outDir": "../../dist/out-tsc",
66
"declaration": true,
7-
"types": ["node"]
7+
"types": ["node"],
88
},
99
"include": ["**/*.ts", "**/*.tsx"],
1010
"exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts", "**/*.test.js"]

0 commit comments

Comments
 (0)