Skip to content

Commit f6d18e5

Browse files
zhoushawzhouxiao.shaw
andauthored
fix(runtime): fixed an exception in global object processing in the micro-front-end scenario (#1859)
Co-authored-by: zhouxiao.shaw <[email protected]>
1 parent 7df24df commit f6d18e5

File tree

7 files changed

+111
-62
lines changed

7 files changed

+111
-62
lines changed

.github/workflows/build-and-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,5 @@ jobs:
3232
- run: npx nx affected -t lint --parallel=7 --exclude='*,!tag:package'
3333
- run: npx nx affected -t test --parallel=3 --exclude='*,!tag:package'
3434
- run: npx nx run-many --target=serve --projects=3000-home,3001-shop,3002-checkout --parallel=3 & echo "done"
35-
- run: sleep 6 && npx nx run-many --target=test:e2e --projects=3000-home,3001-shop,3002-checkout --parallel=1
35+
- run: sleep 10 && npx nx run-many --target=test:e2e --projects=3000-home,3001-shop,3002-checkout --parallel=1
3636
- run: lsof -ti tcp:3000,3001,3002 | xargs kill

apps/3000-home/project.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@
6969
"options": {
7070
"cypressConfig": "apps/3000-home/cypress.config.ts",
7171
"testingType": "e2e",
72-
"baseUrl": "http://localhost:3000"
72+
"baseUrl": "http://localhost:3000",
73+
"key": "27e40c91-5ac3-4433-8a87-651d10f51cf6"
7374
},
7475
"configurations": {
7576
"production": {

apps/3001-shop/project.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,13 @@
4343
"dev": false,
4444
"port": 3001
4545
}
46-
}
46+
},
47+
"dependsOn": [
48+
{
49+
"target": "build",
50+
"dependencies": true
51+
}
52+
]
4753
},
4854
"export": {
4955
"executor": "@nx/next:export",

apps/3002-checkout/project.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,13 @@
4343
"dev": false,
4444
"port": 3002
4545
}
46-
}
46+
},
47+
"dependsOn": [
48+
{
49+
"target": "build",
50+
"dependencies": true
51+
}
52+
]
4753
},
4854
"export": {
4955
"executor": "@nx/next:export",

packages/runtime/project.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@
4242
]
4343
}
4444
},
45+
"build-debug": {
46+
"executor": "nx:run-commands",
47+
"options": {
48+
"parallel": false,
49+
"commands": [
50+
{
51+
"command": "FEDERATION_DEBUG=true nx run runtime:build",
52+
"forwardAllArgs": false
53+
}
54+
]
55+
}
56+
},
4557
"pre-release": {
4658
"executor": "nx:run-commands",
4759
"options": {

packages/runtime/rollup.config.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,17 @@ module.exports = (rollupConfig, projectOptions) => {
1515
const project = projectOptions.project;
1616
const pkg = require(project);
1717

18+
if (rollupConfig.output.format === 'esm' && FEDERATION_DEBUG) {
19+
rollupConfig.output.format = 'iife';
20+
rollupConfig.output.inlineDynamicImports = true;
21+
delete rollupConfig.external;
22+
delete rollupConfig.input.type;
23+
delete rollupConfig.input.helpers;
24+
}
25+
1826
rollupConfig.plugins.push(
1927
replace({
28+
preventAssignment: true,
2029
__VERSION__: `'${pkg.version}'`,
2130
FEDERATION_DEBUG: `'${FEDERATION_DEBUG}'`,
2231
}),

packages/runtime/src/global.ts

Lines changed: 73 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export interface Federation {
2020

2121
// export const nativeGlobal: typeof global = new Function('return this')();
2222
export const nativeGlobal: typeof global = new Function('return this')();
23+
export const Global = nativeGlobal;
2324

2425
declare global {
2526
// eslint-disable-next-line no-var
@@ -32,67 +33,80 @@ declare global {
3233
>;
3334
}
3435

35-
// This section is to prevent encapsulation by certain microfrontend frameworks. Due to reuse policies, sandbox escapes.
36-
// The sandbox in the microfrontend does not replicate the value of 'configurable'.
37-
// If there is no loading content on the global object, this section defines the loading object.
38-
if (
39-
!Object.hasOwnProperty.call(globalThis, '__GLOBAL_LOADING_REMOTE_ENTRY__')
36+
function definePropertyGlobalVal(
37+
target: typeof globalThis,
38+
key: string,
39+
val: any,
4040
) {
41-
Object.defineProperty(globalThis, '__GLOBAL_LOADING_REMOTE_ENTRY__', {
42-
value: {},
41+
Object.defineProperty(target, key, {
42+
value: val,
4343
configurable: false,
44+
writable: true,
4445
});
4546
}
4647

48+
function includeOwnProperty(target: typeof globalThis, key: string) {
49+
return Object.hasOwnProperty.call(target, key);
50+
}
51+
52+
// This section is to prevent encapsulation by certain microfrontend frameworks. Due to reuse policies, sandbox escapes.
53+
// The sandbox in the microfrontend does not replicate the value of 'configurable'.
54+
// If there is no loading content on the global object, this section defines the loading object.
55+
if (!includeOwnProperty(globalThis, '__GLOBAL_LOADING_REMOTE_ENTRY__')) {
56+
definePropertyGlobalVal(globalThis, '__GLOBAL_LOADING_REMOTE_ENTRY__', {});
57+
}
58+
4759
export const globalLoading = globalThis.__GLOBAL_LOADING_REMOTE_ENTRY__;
4860

49-
//
50-
if (nativeGlobal.__VMOK__) {
51-
nativeGlobal.__FEDERATION__ = nativeGlobal.__VMOK__;
52-
} else if (!nativeGlobal.__FEDERATION__) {
53-
nativeGlobal.__FEDERATION__ = {
54-
__GLOBAL_PLUGIN__: [],
55-
__INSTANCES__: [],
56-
moduleInfo: {},
57-
__SHARE__: {},
58-
__MANIFEST_LOADING__: {},
59-
__SHARE_SCOPE_LOADING__: {},
60-
__PRELOADED_MAP__: new Map(),
61-
};
61+
function setGlobalDefaultVal(target: typeof globalThis) {
62+
if (
63+
includeOwnProperty(target, '__VMOK__') &&
64+
!includeOwnProperty(target, '__FEDERATION__')
65+
) {
66+
definePropertyGlobalVal(target, '__FEDERATION__', target.__VMOK__);
67+
}
6268

63-
nativeGlobal.__VMOK__ = nativeGlobal.__FEDERATION__;
69+
if (!includeOwnProperty(target, '__FEDERATION__')) {
70+
definePropertyGlobalVal(target, '__FEDERATION__', {
71+
__GLOBAL_PLUGIN__: [],
72+
__INSTANCES__: [],
73+
moduleInfo: {},
74+
__SHARE__: {},
75+
__MANIFEST_LOADING__: {},
76+
__SHARE_SCOPE_LOADING__: {},
77+
__PRELOADED_MAP__: new Map(),
78+
});
79+
80+
definePropertyGlobalVal(target, '__VMOK__', target.__FEDERATION__);
81+
}
82+
83+
target.__FEDERATION__.__GLOBAL_PLUGIN__ ??= [];
84+
target.__FEDERATION__.__INSTANCES__ ??= [];
85+
target.__FEDERATION__.moduleInfo ??= {};
86+
target.__FEDERATION__.__SHARE__ ??= {};
87+
target.__FEDERATION__.__MANIFEST_LOADING__ ??= {};
88+
target.__FEDERATION__.__SHARE_SCOPE_LOADING__ ??= {};
89+
target.__FEDERATION__.__PRELOADED_MAP__ ??= new Map();
6490
}
6591

66-
nativeGlobal.__FEDERATION__.__GLOBAL_PLUGIN__ ??= [];
67-
nativeGlobal.__FEDERATION__.__INSTANCES__ ??= [];
68-
nativeGlobal.__FEDERATION__.moduleInfo ??= {};
69-
nativeGlobal.__FEDERATION__.__SHARE__ ??= {};
70-
nativeGlobal.__FEDERATION__.__MANIFEST_LOADING__ ??= {};
71-
nativeGlobal.__FEDERATION__.__SHARE_SCOPE_LOADING__ ??= {};
72-
nativeGlobal.__FEDERATION__.__PRELOADED_MAP__ ??= new Map();
73-
74-
export const Global = {
75-
get __FEDERATION__(): (typeof nativeGlobal)['__FEDERATION__'] {
76-
const globalThisVal = new Function('return globalThis')();
77-
return globalThisVal.__FEDERATION__;
78-
},
79-
};
92+
setGlobalDefaultVal(globalThis);
93+
setGlobalDefaultVal(nativeGlobal);
8094

8195
export function resetFederationGlobalInfo(): void {
82-
nativeGlobal.__FEDERATION__.__GLOBAL_PLUGIN__ = [];
83-
nativeGlobal.__FEDERATION__.__INSTANCES__ = [];
84-
nativeGlobal.__FEDERATION__.moduleInfo = {};
85-
nativeGlobal.__FEDERATION__.__SHARE__ = {};
86-
nativeGlobal.__FEDERATION__.__MANIFEST_LOADING__ = {};
87-
nativeGlobal.__FEDERATION__.__SHARE_SCOPE_LOADING__ = {};
96+
globalThis.__FEDERATION__.__GLOBAL_PLUGIN__ = [];
97+
globalThis.__FEDERATION__.__INSTANCES__ = [];
98+
globalThis.__FEDERATION__.moduleInfo = {};
99+
globalThis.__FEDERATION__.__SHARE__ = {};
100+
globalThis.__FEDERATION__.__MANIFEST_LOADING__ = {};
101+
globalThis.__FEDERATION__.__SHARE_SCOPE_LOADING__ = {};
88102
}
89103

90104
export function getGlobalFederationInstance(
91105
name: string,
92106
version: string | undefined,
93107
): FederationHost | undefined {
94108
const buildId = getBuilderId();
95-
return Global.__FEDERATION__.__INSTANCES__.find((GMInstance) => {
109+
return globalThis.__FEDERATION__.__INSTANCES__.find((GMInstance) => {
96110
if (buildId && GMInstance.options.id === getBuilderId()) {
97111
return true;
98112
}
@@ -119,21 +133,21 @@ export function getGlobalFederationInstance(
119133
export function setGlobalFederationInstance(
120134
FederationInstance: FederationHost,
121135
): void {
122-
Global.__FEDERATION__.__INSTANCES__.push(FederationInstance);
136+
globalThis.__FEDERATION__.__INSTANCES__.push(FederationInstance);
123137
}
124138

125139
export function getGlobalFederationConstructor():
126140
| typeof FederationHost
127141
| undefined {
128-
return Global.__FEDERATION__.__DEBUG_CONSTRUCTOR__;
142+
return globalThis.__FEDERATION__.__DEBUG_CONSTRUCTOR__;
129143
}
130144

131145
export function setGlobalFederationConstructor(
132146
FederationConstructor: typeof FederationHost,
133147
): void {
134148
if (isDebugMode()) {
135-
Global.__FEDERATION__.__DEBUG_CONSTRUCTOR__ = FederationConstructor;
136-
Global.__FEDERATION__.__DEBUG_CONSTRUCTOR_VERSION__ = __VERSION__;
149+
globalThis.__FEDERATION__.__DEBUG_CONSTRUCTOR__ = FederationConstructor;
150+
globalThis.__FEDERATION__.__DEBUG_CONSTRUCTOR_VERSION__ = __VERSION__;
137151
}
138152
}
139153

@@ -159,7 +173,7 @@ export function getInfoWithoutType<T extends object>(
159173
}
160174

161175
export const getGlobalSnapshot = (): GlobalModuleInfo =>
162-
Global.__FEDERATION__.moduleInfo;
176+
nativeGlobal.__FEDERATION__.moduleInfo;
163177

164178
export const getTargetSnapshotInfoByModuleInfo = (
165179
moduleInfo: Optional<Remote, 'alias'>,
@@ -196,7 +210,7 @@ export const getTargetSnapshotInfoByModuleInfo = (
196210
const { version, ...resModuleInfo } = moduleInfo;
197211
const moduleKeyWithoutVersion = getFMId(resModuleInfo);
198212
const getModuleInfoWithoutVersion = getInfoWithoutType(
199-
Global.__FEDERATION__.moduleInfo,
213+
nativeGlobal.__FEDERATION__.moduleInfo,
200214
moduleKeyWithoutVersion,
201215
getModuleInfoHook,
202216
).value;
@@ -220,7 +234,7 @@ export const getGlobalSnapshotInfoByModuleInfo = (
220234
): GlobalModuleInfo[string] | undefined =>
221235
getTargetSnapshotInfoByModuleInfo(
222236
moduleInfo,
223-
Global.__FEDERATION__.moduleInfo,
237+
nativeGlobal.__FEDERATION__.moduleInfo,
224238
extraOptions?.getModuleInfoHook,
225239
);
226240

@@ -229,21 +243,21 @@ export const setGlobalSnapshotInfoByModuleInfo = (
229243
moduleDetailInfo: GlobalModuleInfo[string],
230244
): GlobalModuleInfo => {
231245
const moduleKey = getFMId(remoteInfo);
232-
Global.__FEDERATION__.moduleInfo[moduleKey] = moduleDetailInfo;
233-
return Global.__FEDERATION__.moduleInfo;
246+
nativeGlobal.__FEDERATION__.moduleInfo[moduleKey] = moduleDetailInfo;
247+
return nativeGlobal.__FEDERATION__.moduleInfo;
234248
};
235249

236250
export const addGlobalSnapshot = (
237251
moduleInfos: GlobalModuleInfo,
238252
): (() => void) => {
239-
Global.__FEDERATION__.moduleInfo = {
240-
...Global.__FEDERATION__.moduleInfo,
253+
nativeGlobal.__FEDERATION__.moduleInfo = {
254+
...nativeGlobal.__FEDERATION__.moduleInfo,
241255
...moduleInfos,
242256
};
243257
return () => {
244258
const keys = Object.keys(moduleInfos);
245259
for (const key of keys) {
246-
delete Global.__FEDERATION__.moduleInfo[key];
260+
delete nativeGlobal.__FEDERATION__.moduleInfo[key];
247261
}
248262
};
249263
};
@@ -270,7 +284,7 @@ export const getRemoteEntryExports = (
270284
export const registerGlobalPlugins = (
271285
plugins: Array<FederationRuntimePlugin>,
272286
): void => {
273-
const { __GLOBAL_PLUGIN__ } = Global.__FEDERATION__;
287+
const { __GLOBAL_PLUGIN__ } = nativeGlobal.__FEDERATION__;
274288

275289
plugins.forEach((plugin) => {
276290
if (__GLOBAL_PLUGIN__.findIndex((p) => p.name === plugin.name) === -1) {
@@ -282,9 +296,10 @@ export const registerGlobalPlugins = (
282296
};
283297

284298
export const getGlobalHostPlugins = (): Array<FederationRuntimePlugin> =>
285-
Global.__FEDERATION__.__GLOBAL_PLUGIN__;
299+
nativeGlobal.__FEDERATION__.__GLOBAL_PLUGIN__;
286300

287301
export const getPreloaded = (id: string) =>
288-
Global.__FEDERATION__.__PRELOADED_MAP__.get(id);
302+
globalThis.__FEDERATION__.__PRELOADED_MAP__.get(id);
303+
289304
export const setPreloaded = (id: string) =>
290-
Global.__FEDERATION__.__PRELOADED_MAP__.set(id, true);
305+
globalThis.__FEDERATION__.__PRELOADED_MAP__.set(id, true);

0 commit comments

Comments
 (0)