-
Notifications
You must be signed in to change notification settings - Fork 2k
/
Copy pathforLooseSandbox.ts
94 lines (82 loc) · 3.47 KB
/
forLooseSandbox.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/**
* @author Kuitos
* @since 2020-10-13
*/
import { checkActivityFunctions } from 'single-spa';
import type { Freer, SandBox } from '../../../interfaces';
import {
calcAppCount,
isAllAppsUnmounted,
patchHTMLDynamicAppendPrototypeFunctions,
rebuildCSSRules,
recordStyledComponentsCSSRules,
} from './common';
/**
* Just hijack dynamic head append, that could avoid accidentally hijacking the insertion of elements except in head.
* Such a case: ReactDOM.createPortal(<style>.test{color:blue}</style>, container),
* this could make we append the style element into app wrapper but it will cause an error while the react portal unmounting, as ReactDOM could not find the style in body children list.
* @param appName
* @param appWrapperGetter
* @param sandbox
* @param mounting
* @param scopedCSS
* @param excludeAssetFilter
*/
export function patchLooseSandbox(
appName: string,
appWrapperGetter: () => HTMLElement | ShadowRoot,
sandbox: SandBox,
mounting = true,
scopedCSS = false,
excludeAssetFilter?: CallableFunction,
): Freer {
const { proxy } = sandbox;
let dynamicStyleSheetElements: Array<HTMLLinkElement | HTMLStyleElement> = [];
const unpatchDynamicAppendPrototypeFunctions = patchHTMLDynamicAppendPrototypeFunctions(
/*
check if the currently specified application is active
While we switch page from qiankun app to a normal react routing page, the normal one may load stylesheet dynamically while page rendering,
but the url change listener must wait until the current call stack is flushed.
This scenario may cause we record the stylesheet from react routing page dynamic injection,
and remove them after the url change triggered and qiankun app is unmounting
see https://github.com/ReactTraining/history/blob/master/modules/createHashHistory.js#L222-L230
*/
() => checkActivityFunctions(window.location).some((name) => name === appName),
() => ({
appName,
appWrapperGetter,
proxy,
strictGlobal: false,
speedySandbox: false,
scopedCSS,
dynamicStyleSheetElements,
excludeAssetFilter,
}),
);
if (!mounting) calcAppCount(appName, 'increase', 'bootstrapping');
if (mounting) calcAppCount(appName, 'increase', 'mounting');
return function free() {
if (!mounting) calcAppCount(appName, 'decrease', 'bootstrapping');
if (mounting) calcAppCount(appName, 'decrease', 'mounting');
// release the overwrite prototype after all the micro apps unmounted
if (isAllAppsUnmounted()) unpatchDynamicAppendPrototypeFunctions();
recordStyledComponentsCSSRules(dynamicStyleSheetElements);
// As now the sub app content all wrapped with a special id container,
// the dynamic style sheet would be removed automatically while unmounting
return function rebuild() {
rebuildCSSRules(dynamicStyleSheetElements, (stylesheetElement) => {
const appWrapper = appWrapperGetter();
if (!appWrapper.contains(stylesheetElement)) {
// Using document.head.appendChild ensures that appendChild invocation can also directly use the HTMLHeadElement.prototype.appendChild method which is overwritten at mounting phase
document.head.appendChild.call(appWrapper, stylesheetElement);
return true;
}
return false;
});
// As the patcher will be invoked every mounting phase, we could release the cache for gc after rebuilding
if (mounting) {
dynamicStyleSheetElements = [];
}
};
};
}