Skip to content

Commit 8378a77

Browse files
authored
feat(runtime): add loadEntry hook (#2826)
1 parent a32c6f7 commit 8378a77

File tree

9 files changed

+186
-146
lines changed

9 files changed

+186
-146
lines changed

.changeset/modern-suns-arrive.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@module-federation/runtime': minor
3+
'@module-federation/sdk': patch
4+
---
5+
6+
feat(runtime): add loadEntry hook

packages/runtime/src/module/index.ts

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,31 +30,9 @@ class Module {
3030

3131
// Get remoteEntry.js
3232
const remoteEntryExports = await getRemoteEntry({
33+
origin: this.host,
3334
remoteInfo: this.remoteInfo,
3435
remoteEntryExports: this.remoteEntryExports,
35-
createScriptHook: (url: string, attrs: any) => {
36-
const res = this.host.loaderHook.lifecycle.createScript.emit({
37-
url,
38-
attrs,
39-
});
40-
41-
if (!res) return;
42-
43-
if (typeof document === 'undefined') {
44-
//todo: needs real fix
45-
return res as HTMLScriptElement;
46-
}
47-
48-
if (res instanceof HTMLScriptElement) {
49-
return res;
50-
}
51-
52-
if ('script' in res || 'timeout' in res) {
53-
return res;
54-
}
55-
56-
return;
57-
},
5836
});
5937
assert(
6038
remoteEntryExports,

packages/runtime/src/remote/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
PreloadRemoteArgs,
1414
Remote,
1515
RemoteInfo,
16+
RemoteEntryExports,
1617
} from '../type';
1718
import { FederationHost } from '../core';
1819
import {
@@ -131,6 +132,16 @@ export class RemoteHandler {
131132
options: Options;
132133
origin: FederationHost;
133134
}>(),
135+
loadEntry: new AsyncHook<
136+
[
137+
{
138+
createScriptHook: FederationHost['loaderHook']['lifecycle']['createScript'];
139+
remoteInfo: RemoteInfo;
140+
remoteEntryExports?: RemoteEntryExports;
141+
},
142+
],
143+
Promise<RemoteEntryExports>
144+
>(),
134145
});
135146

136147
constructor(host: FederationHost) {

packages/runtime/src/utils/load.ts

Lines changed: 126 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import {
2-
composeKeyWithSeparator,
32
loadScript,
43
loadScriptNode,
5-
CreateScriptHookReturn,
4+
composeKeyWithSeparator,
5+
isBrowserEnv,
66
} from '@module-federation/sdk';
7-
import { assert } from '../utils/logger';
8-
import { getRemoteEntryExports, globalLoading } from '../global';
9-
import { Remote, RemoteEntryExports, RemoteInfo } from '../type';
107
import { DEFAULT_REMOTE_TYPE, DEFAULT_SCOPE } from '../constant';
8+
import { FederationHost } from '../core';
9+
import { globalLoading, getRemoteEntryExports } from '../global';
10+
import { Remote, RemoteEntryExports, RemoteInfo } from '../type';
11+
import { assert } from '../utils';
1112

12-
export async function loadEsmEntry({
13+
async function loadEsmEntry({
1314
entry,
1415
remoteEntryExports,
1516
}: {
@@ -33,7 +34,7 @@ export async function loadEsmEntry({
3334
});
3435
}
3536

36-
export async function loadSystemJsEntry({
37+
async function loadSystemJsEntry({
3738
entry,
3839
remoteEntryExports,
3940
}: {
@@ -57,7 +58,7 @@ export async function loadSystemJsEntry({
5758
});
5859
}
5960

60-
export async function loadEntryScript({
61+
async function loadEntryScript({
6162
name,
6263
globalName,
6364
entry,
@@ -66,10 +67,7 @@ export async function loadEntryScript({
6667
name: string;
6768
globalName: string;
6869
entry: string;
69-
createScriptHook?: (
70-
url: string,
71-
attrs?: Record<string, any> | undefined,
72-
) => CreateScriptHookReturn;
70+
createScriptHook: FederationHost['loaderHook']['lifecycle']['createScript'];
7371
}): Promise<RemoteEntryExports> {
7472
const { entryExports: remoteEntryExports } = getRemoteEntryExports(
7573
name,
@@ -80,35 +78,99 @@ export async function loadEntryScript({
8078
return remoteEntryExports;
8179
}
8280

83-
if (typeof document === 'undefined') {
84-
return loadScriptNode(entry, {
85-
attrs: { name, globalName },
86-
createScriptHook,
81+
return loadScript(entry, {
82+
attrs: {},
83+
createScriptHook: (url, attrs) => {
84+
const res = createScriptHook.emit({ url, attrs });
85+
86+
if (!res) return;
87+
88+
if (res instanceof HTMLScriptElement) {
89+
return res;
90+
}
91+
92+
if ('script' in res || 'timeout' in res) {
93+
return res;
94+
}
95+
96+
return;
97+
},
98+
})
99+
.then(() => {
100+
const { remoteEntryKey, entryExports } = getRemoteEntryExports(
101+
name,
102+
globalName,
103+
);
104+
105+
assert(
106+
entryExports,
107+
`
108+
Unable to use the ${name}'s '${entry}' URL with ${remoteEntryKey}'s globalName to get remoteEntry exports.
109+
Possible reasons could be:\n
110+
1. '${entry}' is not the correct URL, or the remoteEntry resource or name is incorrect.\n
111+
2. ${remoteEntryKey} cannot be used to get remoteEntry exports in the window object.
112+
`,
113+
);
114+
115+
return entryExports;
87116
})
88-
.then(() => {
89-
const { remoteEntryKey, entryExports } = getRemoteEntryExports(
90-
name,
91-
globalName,
92-
);
93-
94-
assert(
95-
entryExports,
96-
`
97-
Unable to use the ${name}'s '${entry}' URL with ${remoteEntryKey}'s globalName to get remoteEntry exports.
98-
Possible reasons could be:\n
99-
1. '${entry}' is not the correct URL, or the remoteEntry resource or name is incorrect.\n
100-
2. ${remoteEntryKey} cannot be used to get remoteEntry exports in the window object.
101-
`,
102-
);
103-
104-
return entryExports;
105-
})
106-
.catch((e) => {
107-
throw e;
108-
});
117+
.catch((e) => {
118+
throw e;
119+
});
120+
}
121+
122+
async function loadEntryDom({
123+
remoteInfo,
124+
remoteEntryExports,
125+
createScriptHook,
126+
}: {
127+
remoteInfo: RemoteInfo;
128+
remoteEntryExports?: RemoteEntryExports;
129+
createScriptHook: FederationHost['loaderHook']['lifecycle']['createScript'];
130+
}) {
131+
const { entry, entryGlobalName: globalName, name, type } = remoteInfo;
132+
switch (type) {
133+
case 'esm':
134+
case 'module':
135+
return loadEsmEntry({ entry, remoteEntryExports });
136+
case 'system':
137+
return loadSystemJsEntry({ entry, remoteEntryExports });
138+
default:
139+
return loadEntryScript({ entry, globalName, name, createScriptHook });
140+
}
141+
}
142+
143+
async function loadEntryNode({
144+
remoteInfo,
145+
createScriptHook,
146+
}: {
147+
remoteInfo: RemoteInfo;
148+
createScriptHook: FederationHost['loaderHook']['lifecycle']['createScript'];
149+
}) {
150+
const { entry, entryGlobalName: globalName, name } = remoteInfo;
151+
const { entryExports: remoteEntryExports } = getRemoteEntryExports(
152+
name,
153+
globalName,
154+
);
155+
156+
if (remoteEntryExports) {
157+
return remoteEntryExports;
109158
}
110159

111-
return loadScript(entry, { attrs: {}, createScriptHook })
160+
return loadScriptNode(entry, {
161+
attrs: { name, globalName },
162+
createScriptHook: (url, attrs) => {
163+
const res = createScriptHook.emit({ url, attrs });
164+
165+
if (!res) return;
166+
167+
if ('url' in res) {
168+
return res;
169+
}
170+
171+
return;
172+
},
173+
})
112174
.then(() => {
113175
const { remoteEntryKey, entryExports } = getRemoteEntryExports(
114176
name,
@@ -138,43 +200,46 @@ export function getRemoteEntryUniqueKey(remoteInfo: RemoteInfo): string {
138200
}
139201

140202
export async function getRemoteEntry({
203+
origin,
141204
remoteEntryExports,
142205
remoteInfo,
143-
createScriptHook,
144206
}: {
207+
origin: FederationHost;
145208
remoteInfo: RemoteInfo;
146209
remoteEntryExports?: RemoteEntryExports | undefined;
147-
createScriptHook?: (
148-
url: string,
149-
attrs?: Record<string, any> | undefined,
150-
) => CreateScriptHookReturn;
151-
}): Promise<RemoteEntryExports | void> {
152-
const { entry, name, type, entryGlobalName } = remoteInfo;
210+
}): Promise<RemoteEntryExports | false | void> {
153211
const uniqueKey = getRemoteEntryUniqueKey(remoteInfo);
154212
if (remoteEntryExports) {
155213
return remoteEntryExports;
156214
}
157215

158216
if (!globalLoading[uniqueKey]) {
159-
if (['esm', 'module'].includes(type)) {
160-
globalLoading[uniqueKey] = loadEsmEntry({
161-
entry,
162-
remoteEntryExports,
163-
});
164-
} else if (type === 'system') {
165-
globalLoading[uniqueKey] = loadSystemJsEntry({
166-
entry,
167-
remoteEntryExports,
168-
});
217+
const loadEntryHook = origin.remoteHandler.hooks.lifecycle.loadEntry;
218+
if (loadEntryHook.listeners.size) {
219+
globalLoading[uniqueKey] = loadEntryHook
220+
.emit({
221+
createScriptHook: origin.loaderHook.lifecycle.createScript,
222+
remoteInfo,
223+
remoteEntryExports,
224+
})
225+
.then((res) => res || undefined);
169226
} else {
170-
globalLoading[uniqueKey] = loadEntryScript({
171-
name,
172-
globalName: entryGlobalName,
173-
entry,
174-
createScriptHook,
175-
});
227+
const createScriptHook = origin.loaderHook.lifecycle.createScript;
228+
if (!isBrowserEnv()) {
229+
globalLoading[uniqueKey] = loadEntryNode({
230+
remoteInfo,
231+
createScriptHook,
232+
});
233+
} else {
234+
globalLoading[uniqueKey] = loadEntryDom({
235+
remoteInfo,
236+
remoteEntryExports,
237+
createScriptHook,
238+
});
239+
}
176240
}
177241
}
242+
178243
return globalLoading[uniqueKey];
179244
}
180245

packages/runtime/src/utils/preload.ts

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -80,57 +80,15 @@ export function preloadAssets(
8080
const module = host.moduleCache.get(remoteInfo.name);
8181
if (module) {
8282
getRemoteEntry({
83+
origin: host,
8384
remoteInfo: moduleInfo,
8485
remoteEntryExports: module.remoteEntryExports,
85-
createScriptHook: (url: string, attrs: any) => {
86-
const res = host.loaderHook.lifecycle.createScript.emit({
87-
url,
88-
attrs,
89-
});
90-
if (!res) return;
91-
92-
if (typeof document === 'undefined') {
93-
//todo: needs real fix
94-
return res as HTMLScriptElement;
95-
}
96-
97-
if (res instanceof HTMLScriptElement) {
98-
return res;
99-
}
100-
101-
if ('script' in res || 'timeout' in res) {
102-
return res;
103-
}
104-
105-
return;
106-
},
10786
});
10887
} else {
10988
getRemoteEntry({
89+
origin: host,
11090
remoteInfo: moduleInfo,
11191
remoteEntryExports: undefined,
112-
createScriptHook: (url: string, attrs: any) => {
113-
const res = host.loaderHook.lifecycle.createScript.emit({
114-
url,
115-
attrs,
116-
});
117-
if (!res) return;
118-
119-
if (typeof document === 'undefined') {
120-
//todo: needs real fix
121-
return res as HTMLScriptElement;
122-
}
123-
124-
if (res instanceof HTMLScriptElement) {
125-
return res;
126-
}
127-
128-
if ('script' in res || 'timeout' in res) {
129-
return res;
130-
}
131-
132-
return;
133-
},
13492
});
13593
}
13694
});

0 commit comments

Comments
 (0)