@micro-zoe/micro-app 0.4.1 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -57
- package/README.zh-cn.md +11 -54
- package/lib/index.d.ts +66 -31
- package/lib/index.esm.js +1797 -1580
- package/lib/index.esm.js.map +1 -1
- package/lib/index.min.js +1 -1
- package/lib/index.min.js.map +1 -1
- package/lib/index.umd.js +1 -1
- package/lib/index.umd.js.map +1 -1
- package/package.json +21 -24
- package/polyfill/jsx-custom-event.js +29 -28
- package/polyfill/jsx-custom-event.js.map +1 -1
- package/typings/global.d.ts +49 -38
package/lib/index.esm.js
CHANGED
|
@@ -1,18 +1,54 @@
|
|
|
1
|
-
const version = '0.
|
|
1
|
+
const version = '0.5.1';
|
|
2
|
+
// do not use isUndefined
|
|
2
3
|
const isBrowser = typeof window !== 'undefined';
|
|
4
|
+
// do not use isUndefined
|
|
3
5
|
const globalThis = (typeof global !== 'undefined')
|
|
4
6
|
? global
|
|
5
7
|
: ((typeof window !== 'undefined')
|
|
6
8
|
? window
|
|
7
9
|
: ((typeof self !== 'undefined') ? self : Function('return this')()));
|
|
10
|
+
// is Undefined
|
|
11
|
+
function isUndefined(target) {
|
|
12
|
+
return target === undefined;
|
|
13
|
+
}
|
|
14
|
+
// is String
|
|
15
|
+
function isString(target) {
|
|
16
|
+
return typeof target === 'string';
|
|
17
|
+
}
|
|
18
|
+
// is Boolean
|
|
19
|
+
function isBoolean(target) {
|
|
20
|
+
return typeof target === 'boolean';
|
|
21
|
+
}
|
|
22
|
+
// is function
|
|
23
|
+
function isFunction(target) {
|
|
24
|
+
return typeof target === 'function';
|
|
25
|
+
}
|
|
26
|
+
// is Array
|
|
27
|
+
const isArray = Array.isArray;
|
|
28
|
+
// is PlainObject
|
|
29
|
+
function isPlainObject(target) {
|
|
30
|
+
return toString.call(target) === '[object Object]';
|
|
31
|
+
}
|
|
32
|
+
// is Promise
|
|
33
|
+
function isPromise(target) {
|
|
34
|
+
return toString.call(target) === '[object Promise]';
|
|
35
|
+
}
|
|
36
|
+
// is bind function
|
|
37
|
+
function isBoundFunction(target) {
|
|
38
|
+
return isFunction(target) && target.name.indexOf('bound ') === 0 && !target.hasOwnProperty('prototype');
|
|
39
|
+
}
|
|
40
|
+
// is ShadowRoot
|
|
41
|
+
function isShadowRoot(target) {
|
|
42
|
+
return typeof ShadowRoot !== 'undefined' && target instanceof ShadowRoot;
|
|
43
|
+
}
|
|
8
44
|
/**
|
|
9
45
|
* format error log
|
|
10
46
|
* @param msg message
|
|
11
47
|
* @param appName app name, default is null
|
|
12
48
|
*/
|
|
13
49
|
function logError(msg, appName = null, ...rest) {
|
|
14
|
-
const appNameTip = appName &&
|
|
15
|
-
if (
|
|
50
|
+
const appNameTip = appName && isString(appName) ? ` app ${appName}:` : '';
|
|
51
|
+
if (isString(msg)) {
|
|
16
52
|
console.error(`[micro-app]${appNameTip} ${msg}`, ...rest);
|
|
17
53
|
}
|
|
18
54
|
else {
|
|
@@ -25,8 +61,8 @@ function logError(msg, appName = null, ...rest) {
|
|
|
25
61
|
* @param appName app name, default is null
|
|
26
62
|
*/
|
|
27
63
|
function logWarn(msg, appName = null, ...rest) {
|
|
28
|
-
const appNameTip = appName &&
|
|
29
|
-
if (
|
|
64
|
+
const appNameTip = appName && isString(appName) ? ` app ${appName}:` : '';
|
|
65
|
+
if (isString(msg)) {
|
|
30
66
|
console.warn(`[micro-app]${appNameTip} ${msg}`, ...rest);
|
|
31
67
|
}
|
|
32
68
|
else {
|
|
@@ -49,11 +85,13 @@ function addProtocol(url) {
|
|
|
49
85
|
return url.startsWith('//') ? `${location.protocol}${url}` : url;
|
|
50
86
|
}
|
|
51
87
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
88
|
+
* format URL address
|
|
89
|
+
* note the scenes:
|
|
90
|
+
* 1. micro-app -> attributeChangedCallback
|
|
91
|
+
* 2. preFetch
|
|
54
92
|
*/
|
|
55
|
-
function
|
|
56
|
-
if (
|
|
93
|
+
function formatAppURL(url, appName = null) {
|
|
94
|
+
if (!isString(url) || !url)
|
|
57
95
|
return '';
|
|
58
96
|
try {
|
|
59
97
|
const { origin, pathname, search } = new URL(addProtocol(url));
|
|
@@ -69,6 +107,20 @@ function formatURL(url, appName = null) {
|
|
|
69
107
|
return '';
|
|
70
108
|
}
|
|
71
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* format name
|
|
112
|
+
* note the scenes:
|
|
113
|
+
* 1. micro-app -> attributeChangedCallback
|
|
114
|
+
* 2. event_center -> EventCenterForMicroApp -> constructor
|
|
115
|
+
* 3. event_center -> EventCenterForBaseApp -> all methods
|
|
116
|
+
* 4. preFetch
|
|
117
|
+
* 5. plugins
|
|
118
|
+
*/
|
|
119
|
+
function formatAppName(name) {
|
|
120
|
+
if (!isString(name) || !name)
|
|
121
|
+
return '';
|
|
122
|
+
return name.replace(/(^\d+)|([^\w\d-_])/gi, '');
|
|
123
|
+
}
|
|
72
124
|
/**
|
|
73
125
|
* Get valid address, such as https://xxx/xx/xx.html to https://xxx/xx/
|
|
74
126
|
* @param url app.url
|
|
@@ -108,20 +160,20 @@ function getLinkFileDir(linkpath) {
|
|
|
108
160
|
/**
|
|
109
161
|
* promise stream
|
|
110
162
|
* @param promiseList promise list
|
|
111
|
-
* @param
|
|
163
|
+
* @param successCb success callback
|
|
112
164
|
* @param errorCb failed callback
|
|
113
165
|
* @param finallyCb finally callback
|
|
114
166
|
*/
|
|
115
|
-
function promiseStream(promiseList,
|
|
167
|
+
function promiseStream(promiseList, successCb, errorCb, finallyCb) {
|
|
116
168
|
let finishedNum = 0;
|
|
117
169
|
function isFinished() {
|
|
118
170
|
if (++finishedNum === promiseList.length && finallyCb)
|
|
119
171
|
finallyCb();
|
|
120
172
|
}
|
|
121
173
|
promiseList.forEach((p, i) => {
|
|
122
|
-
if (
|
|
174
|
+
if (isPromise(p)) {
|
|
123
175
|
p.then((res) => {
|
|
124
|
-
|
|
176
|
+
successCb({
|
|
125
177
|
data: res,
|
|
126
178
|
index: i,
|
|
127
179
|
});
|
|
@@ -135,7 +187,7 @@ function promiseStream(promiseList, successsCb, errorCb, finallyCb) {
|
|
|
135
187
|
});
|
|
136
188
|
}
|
|
137
189
|
else {
|
|
138
|
-
|
|
190
|
+
successCb({
|
|
139
191
|
data: p,
|
|
140
192
|
index: i,
|
|
141
193
|
});
|
|
@@ -190,10 +242,6 @@ function removeDomScope() {
|
|
|
190
242
|
function isSafari() {
|
|
191
243
|
return /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
|
|
192
244
|
}
|
|
193
|
-
// is function
|
|
194
|
-
function isFunction(target) {
|
|
195
|
-
return typeof target === 'function';
|
|
196
|
-
}
|
|
197
245
|
/**
|
|
198
246
|
* Create pure elements
|
|
199
247
|
*/
|
|
@@ -209,7 +257,7 @@ function pureCreateElement(tagName, options) {
|
|
|
209
257
|
* @param target Accept cloned elements
|
|
210
258
|
* @param deep deep clone or transfer dom
|
|
211
259
|
*/
|
|
212
|
-
function
|
|
260
|
+
function cloneContainer(origin, target, deep) {
|
|
213
261
|
target.innerHTML = '';
|
|
214
262
|
if (deep) {
|
|
215
263
|
const clonedNode = origin.cloneNode(true);
|
|
@@ -225,6 +273,23 @@ function cloneNode(origin, target, deep) {
|
|
|
225
273
|
});
|
|
226
274
|
}
|
|
227
275
|
}
|
|
276
|
+
// is invalid key of querySelector
|
|
277
|
+
function isInvalidQuerySelectorKey(key) {
|
|
278
|
+
return !key || /(^\d)|([^\w\d-_\u4e00-\u9fa5])/gi.test(key);
|
|
279
|
+
}
|
|
280
|
+
// unique element
|
|
281
|
+
function isUniqueElement(key) {
|
|
282
|
+
return (/^body$/i.test(key) ||
|
|
283
|
+
/^head$/i.test(key) ||
|
|
284
|
+
/^html$/i.test(key));
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* get micro-app element
|
|
288
|
+
* @param target app container
|
|
289
|
+
*/
|
|
290
|
+
function getRootContainer(target) {
|
|
291
|
+
return (isShadowRoot(target) ? target.host : target);
|
|
292
|
+
}
|
|
228
293
|
|
|
229
294
|
var ObservedAttrName;
|
|
230
295
|
(function (ObservedAttrName) {
|
|
@@ -252,8 +317,23 @@ var lifeCycles;
|
|
|
252
317
|
lifeCycles["ERROR"] = "error";
|
|
253
318
|
})(lifeCycles || (lifeCycles = {}));
|
|
254
319
|
|
|
320
|
+
/**
|
|
321
|
+
* fetch source of html, js, css
|
|
322
|
+
* @param url source path
|
|
323
|
+
* @param appName app name
|
|
324
|
+
* @param config config of fetch
|
|
325
|
+
*/
|
|
326
|
+
function fetchSource(url, appName = null, options = {}) {
|
|
327
|
+
if (isFunction(microApp.fetch)) {
|
|
328
|
+
return microApp.fetch(url, options, appName);
|
|
329
|
+
}
|
|
330
|
+
return fetch(url, options).then((res) => {
|
|
331
|
+
return res.text();
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
255
335
|
const globalEnv = {};
|
|
256
|
-
function
|
|
336
|
+
function initGlobalEnv() {
|
|
257
337
|
if (isBrowser) {
|
|
258
338
|
/**
|
|
259
339
|
* save patch raw methods
|
|
@@ -438,33 +518,35 @@ function scopedRule(rules, prefix) {
|
|
|
438
518
|
*/
|
|
439
519
|
function commonAction(templateStyle, styleElement, originContent, prefix, baseURI, linkpath) {
|
|
440
520
|
var _a, _b;
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
521
|
+
if (!styleElement.__MICRO_APP_HAS_SCOPED__) {
|
|
522
|
+
const rules = Array.from((_b = (_a = templateStyle.sheet) === null || _a === void 0 ? void 0 : _a.cssRules) !== null && _b !== void 0 ? _b : []);
|
|
523
|
+
let result = scopedHost(scopedRule(rules, prefix), baseURI, originContent, linkpath);
|
|
524
|
+
/**
|
|
525
|
+
* Solve the problem of missing content quotes in some Safari browsers
|
|
526
|
+
* docs: https://developer.mozilla.org/zh-CN/docs/Web/CSS/content
|
|
527
|
+
* If there are still problems, it is recommended to use the attr()
|
|
528
|
+
*/
|
|
529
|
+
if (isSafari()) {
|
|
530
|
+
result = result.replace(/([;{]\s*content:\s*)([^\s"][^";}]*)/gm, (all, $1, $2) => {
|
|
531
|
+
if ($2 === 'none' ||
|
|
532
|
+
/^(url\()|(counter\()|(attr\()|(open-quote)|(close-quote)/.test($2)) {
|
|
533
|
+
return all;
|
|
534
|
+
}
|
|
535
|
+
return `${$1}"${$2}"`;
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
styleElement.textContent = result;
|
|
539
|
+
styleElement.__MICRO_APP_HAS_SCOPED__ = true;
|
|
456
540
|
}
|
|
457
|
-
styleElement.textContent = result;
|
|
458
541
|
}
|
|
459
542
|
/**
|
|
460
543
|
* scopedCSS
|
|
461
544
|
* @param styleElement target style element
|
|
462
545
|
* @param appName app name
|
|
463
546
|
*/
|
|
464
|
-
function scopedCSS(styleElement,
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
const prefix = `${microApp.tagName}[name=${appName}]`;
|
|
547
|
+
function scopedCSS(styleElement, app) {
|
|
548
|
+
if (app.scopecss) {
|
|
549
|
+
const prefix = `${microApp.tagName}[name=${app.name}]`;
|
|
468
550
|
let templateStyle = globalEnv.templateStyle;
|
|
469
551
|
if (!templateStyle) {
|
|
470
552
|
globalEnv.templateStyle = templateStyle = pureCreateElement('style');
|
|
@@ -474,7 +556,7 @@ function scopedCSS(styleElement, appName) {
|
|
|
474
556
|
}
|
|
475
557
|
if (styleElement.textContent) {
|
|
476
558
|
templateStyle.textContent = styleElement.textContent;
|
|
477
|
-
commonAction(templateStyle, styleElement, styleElement.textContent, prefix, app.url, styleElement.
|
|
559
|
+
commonAction(templateStyle, styleElement, styleElement.textContent, prefix, app.url, styleElement.__MICRO_APP_LINK_PATH__);
|
|
478
560
|
templateStyle.textContent = '';
|
|
479
561
|
}
|
|
480
562
|
else {
|
|
@@ -485,7 +567,7 @@ function scopedCSS(styleElement, appName) {
|
|
|
485
567
|
if ((!styleElement.textContent && ((_b = (_a = styleElement.sheet) === null || _a === void 0 ? void 0 : _a.cssRules) === null || _b === void 0 ? void 0 : _b.length)) ||
|
|
486
568
|
styleElement.hasAttribute('data-styled'))
|
|
487
569
|
return;
|
|
488
|
-
commonAction(styleElement, styleElement, styleElement.textContent, prefix, app.url, styleElement.
|
|
570
|
+
commonAction(styleElement, styleElement, styleElement.textContent, prefix, app.url, styleElement.__MICRO_APP_LINK_PATH__);
|
|
489
571
|
});
|
|
490
572
|
observer.observe(styleElement, { childList: true });
|
|
491
573
|
}
|
|
@@ -515,7 +597,7 @@ function eventHandler(event, element) {
|
|
|
515
597
|
function dispatchOnLoadEvent(element) {
|
|
516
598
|
const event = new CustomEvent('load');
|
|
517
599
|
eventHandler(event, element);
|
|
518
|
-
if (
|
|
600
|
+
if (isFunction(element.onload)) {
|
|
519
601
|
element.onload(event);
|
|
520
602
|
}
|
|
521
603
|
else {
|
|
@@ -525,7 +607,7 @@ function dispatchOnLoadEvent(element) {
|
|
|
525
607
|
function dispatchOnErrorEvent(element) {
|
|
526
608
|
const event = new CustomEvent('error');
|
|
527
609
|
eventHandler(event, element);
|
|
528
|
-
if (
|
|
610
|
+
if (isFunction(element.onerror)) {
|
|
529
611
|
element.onerror(event);
|
|
530
612
|
}
|
|
531
613
|
else {
|
|
@@ -543,7 +625,7 @@ const globalLinks = new Map();
|
|
|
543
625
|
* @param microAppHead micro-app-head element
|
|
544
626
|
* @param isDynamic dynamic insert
|
|
545
627
|
*/
|
|
546
|
-
function extractLinkFromHtml(link, parent, app,
|
|
628
|
+
function extractLinkFromHtml(link, parent, app, isDynamic = false) {
|
|
547
629
|
const rel = link.getAttribute('rel');
|
|
548
630
|
let href = link.getAttribute('href');
|
|
549
631
|
let replaceComment = null;
|
|
@@ -551,12 +633,9 @@ function extractLinkFromHtml(link, parent, app, microAppHead, isDynamic = false)
|
|
|
551
633
|
href = CompletionPath(href, app.url);
|
|
552
634
|
if (!isDynamic) {
|
|
553
635
|
replaceComment = document.createComment(`link element with href=${href} move to micro-app-head as style element`);
|
|
554
|
-
const placeholderComment = document.createComment(`placeholder for link with href=${href}`);
|
|
555
|
-
// all style elements insert into microAppHead
|
|
556
|
-
microAppHead.appendChild(placeholderComment);
|
|
557
636
|
app.source.links.set(href, {
|
|
558
637
|
code: '',
|
|
559
|
-
placeholder:
|
|
638
|
+
placeholder: replaceComment,
|
|
560
639
|
isGlobal: link.hasAttribute('global'),
|
|
561
640
|
});
|
|
562
641
|
}
|
|
@@ -571,7 +650,7 @@ function extractLinkFromHtml(link, parent, app, microAppHead, isDynamic = false)
|
|
|
571
650
|
}
|
|
572
651
|
}
|
|
573
652
|
else if (rel && ['prefetch', 'preload', 'prerender', 'icon', 'apple-touch-icon'].includes(rel)) {
|
|
574
|
-
// preload prefetch
|
|
653
|
+
// preload prefetch icon ....
|
|
575
654
|
if (isDynamic) {
|
|
576
655
|
replaceComment = document.createComment(`link element with rel=${rel}${href ? ' & href=' + href : ''} removed by micro-app`);
|
|
577
656
|
}
|
|
@@ -625,8 +704,9 @@ function fetchLinkSuccess(url, info, data, microAppHead, app) {
|
|
|
625
704
|
}
|
|
626
705
|
const styleLink = pureCreateElement('style');
|
|
627
706
|
styleLink.textContent = data;
|
|
628
|
-
styleLink.
|
|
629
|
-
|
|
707
|
+
styleLink.__MICRO_APP_LINK_PATH__ = url;
|
|
708
|
+
styleLink.setAttribute('data-origin-href', url);
|
|
709
|
+
microAppHead.replaceChild(scopedCSS(styleLink, app), info.placeholder);
|
|
630
710
|
info.placeholder = null;
|
|
631
711
|
info.code = data;
|
|
632
712
|
}
|
|
@@ -641,7 +721,7 @@ function fetchLinkSuccess(url, info, data, microAppHead, app) {
|
|
|
641
721
|
function foramtDynamicLink(url, info, app, originLink, replaceStyle) {
|
|
642
722
|
if (app.source.links.has(url)) {
|
|
643
723
|
replaceStyle.textContent = app.source.links.get(url).code;
|
|
644
|
-
scopedCSS(replaceStyle, app
|
|
724
|
+
scopedCSS(replaceStyle, app);
|
|
645
725
|
defer(() => dispatchOnLoadEvent(originLink));
|
|
646
726
|
return;
|
|
647
727
|
}
|
|
@@ -650,17 +730,16 @@ function foramtDynamicLink(url, info, app, originLink, replaceStyle) {
|
|
|
650
730
|
info.code = code;
|
|
651
731
|
app.source.links.set(url, info);
|
|
652
732
|
replaceStyle.textContent = code;
|
|
653
|
-
scopedCSS(replaceStyle, app
|
|
733
|
+
scopedCSS(replaceStyle, app);
|
|
654
734
|
defer(() => dispatchOnLoadEvent(originLink));
|
|
655
735
|
return;
|
|
656
736
|
}
|
|
657
737
|
fetchSource(url, app.name).then((data) => {
|
|
658
738
|
info.code = data;
|
|
659
739
|
app.source.links.set(url, info);
|
|
660
|
-
|
|
661
|
-
globalLinks.set(url, data);
|
|
740
|
+
info.isGlobal && globalLinks.set(url, data);
|
|
662
741
|
replaceStyle.textContent = data;
|
|
663
|
-
scopedCSS(replaceStyle, app
|
|
742
|
+
scopedCSS(replaceStyle, app);
|
|
664
743
|
dispatchOnLoadEvent(originLink);
|
|
665
744
|
}).catch((err) => {
|
|
666
745
|
logError(err, app.name);
|
|
@@ -728,7 +807,11 @@ function extractScriptElement(script, parent, app, isDynamic = false) {
|
|
|
728
807
|
return { url: nonceStr, info };
|
|
729
808
|
}
|
|
730
809
|
}
|
|
731
|
-
else {
|
|
810
|
+
else if (!isDynamic) {
|
|
811
|
+
/**
|
|
812
|
+
* script with empty src or empty script.textContent remove in static html
|
|
813
|
+
* & not removed if it created by dynamic
|
|
814
|
+
*/
|
|
732
815
|
replaceComment = document.createComment('script element removed by micro-app');
|
|
733
816
|
}
|
|
734
817
|
if (isDynamic) {
|
|
@@ -796,6 +879,7 @@ function execScripts(scriptList, app, initedHook) {
|
|
|
796
879
|
const deferScriptInfo = [];
|
|
797
880
|
for (const [url, info] of scriptListEntries) {
|
|
798
881
|
if (!info.isDynamic) {
|
|
882
|
+
// Notice the second render
|
|
799
883
|
if (info.defer || info.async) {
|
|
800
884
|
if (info.isExternal && !info.code) {
|
|
801
885
|
deferScriptPromise.push(fetchSource(url, app.name));
|
|
@@ -804,11 +888,11 @@ function execScripts(scriptList, app, initedHook) {
|
|
|
804
888
|
deferScriptPromise.push(info.code);
|
|
805
889
|
}
|
|
806
890
|
deferScriptInfo.push([url, info]);
|
|
807
|
-
|
|
808
|
-
initedHook.moduleCount = initedHook.moduleCount ? ++initedHook.moduleCount : 1;
|
|
891
|
+
info.module && (initedHook.moduleCount = initedHook.moduleCount ? ++initedHook.moduleCount : 1);
|
|
809
892
|
}
|
|
810
893
|
else {
|
|
811
|
-
runScript(url,
|
|
894
|
+
runScript(url, app, info, false);
|
|
895
|
+
initedHook(false);
|
|
812
896
|
}
|
|
813
897
|
}
|
|
814
898
|
}
|
|
@@ -816,9 +900,11 @@ function execScripts(scriptList, app, initedHook) {
|
|
|
816
900
|
Promise.all(deferScriptPromise).then((res) => {
|
|
817
901
|
res.forEach((code, index) => {
|
|
818
902
|
const [url, info] = deferScriptInfo[index];
|
|
819
|
-
|
|
903
|
+
info.code = info.code || code;
|
|
904
|
+
runScript(url, app, info, false, initedHook);
|
|
905
|
+
!info.module && initedHook(false);
|
|
820
906
|
});
|
|
821
|
-
initedHook(
|
|
907
|
+
initedHook(isUndefined(initedHook.moduleCount));
|
|
822
908
|
}).catch((err) => {
|
|
823
909
|
logError(err, app.name);
|
|
824
910
|
initedHook(true);
|
|
@@ -831,25 +917,25 @@ function execScripts(scriptList, app, initedHook) {
|
|
|
831
917
|
/**
|
|
832
918
|
* run code
|
|
833
919
|
* @param url script address
|
|
834
|
-
* @param code js code
|
|
835
920
|
* @param app app
|
|
836
|
-
* @param
|
|
921
|
+
* @param info script info
|
|
837
922
|
* @param isDynamic dynamically created script
|
|
838
923
|
* @param callback callback of module script
|
|
839
924
|
*/
|
|
840
|
-
function runScript(url,
|
|
925
|
+
function runScript(url, app, info, isDynamic, callback) {
|
|
841
926
|
var _a;
|
|
842
927
|
try {
|
|
843
|
-
code = bindScope(url,
|
|
844
|
-
if (app.inline || module) {
|
|
928
|
+
const code = bindScope(url, app, info.code, info.module);
|
|
929
|
+
if (app.inline || info.module) {
|
|
845
930
|
const scriptElement = pureCreateElement('script');
|
|
846
|
-
|
|
931
|
+
runCode2InlineScript(url, code, info.module, scriptElement, callback);
|
|
847
932
|
if (isDynamic)
|
|
848
933
|
return scriptElement;
|
|
934
|
+
// TEST IGNORE
|
|
849
935
|
(_a = app.container) === null || _a === void 0 ? void 0 : _a.querySelector('micro-app-body').appendChild(scriptElement);
|
|
850
936
|
}
|
|
851
937
|
else {
|
|
852
|
-
|
|
938
|
+
runCode2Function(code, info);
|
|
853
939
|
if (isDynamic)
|
|
854
940
|
return document.createComment('dynamic script extract by micro-app');
|
|
855
941
|
}
|
|
@@ -867,19 +953,18 @@ function runScript(url, code, app, module, isDynamic, callback) {
|
|
|
867
953
|
*/
|
|
868
954
|
function runDynamicRemoteScript(url, info, app, originScript) {
|
|
869
955
|
const dispatchScriptOnLoadEvent = () => dispatchOnLoadEvent(originScript);
|
|
956
|
+
// url is unique
|
|
870
957
|
if (app.source.scripts.has(url)) {
|
|
871
958
|
const existInfo = app.source.scripts.get(url);
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
return runScript(url, existInfo.code, app, info.module, true, dispatchScriptOnLoadEvent);
|
|
959
|
+
!existInfo.module && defer(dispatchScriptOnLoadEvent);
|
|
960
|
+
return runScript(url, app, existInfo, true, dispatchScriptOnLoadEvent);
|
|
875
961
|
}
|
|
876
962
|
if (globalScripts.has(url)) {
|
|
877
963
|
const code = globalScripts.get(url);
|
|
878
964
|
info.code = code;
|
|
879
965
|
app.source.scripts.set(url, info);
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
return runScript(url, code, app, info.module, true, dispatchScriptOnLoadEvent);
|
|
966
|
+
!info.module && defer(dispatchScriptOnLoadEvent);
|
|
967
|
+
return runScript(url, app, info, true, dispatchScriptOnLoadEvent);
|
|
883
968
|
}
|
|
884
969
|
let replaceElement;
|
|
885
970
|
if (app.inline || info.module) {
|
|
@@ -891,22 +976,20 @@ function runDynamicRemoteScript(url, info, app, originScript) {
|
|
|
891
976
|
fetchSource(url, app.name).then((code) => {
|
|
892
977
|
info.code = code;
|
|
893
978
|
app.source.scripts.set(url, info);
|
|
894
|
-
|
|
895
|
-
globalScripts.set(url, code);
|
|
979
|
+
info.isGlobal && globalScripts.set(url, code);
|
|
896
980
|
try {
|
|
897
|
-
code = bindScope(url,
|
|
981
|
+
code = bindScope(url, app, code, info.module);
|
|
898
982
|
if (app.inline || info.module) {
|
|
899
|
-
|
|
983
|
+
runCode2InlineScript(url, code, info.module, replaceElement, dispatchScriptOnLoadEvent);
|
|
900
984
|
}
|
|
901
985
|
else {
|
|
902
|
-
|
|
986
|
+
runCode2Function(code, info);
|
|
903
987
|
}
|
|
904
988
|
}
|
|
905
989
|
catch (e) {
|
|
906
990
|
console.error(`[micro-app from runDynamicScript] app ${app.name}: `, e, url);
|
|
907
991
|
}
|
|
908
|
-
|
|
909
|
-
dispatchOnLoadEvent(originScript);
|
|
992
|
+
!info.module && dispatchOnLoadEvent(originScript);
|
|
910
993
|
}).catch((err) => {
|
|
911
994
|
logError(err, app.name);
|
|
912
995
|
dispatchOnErrorEvent(originScript);
|
|
@@ -916,20 +999,17 @@ function runDynamicRemoteScript(url, info, app, originScript) {
|
|
|
916
999
|
/**
|
|
917
1000
|
* common handle for inline script
|
|
918
1001
|
* @param url script address
|
|
919
|
-
* @param code
|
|
1002
|
+
* @param code bound code
|
|
920
1003
|
* @param module type='module' of script
|
|
921
1004
|
* @param scriptElement target script element
|
|
922
1005
|
* @param callback callback of module script
|
|
923
1006
|
*/
|
|
924
|
-
function
|
|
1007
|
+
function runCode2InlineScript(url, code, module, scriptElement, callback) {
|
|
925
1008
|
if (module) {
|
|
926
1009
|
// module script is async, transform it to a blob for subsequent operations
|
|
927
|
-
const blob = new Blob([code], { type: 'text/javascript
|
|
1010
|
+
const blob = new Blob([code], { type: 'text/javascript' });
|
|
928
1011
|
scriptElement.src = URL.createObjectURL(blob);
|
|
929
1012
|
scriptElement.setAttribute('type', 'module');
|
|
930
|
-
if (!url.startsWith('inline-')) {
|
|
931
|
-
scriptElement.setAttribute('originSrc', url);
|
|
932
|
-
}
|
|
933
1013
|
if (callback) {
|
|
934
1014
|
callback.moduleCount && callback.moduleCount--;
|
|
935
1015
|
scriptElement.onload = callback.bind(scriptElement, callback.moduleCount === 0);
|
|
@@ -938,16 +1018,26 @@ function setInlinScriptContent(url, code, module, scriptElement, callback) {
|
|
|
938
1018
|
else {
|
|
939
1019
|
scriptElement.textContent = code;
|
|
940
1020
|
}
|
|
1021
|
+
if (!url.startsWith('inline-')) {
|
|
1022
|
+
scriptElement.setAttribute('data-origin-src', url);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
// init & run code2Function
|
|
1026
|
+
function runCode2Function(code, info) {
|
|
1027
|
+
if (!info.code2Function) {
|
|
1028
|
+
info.code2Function = new Function(code);
|
|
1029
|
+
}
|
|
1030
|
+
info.code2Function.call(window);
|
|
941
1031
|
}
|
|
942
1032
|
/**
|
|
943
1033
|
* bind js scope
|
|
944
1034
|
* @param url script address
|
|
945
|
-
* @param code code
|
|
946
1035
|
* @param app app
|
|
1036
|
+
* @param code code
|
|
947
1037
|
* @param module type='module' of script
|
|
948
1038
|
*/
|
|
949
|
-
function bindScope(url,
|
|
950
|
-
if (
|
|
1039
|
+
function bindScope(url, app, code, module) {
|
|
1040
|
+
if (isPlainObject(microApp.plugins)) {
|
|
951
1041
|
code = usePlugins(url, code, app.name, microApp.plugins);
|
|
952
1042
|
}
|
|
953
1043
|
if (app.sandBox && !module) {
|
|
@@ -965,16 +1055,16 @@ function bindScope(url, code, app, module) {
|
|
|
965
1055
|
*/
|
|
966
1056
|
function usePlugins(url, code, appName, plugins) {
|
|
967
1057
|
var _a;
|
|
968
|
-
if (
|
|
1058
|
+
if (isArray(plugins.global)) {
|
|
969
1059
|
for (const plugin of plugins.global) {
|
|
970
|
-
if (
|
|
1060
|
+
if (isPlainObject(plugin) && isFunction(plugin.loader)) {
|
|
971
1061
|
code = plugin.loader(code, url, plugin.options);
|
|
972
1062
|
}
|
|
973
1063
|
}
|
|
974
1064
|
}
|
|
975
|
-
if (
|
|
1065
|
+
if (isArray((_a = plugins.modules) === null || _a === void 0 ? void 0 : _a[appName])) {
|
|
976
1066
|
for (const plugin of plugins.modules[appName]) {
|
|
977
|
-
if (
|
|
1067
|
+
if (isPlainObject(plugin) && isFunction(plugin.loader)) {
|
|
978
1068
|
code = plugin.loader(code, url, plugin.options);
|
|
979
1069
|
}
|
|
980
1070
|
}
|
|
@@ -982,686 +1072,411 @@ function usePlugins(url, code, appName, plugins) {
|
|
|
982
1072
|
return code;
|
|
983
1073
|
}
|
|
984
1074
|
|
|
985
|
-
// Record element and map element
|
|
986
|
-
const dynamicElementInMicroAppMap = new WeakMap();
|
|
987
1075
|
/**
|
|
988
|
-
*
|
|
989
|
-
* @param
|
|
990
|
-
* @param child new node
|
|
991
|
-
* @param app app
|
|
1076
|
+
* transform html string to dom
|
|
1077
|
+
* @param str string dom
|
|
992
1078
|
*/
|
|
993
|
-
function
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
dynamicElementInMicroAppMap.set(child, replaceComment);
|
|
998
|
-
return replaceComment;
|
|
999
|
-
}
|
|
1000
|
-
else if (app.scopecss && !child.hasAttribute('ignore')) {
|
|
1001
|
-
return scopedCSS(child, app.name);
|
|
1002
|
-
}
|
|
1003
|
-
return child;
|
|
1004
|
-
}
|
|
1005
|
-
else if (child instanceof HTMLLinkElement) {
|
|
1006
|
-
if (child.hasAttribute('exclude')) {
|
|
1007
|
-
const linkReplaceComment = document.createComment('link element with exclude attribute ignored by micro-app');
|
|
1008
|
-
dynamicElementInMicroAppMap.set(child, linkReplaceComment);
|
|
1009
|
-
return linkReplaceComment;
|
|
1010
|
-
}
|
|
1011
|
-
else if (!app.scopecss || child.hasAttribute('ignore')) {
|
|
1012
|
-
return child;
|
|
1013
|
-
}
|
|
1014
|
-
const { url, info, replaceComment } = extractLinkFromHtml(child, parent, app, null, true);
|
|
1015
|
-
if (url && info) {
|
|
1016
|
-
const replaceStyle = pureCreateElement('style');
|
|
1017
|
-
replaceStyle.linkpath = url;
|
|
1018
|
-
foramtDynamicLink(url, info, app, child, replaceStyle);
|
|
1019
|
-
dynamicElementInMicroAppMap.set(child, replaceStyle);
|
|
1020
|
-
return replaceStyle;
|
|
1021
|
-
}
|
|
1022
|
-
else if (replaceComment) {
|
|
1023
|
-
dynamicElementInMicroAppMap.set(child, replaceComment);
|
|
1024
|
-
return replaceComment;
|
|
1025
|
-
}
|
|
1026
|
-
return child;
|
|
1027
|
-
}
|
|
1028
|
-
else if (child instanceof HTMLScriptElement) {
|
|
1029
|
-
const { replaceComment, url, info } = extractScriptElement(child, parent, app, true) || {};
|
|
1030
|
-
if (url && info) {
|
|
1031
|
-
if (info.code) { // inline script
|
|
1032
|
-
const replaceElement = runScript(url, info.code, app, info.module, true);
|
|
1033
|
-
dynamicElementInMicroAppMap.set(child, replaceElement);
|
|
1034
|
-
return replaceElement;
|
|
1035
|
-
}
|
|
1036
|
-
else { // remote script
|
|
1037
|
-
const replaceElement = runDynamicRemoteScript(url, info, app, child);
|
|
1038
|
-
dynamicElementInMicroAppMap.set(child, replaceElement);
|
|
1039
|
-
return replaceElement;
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
else if (replaceComment) {
|
|
1043
|
-
dynamicElementInMicroAppMap.set(child, replaceComment);
|
|
1044
|
-
return replaceComment;
|
|
1045
|
-
}
|
|
1046
|
-
return child;
|
|
1047
|
-
}
|
|
1048
|
-
return child;
|
|
1079
|
+
function getWrapElement(str) {
|
|
1080
|
+
const wrapDiv = pureCreateElement('div');
|
|
1081
|
+
wrapDiv.innerHTML = str;
|
|
1082
|
+
return wrapDiv;
|
|
1049
1083
|
}
|
|
1050
1084
|
/**
|
|
1051
|
-
*
|
|
1085
|
+
* Recursively process each child element
|
|
1086
|
+
* @param parent parent element
|
|
1052
1087
|
* @param app app
|
|
1053
|
-
* @param
|
|
1054
|
-
* @param parent parent node
|
|
1055
|
-
* @param targetChild target node
|
|
1056
|
-
* @param passiveChild second param of insertBefore and replaceChild
|
|
1088
|
+
* @param microAppHead micro-app-head element
|
|
1057
1089
|
*/
|
|
1058
|
-
function
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
if (parent.contains(targetChild)) {
|
|
1074
|
-
return rawMethod.call(parent, targetChild);
|
|
1090
|
+
function flatChildren(parent, app, microAppHead) {
|
|
1091
|
+
const children = Array.from(parent.children);
|
|
1092
|
+
children.length && children.forEach((child) => {
|
|
1093
|
+
flatChildren(child, app);
|
|
1094
|
+
});
|
|
1095
|
+
for (const dom of children) {
|
|
1096
|
+
if (dom instanceof HTMLLinkElement) {
|
|
1097
|
+
if (dom.hasAttribute('exclude')) {
|
|
1098
|
+
parent.replaceChild(document.createComment('link element with exclude attribute ignored by micro-app'), dom);
|
|
1099
|
+
}
|
|
1100
|
+
else if (!dom.hasAttribute('ignore')) {
|
|
1101
|
+
extractLinkFromHtml(dom, parent, app);
|
|
1102
|
+
}
|
|
1103
|
+
else if (dom.hasAttribute('href')) {
|
|
1104
|
+
dom.setAttribute('href', CompletionPath(dom.getAttribute('href'), app.url));
|
|
1075
1105
|
}
|
|
1076
|
-
return targetChild;
|
|
1077
1106
|
}
|
|
1078
|
-
else if (
|
|
1079
|
-
|
|
1107
|
+
else if (dom instanceof HTMLStyleElement) {
|
|
1108
|
+
if (dom.hasAttribute('exclude')) {
|
|
1109
|
+
parent.replaceChild(document.createComment('style element with exclude attribute ignored by micro-app'), dom);
|
|
1110
|
+
}
|
|
1111
|
+
else if (app.scopecss && !dom.hasAttribute('ignore')) {
|
|
1112
|
+
scopedCSS(dom, app);
|
|
1113
|
+
}
|
|
1080
1114
|
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
else if (parent === document.body) {
|
|
1084
|
-
const microAppBody = app.container.querySelector('micro-app-body');
|
|
1085
|
-
if (passiveChild && !microAppBody.contains(passiveChild)) {
|
|
1086
|
-
return globalEnv.rawAppendChild.call(microAppBody, targetChild);
|
|
1115
|
+
else if (dom instanceof HTMLScriptElement) {
|
|
1116
|
+
extractScriptElement(dom, parent, app);
|
|
1087
1117
|
}
|
|
1088
|
-
else if (
|
|
1089
|
-
|
|
1090
|
-
return rawMethod.call(parent, targetChild);
|
|
1091
|
-
}
|
|
1092
|
-
return targetChild;
|
|
1118
|
+
else if (dom instanceof HTMLMetaElement || dom instanceof HTMLTitleElement) {
|
|
1119
|
+
parent.removeChild(dom);
|
|
1093
1120
|
}
|
|
1094
|
-
else if (
|
|
1095
|
-
|
|
1121
|
+
else if (dom instanceof HTMLImageElement && dom.hasAttribute('src')) {
|
|
1122
|
+
dom.setAttribute('src', CompletionPath(dom.getAttribute('src'), app.url));
|
|
1096
1123
|
}
|
|
1097
|
-
return rawMethod.call(microAppBody, targetChild, passiveChild);
|
|
1098
|
-
}
|
|
1099
|
-
else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
|
|
1100
|
-
return rawMethod.call(parent, targetChild);
|
|
1101
1124
|
}
|
|
1102
|
-
return rawMethod.call(parent, targetChild, passiveChild);
|
|
1103
|
-
}
|
|
1104
|
-
// Get the map element
|
|
1105
|
-
function getMappingNode(node) {
|
|
1106
|
-
var _a;
|
|
1107
|
-
return (_a = dynamicElementInMicroAppMap.get(node)) !== null && _a !== void 0 ? _a : node;
|
|
1108
1125
|
}
|
|
1109
1126
|
/**
|
|
1110
|
-
*
|
|
1111
|
-
* @param
|
|
1112
|
-
* @param
|
|
1113
|
-
* @param passiveChild passive node
|
|
1114
|
-
* @param rawMethodraw method
|
|
1127
|
+
* Extract link and script, bind style scope
|
|
1128
|
+
* @param htmlStr html string
|
|
1129
|
+
* @param app app
|
|
1115
1130
|
*/
|
|
1116
|
-
function
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
}
|
|
1125
|
-
return rawMethod.call(parent, newChild, passiveChild);
|
|
1131
|
+
function extractSourceDom(htmlStr, app) {
|
|
1132
|
+
const wrapElement = getWrapElement(htmlStr);
|
|
1133
|
+
const microAppHead = wrapElement.querySelector('micro-app-head');
|
|
1134
|
+
const microAppBody = wrapElement.querySelector('micro-app-body');
|
|
1135
|
+
if (!microAppHead || !microAppBody) {
|
|
1136
|
+
const msg = `element ${microAppHead ? 'body' : 'head'} is missing`;
|
|
1137
|
+
app.onerror(new Error(msg));
|
|
1138
|
+
return logError(msg, app.name);
|
|
1126
1139
|
}
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
}
|
|
1140
|
-
return rawMethod.call(parent, newChild);
|
|
1140
|
+
flatChildren(wrapElement, app);
|
|
1141
|
+
if (app.source.links.size) {
|
|
1142
|
+
fetchLinksFromHtml(wrapElement, app, microAppHead);
|
|
1143
|
+
}
|
|
1144
|
+
else {
|
|
1145
|
+
app.onLoad(wrapElement);
|
|
1146
|
+
}
|
|
1147
|
+
if (app.source.scripts.size) {
|
|
1148
|
+
fetchScriptsFromHtml(wrapElement, app);
|
|
1149
|
+
}
|
|
1150
|
+
else {
|
|
1151
|
+
app.onLoad(wrapElement);
|
|
1141
1152
|
}
|
|
1142
|
-
return rawMethod.call(parent, newChild, passiveChild);
|
|
1143
1153
|
}
|
|
1144
1154
|
/**
|
|
1145
|
-
*
|
|
1155
|
+
* Get and format html
|
|
1156
|
+
* @param app app
|
|
1146
1157
|
*/
|
|
1147
|
-
function
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
const cloneValue = {};
|
|
1154
|
-
Object.getOwnPropertyNames(value).forEach((propertyKey) => {
|
|
1155
|
-
if (!(typeof propertyKey === 'string' && propertyKey.indexOf('__') === 0)) {
|
|
1156
|
-
// @ts-ignore
|
|
1157
|
-
cloneValue[propertyKey] = value[propertyKey];
|
|
1158
|
-
}
|
|
1159
|
-
});
|
|
1160
|
-
this.data = cloneValue;
|
|
1161
|
-
}
|
|
1162
|
-
else if (value !== '[object Object]') {
|
|
1163
|
-
logWarn('property data must be an object', this.getAttribute('name'));
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
else if (((key === 'src' && /^(img|script)$/i.test(this.tagName)) ||
|
|
1167
|
-
(key === 'href' && /^link$/i.test(this.tagName))) &&
|
|
1168
|
-
this.__MICRO_APP_NAME__ &&
|
|
1169
|
-
appInstanceMap.has(this.__MICRO_APP_NAME__)) {
|
|
1170
|
-
const app = appInstanceMap.get(this.__MICRO_APP_NAME__);
|
|
1171
|
-
globalEnv.rawSetAttribute.call(this, key, CompletionPath(value, app.url));
|
|
1172
|
-
}
|
|
1173
|
-
else {
|
|
1174
|
-
globalEnv.rawSetAttribute.call(this, key, value);
|
|
1175
|
-
}
|
|
1176
|
-
};
|
|
1177
|
-
// prototype methods of add element👇
|
|
1178
|
-
Node.prototype.appendChild = function appendChild(newChild) {
|
|
1179
|
-
return commonElementHander(this, newChild, null, globalEnv.rawAppendChild);
|
|
1180
|
-
};
|
|
1181
|
-
Node.prototype.insertBefore = function insertBefore(newChild, refChild) {
|
|
1182
|
-
return commonElementHander(this, newChild, refChild, globalEnv.rawInsertBefore);
|
|
1183
|
-
};
|
|
1184
|
-
Node.prototype.replaceChild = function replaceChild(newChild, oldChild) {
|
|
1185
|
-
return commonElementHander(this, newChild, oldChild, globalEnv.rawReplaceChild);
|
|
1186
|
-
};
|
|
1187
|
-
Element.prototype.append = function append(...nodes) {
|
|
1188
|
-
let i = 0;
|
|
1189
|
-
const length = nodes.length;
|
|
1190
|
-
while (i < length) {
|
|
1191
|
-
commonElementHander(this, nodes[i], null, globalEnv.rawAppend);
|
|
1192
|
-
i++;
|
|
1158
|
+
function extractHtml(app) {
|
|
1159
|
+
fetchSource(app.ssrUrl || app.url, app.name, { cache: 'no-cache' }).then((htmlStr) => {
|
|
1160
|
+
if (!htmlStr) {
|
|
1161
|
+
const msg = 'html is empty, please check in detail';
|
|
1162
|
+
app.onerror(new Error(msg));
|
|
1163
|
+
return logError(msg, app.name);
|
|
1193
1164
|
}
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1165
|
+
htmlStr = htmlStr
|
|
1166
|
+
.replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
|
|
1167
|
+
return match
|
|
1168
|
+
.replace(/<head/i, '<micro-app-head')
|
|
1169
|
+
.replace(/<\/head>/i, '</micro-app-head>');
|
|
1170
|
+
})
|
|
1171
|
+
.replace(/<body[^>]*>[\s\S]*?<\/body>/i, (match) => {
|
|
1172
|
+
return match
|
|
1173
|
+
.replace(/<body/i, '<micro-app-body')
|
|
1174
|
+
.replace(/<\/body>/i, '</micro-app-body>');
|
|
1175
|
+
});
|
|
1176
|
+
extractSourceDom(htmlStr, app);
|
|
1177
|
+
}).catch((e) => {
|
|
1178
|
+
logError(`Failed to fetch data from ${app.url}, micro-app stop rendering`, app.name, e);
|
|
1179
|
+
app.onLoadError(e);
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
const boundedMap = new WeakMap();
|
|
1184
|
+
function isBoundedFunction(value) {
|
|
1185
|
+
if (boundedMap.has(value)) {
|
|
1186
|
+
return boundedMap.get(value);
|
|
1187
|
+
}
|
|
1188
|
+
// bind function
|
|
1189
|
+
const boundFunction = isBoundFunction(value);
|
|
1190
|
+
boundedMap.set(value, boundFunction);
|
|
1191
|
+
return boundFunction;
|
|
1192
|
+
}
|
|
1193
|
+
const constructorMap = new WeakMap();
|
|
1194
|
+
function isConstructor(value) {
|
|
1195
|
+
if (constructorMap.has(value)) {
|
|
1196
|
+
return constructorMap.get(value);
|
|
1197
|
+
}
|
|
1198
|
+
const valueStr = value.toString();
|
|
1199
|
+
const result = (value.prototype &&
|
|
1200
|
+
value.prototype.constructor === value &&
|
|
1201
|
+
Object.getOwnPropertyNames(value.prototype).length > 1) ||
|
|
1202
|
+
/^function\s+[A-Z]/.test(valueStr) ||
|
|
1203
|
+
/^class\s+/.test(valueStr);
|
|
1204
|
+
constructorMap.set(value, result);
|
|
1205
|
+
return result;
|
|
1206
|
+
}
|
|
1207
|
+
const rawWindowMethodMap = new WeakMap();
|
|
1208
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
1209
|
+
function bindFunctionToRawWidow(rawWindow, value) {
|
|
1210
|
+
if (rawWindowMethodMap.has(value)) {
|
|
1211
|
+
return rawWindowMethodMap.get(value);
|
|
1212
|
+
}
|
|
1213
|
+
if (isFunction(value) && !isConstructor(value) && !isBoundedFunction(value)) {
|
|
1214
|
+
const bindRawWindowValue = value.bind(rawWindow);
|
|
1215
|
+
for (const key in value) {
|
|
1216
|
+
bindRawWindowValue[key] = value[key];
|
|
1200
1217
|
}
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
Node.prototype.removeChild = function removeChild(oldChild) {
|
|
1204
|
-
if (oldChild === null || oldChild === void 0 ? void 0 : oldChild.__MICRO_APP_NAME__) {
|
|
1205
|
-
const app = appInstanceMap.get(oldChild.__MICRO_APP_NAME__);
|
|
1206
|
-
if (app === null || app === void 0 ? void 0 : app.container) {
|
|
1207
|
-
return invokePrototypeMethod(app, globalEnv.rawRemoveChild, this, getMappingNode(oldChild));
|
|
1208
|
-
}
|
|
1209
|
-
return globalEnv.rawRemoveChild.call(this, oldChild);
|
|
1218
|
+
if (value.hasOwnProperty('prototype') && !bindRawWindowValue.hasOwnProperty('prototype')) {
|
|
1219
|
+
bindRawWindowValue.prototype = value.prototype;
|
|
1210
1220
|
}
|
|
1211
|
-
|
|
1212
|
-
|
|
1221
|
+
rawWindowMethodMap.set(value, bindRawWindowValue);
|
|
1222
|
+
return bindRawWindowValue;
|
|
1223
|
+
}
|
|
1224
|
+
return value;
|
|
1213
1225
|
}
|
|
1226
|
+
|
|
1227
|
+
// document.onclick binding list, the binding function of each application is unique
|
|
1228
|
+
const documentClickListMap = new Map();
|
|
1229
|
+
let hasRewriteDocumentOnClick = false;
|
|
1214
1230
|
/**
|
|
1215
|
-
*
|
|
1216
|
-
* @param element new element
|
|
1231
|
+
* Rewrite document.onclick and execute it only once
|
|
1217
1232
|
*/
|
|
1218
|
-
function
|
|
1219
|
-
|
|
1220
|
-
if (
|
|
1221
|
-
|
|
1222
|
-
}
|
|
1223
|
-
return element;
|
|
1224
|
-
}
|
|
1225
|
-
// methods of document
|
|
1226
|
-
function patchDocument() {
|
|
1227
|
-
const rawDocument = globalEnv.rawDocument;
|
|
1228
|
-
// create element 👇
|
|
1229
|
-
Document.prototype.createElement = function createElement(tagName, options) {
|
|
1230
|
-
const element = globalEnv.rawCreateElement.call(rawDocument, tagName, options);
|
|
1231
|
-
return markElement(element);
|
|
1232
|
-
};
|
|
1233
|
-
Document.prototype.createElementNS = function createElementNS(namespaceURI, name, options) {
|
|
1234
|
-
const element = globalEnv.rawCreateElementNS.call(rawDocument, namespaceURI, name, options);
|
|
1235
|
-
return markElement(element);
|
|
1236
|
-
};
|
|
1237
|
-
Document.prototype.createDocumentFragment = function createDocumentFragment() {
|
|
1238
|
-
const element = globalEnv.rawCreateDocumentFragment.call(rawDocument);
|
|
1239
|
-
return markElement(element);
|
|
1240
|
-
};
|
|
1241
|
-
// query element👇
|
|
1242
|
-
function querySelector(selectors) {
|
|
1243
|
-
var _a, _b, _c;
|
|
1244
|
-
const appName = getCurrentAppName();
|
|
1245
|
-
if (!appName || selectors === 'head' || selectors === 'body' || selectors === 'html') {
|
|
1246
|
-
return globalEnv.rawQuerySelector.call(rawDocument, selectors);
|
|
1247
|
-
}
|
|
1248
|
-
return (_c = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) === null || _b === void 0 ? void 0 : _b.querySelector(selectors)) !== null && _c !== void 0 ? _c : null;
|
|
1233
|
+
function overwriteDocumentOnClick() {
|
|
1234
|
+
hasRewriteDocumentOnClick = true;
|
|
1235
|
+
if (Object.getOwnPropertyDescriptor(document, 'onclick')) {
|
|
1236
|
+
return logWarn('Cannot redefine document property onclick');
|
|
1249
1237
|
}
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1238
|
+
const rawOnClick = document.onclick;
|
|
1239
|
+
document.onclick = null;
|
|
1240
|
+
let hasDocumentClickInited = false;
|
|
1241
|
+
function onClickHandler(e) {
|
|
1242
|
+
documentClickListMap.forEach((f) => {
|
|
1243
|
+
isFunction(f) && f.call(document, e);
|
|
1244
|
+
});
|
|
1257
1245
|
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1246
|
+
Object.defineProperty(document, 'onclick', {
|
|
1247
|
+
configurable: true,
|
|
1248
|
+
enumerable: true,
|
|
1249
|
+
get() {
|
|
1250
|
+
const appName = getCurrentAppName();
|
|
1251
|
+
return appName ? documentClickListMap.get(appName) : documentClickListMap.get('base');
|
|
1252
|
+
},
|
|
1253
|
+
set(f) {
|
|
1254
|
+
const appName = getCurrentAppName();
|
|
1255
|
+
if (appName) {
|
|
1256
|
+
documentClickListMap.set(appName, f);
|
|
1257
|
+
}
|
|
1258
|
+
else {
|
|
1259
|
+
documentClickListMap.set('base', f);
|
|
1260
|
+
}
|
|
1261
|
+
if (!hasDocumentClickInited && isFunction(f)) {
|
|
1262
|
+
hasDocumentClickInited = true;
|
|
1263
|
+
globalEnv.rawDocumentAddEventListener.call(globalEnv.rawDocument, 'click', onClickHandler, false);
|
|
1264
|
+
}
|
|
1272
1265
|
}
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1266
|
+
});
|
|
1267
|
+
rawOnClick && (document.onclick = rawOnClick);
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* The document event is globally, we need to clear these event bindings when micro application unmounted
|
|
1271
|
+
*/
|
|
1272
|
+
const documentEventListenerMap = new Map();
|
|
1273
|
+
function effectDocumentEvent() {
|
|
1274
|
+
const { rawDocument, rawDocumentAddEventListener, rawDocumentRemoveEventListener, } = globalEnv;
|
|
1275
|
+
!hasRewriteDocumentOnClick && overwriteDocumentOnClick();
|
|
1276
|
+
document.addEventListener = function (type, listener, options) {
|
|
1276
1277
|
var _a;
|
|
1277
1278
|
const appName = getCurrentAppName();
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1279
|
+
/**
|
|
1280
|
+
* ignore bound function of document event in umd mode, used to solve problem of react global events
|
|
1281
|
+
*/
|
|
1282
|
+
if (appName && !(((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.umdMode) && isBoundFunction(listener))) {
|
|
1283
|
+
const appListenersMap = documentEventListenerMap.get(appName);
|
|
1284
|
+
if (appListenersMap) {
|
|
1285
|
+
const appListenerList = appListenersMap.get(type);
|
|
1286
|
+
if (appListenerList) {
|
|
1287
|
+
appListenerList.add(listener);
|
|
1288
|
+
}
|
|
1289
|
+
else {
|
|
1290
|
+
appListenersMap.set(type, new Set([listener]));
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
else {
|
|
1294
|
+
documentEventListenerMap.set(appName, new Map([[type, new Set([listener])]]));
|
|
1295
|
+
}
|
|
1296
|
+
listener && (listener.__MICRO_MARK_OPTIONS__ = options);
|
|
1284
1297
|
}
|
|
1285
|
-
|
|
1298
|
+
rawDocumentAddEventListener.call(rawDocument, type, listener, options);
|
|
1286
1299
|
};
|
|
1287
|
-
|
|
1300
|
+
document.removeEventListener = function (type, listener, options) {
|
|
1301
|
+
var _a;
|
|
1288
1302
|
const appName = getCurrentAppName();
|
|
1289
|
-
if (!appName ||
|
|
1290
|
-
|
|
1303
|
+
if (appName && !(((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.umdMode) && isBoundFunction(listener))) {
|
|
1304
|
+
const appListenersMap = documentEventListenerMap.get(appName);
|
|
1305
|
+
if (appListenersMap) {
|
|
1306
|
+
const appListenerList = appListenersMap.get(type);
|
|
1307
|
+
if ((appListenerList === null || appListenerList === void 0 ? void 0 : appListenerList.size) && appListenerList.has(listener)) {
|
|
1308
|
+
appListenerList.delete(listener);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1291
1311
|
}
|
|
1292
|
-
|
|
1312
|
+
rawDocumentRemoveEventListener.call(rawDocument, type, listener, options);
|
|
1293
1313
|
};
|
|
1294
1314
|
}
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
Document.prototype.querySelector = globalEnv.rawQuerySelector;
|
|
1300
|
-
Document.prototype.querySelectorAll = globalEnv.rawQuerySelectorAll;
|
|
1301
|
-
Document.prototype.getElementById = globalEnv.rawGetElementById;
|
|
1302
|
-
Document.prototype.getElementsByClassName = globalEnv.rawGetElementsByClassName;
|
|
1303
|
-
Document.prototype.getElementsByTagName = globalEnv.rawGetElementsByTagName;
|
|
1304
|
-
Document.prototype.getElementsByName = globalEnv.rawGetElementsByName;
|
|
1305
|
-
}
|
|
1306
|
-
// release patch
|
|
1307
|
-
function releasePatches() {
|
|
1308
|
-
setCurrentAppName(null);
|
|
1309
|
-
releasePatchDocument();
|
|
1310
|
-
Element.prototype.setAttribute = globalEnv.rawSetAttribute;
|
|
1311
|
-
Node.prototype.appendChild = globalEnv.rawAppendChild;
|
|
1312
|
-
Node.prototype.insertBefore = globalEnv.rawInsertBefore;
|
|
1313
|
-
Node.prototype.replaceChild = globalEnv.rawReplaceChild;
|
|
1314
|
-
Node.prototype.removeChild = globalEnv.rawRemoveChild;
|
|
1315
|
-
Element.prototype.append = globalEnv.rawAppend;
|
|
1316
|
-
Element.prototype.prepend = globalEnv.rawPrepend;
|
|
1317
|
-
}
|
|
1318
|
-
// Set the style of micro-app-head and micro-app-body
|
|
1319
|
-
let hasRejectMicroAppStyle = false;
|
|
1320
|
-
function rejectMicroAppStyle() {
|
|
1321
|
-
if (!hasRejectMicroAppStyle) {
|
|
1322
|
-
hasRejectMicroAppStyle = true;
|
|
1323
|
-
const style = pureCreateElement('style');
|
|
1324
|
-
style.setAttribute('type', 'text/css');
|
|
1325
|
-
style.textContent = `\n${microApp.tagName}, micro-app-body { display: block; } \nmicro-app-head { display: none; }`;
|
|
1326
|
-
globalEnv.rawDocument.head.appendChild(style);
|
|
1327
|
-
}
|
|
1328
|
-
}
|
|
1329
|
-
|
|
1330
|
-
function unmountNestedApp() {
|
|
1331
|
-
replaseUnmountOfNestedApp();
|
|
1332
|
-
appInstanceMap.forEach(app => {
|
|
1333
|
-
let element = app.container;
|
|
1334
|
-
if (element) {
|
|
1335
|
-
if (element instanceof ShadowRoot) {
|
|
1336
|
-
element = element.host;
|
|
1337
|
-
}
|
|
1338
|
-
// @ts-ignore
|
|
1339
|
-
element.disconnectedCallback();
|
|
1340
|
-
}
|
|
1341
|
-
});
|
|
1342
|
-
if (!window.__MICRO_APP_UMD_MODE__)
|
|
1343
|
-
appInstanceMap.clear();
|
|
1344
|
-
if (elementInstanceMap.size) {
|
|
1345
|
-
elementInstanceMap.clear();
|
|
1346
|
-
releasePatches();
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
// if micro-app run in micro application, delete all next generation application when unmount event received
|
|
1350
|
-
function listenUmountOfNestedApp() {
|
|
1351
|
-
if (window.__MICRO_APP_ENVIRONMENT__) {
|
|
1352
|
-
window.addEventListener('unmount', unmountNestedApp, false);
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
|
-
// release listener
|
|
1356
|
-
function replaseUnmountOfNestedApp() {
|
|
1357
|
-
if (window.__MICRO_APP_ENVIRONMENT__) {
|
|
1358
|
-
window.removeEventListener('unmount', unmountNestedApp, false);
|
|
1359
|
-
}
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
function eventHandler$1(event, element) {
|
|
1363
|
-
Object.defineProperties(event, {
|
|
1364
|
-
currentTarget: {
|
|
1365
|
-
get() {
|
|
1366
|
-
return element;
|
|
1367
|
-
}
|
|
1368
|
-
},
|
|
1369
|
-
target: {
|
|
1370
|
-
get() {
|
|
1371
|
-
return element;
|
|
1372
|
-
}
|
|
1373
|
-
},
|
|
1374
|
-
});
|
|
1315
|
+
// Clear the document event agent
|
|
1316
|
+
function releaseEffectDocumentEvent() {
|
|
1317
|
+
document.addEventListener = globalEnv.rawDocumentAddEventListener;
|
|
1318
|
+
document.removeEventListener = globalEnv.rawDocumentRemoveEventListener;
|
|
1375
1319
|
}
|
|
1376
1320
|
/**
|
|
1377
|
-
*
|
|
1378
|
-
* @param
|
|
1379
|
-
* @param
|
|
1380
|
-
* @param lifecycleName lifeCycle name
|
|
1381
|
-
* @param error param from error hook
|
|
1321
|
+
* Format event name
|
|
1322
|
+
* @param type event name
|
|
1323
|
+
* @param microWindow micro window
|
|
1382
1324
|
*/
|
|
1383
|
-
function
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
return logError(`element does not exist in lifecycle ${lifecycleName},it seems the app has unmounted`, appName);
|
|
1387
|
-
}
|
|
1388
|
-
else if (element instanceof ShadowRoot) {
|
|
1389
|
-
element = element.host;
|
|
1390
|
-
}
|
|
1391
|
-
const detail = Object.assign({
|
|
1392
|
-
name: appName,
|
|
1393
|
-
container: element,
|
|
1394
|
-
}, error && {
|
|
1395
|
-
error
|
|
1396
|
-
});
|
|
1397
|
-
const event = new CustomEvent(lifecycleName, {
|
|
1398
|
-
detail,
|
|
1399
|
-
});
|
|
1400
|
-
eventHandler$1(event, element);
|
|
1401
|
-
// global hooks
|
|
1402
|
-
// @ts-ignore
|
|
1403
|
-
if (typeof ((_a = microApp.lifeCycles) === null || _a === void 0 ? void 0 : _a[lifecycleName]) === 'function') {
|
|
1404
|
-
// @ts-ignore
|
|
1405
|
-
microApp.lifeCycles[lifecycleName](event);
|
|
1325
|
+
function formatEventType(type, microWindow) {
|
|
1326
|
+
if (type === 'unmount') {
|
|
1327
|
+
return `unmount-${microWindow.__MICRO_APP_NAME__}`;
|
|
1406
1328
|
}
|
|
1407
|
-
|
|
1408
|
-
}
|
|
1409
|
-
/**
|
|
1410
|
-
* Dispatch unmount event to micro app
|
|
1411
|
-
* @param appName app.name
|
|
1412
|
-
*/
|
|
1413
|
-
function dispatchUnmountToMicroApp(appName) {
|
|
1414
|
-
const event = new CustomEvent(`unmount-${appName}`);
|
|
1415
|
-
window.dispatchEvent(event);
|
|
1329
|
+
return type;
|
|
1416
1330
|
}
|
|
1417
|
-
|
|
1418
|
-
// record all micro-app elements
|
|
1419
|
-
const elementInstanceMap = new Map();
|
|
1420
1331
|
/**
|
|
1421
|
-
*
|
|
1422
|
-
* @param
|
|
1332
|
+
* Rewrite side-effect events
|
|
1333
|
+
* @param microWindow micro window
|
|
1423
1334
|
*/
|
|
1424
|
-
function
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
*/
|
|
1437
|
-
this.handleAttributeUpdate = () => {
|
|
1438
|
-
var _a;
|
|
1439
|
-
this.isWating = false;
|
|
1440
|
-
const attrName = this.getAttribute('name');
|
|
1441
|
-
const attrUrl = formatURL(this.getAttribute('url'), this.appName);
|
|
1442
|
-
if (this.legalAttribute('name', attrName) && this.legalAttribute('url', attrUrl)) {
|
|
1443
|
-
const existApp = appInstanceMap.get(attrName);
|
|
1444
|
-
if (attrName !== this.appName && existApp) {
|
|
1445
|
-
// handling of cached and non-prefetch apps
|
|
1446
|
-
if (appStatus.UNMOUNT !== existApp.getAppStatus() && !existApp.isPrefetch) {
|
|
1447
|
-
this.setAttribute('name', this.appName);
|
|
1448
|
-
return logError(`an app named ${attrName} already exists`, this.appName);
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1451
|
-
if (attrName !== this.appName || attrUrl !== this.appUrl) {
|
|
1452
|
-
this.handleUnmount(attrName === this.appName);
|
|
1453
|
-
this.appName = attrName;
|
|
1454
|
-
this.appUrl = attrUrl;
|
|
1455
|
-
((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this).innerHTML = '';
|
|
1456
|
-
/**
|
|
1457
|
-
* when existApp not undefined
|
|
1458
|
-
* if attrName and this.appName are equal, existApp has been unmounted
|
|
1459
|
-
* if attrName and this.appName are not equal, existApp is prefetch or unmounted
|
|
1460
|
-
*/
|
|
1461
|
-
if (existApp && existApp.url === attrUrl) {
|
|
1462
|
-
// mount app
|
|
1463
|
-
this.handleAppMount(existApp);
|
|
1464
|
-
}
|
|
1465
|
-
else {
|
|
1466
|
-
this.handleCreate();
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
}
|
|
1470
|
-
else if (attrName !== this.appName) {
|
|
1471
|
-
this.setAttribute('name', this.appName);
|
|
1472
|
-
}
|
|
1473
|
-
};
|
|
1474
|
-
// cloned node of umd container also trigger constructor, we should skip
|
|
1475
|
-
if (!this.querySelector('micro-app-head')) {
|
|
1476
|
-
this.performWhenFirstCreated();
|
|
1477
|
-
}
|
|
1335
|
+
function effect(microWindow) {
|
|
1336
|
+
const appName = microWindow.__MICRO_APP_NAME__;
|
|
1337
|
+
const eventListenerMap = new Map();
|
|
1338
|
+
const intervalIdMap = new Map();
|
|
1339
|
+
const timeoutIdMap = new Map();
|
|
1340
|
+
const { rawWindow, rawDocument, rawWindowAddEventListener, rawWindowRemoveEventListener, rawSetInterval, rawSetTimeout, rawClearInterval, rawClearTimeout, rawDocumentRemoveEventListener, } = globalEnv;
|
|
1341
|
+
// listener may be null, e.g test-passive
|
|
1342
|
+
microWindow.addEventListener = function (type, listener, options) {
|
|
1343
|
+
type = formatEventType(type, microWindow);
|
|
1344
|
+
const listenerList = eventListenerMap.get(type);
|
|
1345
|
+
if (listenerList) {
|
|
1346
|
+
listenerList.add(listener);
|
|
1478
1347
|
}
|
|
1479
|
-
|
|
1480
|
-
|
|
1348
|
+
else {
|
|
1349
|
+
eventListenerMap.set(type, new Set([listener]));
|
|
1481
1350
|
}
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
connectedCallback() {
|
|
1491
|
-
this.hasConnected = true;
|
|
1492
|
-
if (!elementInstanceMap.has(this)) {
|
|
1493
|
-
this.performWhenFirstCreated();
|
|
1494
|
-
}
|
|
1495
|
-
defer(() => dispatchLifecyclesEvent(this, this.appName, lifeCycles.CREATED));
|
|
1496
|
-
this.initialMount();
|
|
1351
|
+
listener && (listener.__MICRO_MARK_OPTIONS__ = options);
|
|
1352
|
+
rawWindowAddEventListener.call(rawWindow, type, listener, options);
|
|
1353
|
+
};
|
|
1354
|
+
microWindow.removeEventListener = function (type, listener, options) {
|
|
1355
|
+
type = formatEventType(type, microWindow);
|
|
1356
|
+
const listenerList = eventListenerMap.get(type);
|
|
1357
|
+
if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
|
|
1358
|
+
listenerList.delete(listener);
|
|
1497
1359
|
}
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1360
|
+
rawWindowRemoveEventListener.call(rawWindow, type, listener, options);
|
|
1361
|
+
};
|
|
1362
|
+
microWindow.setInterval = function (handler, timeout, ...args) {
|
|
1363
|
+
const intervalId = rawSetInterval.call(rawWindow, handler, timeout, ...args);
|
|
1364
|
+
intervalIdMap.set(intervalId, { handler, timeout, args });
|
|
1365
|
+
return intervalId;
|
|
1366
|
+
};
|
|
1367
|
+
microWindow.setTimeout = function (handler, timeout, ...args) {
|
|
1368
|
+
const timeoutId = rawSetTimeout.call(rawWindow, handler, timeout, ...args);
|
|
1369
|
+
timeoutIdMap.set(timeoutId, { handler, timeout, args });
|
|
1370
|
+
return timeoutId;
|
|
1371
|
+
};
|
|
1372
|
+
microWindow.clearInterval = function (intervalId) {
|
|
1373
|
+
intervalIdMap.delete(intervalId);
|
|
1374
|
+
rawClearInterval.call(rawWindow, intervalId);
|
|
1375
|
+
};
|
|
1376
|
+
microWindow.clearTimeout = function (timeoutId) {
|
|
1377
|
+
timeoutIdMap.delete(timeoutId);
|
|
1378
|
+
rawClearTimeout.call(rawWindow, timeoutId);
|
|
1379
|
+
};
|
|
1380
|
+
const umdWindowListenerMap = new Map();
|
|
1381
|
+
const umdDocumentListenerMap = new Map();
|
|
1382
|
+
let umdIntervalIdMap = new Map();
|
|
1383
|
+
let umdTimeoutIdMap = new Map();
|
|
1384
|
+
let umdOnClickHandler;
|
|
1385
|
+
// record event and timer before exec umdMountHook
|
|
1386
|
+
const recordUmdEffect = () => {
|
|
1387
|
+
// record window event
|
|
1388
|
+
eventListenerMap.forEach((listenerList, type) => {
|
|
1389
|
+
if (listenerList.size) {
|
|
1390
|
+
umdWindowListenerMap.set(type, new Set(listenerList));
|
|
1504
1391
|
}
|
|
1392
|
+
});
|
|
1393
|
+
// record timers
|
|
1394
|
+
if (intervalIdMap.size) {
|
|
1395
|
+
umdIntervalIdMap = new Map(intervalIdMap);
|
|
1505
1396
|
}
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
else if (attr === ObservedAttrName.NAME && !this.appName) {
|
|
1518
|
-
if (this.cacheData) {
|
|
1519
|
-
microApp.setData(newVal, this.cacheData);
|
|
1520
|
-
this.cacheData = null;
|
|
1521
|
-
}
|
|
1522
|
-
this.appName = newVal;
|
|
1523
|
-
this.handleInitialNameAndUrl();
|
|
1524
|
-
}
|
|
1525
|
-
else if (!this.isWating) {
|
|
1526
|
-
this.isWating = true;
|
|
1527
|
-
defer(this.handleAttributeUpdate);
|
|
1397
|
+
if (timeoutIdMap.size) {
|
|
1398
|
+
umdTimeoutIdMap = new Map(timeoutIdMap);
|
|
1399
|
+
}
|
|
1400
|
+
// record onclick handler
|
|
1401
|
+
umdOnClickHandler = documentClickListMap.get(appName);
|
|
1402
|
+
// record document event
|
|
1403
|
+
const documentAppListenersMap = documentEventListenerMap.get(appName);
|
|
1404
|
+
if (documentAppListenersMap) {
|
|
1405
|
+
documentAppListenersMap.forEach((listenerList, type) => {
|
|
1406
|
+
if (listenerList.size) {
|
|
1407
|
+
umdDocumentListenerMap.set(type, new Set(listenerList));
|
|
1528
1408
|
}
|
|
1529
|
-
}
|
|
1409
|
+
});
|
|
1530
1410
|
}
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1411
|
+
};
|
|
1412
|
+
// rebuild event and timer before remount umd app
|
|
1413
|
+
const rebuildUmdEffect = () => {
|
|
1414
|
+
// rebuild window event
|
|
1415
|
+
umdWindowListenerMap.forEach((listenerList, type) => {
|
|
1416
|
+
for (const listener of listenerList) {
|
|
1417
|
+
microWindow.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_MARK_OPTIONS__);
|
|
1535
1418
|
}
|
|
1536
|
-
}
|
|
1537
|
-
//
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
return;
|
|
1552
|
-
if (this.getDisposeResult('shadowDOM') && !this.shadowRoot) {
|
|
1553
|
-
this.attachShadow({ mode: 'open' });
|
|
1419
|
+
});
|
|
1420
|
+
// rebuild timer
|
|
1421
|
+
umdIntervalIdMap.forEach((info) => {
|
|
1422
|
+
microWindow.setInterval(info.handler, info.timeout, ...info.args);
|
|
1423
|
+
});
|
|
1424
|
+
umdTimeoutIdMap.forEach((info) => {
|
|
1425
|
+
microWindow.setTimeout(info.handler, info.timeout, ...info.args);
|
|
1426
|
+
});
|
|
1427
|
+
// rebuild onclick event
|
|
1428
|
+
umdOnClickHandler && documentClickListMap.set(appName, umdOnClickHandler);
|
|
1429
|
+
// rebuild document event
|
|
1430
|
+
setCurrentAppName(appName);
|
|
1431
|
+
umdDocumentListenerMap.forEach((listenerList, type) => {
|
|
1432
|
+
for (const listener of listenerList) {
|
|
1433
|
+
document.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_MARK_OPTIONS__);
|
|
1554
1434
|
}
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
logError(`an app named ${this.appName} already exists`, this.appName);
|
|
1435
|
+
});
|
|
1436
|
+
setCurrentAppName(null);
|
|
1437
|
+
};
|
|
1438
|
+
// release all event listener & interval & timeout when unmount app
|
|
1439
|
+
const releaseEffect = () => {
|
|
1440
|
+
// Clear window binding events
|
|
1441
|
+
if (eventListenerMap.size) {
|
|
1442
|
+
eventListenerMap.forEach((listenerList, type) => {
|
|
1443
|
+
for (const listener of listenerList) {
|
|
1444
|
+
rawWindowRemoveEventListener.call(rawWindow, type, listener);
|
|
1566
1445
|
}
|
|
1567
|
-
}
|
|
1568
|
-
else {
|
|
1569
|
-
this.handleCreate();
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
1572
|
-
/**
|
|
1573
|
-
* judge the attribute is legal
|
|
1574
|
-
* @param name attribute name
|
|
1575
|
-
* @param val attribute value
|
|
1576
|
-
*/
|
|
1577
|
-
legalAttribute(name, val) {
|
|
1578
|
-
if (typeof val !== 'string' || !val) {
|
|
1579
|
-
logError(`unexpected attribute ${name}, please check again`, this.appName);
|
|
1580
|
-
return false;
|
|
1581
|
-
}
|
|
1582
|
-
return true;
|
|
1583
|
-
}
|
|
1584
|
-
/**
|
|
1585
|
-
* mount app
|
|
1586
|
-
* some serious note before mount:
|
|
1587
|
-
* 1. is prefetch ?
|
|
1588
|
-
* 2. is remount in another container ?
|
|
1589
|
-
* 3. is remount with change properties of the container ?
|
|
1590
|
-
*/
|
|
1591
|
-
handleAppMount(app) {
|
|
1592
|
-
app.isPrefetch = false;
|
|
1593
|
-
defer(() => {
|
|
1594
|
-
var _a;
|
|
1595
|
-
return app.mount((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this, this.getDisposeResult('inline'), this.getBaseRouteCompatible());
|
|
1596
1446
|
});
|
|
1447
|
+
eventListenerMap.clear();
|
|
1597
1448
|
}
|
|
1598
|
-
//
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
name: this.appName,
|
|
1603
|
-
url: this.appUrl,
|
|
1604
|
-
container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
|
|
1605
|
-
inline: this.getDisposeResult('inline'),
|
|
1606
|
-
scopecss: !(this.getDisposeResult('disableScopecss') || this.getDisposeResult('shadowDOM')),
|
|
1607
|
-
useSandbox: !this.getDisposeResult('disableSandbox'),
|
|
1608
|
-
macro: this.getDisposeResult('macro'),
|
|
1609
|
-
baseroute: this.getBaseRouteCompatible(),
|
|
1449
|
+
// Clear timers
|
|
1450
|
+
if (intervalIdMap.size) {
|
|
1451
|
+
intervalIdMap.forEach((_, intervalId) => {
|
|
1452
|
+
rawClearInterval.call(rawWindow, intervalId);
|
|
1610
1453
|
});
|
|
1611
|
-
|
|
1612
|
-
}
|
|
1613
|
-
/**
|
|
1614
|
-
* unmount app
|
|
1615
|
-
* @param destory delete cache resources when unmount
|
|
1616
|
-
*/
|
|
1617
|
-
handleUnmount(destory) {
|
|
1618
|
-
const app = appInstanceMap.get(this.appName);
|
|
1619
|
-
if (app && appStatus.UNMOUNT !== app.getAppStatus())
|
|
1620
|
-
app.unmount(destory);
|
|
1621
|
-
}
|
|
1622
|
-
/**
|
|
1623
|
-
* Get configuration
|
|
1624
|
-
* Global setting is lowest priority
|
|
1625
|
-
* @param name Configuration item name
|
|
1626
|
-
*/
|
|
1627
|
-
getDisposeResult(name) {
|
|
1628
|
-
// @ts-ignore
|
|
1629
|
-
return (this.hasAttribute(name) || microApp[name]) && this.getAttribute(name) !== 'false';
|
|
1630
|
-
}
|
|
1631
|
-
/**
|
|
1632
|
-
* 2021-09-08
|
|
1633
|
-
* get baseRoute
|
|
1634
|
-
* getAttribute('baseurl') is compatible writing of versions below 0.3.1
|
|
1635
|
-
*/
|
|
1636
|
-
getBaseRouteCompatible() {
|
|
1637
|
-
var _a, _b;
|
|
1638
|
-
return (_b = (_a = this.getAttribute('baseroute')) !== null && _a !== void 0 ? _a : this.getAttribute('baseurl')) !== null && _b !== void 0 ? _b : '';
|
|
1454
|
+
intervalIdMap.clear();
|
|
1639
1455
|
}
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
microApp.setData(this.appName, value);
|
|
1646
|
-
}
|
|
1647
|
-
else {
|
|
1648
|
-
this.cacheData = value;
|
|
1649
|
-
}
|
|
1456
|
+
if (timeoutIdMap.size) {
|
|
1457
|
+
timeoutIdMap.forEach((_, timeoutId) => {
|
|
1458
|
+
rawClearTimeout.call(rawWindow, timeoutId);
|
|
1459
|
+
});
|
|
1460
|
+
timeoutIdMap.clear();
|
|
1650
1461
|
}
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
get
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
}
|
|
1661
|
-
|
|
1462
|
+
// Clear the function bound by micro application through document.onclick
|
|
1463
|
+
documentClickListMap.delete(appName);
|
|
1464
|
+
// Clear document binding event
|
|
1465
|
+
const documentAppListenersMap = documentEventListenerMap.get(appName);
|
|
1466
|
+
if (documentAppListenersMap) {
|
|
1467
|
+
documentAppListenersMap.forEach((listenerList, type) => {
|
|
1468
|
+
for (const listener of listenerList) {
|
|
1469
|
+
rawDocumentRemoveEventListener.call(rawDocument, type, listener);
|
|
1470
|
+
}
|
|
1471
|
+
});
|
|
1472
|
+
documentAppListenersMap.clear();
|
|
1662
1473
|
}
|
|
1663
|
-
}
|
|
1664
|
-
|
|
1474
|
+
};
|
|
1475
|
+
return {
|
|
1476
|
+
recordUmdEffect,
|
|
1477
|
+
rebuildUmdEffect,
|
|
1478
|
+
releaseEffect,
|
|
1479
|
+
};
|
|
1665
1480
|
}
|
|
1666
1481
|
|
|
1667
1482
|
class EventCenter {
|
|
@@ -1684,7 +1499,7 @@ class EventCenter {
|
|
|
1684
1499
|
*/
|
|
1685
1500
|
on(name, f, autoTrigger = false) {
|
|
1686
1501
|
if (this.isLegalName(name)) {
|
|
1687
|
-
if (
|
|
1502
|
+
if (!isFunction(f)) {
|
|
1688
1503
|
return logError('event-center: Invalid callback function');
|
|
1689
1504
|
}
|
|
1690
1505
|
let eventInfo = this.eventList.get(name);
|
|
@@ -1707,7 +1522,7 @@ class EventCenter {
|
|
|
1707
1522
|
if (this.isLegalName(name)) {
|
|
1708
1523
|
const eventInfo = this.eventList.get(name);
|
|
1709
1524
|
if (eventInfo) {
|
|
1710
|
-
if (
|
|
1525
|
+
if (isFunction(f)) {
|
|
1711
1526
|
eventInfo.callbacks.delete(f);
|
|
1712
1527
|
}
|
|
1713
1528
|
else {
|
|
@@ -1719,7 +1534,7 @@ class EventCenter {
|
|
|
1719
1534
|
// dispatch data
|
|
1720
1535
|
dispatch(name, data) {
|
|
1721
1536
|
if (this.isLegalName(name)) {
|
|
1722
|
-
if (
|
|
1537
|
+
if (!isPlainObject(data)) {
|
|
1723
1538
|
return logError('event-center: data must be object');
|
|
1724
1539
|
}
|
|
1725
1540
|
let eventInfo = this.eventList.get(name);
|
|
@@ -1756,7 +1571,7 @@ const eventCenter = new EventCenter();
|
|
|
1756
1571
|
* @param fromBaseApp is from base app
|
|
1757
1572
|
*/
|
|
1758
1573
|
function formatEventName(appName, fromBaseApp) {
|
|
1759
|
-
if (
|
|
1574
|
+
if (!isString(appName) || !appName)
|
|
1760
1575
|
return '';
|
|
1761
1576
|
return fromBaseApp ? `__from_base_app_${appName}__` : `__from_micro_app_${appName}__`;
|
|
1762
1577
|
}
|
|
@@ -1781,17 +1596,23 @@ class EventCenterForGlobal {
|
|
|
1781
1596
|
* @param cb listener
|
|
1782
1597
|
*/
|
|
1783
1598
|
removeGlobalDataListener(cb) {
|
|
1784
|
-
|
|
1785
|
-
eventCenter.off('global', cb);
|
|
1786
|
-
}
|
|
1599
|
+
isFunction(cb) && eventCenter.off('global', cb);
|
|
1787
1600
|
}
|
|
1788
1601
|
/**
|
|
1789
1602
|
* dispatch global data
|
|
1790
1603
|
* @param data data
|
|
1791
1604
|
*/
|
|
1792
1605
|
setGlobalData(data) {
|
|
1606
|
+
// clear dom scope before dispatch global data, apply to micro app
|
|
1607
|
+
removeDomScope();
|
|
1793
1608
|
eventCenter.dispatch('global', data);
|
|
1794
1609
|
}
|
|
1610
|
+
/**
|
|
1611
|
+
* get global data
|
|
1612
|
+
*/
|
|
1613
|
+
getGlobalData() {
|
|
1614
|
+
return eventCenter.getData('global');
|
|
1615
|
+
}
|
|
1795
1616
|
/**
|
|
1796
1617
|
* clear all listener of global data
|
|
1797
1618
|
* if appName exists, only the specified functions is cleared
|
|
@@ -1819,7 +1640,7 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
|
|
|
1819
1640
|
* @param autoTrigger If there is cached data when first bind listener, whether it needs to trigger, default is false
|
|
1820
1641
|
*/
|
|
1821
1642
|
addDataListener(appName, cb, autoTrigger) {
|
|
1822
|
-
eventCenter.on(formatEventName(appName, false), cb, autoTrigger);
|
|
1643
|
+
eventCenter.on(formatEventName(formatAppName(appName), false), cb, autoTrigger);
|
|
1823
1644
|
}
|
|
1824
1645
|
/**
|
|
1825
1646
|
* remove listener
|
|
@@ -1827,9 +1648,7 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
|
|
|
1827
1648
|
* @param cb listener
|
|
1828
1649
|
*/
|
|
1829
1650
|
removeDataListener(appName, cb) {
|
|
1830
|
-
|
|
1831
|
-
eventCenter.off(formatEventName(appName, false), cb);
|
|
1832
|
-
}
|
|
1651
|
+
isFunction(cb) && eventCenter.off(formatEventName(formatAppName(appName), false), cb);
|
|
1833
1652
|
}
|
|
1834
1653
|
/**
|
|
1835
1654
|
* get data from micro app or base app
|
|
@@ -1837,7 +1656,7 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
|
|
|
1837
1656
|
* @param fromBaseApp whether get data from base app, default is false
|
|
1838
1657
|
*/
|
|
1839
1658
|
getData(appName, fromBaseApp = false) {
|
|
1840
|
-
return eventCenter.getData(formatEventName(appName, fromBaseApp));
|
|
1659
|
+
return eventCenter.getData(formatEventName(formatAppName(appName), fromBaseApp));
|
|
1841
1660
|
}
|
|
1842
1661
|
/**
|
|
1843
1662
|
* Dispatch data to the specified micro app
|
|
@@ -1845,21 +1664,22 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
|
|
|
1845
1664
|
* @param data data
|
|
1846
1665
|
*/
|
|
1847
1666
|
setData(appName, data) {
|
|
1848
|
-
eventCenter.dispatch(formatEventName(appName, true), data);
|
|
1667
|
+
eventCenter.dispatch(formatEventName(formatAppName(appName), true), data);
|
|
1849
1668
|
}
|
|
1850
1669
|
/**
|
|
1851
1670
|
* clear all listener for specified micro app
|
|
1852
1671
|
* @param appName app.name
|
|
1853
1672
|
*/
|
|
1854
1673
|
clearDataListener(appName) {
|
|
1855
|
-
eventCenter.off(formatEventName(appName, false));
|
|
1674
|
+
eventCenter.off(formatEventName(formatAppName(appName), false));
|
|
1856
1675
|
}
|
|
1857
1676
|
}
|
|
1858
1677
|
// Event center for sub app
|
|
1859
1678
|
class EventCenterForMicroApp extends EventCenterForGlobal {
|
|
1860
1679
|
constructor(appName) {
|
|
1861
1680
|
super();
|
|
1862
|
-
this.appName = appName;
|
|
1681
|
+
this.appName = formatAppName(appName);
|
|
1682
|
+
!this.appName && logError(`Invalid appName ${appName}`);
|
|
1863
1683
|
}
|
|
1864
1684
|
/**
|
|
1865
1685
|
* add listener, monitor the data sent by the base app
|
|
@@ -1875,9 +1695,7 @@ class EventCenterForMicroApp extends EventCenterForGlobal {
|
|
|
1875
1695
|
* @param cb listener
|
|
1876
1696
|
*/
|
|
1877
1697
|
removeDataListener(cb) {
|
|
1878
|
-
|
|
1879
|
-
eventCenter.off(formatEventName(this.appName, true), cb);
|
|
1880
|
-
}
|
|
1698
|
+
isFunction(cb) && eventCenter.off(formatEventName(this.appName, true), cb);
|
|
1881
1699
|
}
|
|
1882
1700
|
/**
|
|
1883
1701
|
* get data from base app
|
|
@@ -1893,17 +1711,13 @@ class EventCenterForMicroApp extends EventCenterForGlobal {
|
|
|
1893
1711
|
removeDomScope();
|
|
1894
1712
|
eventCenter.dispatch(formatEventName(this.appName, false), data);
|
|
1895
1713
|
const app = appInstanceMap.get(this.appName);
|
|
1896
|
-
if ((app === null || app === void 0 ? void 0 : app.container) &&
|
|
1714
|
+
if ((app === null || app === void 0 ? void 0 : app.container) && isPlainObject(data)) {
|
|
1897
1715
|
const event = new CustomEvent('datachange', {
|
|
1898
1716
|
detail: {
|
|
1899
1717
|
data,
|
|
1900
1718
|
}
|
|
1901
1719
|
});
|
|
1902
|
-
|
|
1903
|
-
if (element instanceof ShadowRoot) {
|
|
1904
|
-
element = element.host;
|
|
1905
|
-
}
|
|
1906
|
-
element.dispatchEvent(event);
|
|
1720
|
+
getRootContainer(app.container).dispatchEvent(event);
|
|
1907
1721
|
}
|
|
1908
1722
|
}
|
|
1909
1723
|
/**
|
|
@@ -1946,925 +1760,1268 @@ function rebuildDataCenterSnapshot(microAppEventCneter) {
|
|
|
1946
1760
|
}
|
|
1947
1761
|
}
|
|
1948
1762
|
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
if (options && toString.call(options) === '[object Object]') {
|
|
1972
|
-
this.shadowDOM = options.shadowDOM;
|
|
1973
|
-
this.destory = options.destory;
|
|
1974
|
-
this.inline = options.inline;
|
|
1975
|
-
this.disableScopecss = options.disableScopecss;
|
|
1976
|
-
this.disableSandbox = options.disableSandbox;
|
|
1977
|
-
this.macro = options.macro;
|
|
1978
|
-
if (isFunction(options.fetch))
|
|
1979
|
-
this.fetch = options.fetch;
|
|
1980
|
-
if (toString.call(options.lifeCycles) === '[object Object]') {
|
|
1981
|
-
this.lifeCycles = options.lifeCycles;
|
|
1982
|
-
}
|
|
1983
|
-
if (toString.call(options.plugins) === '[object Object]') {
|
|
1984
|
-
this.plugins = options.plugins;
|
|
1985
|
-
}
|
|
1986
|
-
// load app assets when browser is idle
|
|
1987
|
-
if (options.preFetchApps) {
|
|
1988
|
-
preFetch(options.preFetchApps);
|
|
1989
|
-
}
|
|
1990
|
-
// load global assets when browser is idle
|
|
1991
|
-
if (options.globalAssets) {
|
|
1992
|
-
getGlobalAssets(options.globalAssets);
|
|
1993
|
-
}
|
|
1994
|
-
}
|
|
1995
|
-
defineElement(this.tagName);
|
|
1996
|
-
}
|
|
1997
|
-
}
|
|
1998
|
-
var microApp = new MicroApp();
|
|
1999
|
-
|
|
2000
|
-
/**
|
|
2001
|
-
* fetch source of html, js, css
|
|
2002
|
-
* @param url source path
|
|
2003
|
-
* @param appName app name
|
|
2004
|
-
* @param config config of fetch
|
|
2005
|
-
*/
|
|
2006
|
-
function fetchSource(url, appName = null, options = {}) {
|
|
2007
|
-
if (isFunction(microApp.fetch)) {
|
|
2008
|
-
return microApp.fetch(url, options, appName);
|
|
2009
|
-
}
|
|
2010
|
-
return fetch(url, options).then((res) => {
|
|
2011
|
-
return res.text();
|
|
2012
|
-
});
|
|
2013
|
-
}
|
|
2014
|
-
|
|
1763
|
+
// Variables that can escape to rawWindow
|
|
1764
|
+
const staticEscapeProperties = [
|
|
1765
|
+
'System',
|
|
1766
|
+
'__cjsWrapper',
|
|
1767
|
+
'__REACT_ERROR_OVERLAY_GLOBAL_HOOK__',
|
|
1768
|
+
];
|
|
1769
|
+
// Variables that can only assigned to rawWindow
|
|
1770
|
+
const escapeSetterKeyList = [
|
|
1771
|
+
'location',
|
|
1772
|
+
];
|
|
1773
|
+
const unscopables = {
|
|
1774
|
+
undefined: true,
|
|
1775
|
+
Array: true,
|
|
1776
|
+
Object: true,
|
|
1777
|
+
String: true,
|
|
1778
|
+
Boolean: true,
|
|
1779
|
+
Math: true,
|
|
1780
|
+
Number: true,
|
|
1781
|
+
Symbol: true,
|
|
1782
|
+
parseFloat: true,
|
|
1783
|
+
Float32Array: true,
|
|
1784
|
+
};
|
|
2015
1785
|
/**
|
|
2016
|
-
*
|
|
2017
|
-
* @param str string dom
|
|
1786
|
+
* macro task to solve the rendering problem of vue3
|
|
2018
1787
|
*/
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
1788
|
+
let macroTimer;
|
|
1789
|
+
function macroTask(fn) {
|
|
1790
|
+
macroTimer && clearTimeout(macroTimer);
|
|
1791
|
+
macroTimer = setTimeout(fn, 0);
|
|
2023
1792
|
}
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
1793
|
+
class SandBox {
|
|
1794
|
+
constructor(appName, url, macro) {
|
|
1795
|
+
// Scoped global Properties(Properties that can only get and set in microWindow, will not escape to rawWindow)
|
|
1796
|
+
this.scopeProperties = ['webpackJsonp'];
|
|
1797
|
+
// Properties that can be escape to rawWindow
|
|
1798
|
+
this.escapeProperties = [];
|
|
1799
|
+
// Properties newly added to microWindow
|
|
1800
|
+
this.injectedKeys = new Set();
|
|
1801
|
+
// Properties escape to rawWindow, cleared when unmount
|
|
1802
|
+
this.escapeKeys = new Set();
|
|
1803
|
+
// sandbox state
|
|
1804
|
+
this.active = false;
|
|
1805
|
+
this.microWindow = {}; // Proxy target
|
|
1806
|
+
const rawWindow = globalEnv.rawWindow;
|
|
1807
|
+
const rawDocument = globalEnv.rawDocument;
|
|
1808
|
+
const descriptorTargetMap = new Map();
|
|
1809
|
+
const hasOwnProperty = (key) => this.microWindow.hasOwnProperty(key) || rawWindow.hasOwnProperty(key);
|
|
1810
|
+
// get scopeProperties and escapeProperties from plugins
|
|
1811
|
+
this.getScopeProperties(appName);
|
|
1812
|
+
// inject global properties
|
|
1813
|
+
this.inject(this.microWindow, appName, url);
|
|
1814
|
+
// Rewrite global event listener & timeout
|
|
1815
|
+
Object.assign(this, effect(this.microWindow));
|
|
1816
|
+
this.proxyWindow = new Proxy(this.microWindow, {
|
|
1817
|
+
get: (target, key) => {
|
|
1818
|
+
if (key === Symbol.unscopables)
|
|
1819
|
+
return unscopables;
|
|
1820
|
+
if (['window', 'self', 'globalThis'].includes(key)) {
|
|
1821
|
+
return this.proxyWindow;
|
|
1822
|
+
}
|
|
1823
|
+
if (key === 'top' || key === 'parent') {
|
|
1824
|
+
if (rawWindow === rawWindow.parent) { // not in iframe
|
|
1825
|
+
return this.proxyWindow;
|
|
1826
|
+
}
|
|
1827
|
+
return Reflect.get(rawWindow, key); // iframe
|
|
1828
|
+
}
|
|
1829
|
+
if (key === 'hasOwnProperty')
|
|
1830
|
+
return hasOwnProperty;
|
|
1831
|
+
if (key === 'document' || key === 'eval') {
|
|
1832
|
+
if (this.active) {
|
|
1833
|
+
setCurrentAppName(appName);
|
|
1834
|
+
(macro ? macroTask : defer)(() => setCurrentAppName(null));
|
|
1835
|
+
}
|
|
1836
|
+
switch (key) {
|
|
1837
|
+
case 'document':
|
|
1838
|
+
return rawDocument;
|
|
1839
|
+
case 'eval':
|
|
1840
|
+
return eval;
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
if (Reflect.has(target, key)) {
|
|
1844
|
+
return Reflect.get(target, key);
|
|
1845
|
+
}
|
|
1846
|
+
if (this.scopeProperties.includes(key) ||
|
|
1847
|
+
(isString(key) && /^__MICRO_APP_/.test(key))) {
|
|
1848
|
+
return Reflect.get(target, key);
|
|
1849
|
+
}
|
|
1850
|
+
const rawValue = Reflect.get(rawWindow, key);
|
|
1851
|
+
return bindFunctionToRawWidow(rawWindow, rawValue);
|
|
1852
|
+
},
|
|
1853
|
+
set: (target, key, value) => {
|
|
1854
|
+
if (this.active) {
|
|
1855
|
+
if (escapeSetterKeyList.includes(key)) {
|
|
1856
|
+
Reflect.set(rawWindow, key, value);
|
|
1857
|
+
}
|
|
1858
|
+
else if (!target.hasOwnProperty(key) &&
|
|
1859
|
+
rawWindow.hasOwnProperty(key) &&
|
|
1860
|
+
!this.scopeProperties.includes(key)) {
|
|
1861
|
+
const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
|
|
1862
|
+
const { writable, configurable, enumerable } = descriptor;
|
|
1863
|
+
if (writable) {
|
|
1864
|
+
Object.defineProperty(target, key, {
|
|
1865
|
+
configurable,
|
|
1866
|
+
enumerable,
|
|
1867
|
+
writable,
|
|
1868
|
+
value,
|
|
1869
|
+
});
|
|
1870
|
+
this.injectedKeys.add(key);
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
else {
|
|
1874
|
+
Reflect.set(target, key, value);
|
|
1875
|
+
this.injectedKeys.add(key);
|
|
1876
|
+
}
|
|
1877
|
+
if ((this.escapeProperties.includes(key) ||
|
|
1878
|
+
(staticEscapeProperties.includes(key) && !Reflect.has(rawWindow, key))) &&
|
|
1879
|
+
!this.scopeProperties.includes(key)) {
|
|
1880
|
+
Reflect.set(rawWindow, key, value);
|
|
1881
|
+
this.escapeKeys.add(key);
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
return true;
|
|
1885
|
+
},
|
|
1886
|
+
has: (target, key) => {
|
|
1887
|
+
if (this.scopeProperties.includes(key))
|
|
1888
|
+
return key in target;
|
|
1889
|
+
return key in unscopables || key in target || key in rawWindow;
|
|
1890
|
+
},
|
|
1891
|
+
getOwnPropertyDescriptor: (target, key) => {
|
|
1892
|
+
if (target.hasOwnProperty(key)) {
|
|
1893
|
+
descriptorTargetMap.set(key, 'target');
|
|
1894
|
+
return Object.getOwnPropertyDescriptor(target, key);
|
|
1895
|
+
}
|
|
1896
|
+
if (rawWindow.hasOwnProperty(key)) {
|
|
1897
|
+
descriptorTargetMap.set(key, 'rawWindow');
|
|
1898
|
+
const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
|
|
1899
|
+
if (descriptor && !descriptor.configurable) {
|
|
1900
|
+
descriptor.configurable = true;
|
|
1901
|
+
}
|
|
1902
|
+
return descriptor;
|
|
1903
|
+
}
|
|
1904
|
+
return undefined;
|
|
1905
|
+
},
|
|
1906
|
+
defineProperty: (target, key, value) => {
|
|
1907
|
+
const from = descriptorTargetMap.get(key);
|
|
1908
|
+
if (from === 'rawWindow') {
|
|
1909
|
+
return Reflect.defineProperty(rawWindow, key, value);
|
|
1910
|
+
}
|
|
1911
|
+
return Reflect.defineProperty(target, key, value);
|
|
1912
|
+
},
|
|
1913
|
+
ownKeys: (target) => {
|
|
1914
|
+
return unique(Reflect.ownKeys(rawWindow).concat(Reflect.ownKeys(target)));
|
|
1915
|
+
},
|
|
1916
|
+
deleteProperty: (target, key) => {
|
|
1917
|
+
if (target.hasOwnProperty(key)) {
|
|
1918
|
+
this.escapeKeys.has(key) && Reflect.deleteProperty(rawWindow, key);
|
|
1919
|
+
return Reflect.deleteProperty(target, key);
|
|
1920
|
+
}
|
|
1921
|
+
return true;
|
|
1922
|
+
},
|
|
1923
|
+
});
|
|
1924
|
+
}
|
|
1925
|
+
start(baseroute) {
|
|
1926
|
+
if (!this.active) {
|
|
1927
|
+
this.active = true;
|
|
1928
|
+
this.microWindow.__MICRO_APP_BASE_ROUTE__ = this.microWindow.__MICRO_APP_BASE_URL__ = baseroute;
|
|
1929
|
+
// BUG FIX: bable-polyfill@6.x
|
|
1930
|
+
globalEnv.rawWindow._babelPolyfill && (globalEnv.rawWindow._babelPolyfill = false);
|
|
1931
|
+
if (++SandBox.activeCount === 1) {
|
|
1932
|
+
effectDocumentEvent();
|
|
2045
1933
|
}
|
|
2046
1934
|
}
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
1935
|
+
}
|
|
1936
|
+
stop() {
|
|
1937
|
+
if (this.active) {
|
|
1938
|
+
this.active = false;
|
|
1939
|
+
this.releaseEffect();
|
|
1940
|
+
this.microWindow.microApp.clearDataListener();
|
|
1941
|
+
this.microWindow.microApp.clearGlobalDataListener();
|
|
1942
|
+
this.injectedKeys.forEach((key) => {
|
|
1943
|
+
Reflect.deleteProperty(this.microWindow, key);
|
|
1944
|
+
});
|
|
1945
|
+
this.injectedKeys.clear();
|
|
1946
|
+
this.escapeKeys.forEach((key) => {
|
|
1947
|
+
Reflect.deleteProperty(globalEnv.rawWindow, key);
|
|
1948
|
+
});
|
|
1949
|
+
this.escapeKeys.clear();
|
|
1950
|
+
if (--SandBox.activeCount === 0) {
|
|
1951
|
+
releaseEffectDocumentEvent();
|
|
2053
1952
|
}
|
|
2054
1953
|
}
|
|
2055
|
-
else if (dom instanceof HTMLScriptElement) {
|
|
2056
|
-
extractScriptElement(dom, parent, app);
|
|
2057
|
-
}
|
|
2058
|
-
else if (dom instanceof HTMLMetaElement || dom instanceof HTMLTitleElement) {
|
|
2059
|
-
parent.removeChild(dom);
|
|
2060
|
-
}
|
|
2061
|
-
else if (dom instanceof HTMLImageElement && dom.hasAttribute('src')) {
|
|
2062
|
-
dom.setAttribute('src', CompletionPath(dom.getAttribute('src'), app.url));
|
|
2063
|
-
}
|
|
2064
|
-
}
|
|
2065
|
-
}
|
|
2066
|
-
/**
|
|
2067
|
-
* Extract link and script, bind style scope
|
|
2068
|
-
* @param htmlStr html string
|
|
2069
|
-
* @param app app
|
|
2070
|
-
*/
|
|
2071
|
-
function extractSourceDom(htmlStr, app) {
|
|
2072
|
-
const wrapElement = getWrapElement(htmlStr);
|
|
2073
|
-
const microAppHead = wrapElement.querySelector('micro-app-head');
|
|
2074
|
-
const microAppBody = wrapElement.querySelector('micro-app-body');
|
|
2075
|
-
if (!microAppHead || !microAppBody) {
|
|
2076
|
-
const msg = `element ${microAppHead ? 'body' : 'head'} is missing`;
|
|
2077
|
-
app.onerror(new Error(msg));
|
|
2078
|
-
return logError(msg, app.name);
|
|
2079
1954
|
}
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
1955
|
+
// record umd snapshot before the first execution of umdHookMount
|
|
1956
|
+
recordUmdSnapshot() {
|
|
1957
|
+
this.microWindow.__MICRO_APP_UMD_MODE__ = true;
|
|
1958
|
+
this.recordUmdEffect();
|
|
1959
|
+
recordDataCenterSnapshot(this.microWindow.microApp);
|
|
1960
|
+
this.recordUmdinjectedValues = new Map();
|
|
1961
|
+
this.injectedKeys.forEach((key) => {
|
|
1962
|
+
this.recordUmdinjectedValues.set(key, Reflect.get(this.microWindow, key));
|
|
1963
|
+
});
|
|
2083
1964
|
}
|
|
2084
|
-
|
|
2085
|
-
|
|
1965
|
+
// rebuild umd snapshot before remount umd app
|
|
1966
|
+
rebuildUmdSnapshot() {
|
|
1967
|
+
this.recordUmdinjectedValues.forEach((value, key) => {
|
|
1968
|
+
Reflect.set(this.proxyWindow, key, value);
|
|
1969
|
+
});
|
|
1970
|
+
this.rebuildUmdEffect();
|
|
1971
|
+
rebuildDataCenterSnapshot(this.microWindow.microApp);
|
|
2086
1972
|
}
|
|
2087
|
-
|
|
2088
|
-
|
|
1973
|
+
/**
|
|
1974
|
+
* get scopeProperties and escapeProperties from plugins
|
|
1975
|
+
* @param appName app name
|
|
1976
|
+
*/
|
|
1977
|
+
getScopeProperties(appName) {
|
|
1978
|
+
var _a;
|
|
1979
|
+
if (!isPlainObject(microApp.plugins))
|
|
1980
|
+
return;
|
|
1981
|
+
if (isArray(microApp.plugins.global)) {
|
|
1982
|
+
for (const plugin of microApp.plugins.global) {
|
|
1983
|
+
if (isPlainObject(plugin)) {
|
|
1984
|
+
if (isArray(plugin.scopeProperties)) {
|
|
1985
|
+
this.scopeProperties = this.scopeProperties.concat(plugin.scopeProperties);
|
|
1986
|
+
}
|
|
1987
|
+
if (isArray(plugin.escapeProperties)) {
|
|
1988
|
+
this.escapeProperties = this.escapeProperties.concat(plugin.escapeProperties);
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
if (isArray((_a = microApp.plugins.modules) === null || _a === void 0 ? void 0 : _a[appName])) {
|
|
1994
|
+
for (const plugin of microApp.plugins.modules[appName]) {
|
|
1995
|
+
if (isPlainObject(plugin)) {
|
|
1996
|
+
if (isArray(plugin.scopeProperties)) {
|
|
1997
|
+
this.scopeProperties = this.scopeProperties.concat(plugin.scopeProperties);
|
|
1998
|
+
}
|
|
1999
|
+
if (isArray(plugin.escapeProperties)) {
|
|
2000
|
+
this.escapeProperties = this.escapeProperties.concat(plugin.escapeProperties);
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2089
2005
|
}
|
|
2090
|
-
|
|
2091
|
-
|
|
2006
|
+
/**
|
|
2007
|
+
* inject global properties to microWindow
|
|
2008
|
+
* @param microWindow micro window
|
|
2009
|
+
* @param appName app name
|
|
2010
|
+
* @param url app url
|
|
2011
|
+
*/
|
|
2012
|
+
inject(microWindow, appName, url) {
|
|
2013
|
+
microWindow.__MICRO_APP_ENVIRONMENT__ = true;
|
|
2014
|
+
microWindow.__MICRO_APP_NAME__ = appName;
|
|
2015
|
+
microWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
|
|
2016
|
+
microWindow.microApp = new EventCenterForMicroApp(appName);
|
|
2017
|
+
microWindow.rawWindow = globalEnv.rawWindow;
|
|
2018
|
+
microWindow.rawDocument = globalEnv.rawDocument;
|
|
2019
|
+
microWindow.removeDomScope = removeDomScope;
|
|
2092
2020
|
}
|
|
2093
2021
|
}
|
|
2094
|
-
|
|
2095
|
-
* Get and format html
|
|
2096
|
-
* @param app app
|
|
2097
|
-
*/
|
|
2098
|
-
function extractHtml(app) {
|
|
2099
|
-
fetchSource(app.url, app.name, { cache: 'no-cache' }).then((htmlStr) => {
|
|
2100
|
-
if (!htmlStr) {
|
|
2101
|
-
const msg = 'html is empty, please check in detail';
|
|
2102
|
-
app.onerror(new Error(msg));
|
|
2103
|
-
return logError(msg, app.name);
|
|
2104
|
-
}
|
|
2105
|
-
htmlStr = htmlStr
|
|
2106
|
-
.replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
|
|
2107
|
-
return match
|
|
2108
|
-
.replace(/<head/i, '<micro-app-head')
|
|
2109
|
-
.replace(/<\/head>/i, '</micro-app-head>');
|
|
2110
|
-
})
|
|
2111
|
-
.replace(/<body[^>]*>[\s\S]*?<\/body>/i, (match) => {
|
|
2112
|
-
return match
|
|
2113
|
-
.replace(/<body/i, '<micro-app-body')
|
|
2114
|
-
.replace(/<\/body>/i, '</micro-app-body>');
|
|
2115
|
-
});
|
|
2116
|
-
extractSourceDom(htmlStr, app);
|
|
2117
|
-
}).catch((e) => {
|
|
2118
|
-
logError(`Failed to fetch data from ${app.url}, micro-app stop rendering`, app.name, e);
|
|
2119
|
-
app.onLoadError(e);
|
|
2120
|
-
});
|
|
2121
|
-
}
|
|
2022
|
+
SandBox.activeCount = 0; // number of active sandbox
|
|
2122
2023
|
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
return constructorMap.get(value);
|
|
2137
|
-
}
|
|
2138
|
-
const valueStr = value.toString();
|
|
2139
|
-
const result = (value.prototype &&
|
|
2140
|
-
value.prototype.constructor === value &&
|
|
2141
|
-
Object.getOwnPropertyNames(value.prototype).length > 1) ||
|
|
2142
|
-
/^function\s+[A-Z]/.test(valueStr) ||
|
|
2143
|
-
/^class\s+/.test(valueStr);
|
|
2144
|
-
constructorMap.set(value, result);
|
|
2145
|
-
return result;
|
|
2024
|
+
function eventHandler$1(event, element) {
|
|
2025
|
+
Object.defineProperties(event, {
|
|
2026
|
+
currentTarget: {
|
|
2027
|
+
get() {
|
|
2028
|
+
return element;
|
|
2029
|
+
}
|
|
2030
|
+
},
|
|
2031
|
+
target: {
|
|
2032
|
+
get() {
|
|
2033
|
+
return element;
|
|
2034
|
+
}
|
|
2035
|
+
},
|
|
2036
|
+
});
|
|
2146
2037
|
}
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2038
|
+
/**
|
|
2039
|
+
* dispatch lifeCycles event to base app
|
|
2040
|
+
* created, beforemount, mounted, unmount, error
|
|
2041
|
+
* @param element container
|
|
2042
|
+
* @param appName app.name
|
|
2043
|
+
* @param lifecycleName lifeCycle name
|
|
2044
|
+
* @param error param from error hook
|
|
2045
|
+
*/
|
|
2046
|
+
function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
|
|
2047
|
+
var _a;
|
|
2048
|
+
if (!element) {
|
|
2049
|
+
return logError(`element does not exist in lifecycle ${lifecycleName}`, appName);
|
|
2152
2050
|
}
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2051
|
+
element = getRootContainer(element);
|
|
2052
|
+
// clear dom scope before dispatch lifeCycles event to base app, especially mounted & unmount
|
|
2053
|
+
removeDomScope();
|
|
2054
|
+
const detail = Object.assign({
|
|
2055
|
+
name: appName,
|
|
2056
|
+
container: element,
|
|
2057
|
+
}, error && {
|
|
2058
|
+
error
|
|
2059
|
+
});
|
|
2060
|
+
const event = new CustomEvent(lifecycleName, {
|
|
2061
|
+
detail,
|
|
2062
|
+
});
|
|
2063
|
+
eventHandler$1(event, element);
|
|
2064
|
+
// global hooks
|
|
2065
|
+
// @ts-ignore
|
|
2066
|
+
if (isFunction((_a = microApp.lifeCycles) === null || _a === void 0 ? void 0 : _a[lifecycleName])) {
|
|
2067
|
+
// @ts-ignore
|
|
2068
|
+
microApp.lifeCycles[lifecycleName](event);
|
|
2163
2069
|
}
|
|
2164
|
-
|
|
2070
|
+
element.dispatchEvent(event);
|
|
2165
2071
|
}
|
|
2166
|
-
|
|
2167
|
-
// document.onclick binding list, the binding function of each application is unique
|
|
2168
|
-
const documentClickListMap = new Map();
|
|
2169
|
-
let hasRewriteDocumentOnClick = false;
|
|
2170
2072
|
/**
|
|
2171
|
-
*
|
|
2073
|
+
* Dispatch unmount event to micro app
|
|
2074
|
+
* @param appName app.name
|
|
2172
2075
|
*/
|
|
2173
|
-
function
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2076
|
+
function dispatchUnmountToMicroApp(appName) {
|
|
2077
|
+
const event = new CustomEvent(`unmount-${appName}`);
|
|
2078
|
+
window.dispatchEvent(event);
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
// micro app instances
|
|
2082
|
+
const appInstanceMap = new Map();
|
|
2083
|
+
class CreateApp {
|
|
2084
|
+
constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, macro, baseroute, }) {
|
|
2085
|
+
this.status = appStatus.NOT_LOADED;
|
|
2086
|
+
this.loadSourceLevel = 0;
|
|
2087
|
+
this.umdHookMount = null;
|
|
2088
|
+
this.umdHookUnmount = null;
|
|
2089
|
+
this.libraryName = null;
|
|
2090
|
+
this.umdMode = false;
|
|
2091
|
+
this.isPrefetch = false;
|
|
2092
|
+
this.container = null;
|
|
2093
|
+
this.macro = false;
|
|
2094
|
+
this.baseroute = '';
|
|
2095
|
+
this.sandBox = null;
|
|
2096
|
+
this.container = container !== null && container !== void 0 ? container : null;
|
|
2097
|
+
this.inline = inline !== null && inline !== void 0 ? inline : false;
|
|
2098
|
+
this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : '';
|
|
2099
|
+
this.ssrUrl = ssrUrl !== null && ssrUrl !== void 0 ? ssrUrl : '';
|
|
2100
|
+
// optional during init👆
|
|
2101
|
+
this.name = name;
|
|
2102
|
+
this.url = url;
|
|
2103
|
+
this.useSandbox = useSandbox;
|
|
2104
|
+
this.scopecss = this.useSandbox && scopecss;
|
|
2105
|
+
this.macro = macro !== null && macro !== void 0 ? macro : false;
|
|
2106
|
+
this.source = {
|
|
2107
|
+
links: new Map(),
|
|
2108
|
+
scripts: new Map(),
|
|
2109
|
+
};
|
|
2110
|
+
this.loadSourceCode();
|
|
2111
|
+
this.useSandbox && (this.sandBox = new SandBox(name, url, this.macro));
|
|
2178
2112
|
}
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
documentClickListMap.forEach((f) => {
|
|
2184
|
-
typeof f === 'function' && f.call(document, e);
|
|
2185
|
-
});
|
|
2113
|
+
// Load resources
|
|
2114
|
+
loadSourceCode() {
|
|
2115
|
+
this.status = appStatus.LOADING_SOURCE_CODE;
|
|
2116
|
+
extractHtml(this);
|
|
2186
2117
|
}
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
documentClickListMap.set(appName, f);
|
|
2198
|
-
}
|
|
2199
|
-
else {
|
|
2200
|
-
documentClickListMap.set('base', f);
|
|
2201
|
-
}
|
|
2202
|
-
if (!hasDocumentClickInited && typeof f === 'function') {
|
|
2203
|
-
hasDocumentClickInited = true;
|
|
2204
|
-
globalEnv.rawDocumentAddEventListener.call(globalEnv.rawDocument, 'click', onClickHandler, false);
|
|
2205
|
-
}
|
|
2118
|
+
/**
|
|
2119
|
+
* When resource is loaded, mount app if it is not prefetch or unmount
|
|
2120
|
+
*/
|
|
2121
|
+
onLoad(html) {
|
|
2122
|
+
if (++this.loadSourceLevel === 2) {
|
|
2123
|
+
this.source.html = html;
|
|
2124
|
+
if (this.isPrefetch || appStatus.UNMOUNT === this.status)
|
|
2125
|
+
return;
|
|
2126
|
+
this.status = appStatus.LOAD_SOURCE_FINISHED;
|
|
2127
|
+
this.mount();
|
|
2206
2128
|
}
|
|
2207
|
-
});
|
|
2208
|
-
if (rawOnClick) {
|
|
2209
|
-
document.onclick = rawOnClick;
|
|
2210
2129
|
}
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2130
|
+
/**
|
|
2131
|
+
* Error loading HTML
|
|
2132
|
+
* @param e Error
|
|
2133
|
+
*/
|
|
2134
|
+
onLoadError(e) {
|
|
2135
|
+
this.loadSourceLevel = -1;
|
|
2136
|
+
if (appStatus.UNMOUNT !== this.status) {
|
|
2137
|
+
this.onerror(e);
|
|
2138
|
+
this.status = appStatus.LOAD_SOURCE_ERROR;
|
|
2139
|
+
}
|
|
2220
2140
|
}
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2141
|
+
/**
|
|
2142
|
+
* mount app
|
|
2143
|
+
* @param container app container
|
|
2144
|
+
* @param inline js runs in inline mode
|
|
2145
|
+
* @param baseroute route prefix, default is ''
|
|
2146
|
+
*/
|
|
2147
|
+
mount(container, inline, baseroute) {
|
|
2148
|
+
var _a, _b, _c;
|
|
2149
|
+
if (isBoolean(inline) && inline !== this.inline) {
|
|
2150
|
+
this.inline = inline;
|
|
2151
|
+
}
|
|
2152
|
+
this.container = (_a = this.container) !== null && _a !== void 0 ? _a : container;
|
|
2153
|
+
this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : this.baseroute;
|
|
2154
|
+
if (this.loadSourceLevel !== 2) {
|
|
2155
|
+
this.status = appStatus.LOADING_SOURCE_CODE;
|
|
2156
|
+
return;
|
|
2157
|
+
}
|
|
2158
|
+
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
|
|
2159
|
+
this.status = appStatus.MOUNTING;
|
|
2160
|
+
cloneContainer(this.source.html, this.container, !this.umdMode);
|
|
2161
|
+
(_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.start(this.baseroute);
|
|
2162
|
+
let umdHookMountResult; // result of mount function
|
|
2163
|
+
if (!this.umdMode) {
|
|
2164
|
+
let hasDispatchMountedEvent = false;
|
|
2165
|
+
// if all js are executed, param isFinished will be true
|
|
2166
|
+
execScripts(this.source.scripts, this, (isFinished) => {
|
|
2167
|
+
var _a;
|
|
2168
|
+
if (!this.umdMode) {
|
|
2169
|
+
const { mount, unmount } = this.getUmdLibraryHooks();
|
|
2170
|
+
// if mount & unmount is function, the sub app is umd mode
|
|
2171
|
+
if (isFunction(mount) && isFunction(unmount)) {
|
|
2172
|
+
this.umdHookMount = mount;
|
|
2173
|
+
this.umdHookUnmount = unmount;
|
|
2174
|
+
this.umdMode = true;
|
|
2175
|
+
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordUmdSnapshot();
|
|
2176
|
+
try {
|
|
2177
|
+
umdHookMountResult = this.umdHookMount();
|
|
2178
|
+
}
|
|
2179
|
+
catch (e) {
|
|
2180
|
+
logError('an error occurred in the mount function \n', this.name, e);
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2229
2183
|
}
|
|
2230
|
-
|
|
2231
|
-
|
|
2184
|
+
if (!hasDispatchMountedEvent && (isFinished === true || this.umdMode)) {
|
|
2185
|
+
hasDispatchMountedEvent = true;
|
|
2186
|
+
this.handleMounted(umdHookMountResult);
|
|
2232
2187
|
}
|
|
2188
|
+
});
|
|
2189
|
+
}
|
|
2190
|
+
else {
|
|
2191
|
+
(_c = this.sandBox) === null || _c === void 0 ? void 0 : _c.rebuildUmdSnapshot();
|
|
2192
|
+
try {
|
|
2193
|
+
umdHookMountResult = this.umdHookMount();
|
|
2233
2194
|
}
|
|
2234
|
-
|
|
2235
|
-
|
|
2195
|
+
catch (e) {
|
|
2196
|
+
logError('an error occurred in the mount function \n', this.name, e);
|
|
2236
2197
|
}
|
|
2237
|
-
|
|
2198
|
+
this.handleMounted(umdHookMountResult);
|
|
2238
2199
|
}
|
|
2239
|
-
rawDocumentAddEventListener.call(rawDocument, type, listener, options);
|
|
2240
|
-
};
|
|
2241
|
-
document.removeEventListener = function (type, listener, options) {
|
|
2242
|
-
const appName = getCurrentAppName();
|
|
2243
|
-
if (appName) {
|
|
2244
|
-
const appListenersMap = documentEventListenerMap.get(appName);
|
|
2245
|
-
if (appListenersMap) {
|
|
2246
|
-
const appListenerList = appListenersMap.get(type);
|
|
2247
|
-
if ((appListenerList === null || appListenerList === void 0 ? void 0 : appListenerList.size) && appListenerList.has(listener)) {
|
|
2248
|
-
appListenerList.delete(listener);
|
|
2249
|
-
}
|
|
2250
|
-
}
|
|
2251
|
-
}
|
|
2252
|
-
rawDocumentRemoveEventListener.call(rawDocument, type, listener, options);
|
|
2253
|
-
};
|
|
2254
|
-
}
|
|
2255
|
-
// Clear the document event agent
|
|
2256
|
-
function releaseEffectDocumentEvent() {
|
|
2257
|
-
document.addEventListener = globalEnv.rawDocumentAddEventListener;
|
|
2258
|
-
document.removeEventListener = globalEnv.rawDocumentRemoveEventListener;
|
|
2259
|
-
}
|
|
2260
|
-
/**
|
|
2261
|
-
* Format event name
|
|
2262
|
-
* @param type event name
|
|
2263
|
-
* @param microWindow micro window
|
|
2264
|
-
*/
|
|
2265
|
-
function formatEventType(type, microWindow) {
|
|
2266
|
-
if (type === 'unmount') {
|
|
2267
|
-
return `unmount-${microWindow.__MICRO_APP_NAME__}`;
|
|
2268
2200
|
}
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
const intervalIdMap = new Map();
|
|
2279
|
-
const timeoutIdMap = new Map();
|
|
2280
|
-
const { rawWindow, rawDocument, rawWindowAddEventListener, rawWindowRemoveEventListener, rawSetInterval, rawSetTimeout, rawClearInterval, rawClearTimeout, rawDocumentRemoveEventListener, } = globalEnv;
|
|
2281
|
-
// listener may be null, e.g test-passive
|
|
2282
|
-
microWindow.addEventListener = function (type, listener, options) {
|
|
2283
|
-
type = formatEventType(type, microWindow);
|
|
2284
|
-
const listenerList = eventListenerMap.get(type);
|
|
2285
|
-
if (listenerList) {
|
|
2286
|
-
listenerList.add(listener);
|
|
2201
|
+
/**
|
|
2202
|
+
* handle for promise umdHookMount
|
|
2203
|
+
* @param umdHookMountResult result of umdHookMount
|
|
2204
|
+
*/
|
|
2205
|
+
handleMounted(umdHookMountResult) {
|
|
2206
|
+
if (isPromise(umdHookMountResult)) {
|
|
2207
|
+
umdHookMountResult
|
|
2208
|
+
.then(() => this.dispatchMountedEvent())
|
|
2209
|
+
.catch((e) => this.onerror(e));
|
|
2287
2210
|
}
|
|
2288
2211
|
else {
|
|
2289
|
-
|
|
2212
|
+
this.dispatchMountedEvent();
|
|
2290
2213
|
}
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2214
|
+
}
|
|
2215
|
+
/**
|
|
2216
|
+
* dispatch mounted event when app run finished
|
|
2217
|
+
*/
|
|
2218
|
+
dispatchMountedEvent() {
|
|
2219
|
+
if (appStatus.UNMOUNT !== this.status) {
|
|
2220
|
+
this.status = appStatus.MOUNTED;
|
|
2221
|
+
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.MOUNTED);
|
|
2299
2222
|
}
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
let umdIntervalIdMap = new Map();
|
|
2323
|
-
let umdTimeoutIdMap = new Map();
|
|
2324
|
-
let umdOnClickHandler;
|
|
2325
|
-
// record event and timer before exec umdMountHook
|
|
2326
|
-
const recordUmdEffect = () => {
|
|
2327
|
-
// record window event
|
|
2328
|
-
eventListenerMap.forEach((listenerList, type) => {
|
|
2329
|
-
if (listenerList.size) {
|
|
2330
|
-
umdWindowListenerMap.set(type, new Set(listenerList));
|
|
2223
|
+
}
|
|
2224
|
+
/**
|
|
2225
|
+
* unmount app
|
|
2226
|
+
* @param destroy completely destroy, delete cache resources
|
|
2227
|
+
*/
|
|
2228
|
+
unmount(destroy) {
|
|
2229
|
+
if (this.status === appStatus.LOAD_SOURCE_ERROR) {
|
|
2230
|
+
destroy = true;
|
|
2231
|
+
}
|
|
2232
|
+
this.status = appStatus.UNMOUNT;
|
|
2233
|
+
// result of unmount function
|
|
2234
|
+
let umdHookUnmountResult;
|
|
2235
|
+
/**
|
|
2236
|
+
* send an unmount event to the micro app or call umd unmount hook
|
|
2237
|
+
* before the sandbox is cleared
|
|
2238
|
+
*/
|
|
2239
|
+
if (this.umdHookUnmount) {
|
|
2240
|
+
try {
|
|
2241
|
+
umdHookUnmountResult = this.umdHookUnmount();
|
|
2242
|
+
}
|
|
2243
|
+
catch (e) {
|
|
2244
|
+
logError('an error occurred in the unmount function \n', this.name, e);
|
|
2331
2245
|
}
|
|
2332
|
-
});
|
|
2333
|
-
// record timers
|
|
2334
|
-
if (intervalIdMap.size) {
|
|
2335
|
-
umdIntervalIdMap = new Map(intervalIdMap);
|
|
2336
2246
|
}
|
|
2337
|
-
|
|
2338
|
-
|
|
2247
|
+
// dispatch unmount event to micro app
|
|
2248
|
+
dispatchUnmountToMicroApp(this.name);
|
|
2249
|
+
this.handleUnmounted(destroy, umdHookUnmountResult);
|
|
2250
|
+
}
|
|
2251
|
+
/**
|
|
2252
|
+
* handle for promise umdHookUnmount
|
|
2253
|
+
* @param umdHookUnmountResult result of umdHookUnmount
|
|
2254
|
+
*/
|
|
2255
|
+
handleUnmounted(destroy, umdHookUnmountResult) {
|
|
2256
|
+
if (isPromise(umdHookUnmountResult)) {
|
|
2257
|
+
umdHookUnmountResult
|
|
2258
|
+
.then(() => this.actionsForUnmount(destroy))
|
|
2259
|
+
.catch(() => this.actionsForUnmount(destroy));
|
|
2339
2260
|
}
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
// record document event
|
|
2343
|
-
const documentAppListenersMap = documentEventListenerMap.get(appName);
|
|
2344
|
-
if (documentAppListenersMap) {
|
|
2345
|
-
documentAppListenersMap.forEach((listenerList, type) => {
|
|
2346
|
-
if (listenerList.size) {
|
|
2347
|
-
umdDocumentListenerMap.set(type, new Set(listenerList));
|
|
2348
|
-
}
|
|
2349
|
-
});
|
|
2261
|
+
else {
|
|
2262
|
+
this.actionsForUnmount(destroy);
|
|
2350
2263
|
}
|
|
2351
|
-
}
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
});
|
|
2364
|
-
umdTimeoutIdMap.forEach((info) => {
|
|
2365
|
-
microWindow.setTimeout(info.handler, info.timeout, ...info.args);
|
|
2366
|
-
});
|
|
2367
|
-
// rebuild onclick event
|
|
2368
|
-
umdOnClickHandler && documentClickListMap.set(appName, umdOnClickHandler);
|
|
2369
|
-
// rebuild document event
|
|
2370
|
-
setCurrentAppName(appName);
|
|
2371
|
-
umdDocumentListenerMap.forEach((listenerList, type) => {
|
|
2372
|
-
for (const listener of listenerList) {
|
|
2373
|
-
document.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_MARK_OPTIONS__);
|
|
2374
|
-
}
|
|
2375
|
-
});
|
|
2376
|
-
setCurrentAppName(null);
|
|
2377
|
-
};
|
|
2378
|
-
// release all event listener & interval & timeout when unmount app
|
|
2379
|
-
const releaseEffect = () => {
|
|
2380
|
-
// Clear window binding events
|
|
2381
|
-
if (eventListenerMap.size) {
|
|
2382
|
-
eventListenerMap.forEach((listenerList, type) => {
|
|
2383
|
-
for (const listener of listenerList) {
|
|
2384
|
-
rawWindowRemoveEventListener.call(rawWindow, type, listener);
|
|
2385
|
-
}
|
|
2386
|
-
});
|
|
2387
|
-
eventListenerMap.clear();
|
|
2264
|
+
}
|
|
2265
|
+
/**
|
|
2266
|
+
* actions for unmount app
|
|
2267
|
+
* @param destroy completely destroy, delete cache resources
|
|
2268
|
+
*/
|
|
2269
|
+
actionsForUnmount(destroy) {
|
|
2270
|
+
var _a;
|
|
2271
|
+
// dispatch unmount event to base app
|
|
2272
|
+
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.UNMOUNT);
|
|
2273
|
+
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.stop();
|
|
2274
|
+
if (destroy) {
|
|
2275
|
+
this.actionsForCompletelyDestory();
|
|
2388
2276
|
}
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
intervalIdMap.clear();
|
|
2277
|
+
else if (this.umdMode && this.container.childElementCount) {
|
|
2278
|
+
/**
|
|
2279
|
+
* In umd mode, ui frameworks will no longer create style elements to head in lazy load page when render again, so we should save container to keep these elements
|
|
2280
|
+
*/
|
|
2281
|
+
cloneContainer(this.container, this.source.html, false);
|
|
2395
2282
|
}
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2283
|
+
this.container = null;
|
|
2284
|
+
}
|
|
2285
|
+
// actions for completely destroy
|
|
2286
|
+
actionsForCompletelyDestory() {
|
|
2287
|
+
if (!this.useSandbox && this.umdMode) {
|
|
2288
|
+
delete window[this.libraryName];
|
|
2401
2289
|
}
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2290
|
+
appInstanceMap.delete(this.name);
|
|
2291
|
+
}
|
|
2292
|
+
/**
|
|
2293
|
+
* app rendering error
|
|
2294
|
+
* @param e Error
|
|
2295
|
+
*/
|
|
2296
|
+
onerror(e) {
|
|
2297
|
+
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.ERROR, e);
|
|
2298
|
+
}
|
|
2299
|
+
// get app status
|
|
2300
|
+
getAppStatus() {
|
|
2301
|
+
return this.status;
|
|
2302
|
+
}
|
|
2303
|
+
// get umd library, if it not exist, return empty object
|
|
2304
|
+
getUmdLibraryHooks() {
|
|
2305
|
+
var _a, _b;
|
|
2306
|
+
// after execScripts, the app maybe unmounted
|
|
2307
|
+
if (appStatus.UNMOUNT !== this.status) {
|
|
2308
|
+
const global = ((_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow) !== null && _b !== void 0 ? _b : globalEnv.rawWindow);
|
|
2309
|
+
this.libraryName = getRootContainer(this.container).getAttribute('library') || `micro-app-${this.name}`;
|
|
2310
|
+
// do not use isObject
|
|
2311
|
+
return typeof global[this.libraryName] === 'object' ? global[this.libraryName] : {};
|
|
2413
2312
|
}
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2313
|
+
return {};
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
// if app not prefetch & not unmount, then app is active
|
|
2317
|
+
function getActiveApps() {
|
|
2318
|
+
const activeApps = [];
|
|
2319
|
+
appInstanceMap.forEach((app, appName) => {
|
|
2320
|
+
if (appStatus.UNMOUNT !== app.getAppStatus() && !app.isPrefetch) {
|
|
2321
|
+
activeApps.push(appName);
|
|
2322
|
+
}
|
|
2323
|
+
});
|
|
2324
|
+
return activeApps;
|
|
2325
|
+
}
|
|
2326
|
+
// get all registered apps
|
|
2327
|
+
function getAllApps() {
|
|
2328
|
+
return Array.from(appInstanceMap.keys());
|
|
2420
2329
|
}
|
|
2421
2330
|
|
|
2422
|
-
//
|
|
2423
|
-
const
|
|
2424
|
-
'System',
|
|
2425
|
-
'__cjsWrapper',
|
|
2426
|
-
'__REACT_ERROR_OVERLAY_GLOBAL_HOOK__',
|
|
2427
|
-
];
|
|
2428
|
-
// Variables that can only assigned to rawWindow
|
|
2429
|
-
const escapeSetterKeyList = [
|
|
2430
|
-
'location',
|
|
2431
|
-
];
|
|
2432
|
-
const unscopables = {
|
|
2433
|
-
undefined: true,
|
|
2434
|
-
Array: true,
|
|
2435
|
-
Object: true,
|
|
2436
|
-
String: true,
|
|
2437
|
-
Boolean: true,
|
|
2438
|
-
Math: true,
|
|
2439
|
-
Number: true,
|
|
2440
|
-
Symbol: true,
|
|
2441
|
-
parseFloat: true,
|
|
2442
|
-
Float32Array: true,
|
|
2443
|
-
};
|
|
2331
|
+
// Record element and map element
|
|
2332
|
+
const dynamicElementInMicroAppMap = new WeakMap();
|
|
2444
2333
|
/**
|
|
2445
|
-
*
|
|
2334
|
+
* Process the new node and format the style, link and script element
|
|
2335
|
+
* @param parent parent node
|
|
2336
|
+
* @param child new node
|
|
2337
|
+
* @param app app
|
|
2446
2338
|
*/
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
// Properties that can be escape to rawWindow
|
|
2459
|
-
this.escapeProperties = [];
|
|
2460
|
-
this.microWindow = {}; // Proxy target
|
|
2461
|
-
this.injectedKeys = new Set(); // Properties newly added to microWindow
|
|
2462
|
-
this.escapeKeys = new Set(); // Properties escape to rawWindow, cleared when unmount
|
|
2463
|
-
const rawWindow = globalEnv.rawWindow;
|
|
2464
|
-
const rawDocument = globalEnv.rawDocument;
|
|
2465
|
-
const descriptorTargetMap = new Map();
|
|
2466
|
-
const hasOwnProperty = (key) => this.microWindow.hasOwnProperty(key) || rawWindow.hasOwnProperty(key);
|
|
2467
|
-
// get scopeProperties and escapeProperties from plugins
|
|
2468
|
-
this.getScopeProperties(appName);
|
|
2469
|
-
// inject global properties
|
|
2470
|
-
this.inject(this.microWindow, appName, url);
|
|
2471
|
-
// Rewrite global event listener & timeout
|
|
2472
|
-
Object.assign(this, effect(this.microWindow));
|
|
2473
|
-
this.proxyWindow = new Proxy(this.microWindow, {
|
|
2474
|
-
get: (target, key) => {
|
|
2475
|
-
if (key === Symbol.unscopables)
|
|
2476
|
-
return unscopables;
|
|
2477
|
-
if (['window', 'self', 'globalThis'].includes(key)) {
|
|
2478
|
-
return this.proxyWindow;
|
|
2479
|
-
}
|
|
2480
|
-
if (key === 'top' || key === 'parent') {
|
|
2481
|
-
if (rawWindow === rawWindow.parent) { // not in iframe
|
|
2482
|
-
return this.proxyWindow;
|
|
2483
|
-
}
|
|
2484
|
-
return Reflect.get(rawWindow, key); // iframe
|
|
2485
|
-
}
|
|
2486
|
-
if (key === 'hasOwnProperty')
|
|
2487
|
-
return hasOwnProperty;
|
|
2488
|
-
if (key === 'document' || key === 'eval') {
|
|
2489
|
-
if (this.active) {
|
|
2490
|
-
setCurrentAppName(appName);
|
|
2491
|
-
(macro ? macroTask : defer)(() => setCurrentAppName(null));
|
|
2492
|
-
}
|
|
2493
|
-
switch (key) {
|
|
2494
|
-
case 'document':
|
|
2495
|
-
return rawDocument;
|
|
2496
|
-
case 'eval':
|
|
2497
|
-
return eval;
|
|
2498
|
-
}
|
|
2499
|
-
}
|
|
2500
|
-
if (Reflect.has(target, key)) {
|
|
2501
|
-
return Reflect.get(target, key);
|
|
2502
|
-
}
|
|
2503
|
-
if (this.scopeProperties.includes(key) ||
|
|
2504
|
-
(typeof key === 'string' && /^__MICRO_APP_/.test(key))) {
|
|
2505
|
-
return Reflect.get(target, key);
|
|
2506
|
-
}
|
|
2507
|
-
const rawValue = Reflect.get(rawWindow, key);
|
|
2508
|
-
return bindFunctionToRawWidow(rawWindow, rawValue);
|
|
2509
|
-
},
|
|
2510
|
-
set: (target, key, value) => {
|
|
2511
|
-
if (this.active) {
|
|
2512
|
-
if (escapeSetterKeyList.includes(key)) {
|
|
2513
|
-
Reflect.set(rawWindow, key, value);
|
|
2514
|
-
}
|
|
2515
|
-
else if (!target.hasOwnProperty(key) &&
|
|
2516
|
-
rawWindow.hasOwnProperty(key) &&
|
|
2517
|
-
!this.scopeProperties.includes(key)) {
|
|
2518
|
-
const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
|
|
2519
|
-
const { writable, configurable, enumerable } = descriptor;
|
|
2520
|
-
if (writable) {
|
|
2521
|
-
Object.defineProperty(target, key, {
|
|
2522
|
-
configurable,
|
|
2523
|
-
enumerable,
|
|
2524
|
-
writable,
|
|
2525
|
-
value,
|
|
2526
|
-
});
|
|
2527
|
-
this.injectedKeys.add(key);
|
|
2528
|
-
}
|
|
2529
|
-
}
|
|
2530
|
-
else {
|
|
2531
|
-
Reflect.set(target, key, value);
|
|
2532
|
-
this.injectedKeys.add(key);
|
|
2533
|
-
}
|
|
2534
|
-
if ((this.escapeProperties.includes(key) ||
|
|
2535
|
-
(staticEscapeProperties.includes(key) && !Reflect.has(rawWindow, key))) &&
|
|
2536
|
-
!this.scopeProperties.includes(key)) {
|
|
2537
|
-
Reflect.set(rawWindow, key, value);
|
|
2538
|
-
this.escapeKeys.add(key);
|
|
2539
|
-
}
|
|
2540
|
-
}
|
|
2541
|
-
return true;
|
|
2542
|
-
},
|
|
2543
|
-
has: (target, key) => {
|
|
2544
|
-
if (this.scopeProperties.includes(key))
|
|
2545
|
-
return key in target;
|
|
2546
|
-
return key in unscopables || key in target || key in rawWindow;
|
|
2547
|
-
},
|
|
2548
|
-
getOwnPropertyDescriptor: (target, key) => {
|
|
2549
|
-
if (target.hasOwnProperty(key)) {
|
|
2550
|
-
descriptorTargetMap.set(key, 'target');
|
|
2551
|
-
return Object.getOwnPropertyDescriptor(target, key);
|
|
2552
|
-
}
|
|
2553
|
-
if (rawWindow.hasOwnProperty(key)) {
|
|
2554
|
-
descriptorTargetMap.set(key, 'rawWindow');
|
|
2555
|
-
const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
|
|
2556
|
-
if (descriptor && !descriptor.configurable) {
|
|
2557
|
-
descriptor.configurable = true;
|
|
2558
|
-
}
|
|
2559
|
-
return descriptor;
|
|
2560
|
-
}
|
|
2561
|
-
return undefined;
|
|
2562
|
-
},
|
|
2563
|
-
defineProperty: (target, key, value) => {
|
|
2564
|
-
const from = descriptorTargetMap.get(key);
|
|
2565
|
-
if (from === 'rawWindow') {
|
|
2566
|
-
return Reflect.defineProperty(rawWindow, key, value);
|
|
2567
|
-
}
|
|
2568
|
-
return Reflect.defineProperty(target, key, value);
|
|
2569
|
-
},
|
|
2570
|
-
ownKeys: (target) => {
|
|
2571
|
-
return unique(Reflect.ownKeys(rawWindow).concat(Reflect.ownKeys(target)));
|
|
2572
|
-
},
|
|
2573
|
-
deleteProperty: (target, key) => {
|
|
2574
|
-
if (target.hasOwnProperty(key)) {
|
|
2575
|
-
if (this.escapeKeys.has(key)) {
|
|
2576
|
-
Reflect.deleteProperty(rawWindow, key);
|
|
2577
|
-
}
|
|
2578
|
-
return Reflect.deleteProperty(target, key);
|
|
2579
|
-
}
|
|
2580
|
-
return true;
|
|
2581
|
-
},
|
|
2582
|
-
});
|
|
2339
|
+
function handleNewNode(parent, child, app) {
|
|
2340
|
+
if (child instanceof HTMLStyleElement) {
|
|
2341
|
+
if (child.hasAttribute('exclude')) {
|
|
2342
|
+
const replaceComment = document.createComment('style element with exclude attribute ignored by micro-app');
|
|
2343
|
+
dynamicElementInMicroAppMap.set(child, replaceComment);
|
|
2344
|
+
return replaceComment;
|
|
2345
|
+
}
|
|
2346
|
+
else if (app.scopecss && !child.hasAttribute('ignore')) {
|
|
2347
|
+
return scopedCSS(child, app);
|
|
2348
|
+
}
|
|
2349
|
+
return child;
|
|
2583
2350
|
}
|
|
2584
|
-
|
|
2585
|
-
if (
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2351
|
+
else if (child instanceof HTMLLinkElement) {
|
|
2352
|
+
if (child.hasAttribute('exclude')) {
|
|
2353
|
+
const linkReplaceComment = document.createComment('link element with exclude attribute ignored by micro-app');
|
|
2354
|
+
dynamicElementInMicroAppMap.set(child, linkReplaceComment);
|
|
2355
|
+
return linkReplaceComment;
|
|
2356
|
+
}
|
|
2357
|
+
else if (child.hasAttribute('ignore')) {
|
|
2358
|
+
return child;
|
|
2359
|
+
}
|
|
2360
|
+
const { url, info, replaceComment } = extractLinkFromHtml(child, parent, app, true);
|
|
2361
|
+
if (url && info) {
|
|
2362
|
+
const replaceStyle = pureCreateElement('style');
|
|
2363
|
+
replaceStyle.__MICRO_APP_LINK_PATH__ = url;
|
|
2364
|
+
foramtDynamicLink(url, info, app, child, replaceStyle);
|
|
2365
|
+
dynamicElementInMicroAppMap.set(child, replaceStyle);
|
|
2366
|
+
return replaceStyle;
|
|
2367
|
+
}
|
|
2368
|
+
else if (replaceComment) {
|
|
2369
|
+
dynamicElementInMicroAppMap.set(child, replaceComment);
|
|
2370
|
+
return replaceComment;
|
|
2371
|
+
}
|
|
2372
|
+
return child;
|
|
2373
|
+
}
|
|
2374
|
+
else if (child instanceof HTMLScriptElement) {
|
|
2375
|
+
const { replaceComment, url, info } = extractScriptElement(child, parent, app, true) || {};
|
|
2376
|
+
if (url && info) {
|
|
2377
|
+
if (!info.isExternal) { // inline script
|
|
2378
|
+
const replaceElement = runScript(url, app, info, true);
|
|
2379
|
+
dynamicElementInMicroAppMap.set(child, replaceElement);
|
|
2380
|
+
return replaceElement;
|
|
2381
|
+
}
|
|
2382
|
+
else { // remote script
|
|
2383
|
+
const replaceElement = runDynamicRemoteScript(url, info, app, child);
|
|
2384
|
+
dynamicElementInMicroAppMap.set(child, replaceElement);
|
|
2385
|
+
return replaceElement;
|
|
2592
2386
|
}
|
|
2593
2387
|
}
|
|
2388
|
+
else if (replaceComment) {
|
|
2389
|
+
dynamicElementInMicroAppMap.set(child, replaceComment);
|
|
2390
|
+
return replaceComment;
|
|
2391
|
+
}
|
|
2392
|
+
return child;
|
|
2594
2393
|
}
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2394
|
+
return child;
|
|
2395
|
+
}
|
|
2396
|
+
/**
|
|
2397
|
+
* Handle the elements inserted into head and body, and execute normally in other cases
|
|
2398
|
+
* @param app app
|
|
2399
|
+
* @param method raw method
|
|
2400
|
+
* @param parent parent node
|
|
2401
|
+
* @param targetChild target node
|
|
2402
|
+
* @param passiveChild second param of insertBefore and replaceChild
|
|
2403
|
+
*/
|
|
2404
|
+
function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
|
|
2405
|
+
/**
|
|
2406
|
+
* If passiveChild is not the child node, insertBefore replaceChild will have a problem, at this time, it will be degraded to appendChild
|
|
2407
|
+
* E.g: document.head.insertBefore(targetChild, document.head.childNodes[0])
|
|
2408
|
+
*/
|
|
2409
|
+
if (parent === document.head) {
|
|
2410
|
+
const microAppHead = app.container.querySelector('micro-app-head');
|
|
2411
|
+
/**
|
|
2412
|
+
* 1. If passivechild exists, it must be insertBefore or replacechild
|
|
2413
|
+
* 2. When removeChild, targetChild may not be in microAppHead or head
|
|
2414
|
+
*/
|
|
2415
|
+
if (passiveChild && !microAppHead.contains(passiveChild)) {
|
|
2416
|
+
return globalEnv.rawAppendChild.call(microAppHead, targetChild);
|
|
2417
|
+
}
|
|
2418
|
+
else if (rawMethod === globalEnv.rawRemoveChild && !microAppHead.contains(targetChild)) {
|
|
2419
|
+
if (parent.contains(targetChild)) {
|
|
2420
|
+
return rawMethod.call(parent, targetChild);
|
|
2611
2421
|
}
|
|
2422
|
+
return targetChild;
|
|
2423
|
+
}
|
|
2424
|
+
else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
|
|
2425
|
+
return rawMethod.call(microAppHead, targetChild);
|
|
2612
2426
|
}
|
|
2427
|
+
return rawMethod.call(microAppHead, targetChild, passiveChild);
|
|
2613
2428
|
}
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2429
|
+
else if (parent === document.body) {
|
|
2430
|
+
const microAppBody = app.container.querySelector('micro-app-body');
|
|
2431
|
+
if (passiveChild && !microAppBody.contains(passiveChild)) {
|
|
2432
|
+
return globalEnv.rawAppendChild.call(microAppBody, targetChild);
|
|
2433
|
+
}
|
|
2434
|
+
else if (rawMethod === globalEnv.rawRemoveChild && !microAppBody.contains(targetChild)) {
|
|
2435
|
+
if (parent.contains(targetChild)) {
|
|
2436
|
+
return rawMethod.call(parent, targetChild);
|
|
2437
|
+
}
|
|
2438
|
+
return targetChild;
|
|
2439
|
+
}
|
|
2440
|
+
else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
|
|
2441
|
+
return rawMethod.call(microAppBody, targetChild);
|
|
2442
|
+
}
|
|
2443
|
+
return rawMethod.call(microAppBody, targetChild, passiveChild);
|
|
2444
|
+
}
|
|
2445
|
+
else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
|
|
2446
|
+
return rawMethod.call(parent, targetChild);
|
|
2447
|
+
}
|
|
2448
|
+
return rawMethod.call(parent, targetChild, passiveChild);
|
|
2449
|
+
}
|
|
2450
|
+
// Get the map element
|
|
2451
|
+
function getMappingNode(node) {
|
|
2452
|
+
var _a;
|
|
2453
|
+
return (_a = dynamicElementInMicroAppMap.get(node)) !== null && _a !== void 0 ? _a : node;
|
|
2454
|
+
}
|
|
2455
|
+
/**
|
|
2456
|
+
* method of handle new node
|
|
2457
|
+
* @param parent parent node
|
|
2458
|
+
* @param newChild new node
|
|
2459
|
+
* @param passiveChild passive node
|
|
2460
|
+
* @param rawMethodraw method
|
|
2461
|
+
*/
|
|
2462
|
+
function commonElementHander(parent, newChild, passiveChild, rawMethod) {
|
|
2463
|
+
if (newChild === null || newChild === void 0 ? void 0 : newChild.__MICRO_APP_NAME__) {
|
|
2464
|
+
const app = appInstanceMap.get(newChild.__MICRO_APP_NAME__);
|
|
2465
|
+
if (app === null || app === void 0 ? void 0 : app.container) {
|
|
2466
|
+
return invokePrototypeMethod(app, rawMethod, parent, handleNewNode(parent, newChild, app), passiveChild && getMappingNode(passiveChild));
|
|
2467
|
+
}
|
|
2468
|
+
else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
|
|
2469
|
+
return rawMethod.call(parent, newChild);
|
|
2470
|
+
}
|
|
2471
|
+
return rawMethod.call(parent, newChild, passiveChild);
|
|
2472
|
+
}
|
|
2473
|
+
else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
|
|
2474
|
+
const appName = getCurrentAppName();
|
|
2475
|
+
if (!(newChild instanceof Node) && appName) {
|
|
2476
|
+
const app = appInstanceMap.get(appName);
|
|
2477
|
+
if (app === null || app === void 0 ? void 0 : app.container) {
|
|
2478
|
+
if (parent === document.head) {
|
|
2479
|
+
return rawMethod.call(app.container.querySelector('micro-app-head'), newChild);
|
|
2480
|
+
}
|
|
2481
|
+
else if (parent === document.body) {
|
|
2482
|
+
return rawMethod.call(app.container.querySelector('micro-app-body'), newChild);
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
return rawMethod.call(parent, newChild);
|
|
2487
|
+
}
|
|
2488
|
+
return rawMethod.call(parent, newChild, passiveChild);
|
|
2489
|
+
}
|
|
2490
|
+
/**
|
|
2491
|
+
* Rewrite element prototype method
|
|
2492
|
+
*/
|
|
2493
|
+
function patchElementPrototypeMethods() {
|
|
2494
|
+
patchDocument();
|
|
2495
|
+
// Rewrite setAttribute
|
|
2496
|
+
Element.prototype.setAttribute = function setAttribute(key, value) {
|
|
2497
|
+
if (/^micro-app(-\S+)?/i.test(this.tagName) && key === 'data') {
|
|
2498
|
+
if (isPlainObject(value)) {
|
|
2499
|
+
const cloneValue = {};
|
|
2500
|
+
Object.getOwnPropertyNames(value).forEach((propertyKey) => {
|
|
2501
|
+
if (!(isString(propertyKey) && propertyKey.indexOf('__') === 0)) {
|
|
2502
|
+
// @ts-ignore
|
|
2503
|
+
cloneValue[propertyKey] = value[propertyKey];
|
|
2504
|
+
}
|
|
2505
|
+
});
|
|
2506
|
+
this.data = cloneValue;
|
|
2507
|
+
}
|
|
2508
|
+
else if (value !== '[object Object]') {
|
|
2509
|
+
logWarn('property data must be an object', this.getAttribute('name'));
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
else if ((((key === 'src' || key === 'srcset') && /^(img|script)$/i.test(this.tagName)) ||
|
|
2513
|
+
(key === 'href' && /^link$/i.test(this.tagName))) &&
|
|
2514
|
+
this.__MICRO_APP_NAME__ &&
|
|
2515
|
+
appInstanceMap.has(this.__MICRO_APP_NAME__)) {
|
|
2516
|
+
const app = appInstanceMap.get(this.__MICRO_APP_NAME__);
|
|
2517
|
+
globalEnv.rawSetAttribute.call(this, key, CompletionPath(value, app.url));
|
|
2518
|
+
}
|
|
2519
|
+
else {
|
|
2520
|
+
globalEnv.rawSetAttribute.call(this, key, value);
|
|
2521
|
+
}
|
|
2522
|
+
};
|
|
2523
|
+
// prototype methods of add element👇
|
|
2524
|
+
Node.prototype.appendChild = function appendChild(newChild) {
|
|
2525
|
+
return commonElementHander(this, newChild, null, globalEnv.rawAppendChild);
|
|
2526
|
+
};
|
|
2527
|
+
Node.prototype.insertBefore = function insertBefore(newChild, refChild) {
|
|
2528
|
+
return commonElementHander(this, newChild, refChild, globalEnv.rawInsertBefore);
|
|
2529
|
+
};
|
|
2530
|
+
Node.prototype.replaceChild = function replaceChild(newChild, oldChild) {
|
|
2531
|
+
return commonElementHander(this, newChild, oldChild, globalEnv.rawReplaceChild);
|
|
2532
|
+
};
|
|
2533
|
+
Element.prototype.append = function append(...nodes) {
|
|
2534
|
+
let i = 0;
|
|
2535
|
+
const length = nodes.length;
|
|
2536
|
+
while (i < length) {
|
|
2537
|
+
commonElementHander(this, nodes[i], null, globalEnv.rawAppend);
|
|
2538
|
+
i++;
|
|
2539
|
+
}
|
|
2540
|
+
};
|
|
2541
|
+
Element.prototype.prepend = function prepend(...nodes) {
|
|
2542
|
+
let i = nodes.length;
|
|
2543
|
+
while (i > 0) {
|
|
2544
|
+
commonElementHander(this, nodes[i - 1], null, globalEnv.rawPrepend);
|
|
2545
|
+
i--;
|
|
2546
|
+
}
|
|
2547
|
+
};
|
|
2548
|
+
// prototype methods of delete element👇
|
|
2549
|
+
Node.prototype.removeChild = function removeChild(oldChild) {
|
|
2550
|
+
if (oldChild === null || oldChild === void 0 ? void 0 : oldChild.__MICRO_APP_NAME__) {
|
|
2551
|
+
const app = appInstanceMap.get(oldChild.__MICRO_APP_NAME__);
|
|
2552
|
+
if (app === null || app === void 0 ? void 0 : app.container) {
|
|
2553
|
+
return invokePrototypeMethod(app, globalEnv.rawRemoveChild, this, getMappingNode(oldChild));
|
|
2554
|
+
}
|
|
2555
|
+
return globalEnv.rawRemoveChild.call(this, oldChild);
|
|
2556
|
+
}
|
|
2557
|
+
return globalEnv.rawRemoveChild.call(this, oldChild);
|
|
2558
|
+
};
|
|
2559
|
+
}
|
|
2560
|
+
/**
|
|
2561
|
+
* Mark the newly created element in the micro application
|
|
2562
|
+
* @param element new element
|
|
2563
|
+
*/
|
|
2564
|
+
function markElement(element) {
|
|
2565
|
+
const appName = getCurrentAppName();
|
|
2566
|
+
appName && (element.__MICRO_APP_NAME__ = appName);
|
|
2567
|
+
return element;
|
|
2568
|
+
}
|
|
2569
|
+
// methods of document
|
|
2570
|
+
function patchDocument() {
|
|
2571
|
+
const rawDocument = globalEnv.rawDocument;
|
|
2572
|
+
// create element 👇
|
|
2573
|
+
Document.prototype.createElement = function createElement(tagName, options) {
|
|
2574
|
+
const element = globalEnv.rawCreateElement.call(this, tagName, options);
|
|
2575
|
+
return markElement(element);
|
|
2576
|
+
};
|
|
2577
|
+
Document.prototype.createElementNS = function createElementNS(namespaceURI, name, options) {
|
|
2578
|
+
const element = globalEnv.rawCreateElementNS.call(this, namespaceURI, name, options);
|
|
2579
|
+
return markElement(element);
|
|
2580
|
+
};
|
|
2581
|
+
Document.prototype.createDocumentFragment = function createDocumentFragment() {
|
|
2582
|
+
const element = globalEnv.rawCreateDocumentFragment.call(this);
|
|
2583
|
+
return markElement(element);
|
|
2584
|
+
};
|
|
2585
|
+
// query element👇
|
|
2586
|
+
function querySelector(selectors) {
|
|
2587
|
+
var _a, _b, _c;
|
|
2588
|
+
const appName = getCurrentAppName();
|
|
2589
|
+
if (!appName ||
|
|
2590
|
+
!selectors ||
|
|
2591
|
+
isUniqueElement(selectors) ||
|
|
2592
|
+
// see https://github.com/micro-zoe/micro-app/issues/56
|
|
2593
|
+
rawDocument !== this) {
|
|
2594
|
+
return globalEnv.rawQuerySelector.call(this, selectors);
|
|
2595
|
+
}
|
|
2596
|
+
return (_c = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) === null || _b === void 0 ? void 0 : _b.querySelector(selectors)) !== null && _c !== void 0 ? _c : null;
|
|
2623
2597
|
}
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2598
|
+
function querySelectorAll(selectors) {
|
|
2599
|
+
var _a, _b, _c;
|
|
2600
|
+
const appName = getCurrentAppName();
|
|
2601
|
+
if (!appName ||
|
|
2602
|
+
!selectors ||
|
|
2603
|
+
isUniqueElement(selectors) ||
|
|
2604
|
+
rawDocument !== this) {
|
|
2605
|
+
return globalEnv.rawQuerySelectorAll.call(this, selectors);
|
|
2606
|
+
}
|
|
2607
|
+
return (_c = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) === null || _b === void 0 ? void 0 : _b.querySelectorAll(selectors)) !== null && _c !== void 0 ? _c : [];
|
|
2631
2608
|
}
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2609
|
+
Document.prototype.querySelector = querySelector;
|
|
2610
|
+
Document.prototype.querySelectorAll = querySelectorAll;
|
|
2611
|
+
Document.prototype.getElementById = function getElementById(key) {
|
|
2612
|
+
if (!getCurrentAppName() || isInvalidQuerySelectorKey(key)) {
|
|
2613
|
+
return globalEnv.rawGetElementById.call(this, key);
|
|
2614
|
+
}
|
|
2615
|
+
try {
|
|
2616
|
+
return querySelector.call(this, `#${key}`);
|
|
2617
|
+
}
|
|
2618
|
+
catch (_a) {
|
|
2619
|
+
return globalEnv.rawGetElementById.call(this, key);
|
|
2620
|
+
}
|
|
2621
|
+
};
|
|
2622
|
+
Document.prototype.getElementsByClassName = function getElementsByClassName(key) {
|
|
2623
|
+
if (!getCurrentAppName() || isInvalidQuerySelectorKey(key)) {
|
|
2624
|
+
return globalEnv.rawGetElementsByClassName.call(this, key);
|
|
2625
|
+
}
|
|
2626
|
+
try {
|
|
2627
|
+
return querySelectorAll.call(this, `.${key}`);
|
|
2628
|
+
}
|
|
2629
|
+
catch (_a) {
|
|
2630
|
+
return globalEnv.rawGetElementsByClassName.call(this, key);
|
|
2631
|
+
}
|
|
2632
|
+
};
|
|
2633
|
+
Document.prototype.getElementsByTagName = function getElementsByTagName(key) {
|
|
2637
2634
|
var _a;
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
this.scopeProperties = this.scopeProperties.concat(plugin.scopeProperties);
|
|
2645
|
-
}
|
|
2646
|
-
if (toString.call(plugin.escapeProperties) === '[object Array]') {
|
|
2647
|
-
this.escapeProperties = this.escapeProperties.concat(plugin.escapeProperties);
|
|
2648
|
-
}
|
|
2649
|
-
}
|
|
2650
|
-
}
|
|
2635
|
+
const appName = getCurrentAppName();
|
|
2636
|
+
if (!appName ||
|
|
2637
|
+
isUniqueElement(key) ||
|
|
2638
|
+
isInvalidQuerySelectorKey(key) ||
|
|
2639
|
+
(!((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.inline) && /^script$/i.test(key))) {
|
|
2640
|
+
return globalEnv.rawGetElementsByTagName.call(this, key);
|
|
2651
2641
|
}
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
if (typeof plugin === 'object') {
|
|
2655
|
-
if (toString.call(plugin.scopeProperties) === '[object Array]') {
|
|
2656
|
-
this.scopeProperties = this.scopeProperties.concat(plugin.scopeProperties);
|
|
2657
|
-
}
|
|
2658
|
-
if (toString.call(plugin.escapeProperties) === '[object Array]') {
|
|
2659
|
-
this.escapeProperties = this.escapeProperties.concat(plugin.escapeProperties);
|
|
2660
|
-
}
|
|
2661
|
-
}
|
|
2662
|
-
}
|
|
2642
|
+
try {
|
|
2643
|
+
return querySelectorAll.call(this, key);
|
|
2663
2644
|
}
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2645
|
+
catch (_b) {
|
|
2646
|
+
return globalEnv.rawGetElementsByTagName.call(this, key);
|
|
2647
|
+
}
|
|
2648
|
+
};
|
|
2649
|
+
Document.prototype.getElementsByName = function getElementsByName(key) {
|
|
2650
|
+
if (!getCurrentAppName() || isInvalidQuerySelectorKey(key)) {
|
|
2651
|
+
return globalEnv.rawGetElementsByName.call(this, key);
|
|
2652
|
+
}
|
|
2653
|
+
try {
|
|
2654
|
+
return querySelectorAll.call(this, `[name=${key}]`);
|
|
2655
|
+
}
|
|
2656
|
+
catch (_a) {
|
|
2657
|
+
return globalEnv.rawGetElementsByName.call(this, key);
|
|
2658
|
+
}
|
|
2659
|
+
};
|
|
2660
|
+
}
|
|
2661
|
+
function releasePatchDocument() {
|
|
2662
|
+
Document.prototype.createElement = globalEnv.rawCreateElement;
|
|
2663
|
+
Document.prototype.createElementNS = globalEnv.rawCreateElementNS;
|
|
2664
|
+
Document.prototype.createDocumentFragment = globalEnv.rawCreateDocumentFragment;
|
|
2665
|
+
Document.prototype.querySelector = globalEnv.rawQuerySelector;
|
|
2666
|
+
Document.prototype.querySelectorAll = globalEnv.rawQuerySelectorAll;
|
|
2667
|
+
Document.prototype.getElementById = globalEnv.rawGetElementById;
|
|
2668
|
+
Document.prototype.getElementsByClassName = globalEnv.rawGetElementsByClassName;
|
|
2669
|
+
Document.prototype.getElementsByTagName = globalEnv.rawGetElementsByTagName;
|
|
2670
|
+
Document.prototype.getElementsByName = globalEnv.rawGetElementsByName;
|
|
2671
|
+
}
|
|
2672
|
+
// release patch
|
|
2673
|
+
function releasePatches() {
|
|
2674
|
+
setCurrentAppName(null);
|
|
2675
|
+
releasePatchDocument();
|
|
2676
|
+
Element.prototype.setAttribute = globalEnv.rawSetAttribute;
|
|
2677
|
+
Node.prototype.appendChild = globalEnv.rawAppendChild;
|
|
2678
|
+
Node.prototype.insertBefore = globalEnv.rawInsertBefore;
|
|
2679
|
+
Node.prototype.replaceChild = globalEnv.rawReplaceChild;
|
|
2680
|
+
Node.prototype.removeChild = globalEnv.rawRemoveChild;
|
|
2681
|
+
Element.prototype.append = globalEnv.rawAppend;
|
|
2682
|
+
Element.prototype.prepend = globalEnv.rawPrepend;
|
|
2683
|
+
}
|
|
2684
|
+
// Set the style of micro-app-head and micro-app-body
|
|
2685
|
+
let hasRejectMicroAppStyle = false;
|
|
2686
|
+
function rejectMicroAppStyle() {
|
|
2687
|
+
if (!hasRejectMicroAppStyle) {
|
|
2688
|
+
hasRejectMicroAppStyle = true;
|
|
2689
|
+
const style = pureCreateElement('style');
|
|
2690
|
+
style.setAttribute('type', 'text/css');
|
|
2691
|
+
style.textContent = `\n${microApp.tagName}, micro-app-body { display: block; } \nmicro-app-head { display: none; }`;
|
|
2692
|
+
globalEnv.rawDocument.head.appendChild(style);
|
|
2679
2693
|
}
|
|
2680
2694
|
}
|
|
2681
|
-
SandBox.activeCount = 0; // number of active sandbox
|
|
2682
2695
|
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
this.isPrefetch = false;
|
|
2694
|
-
this.container = null;
|
|
2695
|
-
this.macro = false;
|
|
2696
|
-
this.baseroute = '';
|
|
2697
|
-
this.sandBox = null;
|
|
2698
|
-
this.container = container !== null && container !== void 0 ? container : null;
|
|
2699
|
-
this.inline = inline !== null && inline !== void 0 ? inline : false;
|
|
2700
|
-
this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : '';
|
|
2701
|
-
// optional during init👆
|
|
2702
|
-
this.name = name;
|
|
2703
|
-
this.url = url;
|
|
2704
|
-
this.useSandbox = useSandbox;
|
|
2705
|
-
this.scopecss = this.useSandbox && scopecss;
|
|
2706
|
-
this.macro = macro !== null && macro !== void 0 ? macro : false;
|
|
2707
|
-
this.source = {
|
|
2708
|
-
links: new Map(),
|
|
2709
|
-
scripts: new Map(),
|
|
2710
|
-
};
|
|
2711
|
-
this.loadSourceCode();
|
|
2712
|
-
if (this.useSandbox) {
|
|
2713
|
-
this.sandBox = new SandBox(name, url, this.macro);
|
|
2714
|
-
}
|
|
2696
|
+
function unmountNestedApp() {
|
|
2697
|
+
replaseUnmountOfNestedApp();
|
|
2698
|
+
appInstanceMap.forEach(app => {
|
|
2699
|
+
// @ts-ignore
|
|
2700
|
+
app.container && getRootContainer(app.container).disconnectedCallback();
|
|
2701
|
+
});
|
|
2702
|
+
!window.__MICRO_APP_UMD_MODE__ && appInstanceMap.clear();
|
|
2703
|
+
if (elementInstanceMap.size) {
|
|
2704
|
+
elementInstanceMap.clear();
|
|
2705
|
+
releasePatches();
|
|
2715
2706
|
}
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2707
|
+
}
|
|
2708
|
+
// if micro-app run in micro application, delete all next generation application when unmount event received
|
|
2709
|
+
function listenUmountOfNestedApp() {
|
|
2710
|
+
if (window.__MICRO_APP_ENVIRONMENT__) {
|
|
2711
|
+
window.addEventListener('unmount', unmountNestedApp, false);
|
|
2720
2712
|
}
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
this.source.html = html;
|
|
2727
|
-
if (this.isPrefetch || appStatus.UNMOUNT === this.status)
|
|
2728
|
-
return;
|
|
2729
|
-
this.status = appStatus.LOAD_SOURCE_FINISHED;
|
|
2730
|
-
this.mount();
|
|
2731
|
-
}
|
|
2713
|
+
}
|
|
2714
|
+
// release listener
|
|
2715
|
+
function replaseUnmountOfNestedApp() {
|
|
2716
|
+
if (window.__MICRO_APP_ENVIRONMENT__) {
|
|
2717
|
+
window.removeEventListener('unmount', unmountNestedApp, false);
|
|
2732
2718
|
}
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2719
|
+
}
|
|
2720
|
+
|
|
2721
|
+
// record all micro-app elements
|
|
2722
|
+
const elementInstanceMap = new Map();
|
|
2723
|
+
/**
|
|
2724
|
+
* define element
|
|
2725
|
+
* @param tagName element name
|
|
2726
|
+
*/
|
|
2727
|
+
function defineElement(tagName) {
|
|
2728
|
+
class MicroAppElement extends HTMLElement {
|
|
2729
|
+
constructor() {
|
|
2730
|
+
super();
|
|
2731
|
+
this.isWating = false;
|
|
2732
|
+
this.cacheData = null;
|
|
2733
|
+
this.hasConnected = false;
|
|
2734
|
+
this.appName = ''; // app name
|
|
2735
|
+
this.appUrl = ''; // app url
|
|
2736
|
+
this.ssrUrl = ''; // html path in ssr mode
|
|
2737
|
+
this.version = version;
|
|
2738
|
+
/**
|
|
2739
|
+
* handle for change of name an url after element inited
|
|
2740
|
+
*/
|
|
2741
|
+
this.handleAttributeUpdate = () => {
|
|
2742
|
+
var _a;
|
|
2743
|
+
this.isWating = false;
|
|
2744
|
+
const formatAttrName = formatAppName(this.getAttribute('name'));
|
|
2745
|
+
const formatAttrUrl = formatAppURL(this.getAttribute('url'), this.appName);
|
|
2746
|
+
if (this.legalAttribute('name', formatAttrName) && this.legalAttribute('url', formatAttrUrl)) {
|
|
2747
|
+
const existApp = appInstanceMap.get(formatAttrName);
|
|
2748
|
+
if (formatAttrName !== this.appName && existApp) {
|
|
2749
|
+
// handling of cached and non-prefetch apps
|
|
2750
|
+
if (appStatus.UNMOUNT !== existApp.getAppStatus() && !existApp.isPrefetch) {
|
|
2751
|
+
this.setAttribute('name', this.appName);
|
|
2752
|
+
return logError(`an app named ${formatAttrName} already exists`, this.appName);
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
if (formatAttrName !== this.appName || formatAttrUrl !== this.appUrl) {
|
|
2756
|
+
this.handleUnmount(formatAttrName === this.appName);
|
|
2757
|
+
/**
|
|
2758
|
+
* change ssrUrl in ssr mode
|
|
2759
|
+
* do not add judgment of formatAttrUrl === this.appUrl
|
|
2760
|
+
*/
|
|
2761
|
+
if (this.getDisposeResult('ssr')) {
|
|
2762
|
+
this.ssrUrl = CompletionPath(globalEnv.rawWindow.location.pathname, formatAttrUrl);
|
|
2763
|
+
}
|
|
2764
|
+
else if (this.ssrUrl) {
|
|
2765
|
+
this.ssrUrl = '';
|
|
2766
|
+
}
|
|
2767
|
+
this.appName = formatAttrName;
|
|
2768
|
+
this.appUrl = formatAttrUrl;
|
|
2769
|
+
((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this).innerHTML = '';
|
|
2770
|
+
if (formatAttrName !== this.getAttribute('name')) {
|
|
2771
|
+
this.setAttribute('name', this.appName);
|
|
2772
|
+
}
|
|
2773
|
+
/**
|
|
2774
|
+
* when existApp not null:
|
|
2775
|
+
* scene1: if formatAttrName and this.appName are equal: exitApp is the current app, the url must be different, existApp has been unmounted
|
|
2776
|
+
* scene2: if formatAttrName and this.appName are different: existApp must be prefetch or unmounted, if url is equal, then just mount, if url is different, then create new app to replace existApp
|
|
2777
|
+
* scene3: url is different but ssrUrl is equal
|
|
2778
|
+
* scene4: url is equal but ssrUrl is different, if url is equal, name must different
|
|
2779
|
+
*/
|
|
2780
|
+
if (existApp &&
|
|
2781
|
+
existApp.url === this.appUrl &&
|
|
2782
|
+
existApp.ssrUrl === this.ssrUrl) {
|
|
2783
|
+
// mount app
|
|
2784
|
+
this.handleAppMount(existApp);
|
|
2785
|
+
}
|
|
2786
|
+
else {
|
|
2787
|
+
this.handleCreateApp();
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
else if (formatAttrName !== this.appName) {
|
|
2792
|
+
this.setAttribute('name', this.appName);
|
|
2793
|
+
}
|
|
2794
|
+
};
|
|
2795
|
+
// cloned node of umd container also trigger constructor, we should skip
|
|
2796
|
+
if (!this.querySelector('micro-app-head')) {
|
|
2797
|
+
this.performWhenFirstCreated();
|
|
2798
|
+
}
|
|
2742
2799
|
}
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2800
|
+
static get observedAttributes() {
|
|
2801
|
+
return ['name', 'url'];
|
|
2802
|
+
}
|
|
2803
|
+
// 👇 Configuration
|
|
2804
|
+
// name: app name
|
|
2805
|
+
// url: html address
|
|
2806
|
+
// shadowDom: use shadowDOM, default is false
|
|
2807
|
+
// destroy: whether delete cache resources when unmount, default is false
|
|
2808
|
+
// inline: whether js runs in inline script mode, default is false
|
|
2809
|
+
// disableScopecss: whether disable css scoped, default is false
|
|
2810
|
+
// disableSandbox: whether disable sandbox, default is false
|
|
2811
|
+
// macro: used to solve the async render problem of vue3, default is false
|
|
2812
|
+
// baseRoute: route prefix, default is ''
|
|
2813
|
+
connectedCallback() {
|
|
2814
|
+
this.hasConnected = true;
|
|
2815
|
+
if (!elementInstanceMap.has(this)) {
|
|
2816
|
+
this.performWhenFirstCreated();
|
|
2817
|
+
}
|
|
2818
|
+
defer(() => dispatchLifecyclesEvent(this, this.appName, lifeCycles.CREATED));
|
|
2819
|
+
this.initialMount();
|
|
2754
2820
|
}
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
this.
|
|
2759
|
-
|
|
2821
|
+
disconnectedCallback() {
|
|
2822
|
+
this.hasConnected = false;
|
|
2823
|
+
elementInstanceMap.delete(this);
|
|
2824
|
+
this.handleUnmount(this.getDisposeResult('destroy') || this.getDisposeResult('destory'));
|
|
2825
|
+
if (elementInstanceMap.size === 0) {
|
|
2826
|
+
releasePatches();
|
|
2827
|
+
}
|
|
2760
2828
|
}
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
(
|
|
2776
|
-
|
|
2829
|
+
attributeChangedCallback(attr, _oldVal, newVal) {
|
|
2830
|
+
if (this.legalAttribute(attr, newVal) &&
|
|
2831
|
+
this[attr === ObservedAttrName.NAME ? 'appName' : 'appUrl'] !== newVal) {
|
|
2832
|
+
if (attr === ObservedAttrName.URL && !this.appUrl) {
|
|
2833
|
+
newVal = formatAppURL(newVal, this.appName);
|
|
2834
|
+
if (!newVal) {
|
|
2835
|
+
return logError(`Invalid attribute url ${newVal}`, this.appName);
|
|
2836
|
+
}
|
|
2837
|
+
this.appUrl = newVal;
|
|
2838
|
+
this.handleInitialNameAndUrl();
|
|
2839
|
+
}
|
|
2840
|
+
else if (attr === ObservedAttrName.NAME && !this.appName) {
|
|
2841
|
+
const formatNewName = formatAppName(newVal);
|
|
2842
|
+
if (!formatNewName) {
|
|
2843
|
+
return logError(`Invalid attribute name ${newVal}`, this.appName);
|
|
2844
|
+
}
|
|
2845
|
+
if (this.cacheData) {
|
|
2846
|
+
microApp.setData(formatNewName, this.cacheData);
|
|
2847
|
+
this.cacheData = null;
|
|
2777
2848
|
}
|
|
2849
|
+
this.appName = formatNewName;
|
|
2850
|
+
if (formatNewName !== newVal) {
|
|
2851
|
+
this.setAttribute('name', this.appName);
|
|
2852
|
+
}
|
|
2853
|
+
this.handleInitialNameAndUrl();
|
|
2778
2854
|
}
|
|
2779
|
-
if (
|
|
2780
|
-
this.
|
|
2855
|
+
else if (!this.isWating) {
|
|
2856
|
+
this.isWating = true;
|
|
2857
|
+
defer(this.handleAttributeUpdate);
|
|
2781
2858
|
}
|
|
2782
|
-
}
|
|
2859
|
+
}
|
|
2783
2860
|
}
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
this.
|
|
2787
|
-
this.dispatchMountedEvent();
|
|
2861
|
+
// handle for connectedCallback run before attributeChangedCallback
|
|
2862
|
+
handleInitialNameAndUrl() {
|
|
2863
|
+
this.hasConnected && this.initialMount();
|
|
2788
2864
|
}
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
if (appStatus.UNMOUNT !== this.status) {
|
|
2798
|
-
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.MOUNTED);
|
|
2799
|
-
}
|
|
2800
|
-
});
|
|
2865
|
+
// Perform global initialization when the element count is 1
|
|
2866
|
+
performWhenFirstCreated() {
|
|
2867
|
+
if (elementInstanceMap.set(this, true).size === 1) {
|
|
2868
|
+
patchElementPrototypeMethods();
|
|
2869
|
+
rejectMicroAppStyle();
|
|
2870
|
+
replaseUnmountOfNestedApp();
|
|
2871
|
+
listenUmountOfNestedApp();
|
|
2872
|
+
}
|
|
2801
2873
|
}
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2874
|
+
/**
|
|
2875
|
+
* first mount of this app
|
|
2876
|
+
*/
|
|
2877
|
+
initialMount() {
|
|
2878
|
+
if (!this.appName || !this.appUrl)
|
|
2879
|
+
return;
|
|
2880
|
+
if (this.getDisposeResult('shadowDOM') && !this.shadowRoot && isFunction(this.attachShadow)) {
|
|
2881
|
+
this.attachShadow({ mode: 'open' });
|
|
2882
|
+
}
|
|
2883
|
+
if (this.getDisposeResult('ssr')) {
|
|
2884
|
+
this.ssrUrl = CompletionPath(globalEnv.rawWindow.location.pathname, this.appUrl);
|
|
2885
|
+
}
|
|
2886
|
+
else if (this.ssrUrl) {
|
|
2887
|
+
this.ssrUrl = '';
|
|
2888
|
+
}
|
|
2889
|
+
const app = appInstanceMap.get(this.appName);
|
|
2890
|
+
if (app) {
|
|
2891
|
+
const existAppUrl = app.ssrUrl || app.url;
|
|
2892
|
+
const activeAppUrl = this.ssrUrl || this.appUrl;
|
|
2893
|
+
if (existAppUrl === activeAppUrl && (app.isPrefetch ||
|
|
2894
|
+
app.getAppStatus() === appStatus.UNMOUNT)) {
|
|
2895
|
+
this.handleAppMount(app);
|
|
2896
|
+
}
|
|
2897
|
+
else if (app.isPrefetch || app.getAppStatus() === appStatus.UNMOUNT) {
|
|
2898
|
+
/**
|
|
2899
|
+
* url is different & old app is unmounted or prefetch, create new app to replace old one
|
|
2900
|
+
*/
|
|
2901
|
+
logWarn(`the ${app.isPrefetch ? 'prefetch' : 'unmounted'} app with url: ${existAppUrl} is replaced by a new app`, this.appName);
|
|
2902
|
+
this.handleCreateApp();
|
|
2903
|
+
}
|
|
2904
|
+
else {
|
|
2905
|
+
logError(`an app named ${this.appName} already exists`, this.appName);
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
else {
|
|
2909
|
+
this.handleCreateApp();
|
|
2910
|
+
}
|
|
2811
2911
|
}
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
if (!this.useSandbox && this.umdMode) {
|
|
2822
|
-
delete window[this.libraryName];
|
|
2912
|
+
/**
|
|
2913
|
+
* judge the attribute is legal
|
|
2914
|
+
* @param name attribute name
|
|
2915
|
+
* @param val attribute value
|
|
2916
|
+
*/
|
|
2917
|
+
legalAttribute(name, val) {
|
|
2918
|
+
if (!isString(val) || !val) {
|
|
2919
|
+
logError(`unexpected attribute ${name}, please check again`, this.appName);
|
|
2920
|
+
return false;
|
|
2823
2921
|
}
|
|
2824
|
-
|
|
2922
|
+
return true;
|
|
2923
|
+
}
|
|
2924
|
+
/**
|
|
2925
|
+
* mount app
|
|
2926
|
+
* some serious note before mount:
|
|
2927
|
+
* 1. is prefetch ?
|
|
2928
|
+
* 2. is remount in another container ?
|
|
2929
|
+
* 3. is remount with change properties of the container ?
|
|
2930
|
+
*/
|
|
2931
|
+
handleAppMount(app) {
|
|
2932
|
+
app.isPrefetch = false;
|
|
2933
|
+
defer(() => {
|
|
2934
|
+
var _a;
|
|
2935
|
+
return app.mount((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this, this.getDisposeResult('inline'), this.getBaseRouteCompatible());
|
|
2936
|
+
});
|
|
2825
2937
|
}
|
|
2826
|
-
|
|
2938
|
+
// create app instance
|
|
2939
|
+
handleCreateApp() {
|
|
2940
|
+
var _a;
|
|
2827
2941
|
/**
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2942
|
+
* actions for destory old app
|
|
2943
|
+
* fix of unmounted umd app with disableSandbox
|
|
2944
|
+
*/
|
|
2945
|
+
if (appInstanceMap.has(this.appName)) {
|
|
2946
|
+
appInstanceMap.get(this.appName).actionsForCompletelyDestory();
|
|
2947
|
+
}
|
|
2948
|
+
const instance = new CreateApp({
|
|
2949
|
+
name: this.appName,
|
|
2950
|
+
url: this.appUrl,
|
|
2951
|
+
ssrUrl: this.ssrUrl,
|
|
2952
|
+
container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
|
|
2953
|
+
inline: this.getDisposeResult('inline'),
|
|
2954
|
+
scopecss: !(this.getDisposeResult('disableScopecss') || this.getDisposeResult('shadowDOM')),
|
|
2955
|
+
useSandbox: !this.getDisposeResult('disableSandbox'),
|
|
2956
|
+
macro: this.getDisposeResult('macro'),
|
|
2957
|
+
baseroute: this.getBaseRouteCompatible(),
|
|
2958
|
+
});
|
|
2959
|
+
appInstanceMap.set(this.appName, instance);
|
|
2831
2960
|
}
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2961
|
+
/**
|
|
2962
|
+
* unmount app
|
|
2963
|
+
* @param destroy delete cache resources when unmount
|
|
2964
|
+
*/
|
|
2965
|
+
handleUnmount(destroy) {
|
|
2966
|
+
const app = appInstanceMap.get(this.appName);
|
|
2967
|
+
if (app && appStatus.UNMOUNT !== app.getAppStatus())
|
|
2968
|
+
app.unmount(destroy);
|
|
2969
|
+
}
|
|
2970
|
+
/**
|
|
2971
|
+
* Get configuration
|
|
2972
|
+
* Global setting is lowest priority
|
|
2973
|
+
* @param name Configuration item name
|
|
2974
|
+
*/
|
|
2975
|
+
getDisposeResult(name) {
|
|
2976
|
+
// @ts-ignore
|
|
2977
|
+
return (this.hasAttribute(name) || microApp[name]) && this.getAttribute(name) !== 'false';
|
|
2978
|
+
}
|
|
2979
|
+
/**
|
|
2980
|
+
* 2021-09-08
|
|
2981
|
+
* get baseRoute
|
|
2982
|
+
* getAttribute('baseurl') is compatible writing of versions below 0.3.1
|
|
2983
|
+
*/
|
|
2984
|
+
getBaseRouteCompatible() {
|
|
2985
|
+
var _a, _b;
|
|
2986
|
+
return (_b = (_a = this.getAttribute('baseroute')) !== null && _a !== void 0 ? _a : this.getAttribute('baseurl')) !== null && _b !== void 0 ? _b : '';
|
|
2987
|
+
}
|
|
2988
|
+
/**
|
|
2989
|
+
* Data from the base application
|
|
2990
|
+
*/
|
|
2991
|
+
set data(value) {
|
|
2992
|
+
if (this.appName) {
|
|
2993
|
+
microApp.setData(this.appName, value);
|
|
2994
|
+
}
|
|
2995
|
+
else {
|
|
2996
|
+
this.cacheData = value;
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
/**
|
|
3000
|
+
* get data only used in jsx-custom-event once
|
|
3001
|
+
*/
|
|
3002
|
+
get data() {
|
|
3003
|
+
if (this.appName) {
|
|
3004
|
+
return microApp.getData(this.appName, true);
|
|
3005
|
+
}
|
|
3006
|
+
else if (this.cacheData) {
|
|
3007
|
+
return this.cacheData;
|
|
3008
|
+
}
|
|
3009
|
+
return null;
|
|
2853
3010
|
}
|
|
2854
|
-
return {};
|
|
2855
3011
|
}
|
|
3012
|
+
window.customElements.define(tagName, MicroAppElement);
|
|
2856
3013
|
}
|
|
2857
3014
|
|
|
2858
3015
|
function filterPreFetchTarget(apps) {
|
|
2859
3016
|
const validApps = [];
|
|
2860
|
-
if (
|
|
3017
|
+
if (isArray(apps)) {
|
|
2861
3018
|
apps.forEach((item) => {
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
item.url &&
|
|
2866
|
-
|
|
2867
|
-
|
|
3019
|
+
if (isPlainObject(item)) {
|
|
3020
|
+
item.name = formatAppName(item.name);
|
|
3021
|
+
item.url = formatAppURL(item.url, item.name);
|
|
3022
|
+
if (item.name && item.url && !appInstanceMap.has(item.name)) {
|
|
3023
|
+
validApps.push(item);
|
|
3024
|
+
}
|
|
2868
3025
|
}
|
|
2869
3026
|
});
|
|
2870
3027
|
}
|
|
@@ -2891,8 +3048,7 @@ function preFetch(apps) {
|
|
|
2891
3048
|
return logError('preFetch is only supported in browser environment');
|
|
2892
3049
|
}
|
|
2893
3050
|
requestIdleCallback(() => {
|
|
2894
|
-
|
|
2895
|
-
apps = apps();
|
|
3051
|
+
isFunction(apps) && (apps = apps());
|
|
2896
3052
|
filterPreFetchTarget(apps).forEach((item) => {
|
|
2897
3053
|
var _a, _b, _c;
|
|
2898
3054
|
const app = new CreateApp({
|
|
@@ -2912,10 +3068,10 @@ function preFetch(apps) {
|
|
|
2912
3068
|
* @param assets global assets of js, css
|
|
2913
3069
|
*/
|
|
2914
3070
|
function getGlobalAssets(assets) {
|
|
2915
|
-
if (
|
|
3071
|
+
if (isPlainObject(assets)) {
|
|
2916
3072
|
requestIdleCallback(() => {
|
|
2917
|
-
if (
|
|
2918
|
-
const effectiveJs = assets.js.filter((path) =>
|
|
3073
|
+
if (isArray(assets.js)) {
|
|
3074
|
+
const effectiveJs = assets.js.filter((path) => isString(path) && path.includes('.js') && !globalScripts.has(path));
|
|
2919
3075
|
const fetchJSPromise = [];
|
|
2920
3076
|
effectiveJs.forEach((path) => {
|
|
2921
3077
|
fetchJSPromise.push(fetchSource(path));
|
|
@@ -2930,8 +3086,8 @@ function getGlobalAssets(assets) {
|
|
|
2930
3086
|
logError(err);
|
|
2931
3087
|
});
|
|
2932
3088
|
}
|
|
2933
|
-
if (
|
|
2934
|
-
const effectiveCss = assets.css.filter((path) =>
|
|
3089
|
+
if (isArray(assets.css)) {
|
|
3090
|
+
const effectiveCss = assets.css.filter((path) => isString(path) && path.includes('.css') && !globalLinks.has(path));
|
|
2935
3091
|
const fetchCssPromise = [];
|
|
2936
3092
|
effectiveCss.forEach((path) => {
|
|
2937
3093
|
fetchCssPromise.push(fetchSource(path));
|
|
@@ -2950,6 +3106,67 @@ function getGlobalAssets(assets) {
|
|
|
2950
3106
|
}
|
|
2951
3107
|
}
|
|
2952
3108
|
|
|
3109
|
+
class MicroApp extends EventCenterForBaseApp {
|
|
3110
|
+
constructor() {
|
|
3111
|
+
super(...arguments);
|
|
3112
|
+
this.tagName = 'micro-app';
|
|
3113
|
+
this.preFetch = preFetch;
|
|
3114
|
+
}
|
|
3115
|
+
start(options) {
|
|
3116
|
+
if (!isBrowser || !window.customElements) {
|
|
3117
|
+
return logError('micro-app is not supported in this environment');
|
|
3118
|
+
}
|
|
3119
|
+
if (options === null || options === void 0 ? void 0 : options.tagName) {
|
|
3120
|
+
if (/^micro-app(-\S+)?/.test(options.tagName)) {
|
|
3121
|
+
this.tagName = options.tagName;
|
|
3122
|
+
}
|
|
3123
|
+
else {
|
|
3124
|
+
return logError(`${options.tagName} is invalid tagName`);
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
if (window.customElements.get(this.tagName)) {
|
|
3128
|
+
return logWarn(`element ${this.tagName} is already defined`);
|
|
3129
|
+
}
|
|
3130
|
+
initGlobalEnv();
|
|
3131
|
+
if (options && isPlainObject(options)) {
|
|
3132
|
+
this.shadowDOM = options.shadowDOM;
|
|
3133
|
+
this.destroy = options.destroy;
|
|
3134
|
+
/**
|
|
3135
|
+
* compatible with versions below 0.4.2 of destroy
|
|
3136
|
+
* do not merge with the previous line
|
|
3137
|
+
*/
|
|
3138
|
+
// @ts-ignore
|
|
3139
|
+
this.destory = options.destory;
|
|
3140
|
+
this.inline = options.inline;
|
|
3141
|
+
this.disableScopecss = options.disableScopecss;
|
|
3142
|
+
this.disableSandbox = options.disableSandbox;
|
|
3143
|
+
this.macro = options.macro;
|
|
3144
|
+
isFunction(options.fetch) && (this.fetch = options.fetch);
|
|
3145
|
+
isPlainObject(options.lifeCycles) && (this.lifeCycles = options.lifeCycles);
|
|
3146
|
+
// load app assets when browser is idle
|
|
3147
|
+
options.preFetchApps && preFetch(options.preFetchApps);
|
|
3148
|
+
// load global assets when browser is idle
|
|
3149
|
+
options.globalAssets && getGlobalAssets(options.globalAssets);
|
|
3150
|
+
if (isPlainObject(options.plugins)) {
|
|
3151
|
+
const modules = options.plugins.modules;
|
|
3152
|
+
if (isPlainObject(modules)) {
|
|
3153
|
+
for (const appName in modules) {
|
|
3154
|
+
const formattedAppName = formatAppName(appName);
|
|
3155
|
+
if (formattedAppName && appName !== formattedAppName) {
|
|
3156
|
+
modules[formattedAppName] = modules[appName];
|
|
3157
|
+
delete modules[appName];
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
3161
|
+
this.plugins = options.plugins;
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
// define customElement after init
|
|
3165
|
+
defineElement(this.tagName);
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
var microApp = new MicroApp();
|
|
3169
|
+
|
|
2953
3170
|
export default microApp;
|
|
2954
|
-
export { preFetch, pureCreateElement, removeDomScope,
|
|
3171
|
+
export { EventCenterForMicroApp, getActiveApps, getAllApps, preFetch, pureCreateElement, removeDomScope, version };
|
|
2955
3172
|
//# sourceMappingURL=index.esm.js.map
|