@micro-zoe/micro-app 0.8.0 → 0.8.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/lib/index.d.ts +8 -8
- package/lib/index.esm.js +1961 -2002
- 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 +1 -1
- package/typings/global.d.ts +2 -2
package/lib/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const version = '0.8.
|
|
1
|
+
const version = '0.8.1';
|
|
2
2
|
// do not use isUndefined
|
|
3
3
|
const isBrowser = typeof window !== 'undefined';
|
|
4
4
|
// do not use isUndefined
|
|
@@ -153,10 +153,10 @@ function CompletionPath(path, baseURI) {
|
|
|
153
153
|
/**
|
|
154
154
|
* Get the folder where the link resource is located,
|
|
155
155
|
* which is used to complete the relative address in the css
|
|
156
|
-
* @param
|
|
156
|
+
* @param linkPath full link address
|
|
157
157
|
*/
|
|
158
|
-
function getLinkFileDir(
|
|
159
|
-
const pathArr =
|
|
158
|
+
function getLinkFileDir(linkPath) {
|
|
159
|
+
const pathArr = linkPath.split('/');
|
|
160
160
|
pathArr.pop();
|
|
161
161
|
return addProtocol(pathArr.join('/') + '/');
|
|
162
162
|
}
|
|
@@ -233,13 +233,10 @@ let currentMicroAppName = null;
|
|
|
233
233
|
function setCurrentAppName(appName) {
|
|
234
234
|
currentMicroAppName = appName;
|
|
235
235
|
}
|
|
236
|
-
let isWaitingForReset = false;
|
|
237
236
|
function throttleDeferForSetAppName(appName) {
|
|
238
|
-
if (
|
|
239
|
-
isWaitingForReset = true;
|
|
237
|
+
if (currentMicroAppName !== appName) {
|
|
240
238
|
setCurrentAppName(appName);
|
|
241
239
|
defer(() => {
|
|
242
|
-
isWaitingForReset = false;
|
|
243
240
|
setCurrentAppName(null);
|
|
244
241
|
});
|
|
245
242
|
}
|
|
@@ -363,12 +360,12 @@ function fetchSource(url, appName = null, options = {}) {
|
|
|
363
360
|
const rootSelectorREG = /(^|\s+)(html|:root)(?=[\s>~[.#:]+|$)/;
|
|
364
361
|
const bodySelectorREG = /(^|\s+)((html[\s>~]+body)|body)(?=[\s>~[.#:]+|$)/;
|
|
365
362
|
const cssUrlREG = /url\(["']?([^)"']+)["']?\)/gm;
|
|
366
|
-
function parseError(msg,
|
|
367
|
-
msg =
|
|
363
|
+
function parseError(msg, linkPath) {
|
|
364
|
+
msg = linkPath ? `${linkPath}:${msg}` : msg;
|
|
368
365
|
const err = new Error(msg);
|
|
369
366
|
err.reason = msg;
|
|
370
|
-
if (
|
|
371
|
-
err.filename =
|
|
367
|
+
if (linkPath) {
|
|
368
|
+
err.filename = linkPath;
|
|
372
369
|
}
|
|
373
370
|
throw err;
|
|
374
371
|
}
|
|
@@ -376,14 +373,14 @@ function parseError(msg, linkpath) {
|
|
|
376
373
|
* Reference resources https://github.com/reworkcss/css
|
|
377
374
|
* CSSParser mainly deals with 3 scenes: styleRule, @, and comment
|
|
378
375
|
* And scopecss deals with 2 scenes: selector & url
|
|
379
|
-
*
|
|
376
|
+
* And can also disable scopecss with inline comments
|
|
380
377
|
*/
|
|
381
378
|
class CSSParser {
|
|
382
379
|
constructor() {
|
|
383
380
|
this.cssText = ''; // css content
|
|
384
381
|
this.prefix = ''; // prefix as micro-app[name=xxx]
|
|
385
382
|
this.baseURI = ''; // domain name
|
|
386
|
-
this.
|
|
383
|
+
this.linkPath = ''; // link resource address, if it is the style converted from link, it will have linkPath
|
|
387
384
|
this.result = ''; // parsed cssText
|
|
388
385
|
this.scopecssDisable = false; // use block comments /* scopecss-disable */ to disable scopecss in your file, and use /* scopecss-enable */ to enable scopecss
|
|
389
386
|
this.scopecssDisableSelectors = []; // disable or enable scopecss for specific selectors
|
|
@@ -401,16 +398,16 @@ class CSSParser {
|
|
|
401
398
|
// https://developer.mozilla.org/en-US/docs/Web/API/CSSNamespaceRule
|
|
402
399
|
this.namespaceRule = this.createMatcherForNoneBraceAtRule('namespace');
|
|
403
400
|
}
|
|
404
|
-
exec(cssText, prefix, baseURI,
|
|
401
|
+
exec(cssText, prefix, baseURI, linkPath) {
|
|
405
402
|
this.cssText = cssText;
|
|
406
403
|
this.prefix = prefix;
|
|
407
404
|
this.baseURI = baseURI;
|
|
408
|
-
this.
|
|
405
|
+
this.linkPath = linkPath || '';
|
|
409
406
|
this.matchRules();
|
|
410
407
|
return this.result;
|
|
411
408
|
}
|
|
412
409
|
reset() {
|
|
413
|
-
this.cssText = this.prefix = this.baseURI = this.
|
|
410
|
+
this.cssText = this.prefix = this.baseURI = this.linkPath = this.result = '';
|
|
414
411
|
this.scopecssDisable = this.scopecssDisableNextLine = false;
|
|
415
412
|
this.scopecssDisableSelectors = [];
|
|
416
413
|
}
|
|
@@ -430,7 +427,7 @@ class CSSParser {
|
|
|
430
427
|
// reset scopecssDisableNextLine
|
|
431
428
|
this.scopecssDisableNextLine = false;
|
|
432
429
|
if (!selectorList)
|
|
433
|
-
return parseError('selector missing', this.
|
|
430
|
+
return parseError('selector missing', this.linkPath);
|
|
434
431
|
this.result += selectorList.join(', ');
|
|
435
432
|
this.matchComments();
|
|
436
433
|
this.styleDeclarations();
|
|
@@ -440,23 +437,23 @@ class CSSParser {
|
|
|
440
437
|
// https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration
|
|
441
438
|
styleDeclarations() {
|
|
442
439
|
if (!this.matchOpenBrace())
|
|
443
|
-
return parseError("Declaration missing '{'", this.
|
|
440
|
+
return parseError("Declaration missing '{'", this.linkPath);
|
|
444
441
|
this.matchComments();
|
|
445
442
|
while (this.styleDeclaration()) {
|
|
446
443
|
this.matchComments();
|
|
447
444
|
}
|
|
448
445
|
if (!this.matchCloseBrace())
|
|
449
|
-
return parseError("Declaration missing '}'", this.
|
|
446
|
+
return parseError("Declaration missing '}'", this.linkPath);
|
|
450
447
|
return true;
|
|
451
448
|
}
|
|
452
449
|
// match one styleDeclaration at a time
|
|
453
450
|
styleDeclaration() {
|
|
454
451
|
// css property
|
|
455
|
-
if (!this.commonMatch(/^(\*?[
|
|
452
|
+
if (!this.commonMatch(/^(\*?[-#+\/\*\\\w]+(\[[0-9a-z_-]+\])?)\s*/))
|
|
456
453
|
return false;
|
|
457
454
|
// match :
|
|
458
455
|
if (!this.commonMatch(/^:\s*/))
|
|
459
|
-
return parseError("property missing ':'", this.
|
|
456
|
+
return parseError("property missing ':'", this.linkPath);
|
|
460
457
|
// match css value
|
|
461
458
|
const r = this.commonMatch(/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^\)]*?\)|[^};])+)/, true);
|
|
462
459
|
let cssValue = r ? r[0] : '';
|
|
@@ -467,8 +464,8 @@ class CSSParser {
|
|
|
467
464
|
return all;
|
|
468
465
|
}
|
|
469
466
|
// ./a/b.png ../a/b.png a/b.png
|
|
470
|
-
if (/^((\.\.?\/)|[^/])/.test($1) && this.
|
|
471
|
-
this.baseURI = getLinkFileDir(this.
|
|
467
|
+
if (/^((\.\.?\/)|[^/])/.test($1) && this.linkPath) {
|
|
468
|
+
this.baseURI = getLinkFileDir(this.linkPath);
|
|
472
469
|
}
|
|
473
470
|
return `url("${CompletionPath($1, this.baseURI)}")`;
|
|
474
471
|
});
|
|
@@ -520,7 +517,7 @@ class CSSParser {
|
|
|
520
517
|
this.scopecssDisableNextLine = false;
|
|
521
518
|
return this.keyframesRule() ||
|
|
522
519
|
this.mediaRule() ||
|
|
523
|
-
this.
|
|
520
|
+
this.customMediaRule() ||
|
|
524
521
|
this.supportsRule() ||
|
|
525
522
|
this.importRule() ||
|
|
526
523
|
this.charsetRule() ||
|
|
@@ -528,23 +525,23 @@ class CSSParser {
|
|
|
528
525
|
this.documentRule() ||
|
|
529
526
|
this.pageRule() ||
|
|
530
527
|
this.hostRule() ||
|
|
531
|
-
this.
|
|
528
|
+
this.fontFaceRule();
|
|
532
529
|
}
|
|
533
530
|
// https://developer.mozilla.org/en-US/docs/Web/API/CSSKeyframesRule
|
|
534
531
|
keyframesRule() {
|
|
535
532
|
if (!this.commonMatch(/^@([-\w]+)?keyframes\s*/))
|
|
536
533
|
return false;
|
|
537
534
|
if (!this.commonMatch(/^([-\w]+)\s*/))
|
|
538
|
-
return parseError('@keyframes missing name', this.
|
|
535
|
+
return parseError('@keyframes missing name', this.linkPath);
|
|
539
536
|
this.matchComments();
|
|
540
537
|
if (!this.matchOpenBrace())
|
|
541
|
-
return parseError("@keyframes missing '{'", this.
|
|
538
|
+
return parseError("@keyframes missing '{'", this.linkPath);
|
|
542
539
|
this.matchComments();
|
|
543
540
|
while (this.keyframeRule()) {
|
|
544
541
|
this.matchComments();
|
|
545
542
|
}
|
|
546
543
|
if (!this.matchCloseBrace())
|
|
547
|
-
return parseError("@keyframes missing '}'", this.
|
|
544
|
+
return parseError("@keyframes missing '}'", this.linkPath);
|
|
548
545
|
this.matchLeadingSpaces();
|
|
549
546
|
return true;
|
|
550
547
|
}
|
|
@@ -562,7 +559,7 @@ class CSSParser {
|
|
|
562
559
|
return true;
|
|
563
560
|
}
|
|
564
561
|
// https://github.com/postcss/postcss-custom-media
|
|
565
|
-
|
|
562
|
+
customMediaRule() {
|
|
566
563
|
if (!this.commonMatch(/^@custom-media\s+(--[^\s]+)\s*([^{;]+);/))
|
|
567
564
|
return false;
|
|
568
565
|
this.matchLeadingSpaces();
|
|
@@ -578,7 +575,7 @@ class CSSParser {
|
|
|
578
575
|
return this.commonHandlerForAtRuleWithSelfRule('page');
|
|
579
576
|
}
|
|
580
577
|
// https://developer.mozilla.org/en-US/docs/Web/API/CSSFontFaceRule
|
|
581
|
-
|
|
578
|
+
fontFaceRule() {
|
|
582
579
|
if (!this.commonMatch(/^@font-face\s*/))
|
|
583
580
|
return false;
|
|
584
581
|
return this.commonHandlerForAtRuleWithSelfRule('font-face');
|
|
@@ -589,11 +586,11 @@ class CSSParser {
|
|
|
589
586
|
if (!this.commonMatch(reg))
|
|
590
587
|
return false;
|
|
591
588
|
if (!this.matchOpenBrace())
|
|
592
|
-
return parseError(`@${name} missing '{'`, this.
|
|
589
|
+
return parseError(`@${name} missing '{'`, this.linkPath);
|
|
593
590
|
this.matchComments();
|
|
594
591
|
this.matchRules();
|
|
595
592
|
if (!this.matchCloseBrace())
|
|
596
|
-
return parseError(`@${name} missing '}'`, this.
|
|
593
|
+
return parseError(`@${name} missing '}'`, this.linkPath);
|
|
597
594
|
this.matchLeadingSpaces();
|
|
598
595
|
return true;
|
|
599
596
|
};
|
|
@@ -611,13 +608,13 @@ class CSSParser {
|
|
|
611
608
|
// common handler for @font-face, @page
|
|
612
609
|
commonHandlerForAtRuleWithSelfRule(name) {
|
|
613
610
|
if (!this.matchOpenBrace())
|
|
614
|
-
return parseError(`@${name} missing '{'`, this.
|
|
611
|
+
return parseError(`@${name} missing '{'`, this.linkPath);
|
|
615
612
|
this.matchComments();
|
|
616
613
|
while (this.styleDeclaration()) {
|
|
617
614
|
this.matchComments();
|
|
618
615
|
}
|
|
619
616
|
if (!this.matchCloseBrace())
|
|
620
|
-
return parseError(`@${name} missing '}'`, this.
|
|
617
|
+
return parseError(`@${name} missing '}'`, this.linkPath);
|
|
621
618
|
this.matchLeadingSpaces();
|
|
622
619
|
return true;
|
|
623
620
|
}
|
|
@@ -637,7 +634,7 @@ class CSSParser {
|
|
|
637
634
|
++i;
|
|
638
635
|
i += 2;
|
|
639
636
|
if (this.cssText.charAt(i - 1) === '') {
|
|
640
|
-
return parseError('End of comment missing', this.
|
|
637
|
+
return parseError('End of comment missing', this.linkPath);
|
|
641
638
|
}
|
|
642
639
|
// get comment content
|
|
643
640
|
let commentText = this.cssText.slice(2, i - 2);
|
|
@@ -691,12 +688,12 @@ class CSSParser {
|
|
|
691
688
|
/**
|
|
692
689
|
* common method of bind CSS
|
|
693
690
|
*/
|
|
694
|
-
function commonAction(styleElement, appName, prefix, baseURI,
|
|
691
|
+
function commonAction(styleElement, appName, prefix, baseURI, linkPath) {
|
|
695
692
|
if (!styleElement.__MICRO_APP_HAS_SCOPED__) {
|
|
696
693
|
styleElement.__MICRO_APP_HAS_SCOPED__ = true;
|
|
697
694
|
let result = null;
|
|
698
695
|
try {
|
|
699
|
-
result = parser.exec(styleElement.textContent, prefix, baseURI,
|
|
696
|
+
result = parser.exec(styleElement.textContent, prefix, baseURI, linkPath);
|
|
700
697
|
parser.reset();
|
|
701
698
|
}
|
|
702
699
|
catch (e) {
|
|
@@ -883,7 +880,7 @@ function fetchLinkSuccess(url, info, data, microAppHead, app) {
|
|
|
883
880
|
* @param originLink origin link element
|
|
884
881
|
* @param replaceStyle style element which replaced origin link
|
|
885
882
|
*/
|
|
886
|
-
function
|
|
883
|
+
function formatDynamicLink(url, info, app, originLink, replaceStyle) {
|
|
887
884
|
if (app.source.links.has(url)) {
|
|
888
885
|
replaceStyle.textContent = app.source.links.get(url).code;
|
|
889
886
|
scopedCSS(replaceStyle, app);
|
|
@@ -912,2162 +909,2144 @@ function foramtDynamicLink(url, info, app, originLink, replaceStyle) {
|
|
|
912
909
|
});
|
|
913
910
|
}
|
|
914
911
|
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
* Note loop nesting
|
|
918
|
-
* Only prototype or unique values can be put here
|
|
919
|
-
*/
|
|
920
|
-
function initGlobalEnv() {
|
|
921
|
-
if (isBrowser) {
|
|
922
|
-
/**
|
|
923
|
-
* save patch raw methods
|
|
924
|
-
* pay attention to this binding
|
|
925
|
-
*/
|
|
926
|
-
const rawSetAttribute = Element.prototype.setAttribute;
|
|
927
|
-
const rawAppendChild = Element.prototype.appendChild;
|
|
928
|
-
const rawInsertBefore = Element.prototype.insertBefore;
|
|
929
|
-
const rawReplaceChild = Element.prototype.replaceChild;
|
|
930
|
-
const rawRemoveChild = Element.prototype.removeChild;
|
|
931
|
-
const rawAppend = Element.prototype.append;
|
|
932
|
-
const rawPrepend = Element.prototype.prepend;
|
|
933
|
-
const rawCloneNode = Element.prototype.cloneNode;
|
|
934
|
-
const rawCreateElement = Document.prototype.createElement;
|
|
935
|
-
const rawCreateElementNS = Document.prototype.createElementNS;
|
|
936
|
-
const rawCreateDocumentFragment = Document.prototype.createDocumentFragment;
|
|
937
|
-
const rawQuerySelector = Document.prototype.querySelector;
|
|
938
|
-
const rawQuerySelectorAll = Document.prototype.querySelectorAll;
|
|
939
|
-
const rawGetElementById = Document.prototype.getElementById;
|
|
940
|
-
const rawGetElementsByClassName = Document.prototype.getElementsByClassName;
|
|
941
|
-
const rawGetElementsByTagName = Document.prototype.getElementsByTagName;
|
|
942
|
-
const rawGetElementsByName = Document.prototype.getElementsByName;
|
|
943
|
-
const ImageProxy = new Proxy(Image, {
|
|
944
|
-
construct(Target, args) {
|
|
945
|
-
const elementImage = new Target(...args);
|
|
946
|
-
elementImage.__MICRO_APP_NAME__ = getCurrentAppName();
|
|
947
|
-
return elementImage;
|
|
948
|
-
},
|
|
949
|
-
});
|
|
950
|
-
const rawWindow = Function('return window')();
|
|
951
|
-
const rawDocument = Function('return document')();
|
|
952
|
-
const supportModuleScript = isSupportModuleScript();
|
|
953
|
-
/**
|
|
954
|
-
* save effect raw methods
|
|
955
|
-
* pay attention to this binding, especially setInterval, setTimeout, clearInterval, clearTimeout
|
|
956
|
-
*/
|
|
957
|
-
const rawWindowAddEventListener = rawWindow.addEventListener;
|
|
958
|
-
const rawWindowRemoveEventListener = rawWindow.removeEventListener;
|
|
959
|
-
const rawSetInterval = rawWindow.setInterval;
|
|
960
|
-
const rawSetTimeout = rawWindow.setTimeout;
|
|
961
|
-
const rawClearInterval = rawWindow.clearInterval;
|
|
962
|
-
const rawClearTimeout = rawWindow.clearTimeout;
|
|
963
|
-
const rawDocumentAddEventListener = rawDocument.addEventListener;
|
|
964
|
-
const rawDocumentRemoveEventListener = rawDocument.removeEventListener;
|
|
965
|
-
// mark current application as base application
|
|
966
|
-
window.__MICRO_APP_BASE_APPLICATION__ = true;
|
|
967
|
-
Object.assign(globalEnv, {
|
|
968
|
-
// source/patch
|
|
969
|
-
rawSetAttribute,
|
|
970
|
-
rawAppendChild,
|
|
971
|
-
rawInsertBefore,
|
|
972
|
-
rawReplaceChild,
|
|
973
|
-
rawRemoveChild,
|
|
974
|
-
rawAppend,
|
|
975
|
-
rawPrepend,
|
|
976
|
-
rawCloneNode,
|
|
977
|
-
rawCreateElement,
|
|
978
|
-
rawCreateElementNS,
|
|
979
|
-
rawCreateDocumentFragment,
|
|
980
|
-
rawQuerySelector,
|
|
981
|
-
rawQuerySelectorAll,
|
|
982
|
-
rawGetElementById,
|
|
983
|
-
rawGetElementsByClassName,
|
|
984
|
-
rawGetElementsByTagName,
|
|
985
|
-
rawGetElementsByName,
|
|
986
|
-
ImageProxy,
|
|
987
|
-
// common global vars
|
|
988
|
-
rawWindow,
|
|
989
|
-
rawDocument,
|
|
990
|
-
supportModuleScript,
|
|
991
|
-
// sandbox/effect
|
|
992
|
-
rawWindowAddEventListener,
|
|
993
|
-
rawWindowRemoveEventListener,
|
|
994
|
-
rawSetInterval,
|
|
995
|
-
rawSetTimeout,
|
|
996
|
-
rawClearInterval,
|
|
997
|
-
rawClearTimeout,
|
|
998
|
-
rawDocumentAddEventListener,
|
|
999
|
-
rawDocumentRemoveEventListener,
|
|
1000
|
-
});
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
// Global scripts, reuse across apps
|
|
1005
|
-
const globalScripts = new Map();
|
|
912
|
+
// Record element and map element
|
|
913
|
+
const dynamicElementInMicroAppMap = new WeakMap();
|
|
1006
914
|
/**
|
|
1007
|
-
*
|
|
1008
|
-
* @param
|
|
1009
|
-
* @param
|
|
915
|
+
* Process the new node and format the style, link and script element
|
|
916
|
+
* @param parent parent node
|
|
917
|
+
* @param child new node
|
|
1010
918
|
* @param app app
|
|
1011
|
-
* @param isDynamic dynamic insert
|
|
1012
919
|
*/
|
|
1013
|
-
function
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
else if ((script.type && !['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module'].includes(script.type)) ||
|
|
1020
|
-
script.hasAttribute('ignore')) {
|
|
1021
|
-
return null;
|
|
1022
|
-
}
|
|
1023
|
-
else if ((globalEnv.supportModuleScript && script.noModule) ||
|
|
1024
|
-
(!globalEnv.supportModuleScript && script.type === 'module')) {
|
|
1025
|
-
replaceComment = document.createComment(`${script.noModule ? 'noModule' : 'module'} script ignored by micro-app`);
|
|
1026
|
-
}
|
|
1027
|
-
else if (src) { // remote script
|
|
1028
|
-
src = CompletionPath(src, app.url);
|
|
1029
|
-
const info = {
|
|
1030
|
-
code: '',
|
|
1031
|
-
isExternal: true,
|
|
1032
|
-
isDynamic: isDynamic,
|
|
1033
|
-
async: script.hasAttribute('async'),
|
|
1034
|
-
defer: script.defer || script.type === 'module',
|
|
1035
|
-
module: script.type === 'module',
|
|
1036
|
-
isGlobal: script.hasAttribute('global'),
|
|
1037
|
-
};
|
|
1038
|
-
if (!isDynamic) {
|
|
1039
|
-
app.source.scripts.set(src, info);
|
|
1040
|
-
replaceComment = document.createComment(`script with src='${src}' extract by micro-app`);
|
|
920
|
+
function handleNewNode(parent, child, app) {
|
|
921
|
+
if (child instanceof HTMLStyleElement) {
|
|
922
|
+
if (child.hasAttribute('exclude')) {
|
|
923
|
+
const replaceComment = document.createComment('style element with exclude attribute ignored by micro-app');
|
|
924
|
+
dynamicElementInMicroAppMap.set(child, replaceComment);
|
|
925
|
+
return replaceComment;
|
|
1041
926
|
}
|
|
1042
|
-
else {
|
|
1043
|
-
return
|
|
927
|
+
else if (app.scopecss && !child.hasAttribute('ignore')) {
|
|
928
|
+
return scopedCSS(child, app);
|
|
1044
929
|
}
|
|
930
|
+
return child;
|
|
1045
931
|
}
|
|
1046
|
-
else if (
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
isDynamic: isDynamic,
|
|
1052
|
-
async: false,
|
|
1053
|
-
defer: script.type === 'module',
|
|
1054
|
-
module: script.type === 'module',
|
|
1055
|
-
};
|
|
1056
|
-
if (!isDynamic) {
|
|
1057
|
-
app.source.scripts.set(nonceStr, info);
|
|
1058
|
-
replaceComment = document.createComment('inline script extract by micro-app');
|
|
932
|
+
else if (child instanceof HTMLLinkElement) {
|
|
933
|
+
if (child.hasAttribute('exclude')) {
|
|
934
|
+
const linkReplaceComment = document.createComment('link element with exclude attribute ignored by micro-app');
|
|
935
|
+
dynamicElementInMicroAppMap.set(child, linkReplaceComment);
|
|
936
|
+
return linkReplaceComment;
|
|
1059
937
|
}
|
|
1060
|
-
else {
|
|
1061
|
-
return
|
|
938
|
+
else if (child.hasAttribute('ignore')) {
|
|
939
|
+
return child;
|
|
1062
940
|
}
|
|
941
|
+
const { url, info, replaceComment } = extractLinkFromHtml(child, parent, app, true);
|
|
942
|
+
if (url && info) {
|
|
943
|
+
const replaceStyle = pureCreateElement('style');
|
|
944
|
+
replaceStyle.__MICRO_APP_LINK_PATH__ = url;
|
|
945
|
+
formatDynamicLink(url, info, app, child, replaceStyle);
|
|
946
|
+
dynamicElementInMicroAppMap.set(child, replaceStyle);
|
|
947
|
+
return replaceStyle;
|
|
948
|
+
}
|
|
949
|
+
else if (replaceComment) {
|
|
950
|
+
dynamicElementInMicroAppMap.set(child, replaceComment);
|
|
951
|
+
return replaceComment;
|
|
952
|
+
}
|
|
953
|
+
return child;
|
|
1063
954
|
}
|
|
1064
|
-
else if (
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
if (isDynamic) {
|
|
1072
|
-
return { replaceComment };
|
|
1073
|
-
}
|
|
1074
|
-
else {
|
|
1075
|
-
return parent.replaceChild(replaceComment, script);
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
/**
|
|
1079
|
-
* Get remote resources of script
|
|
1080
|
-
* @param wrapElement htmlDom
|
|
1081
|
-
* @param app app
|
|
1082
|
-
*/
|
|
1083
|
-
function fetchScriptsFromHtml(wrapElement, app) {
|
|
1084
|
-
const scriptEntries = Array.from(app.source.scripts.entries());
|
|
1085
|
-
const fetchScriptPromise = [];
|
|
1086
|
-
const fetchScriptPromiseInfo = [];
|
|
1087
|
-
for (const [url, info] of scriptEntries) {
|
|
1088
|
-
if (info.isExternal) {
|
|
1089
|
-
const globalScriptText = globalScripts.get(url);
|
|
1090
|
-
if (globalScriptText) {
|
|
1091
|
-
info.code = globalScriptText;
|
|
955
|
+
else if (child instanceof HTMLScriptElement) {
|
|
956
|
+
const { replaceComment, url, info } = extractScriptElement(child, parent, app, true) || {};
|
|
957
|
+
if (url && info) {
|
|
958
|
+
if (!info.isExternal) { // inline script
|
|
959
|
+
const replaceElement = runScript(url, app, info, true);
|
|
960
|
+
dynamicElementInMicroAppMap.set(child, replaceElement);
|
|
961
|
+
return replaceElement;
|
|
1092
962
|
}
|
|
1093
|
-
else
|
|
1094
|
-
|
|
1095
|
-
|
|
963
|
+
else { // remote script
|
|
964
|
+
const replaceElement = runDynamicRemoteScript(url, info, app, child);
|
|
965
|
+
dynamicElementInMicroAppMap.set(child, replaceElement);
|
|
966
|
+
return replaceElement;
|
|
1096
967
|
}
|
|
1097
968
|
}
|
|
969
|
+
else if (replaceComment) {
|
|
970
|
+
dynamicElementInMicroAppMap.set(child, replaceComment);
|
|
971
|
+
return replaceComment;
|
|
972
|
+
}
|
|
973
|
+
return child;
|
|
1098
974
|
}
|
|
1099
|
-
|
|
1100
|
-
promiseStream(fetchScriptPromise, (res) => {
|
|
1101
|
-
fetchScriptSuccess(fetchScriptPromiseInfo[res.index][0], fetchScriptPromiseInfo[res.index][1], res.data);
|
|
1102
|
-
}, (err) => {
|
|
1103
|
-
logError(err, app.name);
|
|
1104
|
-
}, () => {
|
|
1105
|
-
app.onLoad(wrapElement);
|
|
1106
|
-
});
|
|
1107
|
-
}
|
|
1108
|
-
else {
|
|
1109
|
-
app.onLoad(wrapElement);
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
/**
|
|
1113
|
-
* fetch js succeeded, record the code value
|
|
1114
|
-
* @param url script address
|
|
1115
|
-
* @param info resource script info
|
|
1116
|
-
* @param data code
|
|
1117
|
-
*/
|
|
1118
|
-
function fetchScriptSuccess(url, info, data) {
|
|
1119
|
-
if (info.isGlobal && !globalScripts.has(url)) {
|
|
1120
|
-
globalScripts.set(url, data);
|
|
1121
|
-
}
|
|
1122
|
-
info.code = data;
|
|
975
|
+
return child;
|
|
1123
976
|
}
|
|
1124
977
|
/**
|
|
1125
|
-
*
|
|
1126
|
-
* @param scriptList script list
|
|
978
|
+
* Handle the elements inserted into head and body, and execute normally in other cases
|
|
1127
979
|
* @param app app
|
|
1128
|
-
* @param
|
|
980
|
+
* @param method raw method
|
|
981
|
+
* @param parent parent node
|
|
982
|
+
* @param targetChild target node
|
|
983
|
+
* @param passiveChild second param of insertBefore and replaceChild
|
|
1129
984
|
*/
|
|
1130
|
-
function
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
else {
|
|
1148
|
-
runScript(url, app, info, false);
|
|
1149
|
-
initedHook(false);
|
|
985
|
+
function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
|
|
986
|
+
/**
|
|
987
|
+
* If passiveChild is not the child node, insertBefore replaceChild will have a problem, at this time, it will be degraded to appendChild
|
|
988
|
+
* E.g: document.head.insertBefore(targetChild, document.head.childNodes[0])
|
|
989
|
+
*/
|
|
990
|
+
if (parent === document.head) {
|
|
991
|
+
const microAppHead = app.container.querySelector('micro-app-head');
|
|
992
|
+
/**
|
|
993
|
+
* 1. If passiveChild exists, it must be insertBefore or replaceChild
|
|
994
|
+
* 2. When removeChild, targetChild may not be in microAppHead or head
|
|
995
|
+
*/
|
|
996
|
+
if (passiveChild && !microAppHead.contains(passiveChild)) {
|
|
997
|
+
return globalEnv.rawAppendChild.call(microAppHead, targetChild);
|
|
998
|
+
}
|
|
999
|
+
else if (rawMethod === globalEnv.rawRemoveChild && !microAppHead.contains(targetChild)) {
|
|
1000
|
+
if (parent.contains(targetChild)) {
|
|
1001
|
+
return rawMethod.call(parent, targetChild);
|
|
1150
1002
|
}
|
|
1003
|
+
return targetChild;
|
|
1004
|
+
}
|
|
1005
|
+
else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
|
|
1006
|
+
return rawMethod.call(microAppHead, targetChild);
|
|
1151
1007
|
}
|
|
1008
|
+
return rawMethod.call(microAppHead, targetChild, passiveChild);
|
|
1152
1009
|
}
|
|
1153
|
-
if (
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
initedHook.errorCount === deferScriptPromise.length);
|
|
1169
|
-
});
|
|
1010
|
+
else if (parent === document.body) {
|
|
1011
|
+
const microAppBody = app.container.querySelector('micro-app-body');
|
|
1012
|
+
if (passiveChild && !microAppBody.contains(passiveChild)) {
|
|
1013
|
+
return globalEnv.rawAppendChild.call(microAppBody, targetChild);
|
|
1014
|
+
}
|
|
1015
|
+
else if (rawMethod === globalEnv.rawRemoveChild && !microAppBody.contains(targetChild)) {
|
|
1016
|
+
if (parent.contains(targetChild)) {
|
|
1017
|
+
return rawMethod.call(parent, targetChild);
|
|
1018
|
+
}
|
|
1019
|
+
return targetChild;
|
|
1020
|
+
}
|
|
1021
|
+
else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
|
|
1022
|
+
return rawMethod.call(microAppBody, targetChild);
|
|
1023
|
+
}
|
|
1024
|
+
return rawMethod.call(microAppBody, targetChild, passiveChild);
|
|
1170
1025
|
}
|
|
1171
|
-
else {
|
|
1172
|
-
|
|
1026
|
+
else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
|
|
1027
|
+
return rawMethod.call(parent, targetChild);
|
|
1173
1028
|
}
|
|
1029
|
+
return rawMethod.call(parent, targetChild, passiveChild);
|
|
1030
|
+
}
|
|
1031
|
+
// Get the map element
|
|
1032
|
+
function getMappingNode(node) {
|
|
1033
|
+
var _a;
|
|
1034
|
+
return (_a = dynamicElementInMicroAppMap.get(node)) !== null && _a !== void 0 ? _a : node;
|
|
1174
1035
|
}
|
|
1175
1036
|
/**
|
|
1176
|
-
*
|
|
1177
|
-
* @param
|
|
1178
|
-
* @param
|
|
1179
|
-
* @param
|
|
1180
|
-
* @param
|
|
1181
|
-
* @param callback callback of module script
|
|
1037
|
+
* method of handle new node
|
|
1038
|
+
* @param parent parent node
|
|
1039
|
+
* @param newChild new node
|
|
1040
|
+
* @param passiveChild passive node
|
|
1041
|
+
* @param rawMethod method
|
|
1182
1042
|
*/
|
|
1183
|
-
function
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
const scriptElement = pureCreateElement('script');
|
|
1189
|
-
runCode2InlineScript(url, code, info.module, scriptElement, callback);
|
|
1190
|
-
if (isDynamic)
|
|
1191
|
-
return scriptElement;
|
|
1192
|
-
// TEST IGNORE
|
|
1193
|
-
(_a = app.container) === null || _a === void 0 ? void 0 : _a.querySelector('micro-app-body').appendChild(scriptElement);
|
|
1043
|
+
function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
|
|
1044
|
+
if (newChild === null || newChild === void 0 ? void 0 : newChild.__MICRO_APP_NAME__) {
|
|
1045
|
+
const app = appInstanceMap.get(newChild.__MICRO_APP_NAME__);
|
|
1046
|
+
if (app === null || app === void 0 ? void 0 : app.container) {
|
|
1047
|
+
return invokePrototypeMethod(app, rawMethod, parent, handleNewNode(parent, newChild, app), passiveChild && getMappingNode(passiveChild));
|
|
1194
1048
|
}
|
|
1195
|
-
else {
|
|
1196
|
-
|
|
1197
|
-
if (isDynamic)
|
|
1198
|
-
return document.createComment('dynamic script extract by micro-app');
|
|
1049
|
+
else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
|
|
1050
|
+
return rawMethod.call(parent, newChild);
|
|
1199
1051
|
}
|
|
1052
|
+
return rawMethod.call(parent, newChild, passiveChild);
|
|
1200
1053
|
}
|
|
1201
|
-
|
|
1202
|
-
|
|
1054
|
+
else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
|
|
1055
|
+
const appName = getCurrentAppName();
|
|
1056
|
+
if (!(newChild instanceof Node) && appName) {
|
|
1057
|
+
const app = appInstanceMap.get(appName);
|
|
1058
|
+
if (app === null || app === void 0 ? void 0 : app.container) {
|
|
1059
|
+
if (parent === document.head) {
|
|
1060
|
+
return rawMethod.call(app.container.querySelector('micro-app-head'), newChild);
|
|
1061
|
+
}
|
|
1062
|
+
else if (parent === document.body) {
|
|
1063
|
+
return rawMethod.call(app.container.querySelector('micro-app-body'), newChild);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
return rawMethod.call(parent, newChild);
|
|
1203
1068
|
}
|
|
1069
|
+
return rawMethod.call(parent, newChild, passiveChild);
|
|
1204
1070
|
}
|
|
1205
1071
|
/**
|
|
1206
|
-
*
|
|
1207
|
-
* @param url script address
|
|
1208
|
-
* @param info info
|
|
1209
|
-
* @param app app
|
|
1210
|
-
* @param originScript origin script element
|
|
1072
|
+
* Rewrite element prototype method
|
|
1211
1073
|
*/
|
|
1212
|
-
function
|
|
1213
|
-
|
|
1214
|
-
//
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
}
|
|
1231
|
-
else {
|
|
1232
|
-
replaceElement = document.createComment(`dynamic script with src='${url}' extract by micro-app`);
|
|
1233
|
-
}
|
|
1234
|
-
fetchSource(url, app.name).then((code) => {
|
|
1235
|
-
info.code = code;
|
|
1236
|
-
app.source.scripts.set(url, info);
|
|
1237
|
-
info.isGlobal && globalScripts.set(url, code);
|
|
1238
|
-
try {
|
|
1239
|
-
code = bindScope(url, app, code, info.module);
|
|
1240
|
-
if (app.inline || info.module) {
|
|
1241
|
-
runCode2InlineScript(url, code, info.module, replaceElement, dispatchScriptOnLoadEvent);
|
|
1242
|
-
}
|
|
1243
|
-
else {
|
|
1244
|
-
runCode2Function(code, info);
|
|
1245
|
-
}
|
|
1074
|
+
function patchElementPrototypeMethods() {
|
|
1075
|
+
patchDocument();
|
|
1076
|
+
// prototype methods of add element👇
|
|
1077
|
+
Element.prototype.appendChild = function appendChild(newChild) {
|
|
1078
|
+
return commonElementHandler(this, newChild, null, globalEnv.rawAppendChild);
|
|
1079
|
+
};
|
|
1080
|
+
Element.prototype.insertBefore = function insertBefore(newChild, refChild) {
|
|
1081
|
+
return commonElementHandler(this, newChild, refChild, globalEnv.rawInsertBefore);
|
|
1082
|
+
};
|
|
1083
|
+
Element.prototype.replaceChild = function replaceChild(newChild, oldChild) {
|
|
1084
|
+
return commonElementHandler(this, newChild, oldChild, globalEnv.rawReplaceChild);
|
|
1085
|
+
};
|
|
1086
|
+
Element.prototype.append = function append(...nodes) {
|
|
1087
|
+
let i = 0;
|
|
1088
|
+
const length = nodes.length;
|
|
1089
|
+
while (i < length) {
|
|
1090
|
+
commonElementHandler(this, nodes[i], null, globalEnv.rawAppend);
|
|
1091
|
+
i++;
|
|
1246
1092
|
}
|
|
1247
|
-
|
|
1248
|
-
|
|
1093
|
+
};
|
|
1094
|
+
Element.prototype.prepend = function prepend(...nodes) {
|
|
1095
|
+
let i = nodes.length;
|
|
1096
|
+
while (i > 0) {
|
|
1097
|
+
commonElementHandler(this, nodes[i - 1], null, globalEnv.rawPrepend);
|
|
1098
|
+
i--;
|
|
1249
1099
|
}
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
* @param url script address
|
|
1260
|
-
* @param code bound code
|
|
1261
|
-
* @param module type='module' of script
|
|
1262
|
-
* @param scriptElement target script element
|
|
1263
|
-
* @param callback callback of module script
|
|
1264
|
-
*/
|
|
1265
|
-
function runCode2InlineScript(url, code, module, scriptElement, callback) {
|
|
1266
|
-
if (module) {
|
|
1267
|
-
// module script is async, transform it to a blob for subsequent operations
|
|
1268
|
-
const blob = new Blob([code], { type: 'text/javascript' });
|
|
1269
|
-
scriptElement.src = URL.createObjectURL(blob);
|
|
1270
|
-
scriptElement.setAttribute('type', 'module');
|
|
1271
|
-
if (callback) {
|
|
1272
|
-
callback.moduleCount && callback.moduleCount--;
|
|
1273
|
-
scriptElement.onload = callback.bind(scriptElement, callback.moduleCount === 0);
|
|
1100
|
+
};
|
|
1101
|
+
// prototype methods of delete element👇
|
|
1102
|
+
Element.prototype.removeChild = function removeChild(oldChild) {
|
|
1103
|
+
if (oldChild === null || oldChild === void 0 ? void 0 : oldChild.__MICRO_APP_NAME__) {
|
|
1104
|
+
const app = appInstanceMap.get(oldChild.__MICRO_APP_NAME__);
|
|
1105
|
+
if (app === null || app === void 0 ? void 0 : app.container) {
|
|
1106
|
+
return invokePrototypeMethod(app, globalEnv.rawRemoveChild, this, getMappingNode(oldChild));
|
|
1107
|
+
}
|
|
1108
|
+
return globalEnv.rawRemoveChild.call(this, oldChild);
|
|
1274
1109
|
}
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
}
|
|
1283
|
-
// init & run code2Function
|
|
1284
|
-
function runCode2Function(code, info) {
|
|
1285
|
-
if (!info.code2Function) {
|
|
1286
|
-
info.code2Function = new Function(code);
|
|
1287
|
-
}
|
|
1288
|
-
info.code2Function.call(window);
|
|
1110
|
+
return globalEnv.rawRemoveChild.call(this, oldChild);
|
|
1111
|
+
};
|
|
1112
|
+
// patch cloneNode
|
|
1113
|
+
Element.prototype.cloneNode = function cloneNode(deep) {
|
|
1114
|
+
const clonedNode = globalEnv.rawCloneNode.call(this, deep);
|
|
1115
|
+
this.__MICRO_APP_NAME__ && (clonedNode.__MICRO_APP_NAME__ = this.__MICRO_APP_NAME__);
|
|
1116
|
+
return clonedNode;
|
|
1117
|
+
};
|
|
1289
1118
|
}
|
|
1290
1119
|
/**
|
|
1291
|
-
*
|
|
1292
|
-
* @param
|
|
1293
|
-
* @param app app
|
|
1294
|
-
* @param code code
|
|
1295
|
-
* @param module type='module' of script
|
|
1120
|
+
* Mark the newly created element in the micro application
|
|
1121
|
+
* @param element new element
|
|
1296
1122
|
*/
|
|
1297
|
-
function
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
if (app.sandBox && !module) {
|
|
1302
|
-
globalEnv.rawWindow.__MICRO_APP_PROXY_WINDOW__ = app.sandBox.proxyWindow;
|
|
1303
|
-
return `;(function(proxyWindow){with(proxyWindow.__MICRO_APP_WINDOW__){(function(${globalKeyToBeCached}){;${code}\n}).call(proxyWindow,${globalKeyToBeCached})}})(window.__MICRO_APP_PROXY_WINDOW__);`;
|
|
1304
|
-
}
|
|
1305
|
-
return code;
|
|
1123
|
+
function markElement(element) {
|
|
1124
|
+
const appName = getCurrentAppName();
|
|
1125
|
+
appName && (element.__MICRO_APP_NAME__ = appName);
|
|
1126
|
+
return element;
|
|
1306
1127
|
}
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1128
|
+
// methods of document
|
|
1129
|
+
function patchDocument() {
|
|
1130
|
+
const rawDocument = globalEnv.rawDocument;
|
|
1131
|
+
// create element 👇
|
|
1132
|
+
Document.prototype.createElement = function createElement(tagName, options) {
|
|
1133
|
+
const element = globalEnv.rawCreateElement.call(this, tagName, options);
|
|
1134
|
+
return markElement(element);
|
|
1135
|
+
};
|
|
1136
|
+
Document.prototype.createElementNS = function createElementNS(namespaceURI, name, options) {
|
|
1137
|
+
const element = globalEnv.rawCreateElementNS.call(this, namespaceURI, name, options);
|
|
1138
|
+
return markElement(element);
|
|
1139
|
+
};
|
|
1140
|
+
Document.prototype.createDocumentFragment = function createDocumentFragment() {
|
|
1141
|
+
const element = globalEnv.rawCreateDocumentFragment.call(this);
|
|
1142
|
+
return markElement(element);
|
|
1143
|
+
};
|
|
1144
|
+
// query element👇
|
|
1145
|
+
function querySelector(selectors) {
|
|
1146
|
+
var _a, _b, _c;
|
|
1147
|
+
const appName = getCurrentAppName();
|
|
1148
|
+
if (!appName ||
|
|
1149
|
+
!selectors ||
|
|
1150
|
+
isUniqueElement(selectors) ||
|
|
1151
|
+
// see https://github.com/micro-zoe/micro-app/issues/56
|
|
1152
|
+
rawDocument !== this) {
|
|
1153
|
+
return globalEnv.rawQuerySelector.call(this, selectors);
|
|
1321
1154
|
}
|
|
1155
|
+
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;
|
|
1322
1156
|
}
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1157
|
+
function querySelectorAll(selectors) {
|
|
1158
|
+
var _a, _b, _c;
|
|
1159
|
+
const appName = getCurrentAppName();
|
|
1160
|
+
if (!appName ||
|
|
1161
|
+
!selectors ||
|
|
1162
|
+
isUniqueElement(selectors) ||
|
|
1163
|
+
rawDocument !== this) {
|
|
1164
|
+
return globalEnv.rawQuerySelectorAll.call(this, selectors);
|
|
1328
1165
|
}
|
|
1166
|
+
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 : [];
|
|
1329
1167
|
}
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1168
|
+
Document.prototype.querySelector = querySelector;
|
|
1169
|
+
Document.prototype.querySelectorAll = querySelectorAll;
|
|
1170
|
+
Document.prototype.getElementById = function getElementById(key) {
|
|
1171
|
+
if (!getCurrentAppName() || isInvalidQuerySelectorKey(key)) {
|
|
1172
|
+
return globalEnv.rawGetElementById.call(this, key);
|
|
1173
|
+
}
|
|
1174
|
+
try {
|
|
1175
|
+
return querySelector.call(this, `#${key}`);
|
|
1176
|
+
}
|
|
1177
|
+
catch (_a) {
|
|
1178
|
+
return globalEnv.rawGetElementById.call(this, key);
|
|
1179
|
+
}
|
|
1180
|
+
};
|
|
1181
|
+
Document.prototype.getElementsByClassName = function getElementsByClassName(key) {
|
|
1182
|
+
if (!getCurrentAppName() || isInvalidQuerySelectorKey(key)) {
|
|
1183
|
+
return globalEnv.rawGetElementsByClassName.call(this, key);
|
|
1184
|
+
}
|
|
1185
|
+
try {
|
|
1186
|
+
return querySelectorAll.call(this, `.${key}`);
|
|
1187
|
+
}
|
|
1188
|
+
catch (_a) {
|
|
1189
|
+
return globalEnv.rawGetElementsByClassName.call(this, key);
|
|
1190
|
+
}
|
|
1191
|
+
};
|
|
1192
|
+
Document.prototype.getElementsByTagName = function getElementsByTagName(key) {
|
|
1193
|
+
var _a;
|
|
1194
|
+
const appName = getCurrentAppName();
|
|
1195
|
+
if (!appName ||
|
|
1196
|
+
isUniqueElement(key) ||
|
|
1197
|
+
isInvalidQuerySelectorKey(key) ||
|
|
1198
|
+
(!((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.inline) && /^script$/i.test(key))) {
|
|
1199
|
+
return globalEnv.rawGetElementsByTagName.call(this, key);
|
|
1200
|
+
}
|
|
1201
|
+
try {
|
|
1202
|
+
return querySelectorAll.call(this, key);
|
|
1203
|
+
}
|
|
1204
|
+
catch (_b) {
|
|
1205
|
+
return globalEnv.rawGetElementsByTagName.call(this, key);
|
|
1206
|
+
}
|
|
1207
|
+
};
|
|
1208
|
+
Document.prototype.getElementsByName = function getElementsByName(key) {
|
|
1209
|
+
if (!getCurrentAppName() || isInvalidQuerySelectorKey(key)) {
|
|
1210
|
+
return globalEnv.rawGetElementsByName.call(this, key);
|
|
1211
|
+
}
|
|
1212
|
+
try {
|
|
1213
|
+
return querySelectorAll.call(this, `[name=${key}]`);
|
|
1214
|
+
}
|
|
1215
|
+
catch (_a) {
|
|
1216
|
+
return globalEnv.rawGetElementsByName.call(this, key);
|
|
1217
|
+
}
|
|
1218
|
+
};
|
|
1341
1219
|
}
|
|
1342
1220
|
/**
|
|
1343
|
-
*
|
|
1344
|
-
*
|
|
1345
|
-
*
|
|
1346
|
-
* @param microAppHead micro-app-head element
|
|
1221
|
+
* patchSetAttribute is different from other patch
|
|
1222
|
+
* it not dependent on sandbox
|
|
1223
|
+
* it should exec when micro-app first created & release when all app unmounted
|
|
1347
1224
|
*/
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
if (
|
|
1355
|
-
if (
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
}
|
|
1365
|
-
else if (dom instanceof HTMLStyleElement) {
|
|
1366
|
-
if (dom.hasAttribute('exclude')) {
|
|
1367
|
-
parent.replaceChild(document.createComment('style element with exclude attribute ignored by micro-app'), dom);
|
|
1225
|
+
let hasRewriteSetAttribute = false;
|
|
1226
|
+
function patchSetAttribute() {
|
|
1227
|
+
if (hasRewriteSetAttribute)
|
|
1228
|
+
return;
|
|
1229
|
+
hasRewriteSetAttribute = true;
|
|
1230
|
+
Element.prototype.setAttribute = function setAttribute(key, value) {
|
|
1231
|
+
if (/^micro-app(-\S+)?/i.test(this.tagName) && key === 'data') {
|
|
1232
|
+
if (isPlainObject(value)) {
|
|
1233
|
+
const cloneValue = {};
|
|
1234
|
+
Object.getOwnPropertyNames(value).forEach((propertyKey) => {
|
|
1235
|
+
if (!(isString(propertyKey) && propertyKey.indexOf('__') === 0)) {
|
|
1236
|
+
// @ts-ignore
|
|
1237
|
+
cloneValue[propertyKey] = value[propertyKey];
|
|
1238
|
+
}
|
|
1239
|
+
});
|
|
1240
|
+
this.data = cloneValue;
|
|
1368
1241
|
}
|
|
1369
|
-
else if (
|
|
1370
|
-
|
|
1242
|
+
else if (value !== '[object Object]') {
|
|
1243
|
+
logWarn('property data must be an object', this.getAttribute('name'));
|
|
1371
1244
|
}
|
|
1372
1245
|
}
|
|
1373
|
-
else if (
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1246
|
+
else if ((((key === 'src' || key === 'srcset') && /^(img|script)$/i.test(this.tagName)) ||
|
|
1247
|
+
(key === 'href' && /^link$/i.test(this.tagName))) &&
|
|
1248
|
+
this.__MICRO_APP_NAME__ &&
|
|
1249
|
+
appInstanceMap.has(this.__MICRO_APP_NAME__)) {
|
|
1250
|
+
const app = appInstanceMap.get(this.__MICRO_APP_NAME__);
|
|
1251
|
+
globalEnv.rawSetAttribute.call(this, key, CompletionPath(value, app.url));
|
|
1378
1252
|
}
|
|
1379
|
-
else
|
|
1380
|
-
|
|
1253
|
+
else {
|
|
1254
|
+
globalEnv.rawSetAttribute.call(this, key, value);
|
|
1381
1255
|
}
|
|
1382
|
-
}
|
|
1256
|
+
};
|
|
1383
1257
|
}
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1258
|
+
function releasePatchSetAttribute() {
|
|
1259
|
+
hasRewriteSetAttribute = false;
|
|
1260
|
+
Element.prototype.setAttribute = globalEnv.rawSetAttribute;
|
|
1261
|
+
}
|
|
1262
|
+
function releasePatchDocument() {
|
|
1263
|
+
Document.prototype.createElement = globalEnv.rawCreateElement;
|
|
1264
|
+
Document.prototype.createElementNS = globalEnv.rawCreateElementNS;
|
|
1265
|
+
Document.prototype.createDocumentFragment = globalEnv.rawCreateDocumentFragment;
|
|
1266
|
+
Document.prototype.querySelector = globalEnv.rawQuerySelector;
|
|
1267
|
+
Document.prototype.querySelectorAll = globalEnv.rawQuerySelectorAll;
|
|
1268
|
+
Document.prototype.getElementById = globalEnv.rawGetElementById;
|
|
1269
|
+
Document.prototype.getElementsByClassName = globalEnv.rawGetElementsByClassName;
|
|
1270
|
+
Document.prototype.getElementsByTagName = globalEnv.rawGetElementsByTagName;
|
|
1271
|
+
Document.prototype.getElementsByName = globalEnv.rawGetElementsByName;
|
|
1272
|
+
}
|
|
1273
|
+
// release patch
|
|
1274
|
+
function releasePatches() {
|
|
1275
|
+
setCurrentAppName(null);
|
|
1276
|
+
releasePatchDocument();
|
|
1277
|
+
Element.prototype.appendChild = globalEnv.rawAppendChild;
|
|
1278
|
+
Element.prototype.insertBefore = globalEnv.rawInsertBefore;
|
|
1279
|
+
Element.prototype.replaceChild = globalEnv.rawReplaceChild;
|
|
1280
|
+
Element.prototype.removeChild = globalEnv.rawRemoveChild;
|
|
1281
|
+
Element.prototype.append = globalEnv.rawAppend;
|
|
1282
|
+
Element.prototype.prepend = globalEnv.rawPrepend;
|
|
1283
|
+
Element.prototype.cloneNode = globalEnv.rawCloneNode;
|
|
1284
|
+
}
|
|
1285
|
+
// Set the style of micro-app-head and micro-app-body
|
|
1286
|
+
let hasRejectMicroAppStyle = false;
|
|
1287
|
+
function rejectMicroAppStyle() {
|
|
1288
|
+
if (!hasRejectMicroAppStyle) {
|
|
1289
|
+
hasRejectMicroAppStyle = true;
|
|
1290
|
+
const style = pureCreateElement('style');
|
|
1291
|
+
globalEnv.rawSetAttribute.call(style, 'type', 'text/css');
|
|
1292
|
+
style.textContent = `\n${microApp.tagName}, micro-app-body { display: block; } \nmicro-app-head { display: none; }`;
|
|
1293
|
+
globalEnv.rawDocument.head.appendChild(style);
|
|
1404
1294
|
}
|
|
1405
|
-
|
|
1406
|
-
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
function unmountNestedApp() {
|
|
1298
|
+
releaseUnmountOfNestedApp();
|
|
1299
|
+
appInstanceMap.forEach(app => {
|
|
1300
|
+
// @ts-ignore
|
|
1301
|
+
app.container && getRootContainer(app.container).disconnectedCallback();
|
|
1302
|
+
});
|
|
1303
|
+
!window.__MICRO_APP_UMD_MODE__ && appInstanceMap.clear();
|
|
1304
|
+
}
|
|
1305
|
+
// if micro-app run in micro application, delete all next generation application when unmount event received
|
|
1306
|
+
function listenUmountOfNestedApp() {
|
|
1307
|
+
if (window.__MICRO_APP_ENVIRONMENT__) {
|
|
1308
|
+
window.addEventListener('unmount', unmountNestedApp, false);
|
|
1407
1309
|
}
|
|
1408
|
-
|
|
1409
|
-
|
|
1310
|
+
}
|
|
1311
|
+
// release listener
|
|
1312
|
+
function releaseUnmountOfNestedApp() {
|
|
1313
|
+
if (window.__MICRO_APP_ENVIRONMENT__) {
|
|
1314
|
+
window.removeEventListener('unmount', unmountNestedApp, false);
|
|
1410
1315
|
}
|
|
1411
1316
|
}
|
|
1317
|
+
|
|
1318
|
+
const globalEnv = {};
|
|
1412
1319
|
/**
|
|
1413
|
-
*
|
|
1414
|
-
*
|
|
1320
|
+
* Note loop nesting
|
|
1321
|
+
* Only prototype or unique values can be put here
|
|
1415
1322
|
*/
|
|
1416
|
-
function
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1323
|
+
function initGlobalEnv() {
|
|
1324
|
+
if (isBrowser) {
|
|
1325
|
+
/**
|
|
1326
|
+
* save patch raw methods
|
|
1327
|
+
* pay attention to this binding
|
|
1328
|
+
*/
|
|
1329
|
+
const rawSetAttribute = Element.prototype.setAttribute;
|
|
1330
|
+
const rawAppendChild = Element.prototype.appendChild;
|
|
1331
|
+
const rawInsertBefore = Element.prototype.insertBefore;
|
|
1332
|
+
const rawReplaceChild = Element.prototype.replaceChild;
|
|
1333
|
+
const rawRemoveChild = Element.prototype.removeChild;
|
|
1334
|
+
const rawAppend = Element.prototype.append;
|
|
1335
|
+
const rawPrepend = Element.prototype.prepend;
|
|
1336
|
+
const rawCloneNode = Element.prototype.cloneNode;
|
|
1337
|
+
const rawCreateElement = Document.prototype.createElement;
|
|
1338
|
+
const rawCreateElementNS = Document.prototype.createElementNS;
|
|
1339
|
+
const rawCreateDocumentFragment = Document.prototype.createDocumentFragment;
|
|
1340
|
+
const rawQuerySelector = Document.prototype.querySelector;
|
|
1341
|
+
const rawQuerySelectorAll = Document.prototype.querySelectorAll;
|
|
1342
|
+
const rawGetElementById = Document.prototype.getElementById;
|
|
1343
|
+
const rawGetElementsByClassName = Document.prototype.getElementsByClassName;
|
|
1344
|
+
const rawGetElementsByTagName = Document.prototype.getElementsByTagName;
|
|
1345
|
+
const rawGetElementsByName = Document.prototype.getElementsByName;
|
|
1346
|
+
const ImageProxy = new Proxy(Image, {
|
|
1347
|
+
construct(Target, args) {
|
|
1348
|
+
const elementImage = new Target(...args);
|
|
1349
|
+
elementImage.__MICRO_APP_NAME__ = getCurrentAppName();
|
|
1350
|
+
return elementImage;
|
|
1351
|
+
},
|
|
1433
1352
|
});
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1353
|
+
const rawWindow = Function('return window')();
|
|
1354
|
+
const rawDocument = Function('return document')();
|
|
1355
|
+
const supportModuleScript = isSupportModuleScript();
|
|
1356
|
+
/**
|
|
1357
|
+
* save effect raw methods
|
|
1358
|
+
* pay attention to this binding, especially setInterval, setTimeout, clearInterval, clearTimeout
|
|
1359
|
+
*/
|
|
1360
|
+
const rawWindowAddEventListener = rawWindow.addEventListener;
|
|
1361
|
+
const rawWindowRemoveEventListener = rawWindow.removeEventListener;
|
|
1362
|
+
const rawSetInterval = rawWindow.setInterval;
|
|
1363
|
+
const rawSetTimeout = rawWindow.setTimeout;
|
|
1364
|
+
const rawClearInterval = rawWindow.clearInterval;
|
|
1365
|
+
const rawClearTimeout = rawWindow.clearTimeout;
|
|
1366
|
+
const rawDocumentAddEventListener = rawDocument.addEventListener;
|
|
1367
|
+
const rawDocumentRemoveEventListener = rawDocument.removeEventListener;
|
|
1368
|
+
// mark current application as base application
|
|
1369
|
+
window.__MICRO_APP_BASE_APPLICATION__ = true;
|
|
1370
|
+
Object.assign(globalEnv, {
|
|
1371
|
+
// source/patch
|
|
1372
|
+
rawSetAttribute,
|
|
1373
|
+
rawAppendChild,
|
|
1374
|
+
rawInsertBefore,
|
|
1375
|
+
rawReplaceChild,
|
|
1376
|
+
rawRemoveChild,
|
|
1377
|
+
rawAppend,
|
|
1378
|
+
rawPrepend,
|
|
1379
|
+
rawCloneNode,
|
|
1380
|
+
rawCreateElement,
|
|
1381
|
+
rawCreateElementNS,
|
|
1382
|
+
rawCreateDocumentFragment,
|
|
1383
|
+
rawQuerySelector,
|
|
1384
|
+
rawQuerySelectorAll,
|
|
1385
|
+
rawGetElementById,
|
|
1386
|
+
rawGetElementsByClassName,
|
|
1387
|
+
rawGetElementsByTagName,
|
|
1388
|
+
rawGetElementsByName,
|
|
1389
|
+
ImageProxy,
|
|
1390
|
+
// common global vars
|
|
1391
|
+
rawWindow,
|
|
1392
|
+
rawDocument,
|
|
1393
|
+
supportModuleScript,
|
|
1394
|
+
// sandbox/effect
|
|
1395
|
+
rawWindowAddEventListener,
|
|
1396
|
+
rawWindowRemoveEventListener,
|
|
1397
|
+
rawSetInterval,
|
|
1398
|
+
rawSetTimeout,
|
|
1399
|
+
rawClearInterval,
|
|
1400
|
+
rawClearTimeout,
|
|
1401
|
+
rawDocumentAddEventListener,
|
|
1402
|
+
rawDocumentRemoveEventListener,
|
|
1403
|
+
});
|
|
1404
|
+
// global effect
|
|
1405
|
+
rejectMicroAppStyle();
|
|
1406
|
+
releaseUnmountOfNestedApp();
|
|
1407
|
+
listenUmountOfNestedApp();
|
|
1408
|
+
}
|
|
1439
1409
|
}
|
|
1440
1410
|
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1411
|
+
// Global scripts, reuse across apps
|
|
1412
|
+
const globalScripts = new Map();
|
|
1413
|
+
/**
|
|
1414
|
+
* Extract script elements
|
|
1415
|
+
* @param script script element
|
|
1416
|
+
* @param parent parent element of script
|
|
1417
|
+
* @param app app
|
|
1418
|
+
* @param isDynamic dynamic insert
|
|
1419
|
+
*/
|
|
1420
|
+
function extractScriptElement(script, parent, app, isDynamic = false) {
|
|
1421
|
+
let replaceComment = null;
|
|
1422
|
+
let src = script.getAttribute('src');
|
|
1423
|
+
if (script.hasAttribute('exclude')) {
|
|
1424
|
+
replaceComment = document.createComment('script element with exclude attribute removed by micro-app');
|
|
1444
1425
|
}
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
logError('event-center: Invalid name');
|
|
1449
|
-
return false;
|
|
1450
|
-
}
|
|
1451
|
-
return true;
|
|
1426
|
+
else if ((script.type && !['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module'].includes(script.type)) ||
|
|
1427
|
+
script.hasAttribute('ignore')) {
|
|
1428
|
+
return null;
|
|
1452
1429
|
}
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
* @param f listener
|
|
1457
|
-
* @param autoTrigger If there is cached data when first bind listener, whether it needs to trigger, default is false
|
|
1458
|
-
*/
|
|
1459
|
-
on(name, f, autoTrigger = false) {
|
|
1460
|
-
if (this.isLegalName(name)) {
|
|
1461
|
-
if (!isFunction(f)) {
|
|
1462
|
-
return logError('event-center: Invalid callback function');
|
|
1463
|
-
}
|
|
1464
|
-
let eventInfo = this.eventList.get(name);
|
|
1465
|
-
if (!eventInfo) {
|
|
1466
|
-
eventInfo = {
|
|
1467
|
-
data: {},
|
|
1468
|
-
callbacks: new Set(),
|
|
1469
|
-
};
|
|
1470
|
-
this.eventList.set(name, eventInfo);
|
|
1471
|
-
}
|
|
1472
|
-
else if (autoTrigger && Object.getOwnPropertyNames(eventInfo.data).length) {
|
|
1473
|
-
// auto trigger when data not null
|
|
1474
|
-
f(eventInfo.data);
|
|
1475
|
-
}
|
|
1476
|
-
eventInfo.callbacks.add(f);
|
|
1477
|
-
}
|
|
1430
|
+
else if ((globalEnv.supportModuleScript && script.noModule) ||
|
|
1431
|
+
(!globalEnv.supportModuleScript && script.type === 'module')) {
|
|
1432
|
+
replaceComment = document.createComment(`${script.noModule ? 'noModule' : 'module'} script ignored by micro-app`);
|
|
1478
1433
|
}
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1434
|
+
else if (src) { // remote script
|
|
1435
|
+
src = CompletionPath(src, app.url);
|
|
1436
|
+
const info = {
|
|
1437
|
+
code: '',
|
|
1438
|
+
isExternal: true,
|
|
1439
|
+
isDynamic: isDynamic,
|
|
1440
|
+
async: script.hasAttribute('async'),
|
|
1441
|
+
defer: script.defer || script.type === 'module',
|
|
1442
|
+
module: script.type === 'module',
|
|
1443
|
+
isGlobal: script.hasAttribute('global'),
|
|
1444
|
+
};
|
|
1445
|
+
if (!isDynamic) {
|
|
1446
|
+
app.source.scripts.set(src, info);
|
|
1447
|
+
replaceComment = document.createComment(`script with src='${src}' extract by micro-app`);
|
|
1448
|
+
}
|
|
1449
|
+
else {
|
|
1450
|
+
return { url: src, info };
|
|
1491
1451
|
}
|
|
1492
1452
|
}
|
|
1493
|
-
//
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
}
|
|
1509
|
-
else {
|
|
1510
|
-
eventInfo = {
|
|
1511
|
-
data: data,
|
|
1512
|
-
callbacks: new Set(),
|
|
1513
|
-
};
|
|
1514
|
-
this.eventList.set(name, eventInfo);
|
|
1515
|
-
}
|
|
1453
|
+
else if (script.textContent) { // inline script
|
|
1454
|
+
const nonceStr = createNonceSrc();
|
|
1455
|
+
const info = {
|
|
1456
|
+
code: script.textContent,
|
|
1457
|
+
isExternal: false,
|
|
1458
|
+
isDynamic: isDynamic,
|
|
1459
|
+
async: false,
|
|
1460
|
+
defer: script.type === 'module',
|
|
1461
|
+
module: script.type === 'module',
|
|
1462
|
+
};
|
|
1463
|
+
if (!isDynamic) {
|
|
1464
|
+
app.source.scripts.set(nonceStr, info);
|
|
1465
|
+
replaceComment = document.createComment('inline script extract by micro-app');
|
|
1466
|
+
}
|
|
1467
|
+
else {
|
|
1468
|
+
return { url: nonceStr, info };
|
|
1516
1469
|
}
|
|
1517
1470
|
}
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1471
|
+
else if (!isDynamic) {
|
|
1472
|
+
/**
|
|
1473
|
+
* script with empty src or empty script.textContent remove in static html
|
|
1474
|
+
* & not removed if it created by dynamic
|
|
1475
|
+
*/
|
|
1476
|
+
replaceComment = document.createComment('script element removed by micro-app');
|
|
1477
|
+
}
|
|
1478
|
+
if (isDynamic) {
|
|
1479
|
+
return { replaceComment };
|
|
1480
|
+
}
|
|
1481
|
+
else {
|
|
1482
|
+
return parent.replaceChild(replaceComment, script);
|
|
1523
1483
|
}
|
|
1524
1484
|
}
|
|
1525
|
-
|
|
1526
|
-
const eventCenter = new EventCenter();
|
|
1527
1485
|
/**
|
|
1528
|
-
*
|
|
1529
|
-
* @param
|
|
1530
|
-
* @param
|
|
1486
|
+
* Get remote resources of script
|
|
1487
|
+
* @param wrapElement htmlDom
|
|
1488
|
+
* @param app app
|
|
1531
1489
|
*/
|
|
1532
|
-
function
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
// if appName exists, this is in sub app
|
|
1547
|
-
if (appName) {
|
|
1548
|
-
cb.__APP_NAME__ = appName;
|
|
1549
|
-
cb.__AUTO_TRIGGER__ = autoTrigger;
|
|
1490
|
+
function fetchScriptsFromHtml(wrapElement, app) {
|
|
1491
|
+
const scriptEntries = Array.from(app.source.scripts.entries());
|
|
1492
|
+
const fetchScriptPromise = [];
|
|
1493
|
+
const fetchScriptPromiseInfo = [];
|
|
1494
|
+
for (const [url, info] of scriptEntries) {
|
|
1495
|
+
if (info.isExternal) {
|
|
1496
|
+
const globalScriptText = globalScripts.get(url);
|
|
1497
|
+
if (globalScriptText) {
|
|
1498
|
+
info.code = globalScriptText;
|
|
1499
|
+
}
|
|
1500
|
+
else if (!info.defer && !info.async) {
|
|
1501
|
+
fetchScriptPromise.push(fetchSource(url, app.name));
|
|
1502
|
+
fetchScriptPromiseInfo.push([url, info]);
|
|
1503
|
+
}
|
|
1550
1504
|
}
|
|
1551
|
-
eventCenter.on('global', cb, autoTrigger);
|
|
1552
1505
|
}
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1506
|
+
if (fetchScriptPromise.length) {
|
|
1507
|
+
promiseStream(fetchScriptPromise, (res) => {
|
|
1508
|
+
fetchScriptSuccess(fetchScriptPromiseInfo[res.index][0], fetchScriptPromiseInfo[res.index][1], res.data);
|
|
1509
|
+
}, (err) => {
|
|
1510
|
+
logError(err, app.name);
|
|
1511
|
+
}, () => {
|
|
1512
|
+
app.onLoad(wrapElement);
|
|
1513
|
+
});
|
|
1559
1514
|
}
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
* @param data data
|
|
1563
|
-
*/
|
|
1564
|
-
setGlobalData(data) {
|
|
1565
|
-
// clear dom scope before dispatch global data, apply to micro app
|
|
1566
|
-
removeDomScope();
|
|
1567
|
-
eventCenter.dispatch('global', data);
|
|
1515
|
+
else {
|
|
1516
|
+
app.onLoad(wrapElement);
|
|
1568
1517
|
}
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1518
|
+
}
|
|
1519
|
+
/**
|
|
1520
|
+
* fetch js succeeded, record the code value
|
|
1521
|
+
* @param url script address
|
|
1522
|
+
* @param info resource script info
|
|
1523
|
+
* @param data code
|
|
1524
|
+
*/
|
|
1525
|
+
function fetchScriptSuccess(url, info, data) {
|
|
1526
|
+
if (info.isGlobal && !globalScripts.has(url)) {
|
|
1527
|
+
globalScripts.set(url, data);
|
|
1574
1528
|
}
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1529
|
+
info.code = data;
|
|
1530
|
+
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Execute js in the mount lifecycle
|
|
1533
|
+
* @param scriptList script list
|
|
1534
|
+
* @param app app
|
|
1535
|
+
* @param initHook callback for umd mode
|
|
1536
|
+
*/
|
|
1537
|
+
function execScripts(scriptList, app, initHook) {
|
|
1538
|
+
const scriptListEntries = Array.from(scriptList.entries());
|
|
1539
|
+
const deferScriptPromise = [];
|
|
1540
|
+
const deferScriptInfo = [];
|
|
1541
|
+
for (const [url, info] of scriptListEntries) {
|
|
1542
|
+
if (!info.isDynamic) {
|
|
1543
|
+
// Notice the second render
|
|
1544
|
+
if (info.defer || info.async) {
|
|
1545
|
+
if (info.isExternal && !info.code) {
|
|
1546
|
+
deferScriptPromise.push(fetchSource(url, app.name));
|
|
1547
|
+
}
|
|
1548
|
+
else {
|
|
1549
|
+
deferScriptPromise.push(info.code);
|
|
1588
1550
|
}
|
|
1551
|
+
deferScriptInfo.push([url, info]);
|
|
1552
|
+
info.module && (initHook.moduleCount = initHook.moduleCount ? ++initHook.moduleCount : 1);
|
|
1553
|
+
}
|
|
1554
|
+
else {
|
|
1555
|
+
runScript(url, app, info, false);
|
|
1556
|
+
initHook(false);
|
|
1589
1557
|
}
|
|
1590
1558
|
}
|
|
1591
1559
|
}
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
/**
|
|
1605
|
-
* remove listener
|
|
1606
|
-
* @param appName app.name
|
|
1607
|
-
* @param cb listener
|
|
1608
|
-
*/
|
|
1609
|
-
removeDataListener(appName, cb) {
|
|
1610
|
-
isFunction(cb) && eventCenter.off(formatEventName(formatAppName(appName), false), cb);
|
|
1611
|
-
}
|
|
1612
|
-
/**
|
|
1613
|
-
* get data from micro app or base app
|
|
1614
|
-
* @param appName app.name
|
|
1615
|
-
* @param fromBaseApp whether get data from base app, default is false
|
|
1616
|
-
*/
|
|
1617
|
-
getData(appName, fromBaseApp = false) {
|
|
1618
|
-
return eventCenter.getData(formatEventName(formatAppName(appName), fromBaseApp));
|
|
1619
|
-
}
|
|
1620
|
-
/**
|
|
1621
|
-
* Dispatch data to the specified micro app
|
|
1622
|
-
* @param appName app.name
|
|
1623
|
-
* @param data data
|
|
1624
|
-
*/
|
|
1625
|
-
setData(appName, data) {
|
|
1626
|
-
eventCenter.dispatch(formatEventName(formatAppName(appName), true), data);
|
|
1627
|
-
}
|
|
1628
|
-
/**
|
|
1629
|
-
* clear all listener for specified micro app
|
|
1630
|
-
* @param appName app.name
|
|
1631
|
-
*/
|
|
1632
|
-
clearDataListener(appName) {
|
|
1633
|
-
eventCenter.off(formatEventName(formatAppName(appName), false));
|
|
1634
|
-
}
|
|
1635
|
-
}
|
|
1636
|
-
// Event center for sub app
|
|
1637
|
-
class EventCenterForMicroApp extends EventCenterForGlobal {
|
|
1638
|
-
constructor(appName) {
|
|
1639
|
-
super();
|
|
1640
|
-
this.appName = formatAppName(appName);
|
|
1641
|
-
!this.appName && logError(`Invalid appName ${appName}`);
|
|
1642
|
-
}
|
|
1643
|
-
/**
|
|
1644
|
-
* add listener, monitor the data sent by the base app
|
|
1645
|
-
* @param cb listener
|
|
1646
|
-
* @param autoTrigger If there is cached data when first bind listener, whether it needs to trigger, default is false
|
|
1647
|
-
*/
|
|
1648
|
-
addDataListener(cb, autoTrigger) {
|
|
1649
|
-
cb.__AUTO_TRIGGER__ = autoTrigger;
|
|
1650
|
-
eventCenter.on(formatEventName(this.appName, true), cb, autoTrigger);
|
|
1651
|
-
}
|
|
1652
|
-
/**
|
|
1653
|
-
* remove listener
|
|
1654
|
-
* @param cb listener
|
|
1655
|
-
*/
|
|
1656
|
-
removeDataListener(cb) {
|
|
1657
|
-
isFunction(cb) && eventCenter.off(formatEventName(this.appName, true), cb);
|
|
1658
|
-
}
|
|
1659
|
-
/**
|
|
1660
|
-
* get data from base app
|
|
1661
|
-
*/
|
|
1662
|
-
getData() {
|
|
1663
|
-
return eventCenter.getData(formatEventName(this.appName, true));
|
|
1664
|
-
}
|
|
1665
|
-
/**
|
|
1666
|
-
* dispatch data to base app
|
|
1667
|
-
* @param data data
|
|
1668
|
-
*/
|
|
1669
|
-
dispatch(data) {
|
|
1670
|
-
removeDomScope();
|
|
1671
|
-
eventCenter.dispatch(formatEventName(this.appName, false), data);
|
|
1672
|
-
const app = appInstanceMap.get(this.appName);
|
|
1673
|
-
if ((app === null || app === void 0 ? void 0 : app.container) && isPlainObject(data)) {
|
|
1674
|
-
const event = new CustomEvent('datachange', {
|
|
1675
|
-
detail: {
|
|
1676
|
-
data,
|
|
1560
|
+
if (deferScriptPromise.length) {
|
|
1561
|
+
promiseStream(deferScriptPromise, (res) => {
|
|
1562
|
+
const info = deferScriptInfo[res.index][1];
|
|
1563
|
+
info.code = info.code || res.data;
|
|
1564
|
+
}, (err) => {
|
|
1565
|
+
initHook.errorCount = initHook.errorCount ? ++initHook.errorCount : 1;
|
|
1566
|
+
logError(err, app.name);
|
|
1567
|
+
}, () => {
|
|
1568
|
+
deferScriptInfo.forEach(([url, info]) => {
|
|
1569
|
+
if (info.code) {
|
|
1570
|
+
runScript(url, app, info, false, initHook);
|
|
1571
|
+
!info.module && initHook(false);
|
|
1677
1572
|
}
|
|
1678
1573
|
});
|
|
1679
|
-
|
|
1680
|
-
|
|
1574
|
+
initHook(isUndefined(initHook.moduleCount) ||
|
|
1575
|
+
initHook.errorCount === deferScriptPromise.length);
|
|
1576
|
+
});
|
|
1681
1577
|
}
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
*/
|
|
1685
|
-
clearDataListener() {
|
|
1686
|
-
eventCenter.off(formatEventName(this.appName, true));
|
|
1578
|
+
else {
|
|
1579
|
+
initHook(true);
|
|
1687
1580
|
}
|
|
1688
1581
|
}
|
|
1689
1582
|
/**
|
|
1690
|
-
*
|
|
1691
|
-
* @param
|
|
1583
|
+
* run code
|
|
1584
|
+
* @param url script address
|
|
1585
|
+
* @param app app
|
|
1586
|
+
* @param info script info
|
|
1587
|
+
* @param isDynamic dynamically created script
|
|
1588
|
+
* @param callback callback of module script
|
|
1692
1589
|
*/
|
|
1693
|
-
function
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1590
|
+
function runScript(url, app, info, isDynamic, callback) {
|
|
1591
|
+
var _a;
|
|
1592
|
+
try {
|
|
1593
|
+
const code = bindScope(url, app, info.code, info.module);
|
|
1594
|
+
if (app.inline || info.module) {
|
|
1595
|
+
const scriptElement = pureCreateElement('script');
|
|
1596
|
+
runCode2InlineScript(url, code, info.module, scriptElement, callback);
|
|
1597
|
+
if (isDynamic)
|
|
1598
|
+
return scriptElement;
|
|
1599
|
+
// TEST IGNORE
|
|
1600
|
+
(_a = app.container) === null || _a === void 0 ? void 0 : _a.querySelector('micro-app-body').appendChild(scriptElement);
|
|
1601
|
+
}
|
|
1602
|
+
else {
|
|
1603
|
+
runCode2Function(code, info);
|
|
1604
|
+
if (isDynamic)
|
|
1605
|
+
return document.createComment('dynamic script extract by micro-app');
|
|
1702
1606
|
}
|
|
1703
1607
|
}
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
microAppEventCneter.umdDataListeners.normal = new Set(subAppEventInfo.callbacks);
|
|
1608
|
+
catch (e) {
|
|
1609
|
+
console.error(`[micro-app from runScript] app ${app.name}: `, e);
|
|
1707
1610
|
}
|
|
1708
1611
|
}
|
|
1709
1612
|
/**
|
|
1710
|
-
*
|
|
1711
|
-
* @param
|
|
1613
|
+
* Get dynamically created remote script
|
|
1614
|
+
* @param url script address
|
|
1615
|
+
* @param info info
|
|
1616
|
+
* @param app app
|
|
1617
|
+
* @param originScript origin script element
|
|
1712
1618
|
*/
|
|
1713
|
-
function
|
|
1714
|
-
|
|
1715
|
-
|
|
1619
|
+
function runDynamicRemoteScript(url, info, app, originScript) {
|
|
1620
|
+
const dispatchScriptOnLoadEvent = () => dispatchOnLoadEvent(originScript);
|
|
1621
|
+
// url is unique
|
|
1622
|
+
if (app.source.scripts.has(url)) {
|
|
1623
|
+
const existInfo = app.source.scripts.get(url);
|
|
1624
|
+
!existInfo.module && defer(dispatchScriptOnLoadEvent);
|
|
1625
|
+
return runScript(url, app, existInfo, true, dispatchScriptOnLoadEvent);
|
|
1716
1626
|
}
|
|
1717
|
-
|
|
1718
|
-
|
|
1627
|
+
if (globalScripts.has(url)) {
|
|
1628
|
+
const code = globalScripts.get(url);
|
|
1629
|
+
info.code = code;
|
|
1630
|
+
app.source.scripts.set(url, info);
|
|
1631
|
+
!info.module && defer(dispatchScriptOnLoadEvent);
|
|
1632
|
+
return runScript(url, app, info, true, dispatchScriptOnLoadEvent);
|
|
1719
1633
|
}
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
function isBoundedFunction(value) {
|
|
1724
|
-
if (isBoolean(value.__MICRO_APP_ISBOUND_FUNCTION))
|
|
1725
|
-
return value.__MICRO_APP_ISBOUND_FUNCTION;
|
|
1726
|
-
return value.__MICRO_APP_ISBOUND_FUNCTION = isBoundFunction(value);
|
|
1727
|
-
}
|
|
1728
|
-
function isConstructor(value) {
|
|
1729
|
-
var _a;
|
|
1730
|
-
if (isBoolean(value.__MICRO_APP_ISCONSTRUCTOR))
|
|
1731
|
-
return value.__MICRO_APP_ISCONSTRUCTOR;
|
|
1732
|
-
const valueStr = value.toString();
|
|
1733
|
-
const result = (((_a = value.prototype) === null || _a === void 0 ? void 0 : _a.constructor) === value &&
|
|
1734
|
-
Object.getOwnPropertyNames(value.prototype).length > 1) ||
|
|
1735
|
-
/^function\s+[A-Z]/.test(valueStr) ||
|
|
1736
|
-
/^class\s+/.test(valueStr);
|
|
1737
|
-
return value.__MICRO_APP_ISCONSTRUCTOR = result;
|
|
1738
|
-
}
|
|
1739
|
-
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
1740
|
-
function bindFunctionToRawWindow(rawWindow, value) {
|
|
1741
|
-
if (value.__MICRO_APP_BOUND_WINDOW_FUNCTION)
|
|
1742
|
-
return value.__MICRO_APP_BOUND_WINDOW_FUNCTION;
|
|
1743
|
-
if (!isConstructor(value) && !isBoundedFunction(value)) {
|
|
1744
|
-
const bindRawWindowValue = value.bind(rawWindow);
|
|
1745
|
-
for (const key in value) {
|
|
1746
|
-
bindRawWindowValue[key] = value[key];
|
|
1747
|
-
}
|
|
1748
|
-
if (value.hasOwnProperty('prototype')) {
|
|
1749
|
-
rawDefineProperty(bindRawWindowValue, 'prototype', {
|
|
1750
|
-
value: value.prototype,
|
|
1751
|
-
configurable: true,
|
|
1752
|
-
enumerable: false,
|
|
1753
|
-
writable: true,
|
|
1754
|
-
});
|
|
1755
|
-
}
|
|
1756
|
-
return value.__MICRO_APP_BOUND_WINDOW_FUNCTION = bindRawWindowValue;
|
|
1757
|
-
}
|
|
1758
|
-
return value;
|
|
1759
|
-
}
|
|
1760
|
-
|
|
1761
|
-
// document.onclick binding list, the binding function of each application is unique
|
|
1762
|
-
const documentClickListMap = new Map();
|
|
1763
|
-
let hasRewriteDocumentOnClick = false;
|
|
1764
|
-
/**
|
|
1765
|
-
* Rewrite document.onclick and execute it only once
|
|
1766
|
-
*/
|
|
1767
|
-
function overwriteDocumentOnClick() {
|
|
1768
|
-
hasRewriteDocumentOnClick = true;
|
|
1769
|
-
if (Object.getOwnPropertyDescriptor(document, 'onclick')) {
|
|
1770
|
-
return logWarn('Cannot redefine document property onclick');
|
|
1634
|
+
let replaceElement;
|
|
1635
|
+
if (app.inline || info.module) {
|
|
1636
|
+
replaceElement = pureCreateElement('script');
|
|
1771
1637
|
}
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
let hasDocumentClickInited = false;
|
|
1775
|
-
function onClickHandler(e) {
|
|
1776
|
-
documentClickListMap.forEach((f) => {
|
|
1777
|
-
isFunction(f) && f.call(document, e);
|
|
1778
|
-
});
|
|
1638
|
+
else {
|
|
1639
|
+
replaceElement = document.createComment(`dynamic script with src='${url}' extract by micro-app`);
|
|
1779
1640
|
}
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
const appName = getCurrentAppName();
|
|
1789
|
-
if (appName) {
|
|
1790
|
-
documentClickListMap.set(appName, f);
|
|
1641
|
+
fetchSource(url, app.name).then((code) => {
|
|
1642
|
+
info.code = code;
|
|
1643
|
+
app.source.scripts.set(url, info);
|
|
1644
|
+
info.isGlobal && globalScripts.set(url, code);
|
|
1645
|
+
try {
|
|
1646
|
+
code = bindScope(url, app, code, info.module);
|
|
1647
|
+
if (app.inline || info.module) {
|
|
1648
|
+
runCode2InlineScript(url, code, info.module, replaceElement, dispatchScriptOnLoadEvent);
|
|
1791
1649
|
}
|
|
1792
1650
|
else {
|
|
1793
|
-
|
|
1794
|
-
}
|
|
1795
|
-
if (!hasDocumentClickInited && isFunction(f)) {
|
|
1796
|
-
hasDocumentClickInited = true;
|
|
1797
|
-
globalEnv.rawDocumentAddEventListener.call(globalEnv.rawDocument, 'click', onClickHandler, false);
|
|
1651
|
+
runCode2Function(code, info);
|
|
1798
1652
|
}
|
|
1799
1653
|
}
|
|
1654
|
+
catch (e) {
|
|
1655
|
+
console.error(`[micro-app from runDynamicScript] app ${app.name}: `, e, url);
|
|
1656
|
+
}
|
|
1657
|
+
!info.module && dispatchOnLoadEvent(originScript);
|
|
1658
|
+
}).catch((err) => {
|
|
1659
|
+
logError(err, app.name);
|
|
1660
|
+
dispatchOnErrorEvent(originScript);
|
|
1800
1661
|
});
|
|
1801
|
-
|
|
1662
|
+
return replaceElement;
|
|
1802
1663
|
}
|
|
1803
1664
|
/**
|
|
1804
|
-
*
|
|
1665
|
+
* common handle for inline script
|
|
1666
|
+
* @param url script address
|
|
1667
|
+
* @param code bound code
|
|
1668
|
+
* @param module type='module' of script
|
|
1669
|
+
* @param scriptElement target script element
|
|
1670
|
+
* @param callback callback of module script
|
|
1805
1671
|
*/
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
*/
|
|
1816
|
-
if (appName && !(((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.umdMode) && isBoundFunction(listener))) {
|
|
1817
|
-
const appListenersMap = documentEventListenerMap.get(appName);
|
|
1818
|
-
if (appListenersMap) {
|
|
1819
|
-
const appListenerList = appListenersMap.get(type);
|
|
1820
|
-
if (appListenerList) {
|
|
1821
|
-
appListenerList.add(listener);
|
|
1822
|
-
}
|
|
1823
|
-
else {
|
|
1824
|
-
appListenersMap.set(type, new Set([listener]));
|
|
1825
|
-
}
|
|
1826
|
-
}
|
|
1827
|
-
else {
|
|
1828
|
-
documentEventListenerMap.set(appName, new Map([[type, new Set([listener])]]));
|
|
1829
|
-
}
|
|
1830
|
-
listener && (listener.__MICRO_APP_MARK_OPTIONS__ = options);
|
|
1831
|
-
}
|
|
1832
|
-
rawDocumentAddEventListener.call(rawDocument, type, listener, options);
|
|
1833
|
-
};
|
|
1834
|
-
document.removeEventListener = function (type, listener, options) {
|
|
1835
|
-
var _a;
|
|
1836
|
-
const appName = getCurrentAppName();
|
|
1837
|
-
if (appName && !(((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.umdMode) && isBoundFunction(listener))) {
|
|
1838
|
-
const appListenersMap = documentEventListenerMap.get(appName);
|
|
1839
|
-
if (appListenersMap) {
|
|
1840
|
-
const appListenerList = appListenersMap.get(type);
|
|
1841
|
-
if ((appListenerList === null || appListenerList === void 0 ? void 0 : appListenerList.size) && appListenerList.has(listener)) {
|
|
1842
|
-
appListenerList.delete(listener);
|
|
1843
|
-
}
|
|
1844
|
-
}
|
|
1672
|
+
function runCode2InlineScript(url, code, module, scriptElement, callback) {
|
|
1673
|
+
if (module) {
|
|
1674
|
+
// module script is async, transform it to a blob for subsequent operations
|
|
1675
|
+
const blob = new Blob([code], { type: 'text/javascript' });
|
|
1676
|
+
scriptElement.src = URL.createObjectURL(blob);
|
|
1677
|
+
scriptElement.setAttribute('type', 'module');
|
|
1678
|
+
if (callback) {
|
|
1679
|
+
callback.moduleCount && callback.moduleCount--;
|
|
1680
|
+
scriptElement.onload = callback.bind(scriptElement, callback.moduleCount === 0);
|
|
1845
1681
|
}
|
|
1846
|
-
|
|
1847
|
-
|
|
1682
|
+
}
|
|
1683
|
+
else {
|
|
1684
|
+
scriptElement.textContent = code;
|
|
1685
|
+
}
|
|
1686
|
+
if (!url.startsWith('inline-')) {
|
|
1687
|
+
scriptElement.setAttribute('data-origin-src', url);
|
|
1688
|
+
}
|
|
1848
1689
|
}
|
|
1849
|
-
//
|
|
1850
|
-
function
|
|
1851
|
-
|
|
1852
|
-
|
|
1690
|
+
// init & run code2Function
|
|
1691
|
+
function runCode2Function(code, info) {
|
|
1692
|
+
if (!info.code2Function) {
|
|
1693
|
+
info.code2Function = new Function(code);
|
|
1694
|
+
}
|
|
1695
|
+
info.code2Function.call(window);
|
|
1853
1696
|
}
|
|
1854
|
-
// this events should be sent to the specified app
|
|
1855
|
-
const formatEventList = ['unmount', 'appstate-change'];
|
|
1856
1697
|
/**
|
|
1857
|
-
*
|
|
1858
|
-
* @param
|
|
1859
|
-
* @param
|
|
1698
|
+
* bind js scope
|
|
1699
|
+
* @param url script address
|
|
1700
|
+
* @param app app
|
|
1701
|
+
* @param code code
|
|
1702
|
+
* @param module type='module' of script
|
|
1860
1703
|
*/
|
|
1861
|
-
function
|
|
1862
|
-
if (
|
|
1863
|
-
|
|
1704
|
+
function bindScope(url, app, code, module) {
|
|
1705
|
+
if (isPlainObject(microApp.plugins)) {
|
|
1706
|
+
code = usePlugins(url, code, app.name, microApp.plugins);
|
|
1864
1707
|
}
|
|
1865
|
-
|
|
1708
|
+
if (app.sandBox && !module) {
|
|
1709
|
+
globalEnv.rawWindow.__MICRO_APP_PROXY_WINDOW__ = app.sandBox.proxyWindow;
|
|
1710
|
+
return `;(function(proxyWindow){with(proxyWindow.__MICRO_APP_WINDOW__){(function(${globalKeyToBeCached}){;${code}\n}).call(proxyWindow,${globalKeyToBeCached})}})(window.__MICRO_APP_PROXY_WINDOW__);`;
|
|
1711
|
+
}
|
|
1712
|
+
return code;
|
|
1866
1713
|
}
|
|
1867
1714
|
/**
|
|
1868
|
-
*
|
|
1869
|
-
* @param
|
|
1715
|
+
* Call the plugin to process the file
|
|
1716
|
+
* @param url script address
|
|
1717
|
+
* @param code code
|
|
1718
|
+
* @param appName app name
|
|
1719
|
+
* @param plugins plugin list
|
|
1870
1720
|
*/
|
|
1871
|
-
function
|
|
1872
|
-
|
|
1873
|
-
const
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
if (
|
|
1882
|
-
|
|
1721
|
+
function usePlugins(url, code, appName, plugins) {
|
|
1722
|
+
var _a;
|
|
1723
|
+
const newCode = processCode(plugins.global, code, url);
|
|
1724
|
+
return processCode((_a = plugins.modules) === null || _a === void 0 ? void 0 : _a[appName], newCode, url);
|
|
1725
|
+
}
|
|
1726
|
+
function processCode(configs, code, url) {
|
|
1727
|
+
if (!isArray(configs)) {
|
|
1728
|
+
return code;
|
|
1729
|
+
}
|
|
1730
|
+
return configs.reduce((preCode, config) => {
|
|
1731
|
+
if (isPlainObject(config) && isFunction(config.loader)) {
|
|
1732
|
+
return config.loader(preCode, url, config.options);
|
|
1883
1733
|
}
|
|
1884
|
-
|
|
1885
|
-
|
|
1734
|
+
return preCode;
|
|
1735
|
+
}, code);
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
/**
|
|
1739
|
+
* transform html string to dom
|
|
1740
|
+
* @param str string dom
|
|
1741
|
+
*/
|
|
1742
|
+
function getWrapElement(str) {
|
|
1743
|
+
const wrapDiv = pureCreateElement('div');
|
|
1744
|
+
wrapDiv.innerHTML = str;
|
|
1745
|
+
return wrapDiv;
|
|
1746
|
+
}
|
|
1747
|
+
/**
|
|
1748
|
+
* Recursively process each child element
|
|
1749
|
+
* @param parent parent element
|
|
1750
|
+
* @param app app
|
|
1751
|
+
* @param microAppHead micro-app-head element
|
|
1752
|
+
*/
|
|
1753
|
+
function flatChildren(parent, app, microAppHead) {
|
|
1754
|
+
const children = Array.from(parent.children);
|
|
1755
|
+
children.length && children.forEach((child) => {
|
|
1756
|
+
flatChildren(child, app);
|
|
1757
|
+
});
|
|
1758
|
+
for (const dom of children) {
|
|
1759
|
+
if (dom instanceof HTMLLinkElement) {
|
|
1760
|
+
if (dom.hasAttribute('exclude')) {
|
|
1761
|
+
parent.replaceChild(document.createComment('link element with exclude attribute ignored by micro-app'), dom);
|
|
1762
|
+
}
|
|
1763
|
+
else if (!dom.hasAttribute('ignore')) {
|
|
1764
|
+
extractLinkFromHtml(dom, parent, app);
|
|
1765
|
+
}
|
|
1766
|
+
else if (dom.hasAttribute('href')) {
|
|
1767
|
+
dom.setAttribute('href', CompletionPath(dom.getAttribute('href'), app.url));
|
|
1768
|
+
}
|
|
1886
1769
|
}
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
listenerList.delete(listener);
|
|
1770
|
+
else if (dom instanceof HTMLStyleElement) {
|
|
1771
|
+
if (dom.hasAttribute('exclude')) {
|
|
1772
|
+
parent.replaceChild(document.createComment('style element with exclude attribute ignored by micro-app'), dom);
|
|
1773
|
+
}
|
|
1774
|
+
else if (app.scopecss && !dom.hasAttribute('ignore')) {
|
|
1775
|
+
scopedCSS(dom, app);
|
|
1776
|
+
}
|
|
1895
1777
|
}
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
microAppWindow.setInterval = function (handler, timeout, ...args) {
|
|
1899
|
-
const intervalId = rawSetInterval.call(rawWindow, handler, timeout, ...args);
|
|
1900
|
-
intervalIdMap.set(intervalId, { handler, timeout, args });
|
|
1901
|
-
return intervalId;
|
|
1902
|
-
};
|
|
1903
|
-
microAppWindow.setTimeout = function (handler, timeout, ...args) {
|
|
1904
|
-
const timeoutId = rawSetTimeout.call(rawWindow, handler, timeout, ...args);
|
|
1905
|
-
timeoutIdMap.set(timeoutId, { handler, timeout, args });
|
|
1906
|
-
return timeoutId;
|
|
1907
|
-
};
|
|
1908
|
-
microAppWindow.clearInterval = function (intervalId) {
|
|
1909
|
-
intervalIdMap.delete(intervalId);
|
|
1910
|
-
rawClearInterval.call(rawWindow, intervalId);
|
|
1911
|
-
};
|
|
1912
|
-
microAppWindow.clearTimeout = function (timeoutId) {
|
|
1913
|
-
timeoutIdMap.delete(timeoutId);
|
|
1914
|
-
rawClearTimeout.call(rawWindow, timeoutId);
|
|
1915
|
-
};
|
|
1916
|
-
const umdWindowListenerMap = new Map();
|
|
1917
|
-
const umdDocumentListenerMap = new Map();
|
|
1918
|
-
let umdIntervalIdMap = new Map();
|
|
1919
|
-
let umdTimeoutIdMap = new Map();
|
|
1920
|
-
let umdOnClickHandler;
|
|
1921
|
-
// record event and timer before exec umdMountHook
|
|
1922
|
-
const recordUmdEffect = () => {
|
|
1923
|
-
// record window event
|
|
1924
|
-
eventListenerMap.forEach((listenerList, type) => {
|
|
1925
|
-
if (listenerList.size) {
|
|
1926
|
-
umdWindowListenerMap.set(type, new Set(listenerList));
|
|
1927
|
-
}
|
|
1928
|
-
});
|
|
1929
|
-
// record timers
|
|
1930
|
-
if (intervalIdMap.size) {
|
|
1931
|
-
umdIntervalIdMap = new Map(intervalIdMap);
|
|
1778
|
+
else if (dom instanceof HTMLScriptElement) {
|
|
1779
|
+
extractScriptElement(dom, parent, app);
|
|
1932
1780
|
}
|
|
1933
|
-
if (
|
|
1934
|
-
|
|
1781
|
+
else if (dom instanceof HTMLMetaElement || dom instanceof HTMLTitleElement) {
|
|
1782
|
+
parent.removeChild(dom);
|
|
1935
1783
|
}
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
// record document event
|
|
1939
|
-
const documentAppListenersMap = documentEventListenerMap.get(appName);
|
|
1940
|
-
if (documentAppListenersMap) {
|
|
1941
|
-
documentAppListenersMap.forEach((listenerList, type) => {
|
|
1942
|
-
if (listenerList.size) {
|
|
1943
|
-
umdDocumentListenerMap.set(type, new Set(listenerList));
|
|
1944
|
-
}
|
|
1945
|
-
});
|
|
1784
|
+
else if (dom instanceof HTMLImageElement && dom.hasAttribute('src')) {
|
|
1785
|
+
dom.setAttribute('src', CompletionPath(dom.getAttribute('src'), app.url));
|
|
1946
1786
|
}
|
|
1947
|
-
}
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
}
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
/**
|
|
1790
|
+
* Extract link and script, bind style scope
|
|
1791
|
+
* @param htmlStr html string
|
|
1792
|
+
* @param app app
|
|
1793
|
+
*/
|
|
1794
|
+
function extractSourceDom(htmlStr, app) {
|
|
1795
|
+
const wrapElement = getWrapElement(htmlStr);
|
|
1796
|
+
const microAppHead = wrapElement.querySelector('micro-app-head');
|
|
1797
|
+
const microAppBody = wrapElement.querySelector('micro-app-body');
|
|
1798
|
+
if (!microAppHead || !microAppBody) {
|
|
1799
|
+
const msg = `element ${microAppHead ? 'body' : 'head'} is missing`;
|
|
1800
|
+
app.onerror(new Error(msg));
|
|
1801
|
+
return logError(msg, app.name);
|
|
1802
|
+
}
|
|
1803
|
+
flatChildren(wrapElement, app);
|
|
1804
|
+
if (app.source.links.size) {
|
|
1805
|
+
fetchLinksFromHtml(wrapElement, app, microAppHead);
|
|
1806
|
+
}
|
|
1807
|
+
else {
|
|
1808
|
+
app.onLoad(wrapElement);
|
|
1809
|
+
}
|
|
1810
|
+
if (app.source.scripts.size) {
|
|
1811
|
+
fetchScriptsFromHtml(wrapElement, app);
|
|
1812
|
+
}
|
|
1813
|
+
else {
|
|
1814
|
+
app.onLoad(wrapElement);
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
/**
|
|
1818
|
+
* Get and format html
|
|
1819
|
+
* @param app app
|
|
1820
|
+
*/
|
|
1821
|
+
function extractHtml(app) {
|
|
1822
|
+
fetchSource(app.ssrUrl || app.url, app.name, { cache: 'no-cache' }).then((htmlStr) => {
|
|
1823
|
+
if (!htmlStr) {
|
|
1824
|
+
const msg = 'html is empty, please check in detail';
|
|
1825
|
+
app.onerror(new Error(msg));
|
|
1826
|
+
return logError(msg, app.name);
|
|
1984
1827
|
}
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1828
|
+
htmlStr = htmlStr
|
|
1829
|
+
.replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
|
|
1830
|
+
return match
|
|
1831
|
+
.replace(/<head/i, '<micro-app-head')
|
|
1832
|
+
.replace(/<\/head>/i, '</micro-app-head>');
|
|
1833
|
+
})
|
|
1834
|
+
.replace(/<body[^>]*>[\s\S]*?<\/body>/i, (match) => {
|
|
1835
|
+
return match
|
|
1836
|
+
.replace(/<body/i, '<micro-app-body')
|
|
1837
|
+
.replace(/<\/body>/i, '</micro-app-body>');
|
|
1838
|
+
});
|
|
1839
|
+
extractSourceDom(htmlStr, app);
|
|
1840
|
+
}).catch((e) => {
|
|
1841
|
+
logError(`Failed to fetch data from ${app.url}, micro-app stop rendering`, app.name, e);
|
|
1842
|
+
app.onLoadError(e);
|
|
1843
|
+
});
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
class EventCenter {
|
|
1847
|
+
constructor() {
|
|
1848
|
+
this.eventList = new Map();
|
|
1849
|
+
}
|
|
1850
|
+
// whether the name is legal
|
|
1851
|
+
isLegalName(name) {
|
|
1852
|
+
if (!name) {
|
|
1853
|
+
logError('event-center: Invalid name');
|
|
1854
|
+
return false;
|
|
1991
1855
|
}
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1856
|
+
return true;
|
|
1857
|
+
}
|
|
1858
|
+
/**
|
|
1859
|
+
* add listener
|
|
1860
|
+
* @param name event name
|
|
1861
|
+
* @param f listener
|
|
1862
|
+
* @param autoTrigger If there is cached data when first bind listener, whether it needs to trigger, default is false
|
|
1863
|
+
*/
|
|
1864
|
+
on(name, f, autoTrigger = false) {
|
|
1865
|
+
if (this.isLegalName(name)) {
|
|
1866
|
+
if (!isFunction(f)) {
|
|
1867
|
+
return logError('event-center: Invalid callback function');
|
|
1868
|
+
}
|
|
1869
|
+
let eventInfo = this.eventList.get(name);
|
|
1870
|
+
if (!eventInfo) {
|
|
1871
|
+
eventInfo = {
|
|
1872
|
+
data: {},
|
|
1873
|
+
callbacks: new Set(),
|
|
1874
|
+
};
|
|
1875
|
+
this.eventList.set(name, eventInfo);
|
|
1876
|
+
}
|
|
1877
|
+
else if (autoTrigger && Object.getOwnPropertyNames(eventInfo.data).length) {
|
|
1878
|
+
// auto trigger when data not null
|
|
1879
|
+
f(eventInfo.data);
|
|
1880
|
+
}
|
|
1881
|
+
eventInfo.callbacks.add(f);
|
|
1997
1882
|
}
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
1883
|
+
}
|
|
1884
|
+
// remove listener, but the data is not cleared
|
|
1885
|
+
off(name, f) {
|
|
1886
|
+
if (this.isLegalName(name)) {
|
|
1887
|
+
const eventInfo = this.eventList.get(name);
|
|
1888
|
+
if (eventInfo) {
|
|
1889
|
+
if (isFunction(f)) {
|
|
1890
|
+
eventInfo.callbacks.delete(f);
|
|
2006
1891
|
}
|
|
2007
|
-
|
|
2008
|
-
|
|
1892
|
+
else {
|
|
1893
|
+
eventInfo.callbacks.clear();
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
2009
1896
|
}
|
|
2010
|
-
};
|
|
2011
|
-
return {
|
|
2012
|
-
recordUmdEffect,
|
|
2013
|
-
rebuildUmdEffect,
|
|
2014
|
-
releaseEffect,
|
|
2015
|
-
};
|
|
2016
|
-
}
|
|
2017
|
-
// window.addEventListener('mousedown', (e: Event) => {
|
|
2018
|
-
// const targetNode = e.target
|
|
2019
|
-
// const activeApps = getActiveApps(true)
|
|
2020
|
-
// let isScopeOfMicroApp = false
|
|
2021
|
-
// for (const appName of activeApps) {
|
|
2022
|
-
// const app = appInstanceMap.get(appName)!
|
|
2023
|
-
// if (targetNode instanceof Node && app.container!.contains(targetNode)) {
|
|
2024
|
-
// isScopeOfMicroApp = true
|
|
2025
|
-
// // console.log(111111, appName)
|
|
2026
|
-
// setCurrentAppName(appName)
|
|
2027
|
-
// break
|
|
2028
|
-
// }
|
|
2029
|
-
// }
|
|
2030
|
-
// if (!isScopeOfMicroApp) {
|
|
2031
|
-
// setCurrentAppName(null)
|
|
2032
|
-
// }
|
|
2033
|
-
// }, false)
|
|
2034
|
-
// let isWaitingForMacroReset = false
|
|
2035
|
-
// window.addEventListener('mouseup', () => {
|
|
2036
|
-
// if (!isWaitingForMacroReset && getCurrentAppName()) {
|
|
2037
|
-
// isWaitingForMacroReset = true
|
|
2038
|
-
// setTimeout(() => {
|
|
2039
|
-
// setCurrentAppName(null)
|
|
2040
|
-
// isWaitingForMacroReset = false
|
|
2041
|
-
// })
|
|
2042
|
-
// }
|
|
2043
|
-
// }, false)
|
|
2044
|
-
|
|
2045
|
-
// Variables that can escape to rawWindow
|
|
2046
|
-
const staticEscapeProperties = [
|
|
2047
|
-
'System',
|
|
2048
|
-
'__cjsWrapper',
|
|
2049
|
-
];
|
|
2050
|
-
// Variables that can only assigned to rawWindow
|
|
2051
|
-
const escapeSetterKeyList = [
|
|
2052
|
-
'location',
|
|
2053
|
-
];
|
|
2054
|
-
const globalPropertyList = ['window', 'self', 'globalThis'];
|
|
2055
|
-
class SandBox {
|
|
2056
|
-
constructor(appName, url) {
|
|
2057
|
-
// Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
|
|
2058
|
-
this.scopeProperties = ['webpackJsonp'];
|
|
2059
|
-
// Properties that can be escape to rawWindow
|
|
2060
|
-
this.escapeProperties = [];
|
|
2061
|
-
// Properties newly added to microAppWindow
|
|
2062
|
-
this.injectedKeys = new Set();
|
|
2063
|
-
// Properties escape to rawWindow, cleared when unmount
|
|
2064
|
-
this.escapeKeys = new Set();
|
|
2065
|
-
// sandbox state
|
|
2066
|
-
this.active = false;
|
|
2067
|
-
this.microAppWindow = {}; // Proxy target
|
|
2068
|
-
// get scopeProperties and escapeProperties from plugins
|
|
2069
|
-
this.getScopeProperties(appName);
|
|
2070
|
-
// create proxyWindow with Proxy(microAppWindow)
|
|
2071
|
-
this.proxyWindow = this.createProxyWindow();
|
|
2072
|
-
// inject global properties
|
|
2073
|
-
this.initMicroAppWindow(this.microAppWindow, appName, url);
|
|
2074
|
-
// Rewrite global event listener & timeout
|
|
2075
|
-
Object.assign(this, effect(this.microAppWindow));
|
|
2076
1897
|
}
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
1898
|
+
// dispatch data
|
|
1899
|
+
dispatch(name, data) {
|
|
1900
|
+
if (this.isLegalName(name)) {
|
|
1901
|
+
if (!isPlainObject(data)) {
|
|
1902
|
+
return logError('event-center: data must be object');
|
|
1903
|
+
}
|
|
1904
|
+
let eventInfo = this.eventList.get(name);
|
|
1905
|
+
if (eventInfo) {
|
|
1906
|
+
// Update when the data is not equal
|
|
1907
|
+
if (eventInfo.data !== data) {
|
|
1908
|
+
eventInfo.data = data;
|
|
1909
|
+
for (const f of eventInfo.callbacks) {
|
|
1910
|
+
f(data);
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
else {
|
|
1915
|
+
eventInfo = {
|
|
1916
|
+
data: data,
|
|
1917
|
+
callbacks: new Set(),
|
|
1918
|
+
};
|
|
1919
|
+
this.eventList.set(name, eventInfo);
|
|
2085
1920
|
}
|
|
2086
1921
|
}
|
|
2087
1922
|
}
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
1923
|
+
// get data
|
|
1924
|
+
getData(name) {
|
|
1925
|
+
var _a;
|
|
1926
|
+
const eventInfo = this.eventList.get(name);
|
|
1927
|
+
return (_a = eventInfo === null || eventInfo === void 0 ? void 0 : eventInfo.data) !== null && _a !== void 0 ? _a : null;
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
const eventCenter = new EventCenter();
|
|
1932
|
+
/**
|
|
1933
|
+
* Format event name
|
|
1934
|
+
* @param appName app.name
|
|
1935
|
+
* @param fromBaseApp is from base app
|
|
1936
|
+
*/
|
|
1937
|
+
function formatEventName(appName, fromBaseApp) {
|
|
1938
|
+
if (!isString(appName) || !appName)
|
|
1939
|
+
return '';
|
|
1940
|
+
return fromBaseApp ? `__from_base_app_${appName}__` : `__from_micro_app_${appName}__`;
|
|
1941
|
+
}
|
|
1942
|
+
// Global data
|
|
1943
|
+
class EventCenterForGlobal {
|
|
1944
|
+
/**
|
|
1945
|
+
* add listener of global data
|
|
1946
|
+
* @param cb listener
|
|
1947
|
+
* @param autoTrigger If there is cached data when first bind listener, whether it needs to trigger, default is false
|
|
1948
|
+
*/
|
|
1949
|
+
addGlobalDataListener(cb, autoTrigger) {
|
|
1950
|
+
const appName = this.appName;
|
|
1951
|
+
// if appName exists, this is in sub app
|
|
1952
|
+
if (appName) {
|
|
1953
|
+
cb.__APP_NAME__ = appName;
|
|
1954
|
+
cb.__AUTO_TRIGGER__ = autoTrigger;
|
|
2105
1955
|
}
|
|
1956
|
+
eventCenter.on('global', cb, autoTrigger);
|
|
2106
1957
|
}
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
this.injectedKeys.forEach((key) => {
|
|
2114
|
-
this.recordUmdinjectedValues.set(key, Reflect.get(this.microAppWindow, key));
|
|
2115
|
-
});
|
|
1958
|
+
/**
|
|
1959
|
+
* remove listener of global data
|
|
1960
|
+
* @param cb listener
|
|
1961
|
+
*/
|
|
1962
|
+
removeGlobalDataListener(cb) {
|
|
1963
|
+
isFunction(cb) && eventCenter.off('global', cb);
|
|
2116
1964
|
}
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
1965
|
+
/**
|
|
1966
|
+
* dispatch global data
|
|
1967
|
+
* @param data data
|
|
1968
|
+
*/
|
|
1969
|
+
setGlobalData(data) {
|
|
1970
|
+
// clear dom scope before dispatch global data, apply to micro app
|
|
1971
|
+
removeDomScope();
|
|
1972
|
+
eventCenter.dispatch('global', data);
|
|
2124
1973
|
}
|
|
2125
1974
|
/**
|
|
2126
|
-
* get
|
|
2127
|
-
* @param appName app name
|
|
1975
|
+
* get global data
|
|
2128
1976
|
*/
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
if (isArray((_a = microApp.plugins.modules) === null || _a === void 0 ? void 0 : _a[appName])) {
|
|
2146
|
-
for (const plugin of microApp.plugins.modules[appName]) {
|
|
2147
|
-
if (isPlainObject(plugin)) {
|
|
2148
|
-
if (isArray(plugin.scopeProperties)) {
|
|
2149
|
-
this.scopeProperties = this.scopeProperties.concat(plugin.scopeProperties);
|
|
2150
|
-
}
|
|
2151
|
-
if (isArray(plugin.escapeProperties)) {
|
|
2152
|
-
this.escapeProperties = this.escapeProperties.concat(plugin.escapeProperties);
|
|
2153
|
-
}
|
|
1977
|
+
getGlobalData() {
|
|
1978
|
+
return eventCenter.getData('global');
|
|
1979
|
+
}
|
|
1980
|
+
/**
|
|
1981
|
+
* clear all listener of global data
|
|
1982
|
+
* if appName exists, only the specified functions is cleared
|
|
1983
|
+
* if appName not exists, only clear the base app functions
|
|
1984
|
+
*/
|
|
1985
|
+
clearGlobalDataListener() {
|
|
1986
|
+
const appName = this.appName;
|
|
1987
|
+
const eventInfo = eventCenter.eventList.get('global');
|
|
1988
|
+
if (eventInfo) {
|
|
1989
|
+
for (const cb of eventInfo.callbacks) {
|
|
1990
|
+
if ((appName && appName === cb.__APP_NAME__) ||
|
|
1991
|
+
!(appName || cb.__APP_NAME__)) {
|
|
1992
|
+
eventInfo.callbacks.delete(cb);
|
|
2154
1993
|
}
|
|
2155
1994
|
}
|
|
2156
1995
|
}
|
|
2157
1996
|
}
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
const rawValue = Reflect.get(rawWindow, key);
|
|
2170
|
-
return isFunction(rawValue) ? bindFunctionToRawWindow(rawWindow, rawValue) : rawValue;
|
|
2171
|
-
},
|
|
2172
|
-
set: (target, key, value) => {
|
|
2173
|
-
if (this.active) {
|
|
2174
|
-
if (escapeSetterKeyList.includes(key)) {
|
|
2175
|
-
Reflect.set(rawWindow, key, value);
|
|
2176
|
-
}
|
|
2177
|
-
else if (
|
|
2178
|
-
// target.hasOwnProperty has been rewritten
|
|
2179
|
-
!rawHasOwnProperty.call(target, key) &&
|
|
2180
|
-
rawHasOwnProperty.call(rawWindow, key) &&
|
|
2181
|
-
!this.scopeProperties.includes(key)) {
|
|
2182
|
-
const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
|
|
2183
|
-
const { configurable, enumerable, writable, set } = descriptor;
|
|
2184
|
-
// set value because it can be set
|
|
2185
|
-
rawDefineProperty(target, key, {
|
|
2186
|
-
value,
|
|
2187
|
-
configurable,
|
|
2188
|
-
enumerable,
|
|
2189
|
-
writable: writable !== null && writable !== void 0 ? writable : !!set,
|
|
2190
|
-
});
|
|
2191
|
-
this.injectedKeys.add(key);
|
|
2192
|
-
}
|
|
2193
|
-
else {
|
|
2194
|
-
Reflect.set(target, key, value);
|
|
2195
|
-
this.injectedKeys.add(key);
|
|
2196
|
-
}
|
|
2197
|
-
if ((this.escapeProperties.includes(key) ||
|
|
2198
|
-
(staticEscapeProperties.includes(key) && !Reflect.has(rawWindow, key))) &&
|
|
2199
|
-
!this.scopeProperties.includes(key)) {
|
|
2200
|
-
Reflect.set(rawWindow, key, value);
|
|
2201
|
-
this.escapeKeys.add(key);
|
|
2202
|
-
}
|
|
2203
|
-
}
|
|
2204
|
-
return true;
|
|
2205
|
-
},
|
|
2206
|
-
has: (target, key) => {
|
|
2207
|
-
if (this.scopeProperties.includes(key))
|
|
2208
|
-
return key in target;
|
|
2209
|
-
return key in target || key in rawWindow;
|
|
2210
|
-
},
|
|
2211
|
-
// Object.getOwnPropertyDescriptor(window, key)
|
|
2212
|
-
getOwnPropertyDescriptor: (target, key) => {
|
|
2213
|
-
if (rawHasOwnProperty.call(target, key)) {
|
|
2214
|
-
descriptorTargetMap.set(key, 'target');
|
|
2215
|
-
return Object.getOwnPropertyDescriptor(target, key);
|
|
2216
|
-
}
|
|
2217
|
-
if (rawHasOwnProperty.call(rawWindow, key)) {
|
|
2218
|
-
descriptorTargetMap.set(key, 'rawWindow');
|
|
2219
|
-
const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
|
|
2220
|
-
if (descriptor && !descriptor.configurable) {
|
|
2221
|
-
descriptor.configurable = true;
|
|
2222
|
-
}
|
|
2223
|
-
return descriptor;
|
|
2224
|
-
}
|
|
2225
|
-
return undefined;
|
|
2226
|
-
},
|
|
2227
|
-
// Object.defineProperty(window, key, Descriptor)
|
|
2228
|
-
defineProperty: (target, key, value) => {
|
|
2229
|
-
const from = descriptorTargetMap.get(key);
|
|
2230
|
-
if (from === 'rawWindow') {
|
|
2231
|
-
return Reflect.defineProperty(rawWindow, key, value);
|
|
2232
|
-
}
|
|
2233
|
-
return Reflect.defineProperty(target, key, value);
|
|
2234
|
-
},
|
|
2235
|
-
// Object.getOwnPropertyNames(window)
|
|
2236
|
-
ownKeys: (target) => {
|
|
2237
|
-
return unique(Reflect.ownKeys(rawWindow).concat(Reflect.ownKeys(target)));
|
|
2238
|
-
},
|
|
2239
|
-
deleteProperty: (target, key) => {
|
|
2240
|
-
if (rawHasOwnProperty.call(target, key)) {
|
|
2241
|
-
this.injectedKeys.has(key) && this.injectedKeys.delete(key);
|
|
2242
|
-
this.escapeKeys.has(key) && Reflect.deleteProperty(rawWindow, key);
|
|
2243
|
-
return Reflect.deleteProperty(target, key);
|
|
2244
|
-
}
|
|
2245
|
-
return true;
|
|
2246
|
-
},
|
|
2247
|
-
});
|
|
1997
|
+
}
|
|
1998
|
+
// Event center for base app
|
|
1999
|
+
class EventCenterForBaseApp extends EventCenterForGlobal {
|
|
2000
|
+
/**
|
|
2001
|
+
* add listener
|
|
2002
|
+
* @param appName app.name
|
|
2003
|
+
* @param cb listener
|
|
2004
|
+
* @param autoTrigger If there is cached data when first bind listener, whether it needs to trigger, default is false
|
|
2005
|
+
*/
|
|
2006
|
+
addDataListener(appName, cb, autoTrigger) {
|
|
2007
|
+
eventCenter.on(formatEventName(formatAppName(appName), false), cb, autoTrigger);
|
|
2248
2008
|
}
|
|
2249
2009
|
/**
|
|
2250
|
-
*
|
|
2251
|
-
* @param
|
|
2252
|
-
* @param
|
|
2253
|
-
* @param url app url
|
|
2010
|
+
* remove listener
|
|
2011
|
+
* @param appName app.name
|
|
2012
|
+
* @param cb listener
|
|
2254
2013
|
*/
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
microAppWindow.__MICRO_APP_NAME__ = appName;
|
|
2258
|
-
microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
|
|
2259
|
-
microAppWindow.__MICRO_APP_WINDOW__ = microAppWindow;
|
|
2260
|
-
microAppWindow.microApp = new EventCenterForMicroApp(appName);
|
|
2261
|
-
microAppWindow.rawWindow = globalEnv.rawWindow;
|
|
2262
|
-
microAppWindow.rawDocument = globalEnv.rawDocument;
|
|
2263
|
-
microAppWindow.removeDomScope = removeDomScope;
|
|
2264
|
-
microAppWindow.hasOwnProperty = (key) => rawHasOwnProperty.call(microAppWindow, key) || rawHasOwnProperty.call(globalEnv.rawWindow, key);
|
|
2265
|
-
this.setMappingPropertiesWithRawDescriptor(microAppWindow);
|
|
2266
|
-
this.setHijackProperties(microAppWindow, appName);
|
|
2014
|
+
removeDataListener(appName, cb) {
|
|
2015
|
+
isFunction(cb) && eventCenter.off(formatEventName(formatAppName(appName), false), cb);
|
|
2267
2016
|
}
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2017
|
+
/**
|
|
2018
|
+
* get data from micro app or base app
|
|
2019
|
+
* @param appName app.name
|
|
2020
|
+
* @param fromBaseApp whether get data from base app, default is false
|
|
2021
|
+
*/
|
|
2022
|
+
getData(appName, fromBaseApp = false) {
|
|
2023
|
+
return eventCenter.getData(formatEventName(formatAppName(appName), fromBaseApp));
|
|
2024
|
+
}
|
|
2025
|
+
/**
|
|
2026
|
+
* Dispatch data to the specified micro app
|
|
2027
|
+
* @param appName app.name
|
|
2028
|
+
* @param data data
|
|
2029
|
+
*/
|
|
2030
|
+
setData(appName, data) {
|
|
2031
|
+
eventCenter.dispatch(formatEventName(formatAppName(appName), true), data);
|
|
2032
|
+
}
|
|
2033
|
+
/**
|
|
2034
|
+
* clear all listener for specified micro app
|
|
2035
|
+
* @param appName app.name
|
|
2036
|
+
*/
|
|
2037
|
+
clearDataListener(appName) {
|
|
2038
|
+
eventCenter.off(formatEventName(formatAppName(appName), false));
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
// Event center for sub app
|
|
2042
|
+
class EventCenterForMicroApp extends EventCenterForGlobal {
|
|
2043
|
+
constructor(appName) {
|
|
2044
|
+
super();
|
|
2045
|
+
this.appName = formatAppName(appName);
|
|
2046
|
+
!this.appName && logError(`Invalid appName ${appName}`);
|
|
2047
|
+
}
|
|
2048
|
+
/**
|
|
2049
|
+
* add listener, monitor the data sent by the base app
|
|
2050
|
+
* @param cb listener
|
|
2051
|
+
* @param autoTrigger If there is cached data when first bind listener, whether it needs to trigger, default is false
|
|
2052
|
+
*/
|
|
2053
|
+
addDataListener(cb, autoTrigger) {
|
|
2054
|
+
cb.__AUTO_TRIGGER__ = autoTrigger;
|
|
2055
|
+
eventCenter.on(formatEventName(this.appName, true), cb, autoTrigger);
|
|
2056
|
+
}
|
|
2057
|
+
/**
|
|
2058
|
+
* remove listener
|
|
2059
|
+
* @param cb listener
|
|
2060
|
+
*/
|
|
2061
|
+
removeDataListener(cb) {
|
|
2062
|
+
isFunction(cb) && eventCenter.off(formatEventName(this.appName, true), cb);
|
|
2063
|
+
}
|
|
2064
|
+
/**
|
|
2065
|
+
* get data from base app
|
|
2066
|
+
*/
|
|
2067
|
+
getData() {
|
|
2068
|
+
return eventCenter.getData(formatEventName(this.appName, true));
|
|
2069
|
+
}
|
|
2070
|
+
/**
|
|
2071
|
+
* dispatch data to base app
|
|
2072
|
+
* @param data data
|
|
2073
|
+
*/
|
|
2074
|
+
dispatch(data) {
|
|
2075
|
+
removeDomScope();
|
|
2076
|
+
eventCenter.dispatch(formatEventName(this.appName, false), data);
|
|
2077
|
+
const app = appInstanceMap.get(this.appName);
|
|
2078
|
+
if ((app === null || app === void 0 ? void 0 : app.container) && isPlainObject(data)) {
|
|
2079
|
+
const event = new CustomEvent('datachange', {
|
|
2080
|
+
detail: {
|
|
2081
|
+
data,
|
|
2082
|
+
}
|
|
2083
|
+
});
|
|
2084
|
+
getRootContainer(app.container).dispatchEvent(event);
|
|
2274
2085
|
}
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2086
|
+
}
|
|
2087
|
+
/**
|
|
2088
|
+
* clear all listeners
|
|
2089
|
+
*/
|
|
2090
|
+
clearDataListener() {
|
|
2091
|
+
eventCenter.off(formatEventName(this.appName, true));
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
/**
|
|
2095
|
+
* Record UMD function before exec umdHookMount
|
|
2096
|
+
* @param microAppEventCenter
|
|
2097
|
+
*/
|
|
2098
|
+
function recordDataCenterSnapshot(microAppEventCenter) {
|
|
2099
|
+
const appName = microAppEventCenter.appName;
|
|
2100
|
+
microAppEventCenter.umdDataListeners = { global: new Set(), normal: new Set() };
|
|
2101
|
+
const globalEventInfo = eventCenter.eventList.get('global');
|
|
2102
|
+
if (globalEventInfo) {
|
|
2103
|
+
for (const cb of globalEventInfo.callbacks) {
|
|
2104
|
+
if (appName === cb.__APP_NAME__) {
|
|
2105
|
+
microAppEventCenter.umdDataListeners.global.add(cb);
|
|
2106
|
+
}
|
|
2278
2107
|
}
|
|
2279
|
-
rawDefineProperty(microAppWindow, 'top', this.createDescriptorFormicroAppWindow('top', topValue));
|
|
2280
|
-
rawDefineProperty(microAppWindow, 'parent', this.createDescriptorFormicroAppWindow('parent', parentValue));
|
|
2281
|
-
globalPropertyList.forEach((key) => {
|
|
2282
|
-
rawDefineProperty(microAppWindow, key, this.createDescriptorFormicroAppWindow(key, this.proxyWindow));
|
|
2283
|
-
});
|
|
2284
2108
|
}
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
value,
|
|
2289
|
-
configurable,
|
|
2290
|
-
enumerable,
|
|
2291
|
-
writable: writable !== null && writable !== void 0 ? writable : !!set
|
|
2292
|
-
};
|
|
2293
|
-
return descriptor;
|
|
2109
|
+
const subAppEventInfo = eventCenter.eventList.get(formatEventName(appName, true));
|
|
2110
|
+
if (subAppEventInfo) {
|
|
2111
|
+
microAppEventCenter.umdDataListeners.normal = new Set(subAppEventInfo.callbacks);
|
|
2294
2112
|
}
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2113
|
+
}
|
|
2114
|
+
/**
|
|
2115
|
+
* Rebind the UMD function of the record before remount
|
|
2116
|
+
* @param microAppEventCenter instance of EventCenterForMicroApp
|
|
2117
|
+
*/
|
|
2118
|
+
function rebuildDataCenterSnapshot(microAppEventCenter) {
|
|
2119
|
+
for (const cb of microAppEventCenter.umdDataListeners.global) {
|
|
2120
|
+
microAppEventCenter.addGlobalDataListener(cb, cb.__AUTO_TRIGGER__);
|
|
2121
|
+
}
|
|
2122
|
+
for (const cb of microAppEventCenter.umdDataListeners.normal) {
|
|
2123
|
+
microAppEventCenter.addDataListener(cb, cb.__AUTO_TRIGGER__);
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
/* eslint-disable no-return-assign */
|
|
2128
|
+
function isBoundedFunction(value) {
|
|
2129
|
+
if (isBoolean(value.__MICRO_APP_IS_BOUND_FUNCTION__))
|
|
2130
|
+
return value.__MICRO_APP_IS_BOUND_FUNCTION__;
|
|
2131
|
+
return value.__MICRO_APP_IS_BOUND_FUNCTION__ = isBoundFunction(value);
|
|
2132
|
+
}
|
|
2133
|
+
function isConstructor(value) {
|
|
2134
|
+
var _a;
|
|
2135
|
+
if (isBoolean(value.__MICRO_APP_IS_CONSTRUCTOR__))
|
|
2136
|
+
return value.__MICRO_APP_IS_CONSTRUCTOR__;
|
|
2137
|
+
const valueStr = value.toString();
|
|
2138
|
+
const result = (((_a = value.prototype) === null || _a === void 0 ? void 0 : _a.constructor) === value &&
|
|
2139
|
+
Object.getOwnPropertyNames(value.prototype).length > 1) ||
|
|
2140
|
+
/^function\s+[A-Z]/.test(valueStr) ||
|
|
2141
|
+
/^class\s+/.test(valueStr);
|
|
2142
|
+
return value.__MICRO_APP_IS_CONSTRUCTOR__ = result;
|
|
2143
|
+
}
|
|
2144
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
2145
|
+
function bindFunctionToRawWindow(rawWindow, value) {
|
|
2146
|
+
if (value.__MICRO_APP_BOUND_WINDOW_FUNCTION__)
|
|
2147
|
+
return value.__MICRO_APP_BOUND_WINDOW_FUNCTION__;
|
|
2148
|
+
if (!isConstructor(value) && !isBoundedFunction(value)) {
|
|
2149
|
+
const bindRawWindowValue = value.bind(rawWindow);
|
|
2150
|
+
for (const key in value) {
|
|
2151
|
+
bindRawWindowValue[key] = value[key];
|
|
2152
|
+
}
|
|
2153
|
+
if (value.hasOwnProperty('prototype')) {
|
|
2154
|
+
rawDefineProperty(bindRawWindowValue, 'prototype', {
|
|
2155
|
+
value: value.prototype,
|
|
2326
2156
|
configurable: true,
|
|
2327
2157
|
enumerable: false,
|
|
2328
|
-
|
|
2329
|
-
|
|
2158
|
+
writable: true,
|
|
2159
|
+
});
|
|
2160
|
+
}
|
|
2161
|
+
return value.__MICRO_APP_BOUND_WINDOW_FUNCTION__ = bindRawWindowValue;
|
|
2330
2162
|
}
|
|
2163
|
+
return value;
|
|
2331
2164
|
}
|
|
2332
|
-
SandBox.activeCount = 0; // number of active sandbox
|
|
2333
2165
|
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
get() {
|
|
2338
|
-
return element;
|
|
2339
|
-
}
|
|
2340
|
-
},
|
|
2341
|
-
target: {
|
|
2342
|
-
get() {
|
|
2343
|
-
return element;
|
|
2344
|
-
}
|
|
2345
|
-
},
|
|
2346
|
-
});
|
|
2347
|
-
}
|
|
2166
|
+
// document.onclick binding list, the binding function of each application is unique
|
|
2167
|
+
const documentClickListMap = new Map();
|
|
2168
|
+
let hasRewriteDocumentOnClick = false;
|
|
2348
2169
|
/**
|
|
2349
|
-
*
|
|
2350
|
-
* created, beforemount, mounted, unmount, error
|
|
2351
|
-
* @param element container
|
|
2352
|
-
* @param appName app.name
|
|
2353
|
-
* @param lifecycleName lifeCycle name
|
|
2354
|
-
* @param error param from error hook
|
|
2170
|
+
* Rewrite document.onclick and execute it only once
|
|
2355
2171
|
*/
|
|
2356
|
-
function
|
|
2357
|
-
|
|
2358
|
-
if (
|
|
2359
|
-
return
|
|
2172
|
+
function overwriteDocumentOnClick() {
|
|
2173
|
+
hasRewriteDocumentOnClick = true;
|
|
2174
|
+
if (Object.getOwnPropertyDescriptor(document, 'onclick')) {
|
|
2175
|
+
return logWarn('Cannot redefine document property onclick');
|
|
2360
2176
|
}
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
error
|
|
2369
|
-
});
|
|
2370
|
-
const event = new CustomEvent(lifecycleName, {
|
|
2371
|
-
detail,
|
|
2372
|
-
});
|
|
2373
|
-
formatEventInfo(event, element);
|
|
2374
|
-
// global hooks
|
|
2375
|
-
// @ts-ignore
|
|
2376
|
-
if (isFunction((_a = microApp.lifeCycles) === null || _a === void 0 ? void 0 : _a[lifecycleName])) {
|
|
2377
|
-
// @ts-ignore
|
|
2378
|
-
microApp.lifeCycles[lifecycleName](event);
|
|
2177
|
+
const rawOnClick = document.onclick;
|
|
2178
|
+
document.onclick = null;
|
|
2179
|
+
let hasDocumentClickInited = false;
|
|
2180
|
+
function onClickHandler(e) {
|
|
2181
|
+
documentClickListMap.forEach((f) => {
|
|
2182
|
+
isFunction(f) && f.call(document, e);
|
|
2183
|
+
});
|
|
2379
2184
|
}
|
|
2380
|
-
|
|
2185
|
+
rawDefineProperty(document, 'onclick', {
|
|
2186
|
+
configurable: true,
|
|
2187
|
+
enumerable: true,
|
|
2188
|
+
get() {
|
|
2189
|
+
const appName = getCurrentAppName();
|
|
2190
|
+
return appName ? documentClickListMap.get(appName) : documentClickListMap.get('base');
|
|
2191
|
+
},
|
|
2192
|
+
set(f) {
|
|
2193
|
+
const appName = getCurrentAppName();
|
|
2194
|
+
if (appName) {
|
|
2195
|
+
documentClickListMap.set(appName, f);
|
|
2196
|
+
}
|
|
2197
|
+
else {
|
|
2198
|
+
documentClickListMap.set('base', f);
|
|
2199
|
+
}
|
|
2200
|
+
if (!hasDocumentClickInited && isFunction(f)) {
|
|
2201
|
+
hasDocumentClickInited = true;
|
|
2202
|
+
globalEnv.rawDocumentAddEventListener.call(globalEnv.rawDocument, 'click', onClickHandler, false);
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
});
|
|
2206
|
+
rawOnClick && (document.onclick = rawOnClick);
|
|
2381
2207
|
}
|
|
2382
2208
|
/**
|
|
2383
|
-
*
|
|
2384
|
-
* @param eventName event name
|
|
2385
|
-
* @param appName app name
|
|
2386
|
-
* @param detail event detail
|
|
2209
|
+
* The document event is globally, we need to clear these event bindings when micro application unmounted
|
|
2387
2210
|
*/
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : '';
|
|
2414
|
-
this.ssrUrl = ssrUrl !== null && ssrUrl !== void 0 ? ssrUrl : '';
|
|
2415
|
-
// optional during init👆
|
|
2416
|
-
this.name = name;
|
|
2417
|
-
this.url = url;
|
|
2418
|
-
this.useSandbox = useSandbox;
|
|
2419
|
-
this.scopecss = this.useSandbox && scopecss;
|
|
2420
|
-
this.source = {
|
|
2421
|
-
links: new Map(),
|
|
2422
|
-
scripts: new Map(),
|
|
2423
|
-
};
|
|
2424
|
-
this.loadSourceCode();
|
|
2425
|
-
this.useSandbox && (this.sandBox = new SandBox(name, url));
|
|
2426
|
-
}
|
|
2427
|
-
// Load resources
|
|
2428
|
-
loadSourceCode() {
|
|
2429
|
-
this.state = appStates.LOADING_SOURCE_CODE;
|
|
2430
|
-
extractHtml(this);
|
|
2431
|
-
}
|
|
2432
|
-
/**
|
|
2433
|
-
* When resource is loaded, mount app if it is not prefetch or unmount
|
|
2434
|
-
*/
|
|
2435
|
-
onLoad(html) {
|
|
2436
|
-
if (++this.loadSourceLevel === 2) {
|
|
2437
|
-
this.source.html = html;
|
|
2438
|
-
if (this.isPrefetch || appStates.UNMOUNT === this.state)
|
|
2439
|
-
return;
|
|
2440
|
-
this.state = appStates.LOAD_SOURCE_FINISHED;
|
|
2441
|
-
this.mount();
|
|
2211
|
+
const documentEventListenerMap = new Map();
|
|
2212
|
+
function effectDocumentEvent() {
|
|
2213
|
+
const { rawDocument, rawDocumentAddEventListener, rawDocumentRemoveEventListener, } = globalEnv;
|
|
2214
|
+
!hasRewriteDocumentOnClick && overwriteDocumentOnClick();
|
|
2215
|
+
document.addEventListener = function (type, listener, options) {
|
|
2216
|
+
var _a;
|
|
2217
|
+
const appName = getCurrentAppName();
|
|
2218
|
+
/**
|
|
2219
|
+
* ignore bound function of document event in umd mode, used to solve problem of react global events
|
|
2220
|
+
*/
|
|
2221
|
+
if (appName && !(((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.umdMode) && isBoundFunction(listener))) {
|
|
2222
|
+
const appListenersMap = documentEventListenerMap.get(appName);
|
|
2223
|
+
if (appListenersMap) {
|
|
2224
|
+
const appListenerList = appListenersMap.get(type);
|
|
2225
|
+
if (appListenerList) {
|
|
2226
|
+
appListenerList.add(listener);
|
|
2227
|
+
}
|
|
2228
|
+
else {
|
|
2229
|
+
appListenersMap.set(type, new Set([listener]));
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
else {
|
|
2233
|
+
documentEventListenerMap.set(appName, new Map([[type, new Set([listener])]]));
|
|
2234
|
+
}
|
|
2235
|
+
listener && (listener.__MICRO_APP_MARK_OPTIONS__ = options);
|
|
2442
2236
|
}
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2237
|
+
rawDocumentAddEventListener.call(rawDocument, type, listener, options);
|
|
2238
|
+
};
|
|
2239
|
+
document.removeEventListener = function (type, listener, options) {
|
|
2240
|
+
var _a;
|
|
2241
|
+
const appName = getCurrentAppName();
|
|
2242
|
+
if (appName && !(((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.umdMode) && isBoundFunction(listener))) {
|
|
2243
|
+
const appListenersMap = documentEventListenerMap.get(appName);
|
|
2244
|
+
if (appListenersMap) {
|
|
2245
|
+
const appListenerList = appListenersMap.get(type);
|
|
2246
|
+
if ((appListenerList === null || appListenerList === void 0 ? void 0 : appListenerList.size) && appListenerList.has(listener)) {
|
|
2247
|
+
appListenerList.delete(listener);
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2453
2250
|
}
|
|
2251
|
+
rawDocumentRemoveEventListener.call(rawDocument, type, listener, options);
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2254
|
+
// Clear the document event agent
|
|
2255
|
+
function releaseEffectDocumentEvent() {
|
|
2256
|
+
document.addEventListener = globalEnv.rawDocumentAddEventListener;
|
|
2257
|
+
document.removeEventListener = globalEnv.rawDocumentRemoveEventListener;
|
|
2258
|
+
}
|
|
2259
|
+
// this events should be sent to the specified app
|
|
2260
|
+
const formatEventList = ['unmount', 'appstate-change'];
|
|
2261
|
+
/**
|
|
2262
|
+
* Format event name
|
|
2263
|
+
* @param type event name
|
|
2264
|
+
* @param microAppWindow micro window
|
|
2265
|
+
*/
|
|
2266
|
+
function formatEventType(type, microAppWindow) {
|
|
2267
|
+
if (formatEventList.includes(type)) {
|
|
2268
|
+
return `${type}-${microAppWindow.__MICRO_APP_NAME__}`;
|
|
2454
2269
|
}
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2270
|
+
return type;
|
|
2271
|
+
}
|
|
2272
|
+
/**
|
|
2273
|
+
* Rewrite side-effect events
|
|
2274
|
+
* @param microAppWindow micro window
|
|
2275
|
+
*/
|
|
2276
|
+
function effect(microAppWindow) {
|
|
2277
|
+
const appName = microAppWindow.__MICRO_APP_NAME__;
|
|
2278
|
+
const eventListenerMap = new Map();
|
|
2279
|
+
const intervalIdMap = new Map();
|
|
2280
|
+
const timeoutIdMap = new Map();
|
|
2281
|
+
const { rawWindow, rawDocument, rawWindowAddEventListener, rawWindowRemoveEventListener, rawSetInterval, rawSetTimeout, rawClearInterval, rawClearTimeout, rawDocumentRemoveEventListener, } = globalEnv;
|
|
2282
|
+
// listener may be null, e.g test-passive
|
|
2283
|
+
microAppWindow.addEventListener = function (type, listener, options) {
|
|
2284
|
+
type = formatEventType(type, microAppWindow);
|
|
2285
|
+
const listenerList = eventListenerMap.get(type);
|
|
2286
|
+
if (listenerList) {
|
|
2287
|
+
listenerList.add(listener);
|
|
2465
2288
|
}
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
if (this.loadSourceLevel !== 2) {
|
|
2469
|
-
this.state = appStates.LOADING_SOURCE_CODE;
|
|
2470
|
-
return;
|
|
2289
|
+
else {
|
|
2290
|
+
eventListenerMap.set(type, new Set([listener]));
|
|
2471
2291
|
}
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2292
|
+
listener && (listener.__MICRO_APP_MARK_OPTIONS__ = options);
|
|
2293
|
+
rawWindowAddEventListener.call(rawWindow, type, listener, options);
|
|
2294
|
+
};
|
|
2295
|
+
microAppWindow.removeEventListener = function (type, listener, options) {
|
|
2296
|
+
type = formatEventType(type, microAppWindow);
|
|
2297
|
+
const listenerList = eventListenerMap.get(type);
|
|
2298
|
+
if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
|
|
2299
|
+
listenerList.delete(listener);
|
|
2300
|
+
}
|
|
2301
|
+
rawWindowRemoveEventListener.call(rawWindow, type, listener, options);
|
|
2302
|
+
};
|
|
2303
|
+
microAppWindow.setInterval = function (handler, timeout, ...args) {
|
|
2304
|
+
const intervalId = rawSetInterval.call(rawWindow, handler, timeout, ...args);
|
|
2305
|
+
intervalIdMap.set(intervalId, { handler, timeout, args });
|
|
2306
|
+
return intervalId;
|
|
2307
|
+
};
|
|
2308
|
+
microAppWindow.setTimeout = function (handler, timeout, ...args) {
|
|
2309
|
+
const timeoutId = rawSetTimeout.call(rawWindow, handler, timeout, ...args);
|
|
2310
|
+
timeoutIdMap.set(timeoutId, { handler, timeout, args });
|
|
2311
|
+
return timeoutId;
|
|
2312
|
+
};
|
|
2313
|
+
microAppWindow.clearInterval = function (intervalId) {
|
|
2314
|
+
intervalIdMap.delete(intervalId);
|
|
2315
|
+
rawClearInterval.call(rawWindow, intervalId);
|
|
2316
|
+
};
|
|
2317
|
+
microAppWindow.clearTimeout = function (timeoutId) {
|
|
2318
|
+
timeoutIdMap.delete(timeoutId);
|
|
2319
|
+
rawClearTimeout.call(rawWindow, timeoutId);
|
|
2320
|
+
};
|
|
2321
|
+
const umdWindowListenerMap = new Map();
|
|
2322
|
+
const umdDocumentListenerMap = new Map();
|
|
2323
|
+
let umdIntervalIdMap = new Map();
|
|
2324
|
+
let umdTimeoutIdMap = new Map();
|
|
2325
|
+
let umdOnClickHandler;
|
|
2326
|
+
// record event and timer before exec umdMountHook
|
|
2327
|
+
const recordUmdEffect = () => {
|
|
2328
|
+
// record window event
|
|
2329
|
+
eventListenerMap.forEach((listenerList, type) => {
|
|
2330
|
+
if (listenerList.size) {
|
|
2331
|
+
umdWindowListenerMap.set(type, new Set(listenerList));
|
|
2332
|
+
}
|
|
2333
|
+
});
|
|
2334
|
+
// record timers
|
|
2335
|
+
if (intervalIdMap.size) {
|
|
2336
|
+
umdIntervalIdMap = new Map(intervalIdMap);
|
|
2337
|
+
}
|
|
2338
|
+
if (timeoutIdMap.size) {
|
|
2339
|
+
umdTimeoutIdMap = new Map(timeoutIdMap);
|
|
2340
|
+
}
|
|
2341
|
+
// record onclick handler
|
|
2342
|
+
umdOnClickHandler = documentClickListMap.get(appName);
|
|
2343
|
+
// record document event
|
|
2344
|
+
const documentAppListenersMap = documentEventListenerMap.get(appName);
|
|
2345
|
+
if (documentAppListenersMap) {
|
|
2346
|
+
documentAppListenersMap.forEach((listenerList, type) => {
|
|
2347
|
+
if (listenerList.size) {
|
|
2348
|
+
umdDocumentListenerMap.set(type, new Set(listenerList));
|
|
2501
2349
|
}
|
|
2502
2350
|
});
|
|
2503
2351
|
}
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2352
|
+
};
|
|
2353
|
+
// rebuild event and timer before remount umd app
|
|
2354
|
+
const rebuildUmdEffect = () => {
|
|
2355
|
+
// rebuild window event
|
|
2356
|
+
umdWindowListenerMap.forEach((listenerList, type) => {
|
|
2357
|
+
for (const listener of listenerList) {
|
|
2358
|
+
microAppWindow.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
|
|
2508
2359
|
}
|
|
2509
|
-
|
|
2510
|
-
|
|
2360
|
+
});
|
|
2361
|
+
// rebuild timer
|
|
2362
|
+
umdIntervalIdMap.forEach((info) => {
|
|
2363
|
+
microAppWindow.setInterval(info.handler, info.timeout, ...info.args);
|
|
2364
|
+
});
|
|
2365
|
+
umdTimeoutIdMap.forEach((info) => {
|
|
2366
|
+
microAppWindow.setTimeout(info.handler, info.timeout, ...info.args);
|
|
2367
|
+
});
|
|
2368
|
+
// rebuild onclick event
|
|
2369
|
+
umdOnClickHandler && documentClickListMap.set(appName, umdOnClickHandler);
|
|
2370
|
+
// rebuild document event
|
|
2371
|
+
setCurrentAppName(appName);
|
|
2372
|
+
umdDocumentListenerMap.forEach((listenerList, type) => {
|
|
2373
|
+
for (const listener of listenerList) {
|
|
2374
|
+
document.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
|
|
2511
2375
|
}
|
|
2512
|
-
|
|
2376
|
+
});
|
|
2377
|
+
setCurrentAppName(null);
|
|
2378
|
+
};
|
|
2379
|
+
// release all event listener & interval & timeout when unmount app
|
|
2380
|
+
const releaseEffect = () => {
|
|
2381
|
+
// Clear window binding events
|
|
2382
|
+
if (eventListenerMap.size) {
|
|
2383
|
+
eventListenerMap.forEach((listenerList, type) => {
|
|
2384
|
+
for (const listener of listenerList) {
|
|
2385
|
+
rawWindowRemoveEventListener.call(rawWindow, type, listener);
|
|
2386
|
+
}
|
|
2387
|
+
});
|
|
2388
|
+
eventListenerMap.clear();
|
|
2513
2389
|
}
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
if (isPromise(umdHookMountResult)) {
|
|
2521
|
-
umdHookMountResult
|
|
2522
|
-
.then(() => this.dispatchMountedEvent())
|
|
2523
|
-
.catch((e) => this.onerror(e));
|
|
2390
|
+
// Clear timers
|
|
2391
|
+
if (intervalIdMap.size) {
|
|
2392
|
+
intervalIdMap.forEach((_, intervalId) => {
|
|
2393
|
+
rawClearInterval.call(rawWindow, intervalId);
|
|
2394
|
+
});
|
|
2395
|
+
intervalIdMap.clear();
|
|
2524
2396
|
}
|
|
2525
|
-
|
|
2526
|
-
|
|
2397
|
+
if (timeoutIdMap.size) {
|
|
2398
|
+
timeoutIdMap.forEach((_, timeoutId) => {
|
|
2399
|
+
rawClearTimeout.call(rawWindow, timeoutId);
|
|
2400
|
+
});
|
|
2401
|
+
timeoutIdMap.clear();
|
|
2527
2402
|
}
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2403
|
+
// Clear the function bound by micro application through document.onclick
|
|
2404
|
+
documentClickListMap.delete(appName);
|
|
2405
|
+
// Clear document binding event
|
|
2406
|
+
const documentAppListenersMap = documentEventListenerMap.get(appName);
|
|
2407
|
+
if (documentAppListenersMap) {
|
|
2408
|
+
documentAppListenersMap.forEach((listenerList, type) => {
|
|
2409
|
+
for (const listener of listenerList) {
|
|
2410
|
+
rawDocumentRemoveEventListener.call(rawDocument, type, listener);
|
|
2411
|
+
}
|
|
2412
|
+
});
|
|
2413
|
+
documentAppListenersMap.clear();
|
|
2536
2414
|
}
|
|
2415
|
+
};
|
|
2416
|
+
return {
|
|
2417
|
+
recordUmdEffect,
|
|
2418
|
+
rebuildUmdEffect,
|
|
2419
|
+
releaseEffect,
|
|
2420
|
+
};
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
// Variables that can escape to rawWindow
|
|
2424
|
+
const staticEscapeProperties = [
|
|
2425
|
+
'System',
|
|
2426
|
+
'__cjsWrapper',
|
|
2427
|
+
];
|
|
2428
|
+
// Variables that can only assigned to rawWindow
|
|
2429
|
+
const escapeSetterKeyList = [
|
|
2430
|
+
'location',
|
|
2431
|
+
];
|
|
2432
|
+
const globalPropertyList = ['window', 'self', 'globalThis'];
|
|
2433
|
+
class SandBox {
|
|
2434
|
+
constructor(appName, url) {
|
|
2435
|
+
// Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
|
|
2436
|
+
this.scopeProperties = ['webpackJsonp'];
|
|
2437
|
+
// Properties that can be escape to rawWindow
|
|
2438
|
+
this.escapeProperties = [];
|
|
2439
|
+
// Properties newly added to microAppWindow
|
|
2440
|
+
this.injectedKeys = new Set();
|
|
2441
|
+
// Properties escape to rawWindow, cleared when unmount
|
|
2442
|
+
this.escapeKeys = new Set();
|
|
2443
|
+
// sandbox state
|
|
2444
|
+
this.active = false;
|
|
2445
|
+
this.microAppWindow = {}; // Proxy target
|
|
2446
|
+
// get scopeProperties and escapeProperties from plugins
|
|
2447
|
+
this.getSpecialProperties(appName);
|
|
2448
|
+
// create proxyWindow with Proxy(microAppWindow)
|
|
2449
|
+
this.proxyWindow = this.createProxyWindow(appName);
|
|
2450
|
+
// inject global properties
|
|
2451
|
+
this.initMicroAppWindow(this.microAppWindow, appName, url);
|
|
2452
|
+
// Rewrite global event listener & timeout
|
|
2453
|
+
Object.assign(this, effect(this.microAppWindow));
|
|
2537
2454
|
}
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
this.state = appStates.UNMOUNT;
|
|
2548
|
-
this.keepAliveState = null;
|
|
2549
|
-
this.keepAliveContainer = null;
|
|
2550
|
-
// result of unmount function
|
|
2551
|
-
let umdHookUnmountResult;
|
|
2552
|
-
/**
|
|
2553
|
-
* send an unmount event to the micro app or call umd unmount hook
|
|
2554
|
-
* before the sandbox is cleared
|
|
2555
|
-
*/
|
|
2556
|
-
if (this.umdHookUnmount) {
|
|
2557
|
-
try {
|
|
2558
|
-
umdHookUnmountResult = this.umdHookUnmount();
|
|
2559
|
-
}
|
|
2560
|
-
catch (e) {
|
|
2561
|
-
logError('an error occurred in the unmount function \n', this.name, e);
|
|
2455
|
+
start(baseRoute) {
|
|
2456
|
+
if (!this.active) {
|
|
2457
|
+
this.active = true;
|
|
2458
|
+
this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseRoute;
|
|
2459
|
+
// BUG FIX: bable-polyfill@6.x
|
|
2460
|
+
globalEnv.rawWindow._babelPolyfill && (globalEnv.rawWindow._babelPolyfill = false);
|
|
2461
|
+
if (++SandBox.activeCount === 1) {
|
|
2462
|
+
effectDocumentEvent();
|
|
2463
|
+
patchElementPrototypeMethods();
|
|
2562
2464
|
}
|
|
2563
2465
|
}
|
|
2564
|
-
// dispatch unmount event to micro app
|
|
2565
|
-
dispatchCustomEventToMicroApp('unmount', this.name);
|
|
2566
|
-
this.handleUnmounted(destroy, umdHookUnmountResult, unmountcb);
|
|
2567
2466
|
}
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
this.
|
|
2467
|
+
stop() {
|
|
2468
|
+
if (this.active) {
|
|
2469
|
+
this.active = false;
|
|
2470
|
+
this.releaseEffect();
|
|
2471
|
+
this.microAppWindow.microApp.clearDataListener();
|
|
2472
|
+
this.microAppWindow.microApp.clearGlobalDataListener();
|
|
2473
|
+
this.injectedKeys.forEach((key) => {
|
|
2474
|
+
Reflect.deleteProperty(this.microAppWindow, key);
|
|
2475
|
+
});
|
|
2476
|
+
this.injectedKeys.clear();
|
|
2477
|
+
this.escapeKeys.forEach((key) => {
|
|
2478
|
+
Reflect.deleteProperty(globalEnv.rawWindow, key);
|
|
2479
|
+
});
|
|
2480
|
+
this.escapeKeys.clear();
|
|
2481
|
+
if (--SandBox.activeCount === 0) {
|
|
2482
|
+
releaseEffectDocumentEvent();
|
|
2483
|
+
releasePatches();
|
|
2484
|
+
}
|
|
2582
2485
|
}
|
|
2583
2486
|
}
|
|
2487
|
+
// record umd snapshot before the first execution of umdHookMount
|
|
2488
|
+
recordUmdSnapshot() {
|
|
2489
|
+
this.microAppWindow.__MICRO_APP_UMD_MODE__ = true;
|
|
2490
|
+
this.recordUmdEffect();
|
|
2491
|
+
recordDataCenterSnapshot(this.microAppWindow.microApp);
|
|
2492
|
+
this.recordUmdInjectedValues = new Map();
|
|
2493
|
+
this.injectedKeys.forEach((key) => {
|
|
2494
|
+
this.recordUmdInjectedValues.set(key, Reflect.get(this.microAppWindow, key));
|
|
2495
|
+
});
|
|
2496
|
+
}
|
|
2497
|
+
// rebuild umd snapshot before remount umd app
|
|
2498
|
+
rebuildUmdSnapshot() {
|
|
2499
|
+
this.recordUmdInjectedValues.forEach((value, key) => {
|
|
2500
|
+
Reflect.set(this.proxyWindow, key, value);
|
|
2501
|
+
});
|
|
2502
|
+
this.rebuildUmdEffect();
|
|
2503
|
+
rebuildDataCenterSnapshot(this.microAppWindow.microApp);
|
|
2504
|
+
}
|
|
2584
2505
|
/**
|
|
2585
|
-
*
|
|
2586
|
-
* @param
|
|
2587
|
-
* @param unmountcb callback of unmount
|
|
2506
|
+
* get scopeProperties and escapeProperties from plugins
|
|
2507
|
+
* @param appName app name
|
|
2588
2508
|
*/
|
|
2589
|
-
|
|
2509
|
+
getSpecialProperties(appName) {
|
|
2590
2510
|
var _a;
|
|
2591
|
-
(
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
else if (this.umdMode && this.container.childElementCount) {
|
|
2596
|
-
cloneContainer(this.container, this.source.html, false);
|
|
2597
|
-
}
|
|
2598
|
-
// dispatch unmount event to base app
|
|
2599
|
-
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.UNMOUNT);
|
|
2600
|
-
this.container.innerHTML = '';
|
|
2601
|
-
this.container = null;
|
|
2602
|
-
unmountcb && unmountcb();
|
|
2511
|
+
if (!isPlainObject(microApp.plugins))
|
|
2512
|
+
return;
|
|
2513
|
+
this.commonActionForSpecialProperties(microApp.plugins.global);
|
|
2514
|
+
this.commonActionForSpecialProperties((_a = microApp.plugins.modules) === null || _a === void 0 ? void 0 : _a[appName]);
|
|
2603
2515
|
}
|
|
2604
|
-
//
|
|
2605
|
-
|
|
2606
|
-
if (
|
|
2607
|
-
|
|
2516
|
+
// common action for global plugins and module plugins
|
|
2517
|
+
commonActionForSpecialProperties(plugins) {
|
|
2518
|
+
if (isArray(plugins)) {
|
|
2519
|
+
for (const plugin of plugins) {
|
|
2520
|
+
if (isPlainObject(plugin)) {
|
|
2521
|
+
if (isArray(plugin.scopeProperties)) {
|
|
2522
|
+
this.scopeProperties = this.scopeProperties.concat(plugin.scopeProperties);
|
|
2523
|
+
}
|
|
2524
|
+
if (isArray(plugin.escapeProperties)) {
|
|
2525
|
+
this.escapeProperties = this.escapeProperties.concat(plugin.escapeProperties);
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2608
2529
|
}
|
|
2609
|
-
appInstanceMap.delete(this.name);
|
|
2610
|
-
}
|
|
2611
|
-
// hidden app when disconnectedCallback called with keep-alive
|
|
2612
|
-
hiddenKeepAliveApp() {
|
|
2613
|
-
const oldContainer = this.container;
|
|
2614
|
-
cloneContainer(this.container, this.keepAliveContainer ? this.keepAliveContainer : (this.keepAliveContainer = document.createElement('div')), false);
|
|
2615
|
-
this.container = this.keepAliveContainer;
|
|
2616
|
-
this.keepAliveState = keepAliveStates.KEEP_ALIVE_HIDDEN;
|
|
2617
|
-
// event should dispatch before clone node
|
|
2618
|
-
// dispatch afterhidden event to micro-app
|
|
2619
|
-
dispatchCustomEventToMicroApp('appstate-change', this.name, {
|
|
2620
|
-
appState: 'afterhidden',
|
|
2621
|
-
});
|
|
2622
|
-
// dispatch afterhidden event to base app
|
|
2623
|
-
dispatchLifecyclesEvent(oldContainer, this.name, lifeCycles.AFTERHIDDEN);
|
|
2624
2530
|
}
|
|
2625
|
-
//
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2531
|
+
// create proxyWindow with Proxy(microAppWindow)
|
|
2532
|
+
createProxyWindow(appName) {
|
|
2533
|
+
const rawWindow = globalEnv.rawWindow;
|
|
2534
|
+
const descriptorTargetMap = new Map();
|
|
2535
|
+
// window.xxx will trigger proxy
|
|
2536
|
+
return new Proxy(this.microAppWindow, {
|
|
2537
|
+
get: (target, key) => {
|
|
2538
|
+
throttleDeferForSetAppName(appName);
|
|
2539
|
+
if (Reflect.has(target, key) ||
|
|
2540
|
+
(isString(key) && /^__MICRO_APP_/.test(key)) ||
|
|
2541
|
+
this.scopeProperties.includes(key))
|
|
2542
|
+
return Reflect.get(target, key);
|
|
2543
|
+
const rawValue = Reflect.get(rawWindow, key);
|
|
2544
|
+
return isFunction(rawValue) ? bindFunctionToRawWindow(rawWindow, rawValue) : rawValue;
|
|
2545
|
+
},
|
|
2546
|
+
set: (target, key, value) => {
|
|
2547
|
+
if (this.active) {
|
|
2548
|
+
if (escapeSetterKeyList.includes(key)) {
|
|
2549
|
+
Reflect.set(rawWindow, key, value);
|
|
2550
|
+
}
|
|
2551
|
+
else if (
|
|
2552
|
+
// target.hasOwnProperty has been rewritten
|
|
2553
|
+
!rawHasOwnProperty.call(target, key) &&
|
|
2554
|
+
rawHasOwnProperty.call(rawWindow, key) &&
|
|
2555
|
+
!this.scopeProperties.includes(key)) {
|
|
2556
|
+
const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
|
|
2557
|
+
const { configurable, enumerable, writable, set } = descriptor;
|
|
2558
|
+
// set value because it can be set
|
|
2559
|
+
rawDefineProperty(target, key, {
|
|
2560
|
+
value,
|
|
2561
|
+
configurable,
|
|
2562
|
+
enumerable,
|
|
2563
|
+
writable: writable !== null && writable !== void 0 ? writable : !!set,
|
|
2564
|
+
});
|
|
2565
|
+
this.injectedKeys.add(key);
|
|
2566
|
+
}
|
|
2567
|
+
else {
|
|
2568
|
+
Reflect.set(target, key, value);
|
|
2569
|
+
this.injectedKeys.add(key);
|
|
2570
|
+
}
|
|
2571
|
+
if ((this.escapeProperties.includes(key) ||
|
|
2572
|
+
(staticEscapeProperties.includes(key) && !Reflect.has(rawWindow, key))) &&
|
|
2573
|
+
!this.scopeProperties.includes(key)) {
|
|
2574
|
+
Reflect.set(rawWindow, key, value);
|
|
2575
|
+
this.escapeKeys.add(key);
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
return true;
|
|
2579
|
+
},
|
|
2580
|
+
has: (target, key) => {
|
|
2581
|
+
if (this.scopeProperties.includes(key))
|
|
2582
|
+
return key in target;
|
|
2583
|
+
return key in target || key in rawWindow;
|
|
2584
|
+
},
|
|
2585
|
+
// Object.getOwnPropertyDescriptor(window, key)
|
|
2586
|
+
getOwnPropertyDescriptor: (target, key) => {
|
|
2587
|
+
if (rawHasOwnProperty.call(target, key)) {
|
|
2588
|
+
descriptorTargetMap.set(key, 'target');
|
|
2589
|
+
return Object.getOwnPropertyDescriptor(target, key);
|
|
2590
|
+
}
|
|
2591
|
+
if (rawHasOwnProperty.call(rawWindow, key)) {
|
|
2592
|
+
descriptorTargetMap.set(key, 'rawWindow');
|
|
2593
|
+
const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
|
|
2594
|
+
if (descriptor && !descriptor.configurable) {
|
|
2595
|
+
descriptor.configurable = true;
|
|
2596
|
+
}
|
|
2597
|
+
return descriptor;
|
|
2598
|
+
}
|
|
2599
|
+
return undefined;
|
|
2600
|
+
},
|
|
2601
|
+
// Object.defineProperty(window, key, Descriptor)
|
|
2602
|
+
defineProperty: (target, key, value) => {
|
|
2603
|
+
const from = descriptorTargetMap.get(key);
|
|
2604
|
+
if (from === 'rawWindow') {
|
|
2605
|
+
return Reflect.defineProperty(rawWindow, key, value);
|
|
2606
|
+
}
|
|
2607
|
+
return Reflect.defineProperty(target, key, value);
|
|
2608
|
+
},
|
|
2609
|
+
// Object.getOwnPropertyNames(window)
|
|
2610
|
+
ownKeys: (target) => {
|
|
2611
|
+
return unique(Reflect.ownKeys(rawWindow).concat(Reflect.ownKeys(target)));
|
|
2612
|
+
},
|
|
2613
|
+
deleteProperty: (target, key) => {
|
|
2614
|
+
if (rawHasOwnProperty.call(target, key)) {
|
|
2615
|
+
this.injectedKeys.has(key) && this.injectedKeys.delete(key);
|
|
2616
|
+
this.escapeKeys.has(key) && Reflect.deleteProperty(rawWindow, key);
|
|
2617
|
+
return Reflect.deleteProperty(target, key);
|
|
2618
|
+
}
|
|
2619
|
+
return true;
|
|
2620
|
+
},
|
|
2639
2621
|
});
|
|
2640
|
-
// dispatch aftershow event to base app
|
|
2641
|
-
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.AFTERSHOW);
|
|
2642
2622
|
}
|
|
2643
2623
|
/**
|
|
2644
|
-
*
|
|
2645
|
-
* @param
|
|
2624
|
+
* inject global properties to microAppWindow
|
|
2625
|
+
* @param microAppWindow micro window
|
|
2626
|
+
* @param appName app name
|
|
2627
|
+
* @param url app url
|
|
2646
2628
|
*/
|
|
2647
|
-
|
|
2648
|
-
|
|
2629
|
+
initMicroAppWindow(microAppWindow, appName, url) {
|
|
2630
|
+
microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
|
|
2631
|
+
microAppWindow.__MICRO_APP_NAME__ = appName;
|
|
2632
|
+
microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
|
|
2633
|
+
microAppWindow.__MICRO_APP_WINDOW__ = microAppWindow;
|
|
2634
|
+
microAppWindow.microApp = new EventCenterForMicroApp(appName);
|
|
2635
|
+
microAppWindow.rawWindow = globalEnv.rawWindow;
|
|
2636
|
+
microAppWindow.rawDocument = globalEnv.rawDocument;
|
|
2637
|
+
microAppWindow.removeDomScope = removeDomScope;
|
|
2638
|
+
microAppWindow.hasOwnProperty = (key) => rawHasOwnProperty.call(microAppWindow, key) || rawHasOwnProperty.call(globalEnv.rawWindow, key);
|
|
2639
|
+
this.setMappingPropertiesWithRawDescriptor(microAppWindow);
|
|
2640
|
+
this.setHijackProperties(microAppWindow, appName);
|
|
2649
2641
|
}
|
|
2650
|
-
//
|
|
2651
|
-
|
|
2652
|
-
|
|
2642
|
+
// properties associated with the native window
|
|
2643
|
+
setMappingPropertiesWithRawDescriptor(microAppWindow) {
|
|
2644
|
+
let topValue, parentValue;
|
|
2645
|
+
const rawWindow = globalEnv.rawWindow;
|
|
2646
|
+
if (rawWindow === rawWindow.parent) { // not in iframe
|
|
2647
|
+
topValue = parentValue = this.proxyWindow;
|
|
2648
|
+
}
|
|
2649
|
+
else { // in iframe
|
|
2650
|
+
topValue = rawWindow.top;
|
|
2651
|
+
parentValue = rawWindow.parent;
|
|
2652
|
+
}
|
|
2653
|
+
rawDefineProperty(microAppWindow, 'top', this.createDescriptorForMicroAppWindow('top', topValue));
|
|
2654
|
+
rawDefineProperty(microAppWindow, 'parent', this.createDescriptorForMicroAppWindow('parent', parentValue));
|
|
2655
|
+
globalPropertyList.forEach((key) => {
|
|
2656
|
+
rawDefineProperty(microAppWindow, key, this.createDescriptorForMicroAppWindow(key, this.proxyWindow));
|
|
2657
|
+
});
|
|
2653
2658
|
}
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2659
|
+
createDescriptorForMicroAppWindow(key, value) {
|
|
2660
|
+
const { configurable = true, enumerable = true, writable, set } = Object.getOwnPropertyDescriptor(globalEnv.rawWindow, key) || { writable: true };
|
|
2661
|
+
const descriptor = {
|
|
2662
|
+
value,
|
|
2663
|
+
configurable,
|
|
2664
|
+
enumerable,
|
|
2665
|
+
writable: writable !== null && writable !== void 0 ? writable : !!set
|
|
2666
|
+
};
|
|
2667
|
+
return descriptor;
|
|
2657
2668
|
}
|
|
2658
|
-
//
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
+
// set hijack Properties to microAppWindow
|
|
2670
|
+
setHijackProperties(microAppWindow, appName) {
|
|
2671
|
+
let modifiedEval, modifiedImage;
|
|
2672
|
+
rawDefineProperties(microAppWindow, {
|
|
2673
|
+
document: {
|
|
2674
|
+
get() {
|
|
2675
|
+
throttleDeferForSetAppName(appName);
|
|
2676
|
+
return globalEnv.rawDocument;
|
|
2677
|
+
},
|
|
2678
|
+
configurable: false,
|
|
2679
|
+
enumerable: true,
|
|
2680
|
+
},
|
|
2681
|
+
eval: {
|
|
2682
|
+
get() {
|
|
2683
|
+
throttleDeferForSetAppName(appName);
|
|
2684
|
+
return modifiedEval || eval;
|
|
2685
|
+
},
|
|
2686
|
+
set: (value) => {
|
|
2687
|
+
modifiedEval = value;
|
|
2688
|
+
},
|
|
2689
|
+
configurable: true,
|
|
2690
|
+
enumerable: false,
|
|
2691
|
+
},
|
|
2692
|
+
Image: {
|
|
2693
|
+
get() {
|
|
2694
|
+
throttleDeferForSetAppName(appName);
|
|
2695
|
+
return modifiedImage || globalEnv.ImageProxy;
|
|
2696
|
+
},
|
|
2697
|
+
set: (value) => {
|
|
2698
|
+
modifiedImage = value;
|
|
2699
|
+
},
|
|
2700
|
+
configurable: true,
|
|
2701
|
+
enumerable: false,
|
|
2702
|
+
},
|
|
2703
|
+
});
|
|
2669
2704
|
}
|
|
2670
2705
|
}
|
|
2706
|
+
SandBox.activeCount = 0; // number of active sandbox
|
|
2671
2707
|
|
|
2672
|
-
|
|
2673
|
-
|
|
2708
|
+
function formatEventInfo(event, element) {
|
|
2709
|
+
Object.defineProperties(event, {
|
|
2710
|
+
currentTarget: {
|
|
2711
|
+
get() {
|
|
2712
|
+
return element;
|
|
2713
|
+
}
|
|
2714
|
+
},
|
|
2715
|
+
target: {
|
|
2716
|
+
get() {
|
|
2717
|
+
return element;
|
|
2718
|
+
}
|
|
2719
|
+
},
|
|
2720
|
+
});
|
|
2721
|
+
}
|
|
2674
2722
|
/**
|
|
2675
|
-
*
|
|
2676
|
-
*
|
|
2677
|
-
* @param
|
|
2678
|
-
* @param
|
|
2723
|
+
* dispatch lifeCycles event to base app
|
|
2724
|
+
* created, beforemount, mounted, unmount, error
|
|
2725
|
+
* @param element container
|
|
2726
|
+
* @param appName app.name
|
|
2727
|
+
* @param lifecycleName lifeCycle name
|
|
2728
|
+
* @param error param from error hook
|
|
2679
2729
|
*/
|
|
2680
|
-
function
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
dynamicElementInMicroAppMap.set(child, replaceComment);
|
|
2685
|
-
return replaceComment;
|
|
2686
|
-
}
|
|
2687
|
-
else if (app.scopecss && !child.hasAttribute('ignore')) {
|
|
2688
|
-
return scopedCSS(child, app);
|
|
2689
|
-
}
|
|
2690
|
-
return child;
|
|
2730
|
+
function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
|
|
2731
|
+
var _a;
|
|
2732
|
+
if (!element) {
|
|
2733
|
+
return logError(`element does not exist in lifecycle ${lifecycleName}`, appName);
|
|
2691
2734
|
}
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
dynamicElementInMicroAppMap.set(child, replaceComment);
|
|
2711
|
-
return replaceComment;
|
|
2712
|
-
}
|
|
2713
|
-
return child;
|
|
2735
|
+
element = getRootContainer(element);
|
|
2736
|
+
// clear dom scope before dispatch lifeCycles event to base app, especially mounted & unmount
|
|
2737
|
+
removeDomScope();
|
|
2738
|
+
const detail = Object.assign({
|
|
2739
|
+
name: appName,
|
|
2740
|
+
container: element,
|
|
2741
|
+
}, error && {
|
|
2742
|
+
error
|
|
2743
|
+
});
|
|
2744
|
+
const event = new CustomEvent(lifecycleName, {
|
|
2745
|
+
detail,
|
|
2746
|
+
});
|
|
2747
|
+
formatEventInfo(event, element);
|
|
2748
|
+
// global hooks
|
|
2749
|
+
// @ts-ignore
|
|
2750
|
+
if (isFunction((_a = microApp.lifeCycles) === null || _a === void 0 ? void 0 : _a[lifecycleName])) {
|
|
2751
|
+
// @ts-ignore
|
|
2752
|
+
microApp.lifeCycles[lifecycleName](event);
|
|
2714
2753
|
}
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2754
|
+
element.dispatchEvent(event);
|
|
2755
|
+
}
|
|
2756
|
+
/**
|
|
2757
|
+
* Dispatch custom event to micro app
|
|
2758
|
+
* @param eventName event name
|
|
2759
|
+
* @param appName app name
|
|
2760
|
+
* @param detail event detail
|
|
2761
|
+
*/
|
|
2762
|
+
function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
|
|
2763
|
+
const event = new CustomEvent(`${eventName}-${appName}`, {
|
|
2764
|
+
detail,
|
|
2765
|
+
});
|
|
2766
|
+
window.dispatchEvent(event);
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
// micro app instances
|
|
2770
|
+
const appInstanceMap = new Map();
|
|
2771
|
+
class CreateApp {
|
|
2772
|
+
constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, baseroute, }) {
|
|
2773
|
+
this.state = appStates.NOT_LOADED;
|
|
2774
|
+
this.keepAliveState = null;
|
|
2775
|
+
this.keepAliveContainer = null;
|
|
2776
|
+
this.loadSourceLevel = 0;
|
|
2777
|
+
this.umdHookMount = null;
|
|
2778
|
+
this.umdHookUnmount = null;
|
|
2779
|
+
this.libraryName = null;
|
|
2780
|
+
this.umdMode = false;
|
|
2781
|
+
this.isPrefetch = false;
|
|
2782
|
+
this.container = null;
|
|
2783
|
+
this.baseroute = '';
|
|
2784
|
+
this.sandBox = null;
|
|
2785
|
+
this.container = container !== null && container !== void 0 ? container : null;
|
|
2786
|
+
this.inline = inline !== null && inline !== void 0 ? inline : false;
|
|
2787
|
+
this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : '';
|
|
2788
|
+
this.ssrUrl = ssrUrl !== null && ssrUrl !== void 0 ? ssrUrl : '';
|
|
2789
|
+
// optional during init👆
|
|
2790
|
+
this.name = name;
|
|
2791
|
+
this.url = url;
|
|
2792
|
+
this.useSandbox = useSandbox;
|
|
2793
|
+
this.scopecss = this.useSandbox && scopecss;
|
|
2794
|
+
this.source = {
|
|
2795
|
+
links: new Map(),
|
|
2796
|
+
scripts: new Map(),
|
|
2797
|
+
};
|
|
2798
|
+
this.loadSourceCode();
|
|
2799
|
+
this.useSandbox && (this.sandBox = new SandBox(name, url));
|
|
2800
|
+
}
|
|
2801
|
+
// Load resources
|
|
2802
|
+
loadSourceCode() {
|
|
2803
|
+
this.state = appStates.LOADING_SOURCE_CODE;
|
|
2804
|
+
extractHtml(this);
|
|
2734
2805
|
}
|
|
2735
|
-
return child;
|
|
2736
|
-
}
|
|
2737
|
-
/**
|
|
2738
|
-
* Handle the elements inserted into head and body, and execute normally in other cases
|
|
2739
|
-
* @param app app
|
|
2740
|
-
* @param method raw method
|
|
2741
|
-
* @param parent parent node
|
|
2742
|
-
* @param targetChild target node
|
|
2743
|
-
* @param passiveChild second param of insertBefore and replaceChild
|
|
2744
|
-
*/
|
|
2745
|
-
function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
|
|
2746
2806
|
/**
|
|
2747
|
-
*
|
|
2748
|
-
* E.g: document.head.insertBefore(targetChild, document.head.childNodes[0])
|
|
2807
|
+
* When resource is loaded, mount app if it is not prefetch or unmount
|
|
2749
2808
|
*/
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
return globalEnv.rawAppendChild.call(microAppHead, targetChild);
|
|
2758
|
-
}
|
|
2759
|
-
else if (rawMethod === globalEnv.rawRemoveChild && !microAppHead.contains(targetChild)) {
|
|
2760
|
-
if (parent.contains(targetChild)) {
|
|
2761
|
-
return rawMethod.call(parent, targetChild);
|
|
2762
|
-
}
|
|
2763
|
-
return targetChild;
|
|
2764
|
-
}
|
|
2765
|
-
else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
|
|
2766
|
-
return rawMethod.call(microAppHead, targetChild);
|
|
2809
|
+
onLoad(html) {
|
|
2810
|
+
if (++this.loadSourceLevel === 2) {
|
|
2811
|
+
this.source.html = html;
|
|
2812
|
+
if (this.isPrefetch || appStates.UNMOUNT === this.state)
|
|
2813
|
+
return;
|
|
2814
|
+
this.state = appStates.LOAD_SOURCE_FINISHED;
|
|
2815
|
+
this.mount();
|
|
2767
2816
|
}
|
|
2768
|
-
return rawMethod.call(microAppHead, targetChild, passiveChild);
|
|
2769
2817
|
}
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
return targetChild;
|
|
2780
|
-
}
|
|
2781
|
-
else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
|
|
2782
|
-
return rawMethod.call(microAppBody, targetChild);
|
|
2818
|
+
/**
|
|
2819
|
+
* Error loading HTML
|
|
2820
|
+
* @param e Error
|
|
2821
|
+
*/
|
|
2822
|
+
onLoadError(e) {
|
|
2823
|
+
this.loadSourceLevel = -1;
|
|
2824
|
+
if (appStates.UNMOUNT !== this.state) {
|
|
2825
|
+
this.onerror(e);
|
|
2826
|
+
this.state = appStates.LOAD_SOURCE_ERROR;
|
|
2783
2827
|
}
|
|
2784
|
-
return rawMethod.call(microAppBody, targetChild, passiveChild);
|
|
2785
2828
|
}
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
/**
|
|
2797
|
-
* method of handle new node
|
|
2798
|
-
* @param parent parent node
|
|
2799
|
-
* @param newChild new node
|
|
2800
|
-
* @param passiveChild passive node
|
|
2801
|
-
* @param rawMethodraw method
|
|
2802
|
-
*/
|
|
2803
|
-
function commonElementHander(parent, newChild, passiveChild, rawMethod) {
|
|
2804
|
-
if (newChild === null || newChild === void 0 ? void 0 : newChild.__MICRO_APP_NAME__) {
|
|
2805
|
-
const app = appInstanceMap.get(newChild.__MICRO_APP_NAME__);
|
|
2806
|
-
if (app === null || app === void 0 ? void 0 : app.container) {
|
|
2807
|
-
return invokePrototypeMethod(app, rawMethod, parent, handleNewNode(parent, newChild, app), passiveChild && getMappingNode(passiveChild));
|
|
2829
|
+
/**
|
|
2830
|
+
* mount app
|
|
2831
|
+
* @param container app container
|
|
2832
|
+
* @param inline js runs in inline mode
|
|
2833
|
+
* @param baseroute route prefix, default is ''
|
|
2834
|
+
*/
|
|
2835
|
+
mount(container, inline, baseroute) {
|
|
2836
|
+
var _a, _b, _c;
|
|
2837
|
+
if (isBoolean(inline) && inline !== this.inline) {
|
|
2838
|
+
this.inline = inline;
|
|
2808
2839
|
}
|
|
2809
|
-
|
|
2810
|
-
|
|
2840
|
+
this.container = (_a = this.container) !== null && _a !== void 0 ? _a : container;
|
|
2841
|
+
this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : this.baseroute;
|
|
2842
|
+
if (this.loadSourceLevel !== 2) {
|
|
2843
|
+
this.state = appStates.LOADING_SOURCE_CODE;
|
|
2844
|
+
return;
|
|
2811
2845
|
}
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2846
|
+
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
|
|
2847
|
+
this.state = appStates.MOUNTING;
|
|
2848
|
+
cloneContainer(this.source.html, this.container, !this.umdMode);
|
|
2849
|
+
(_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.start(this.baseroute);
|
|
2850
|
+
let umdHookMountResult; // result of mount function
|
|
2851
|
+
if (!this.umdMode) {
|
|
2852
|
+
let hasDispatchMountedEvent = false;
|
|
2853
|
+
// if all js are executed, param isFinished will be true
|
|
2854
|
+
execScripts(this.source.scripts, this, (isFinished) => {
|
|
2855
|
+
var _a;
|
|
2856
|
+
if (!this.umdMode) {
|
|
2857
|
+
const { mount, unmount } = this.getUmdLibraryHooks();
|
|
2858
|
+
// if mount & unmount is function, the sub app is umd mode
|
|
2859
|
+
if (isFunction(mount) && isFunction(unmount)) {
|
|
2860
|
+
this.umdHookMount = mount;
|
|
2861
|
+
this.umdHookUnmount = unmount;
|
|
2862
|
+
this.umdMode = true;
|
|
2863
|
+
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordUmdSnapshot();
|
|
2864
|
+
try {
|
|
2865
|
+
umdHookMountResult = this.umdHookMount();
|
|
2866
|
+
}
|
|
2867
|
+
catch (e) {
|
|
2868
|
+
logError('an error occurred in the mount function \n', this.name, e);
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2821
2871
|
}
|
|
2822
|
-
|
|
2823
|
-
|
|
2872
|
+
if (!hasDispatchMountedEvent && (isFinished === true || this.umdMode)) {
|
|
2873
|
+
hasDispatchMountedEvent = true;
|
|
2874
|
+
this.handleMounted(umdHookMountResult);
|
|
2824
2875
|
}
|
|
2825
|
-
}
|
|
2826
|
-
}
|
|
2827
|
-
return rawMethod.call(parent, newChild);
|
|
2828
|
-
}
|
|
2829
|
-
return rawMethod.call(parent, newChild, passiveChild);
|
|
2830
|
-
}
|
|
2831
|
-
/**
|
|
2832
|
-
* Rewrite element prototype method
|
|
2833
|
-
*/
|
|
2834
|
-
function patchElementPrototypeMethods() {
|
|
2835
|
-
patchDocument();
|
|
2836
|
-
// Rewrite setAttribute
|
|
2837
|
-
Element.prototype.setAttribute = function setAttribute(key, value) {
|
|
2838
|
-
if (/^micro-app(-\S+)?/i.test(this.tagName) && key === 'data') {
|
|
2839
|
-
if (isPlainObject(value)) {
|
|
2840
|
-
const cloneValue = {};
|
|
2841
|
-
Object.getOwnPropertyNames(value).forEach((propertyKey) => {
|
|
2842
|
-
if (!(isString(propertyKey) && propertyKey.indexOf('__') === 0)) {
|
|
2843
|
-
// @ts-ignore
|
|
2844
|
-
cloneValue[propertyKey] = value[propertyKey];
|
|
2845
|
-
}
|
|
2846
|
-
});
|
|
2847
|
-
this.data = cloneValue;
|
|
2848
|
-
}
|
|
2849
|
-
else if (value !== '[object Object]') {
|
|
2850
|
-
logWarn('property data must be an object', this.getAttribute('name'));
|
|
2851
|
-
}
|
|
2852
|
-
}
|
|
2853
|
-
else if ((((key === 'src' || key === 'srcset') && /^(img|script)$/i.test(this.tagName)) ||
|
|
2854
|
-
(key === 'href' && /^link$/i.test(this.tagName))) &&
|
|
2855
|
-
this.__MICRO_APP_NAME__ &&
|
|
2856
|
-
appInstanceMap.has(this.__MICRO_APP_NAME__)) {
|
|
2857
|
-
const app = appInstanceMap.get(this.__MICRO_APP_NAME__);
|
|
2858
|
-
globalEnv.rawSetAttribute.call(this, key, CompletionPath(value, app.url));
|
|
2876
|
+
});
|
|
2859
2877
|
}
|
|
2860
2878
|
else {
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
return commonElementHander(this, newChild, refChild, globalEnv.rawInsertBefore);
|
|
2870
|
-
};
|
|
2871
|
-
Element.prototype.replaceChild = function replaceChild(newChild, oldChild) {
|
|
2872
|
-
return commonElementHander(this, newChild, oldChild, globalEnv.rawReplaceChild);
|
|
2873
|
-
};
|
|
2874
|
-
Element.prototype.append = function append(...nodes) {
|
|
2875
|
-
let i = 0;
|
|
2876
|
-
const length = nodes.length;
|
|
2877
|
-
while (i < length) {
|
|
2878
|
-
commonElementHander(this, nodes[i], null, globalEnv.rawAppend);
|
|
2879
|
-
i++;
|
|
2880
|
-
}
|
|
2881
|
-
};
|
|
2882
|
-
Element.prototype.prepend = function prepend(...nodes) {
|
|
2883
|
-
let i = nodes.length;
|
|
2884
|
-
while (i > 0) {
|
|
2885
|
-
commonElementHander(this, nodes[i - 1], null, globalEnv.rawPrepend);
|
|
2886
|
-
i--;
|
|
2887
|
-
}
|
|
2888
|
-
};
|
|
2889
|
-
// prototype methods of delete element👇
|
|
2890
|
-
Element.prototype.removeChild = function removeChild(oldChild) {
|
|
2891
|
-
if (oldChild === null || oldChild === void 0 ? void 0 : oldChild.__MICRO_APP_NAME__) {
|
|
2892
|
-
const app = appInstanceMap.get(oldChild.__MICRO_APP_NAME__);
|
|
2893
|
-
if (app === null || app === void 0 ? void 0 : app.container) {
|
|
2894
|
-
return invokePrototypeMethod(app, globalEnv.rawRemoveChild, this, getMappingNode(oldChild));
|
|
2895
|
-
}
|
|
2896
|
-
return globalEnv.rawRemoveChild.call(this, oldChild);
|
|
2897
|
-
}
|
|
2898
|
-
return globalEnv.rawRemoveChild.call(this, oldChild);
|
|
2899
|
-
};
|
|
2900
|
-
// patch cloneNode
|
|
2901
|
-
Element.prototype.cloneNode = function cloneNode(deep) {
|
|
2902
|
-
const clonedNode = globalEnv.rawCloneNode.call(this, deep);
|
|
2903
|
-
this.__MICRO_APP_NAME__ && (clonedNode.__MICRO_APP_NAME__ = this.__MICRO_APP_NAME__);
|
|
2904
|
-
return clonedNode;
|
|
2905
|
-
};
|
|
2906
|
-
}
|
|
2907
|
-
/**
|
|
2908
|
-
* Mark the newly created element in the micro application
|
|
2909
|
-
* @param element new element
|
|
2910
|
-
*/
|
|
2911
|
-
function markElement(element) {
|
|
2912
|
-
const appName = getCurrentAppName();
|
|
2913
|
-
appName && (element.__MICRO_APP_NAME__ = appName);
|
|
2914
|
-
return element;
|
|
2915
|
-
}
|
|
2916
|
-
// methods of document
|
|
2917
|
-
function patchDocument() {
|
|
2918
|
-
const rawDocument = globalEnv.rawDocument;
|
|
2919
|
-
// create element 👇
|
|
2920
|
-
Document.prototype.createElement = function createElement(tagName, options) {
|
|
2921
|
-
const element = globalEnv.rawCreateElement.call(this, tagName, options);
|
|
2922
|
-
return markElement(element);
|
|
2923
|
-
};
|
|
2924
|
-
Document.prototype.createElementNS = function createElementNS(namespaceURI, name, options) {
|
|
2925
|
-
const element = globalEnv.rawCreateElementNS.call(this, namespaceURI, name, options);
|
|
2926
|
-
return markElement(element);
|
|
2927
|
-
};
|
|
2928
|
-
Document.prototype.createDocumentFragment = function createDocumentFragment() {
|
|
2929
|
-
const element = globalEnv.rawCreateDocumentFragment.call(this);
|
|
2930
|
-
return markElement(element);
|
|
2931
|
-
};
|
|
2932
|
-
// query element👇
|
|
2933
|
-
function querySelector(selectors) {
|
|
2934
|
-
var _a, _b, _c;
|
|
2935
|
-
const appName = getCurrentAppName();
|
|
2936
|
-
if (!appName ||
|
|
2937
|
-
!selectors ||
|
|
2938
|
-
isUniqueElement(selectors) ||
|
|
2939
|
-
// see https://github.com/micro-zoe/micro-app/issues/56
|
|
2940
|
-
rawDocument !== this) {
|
|
2941
|
-
return globalEnv.rawQuerySelector.call(this, selectors);
|
|
2879
|
+
(_c = this.sandBox) === null || _c === void 0 ? void 0 : _c.rebuildUmdSnapshot();
|
|
2880
|
+
try {
|
|
2881
|
+
umdHookMountResult = this.umdHookMount();
|
|
2882
|
+
}
|
|
2883
|
+
catch (e) {
|
|
2884
|
+
logError('an error occurred in the mount function \n', this.name, e);
|
|
2885
|
+
}
|
|
2886
|
+
this.handleMounted(umdHookMountResult);
|
|
2942
2887
|
}
|
|
2943
|
-
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;
|
|
2944
2888
|
}
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2889
|
+
/**
|
|
2890
|
+
* handle for promise umdHookMount
|
|
2891
|
+
* @param umdHookMountResult result of umdHookMount
|
|
2892
|
+
*/
|
|
2893
|
+
handleMounted(umdHookMountResult) {
|
|
2894
|
+
if (isPromise(umdHookMountResult)) {
|
|
2895
|
+
umdHookMountResult
|
|
2896
|
+
.then(() => this.dispatchMountedEvent())
|
|
2897
|
+
.catch((e) => this.onerror(e));
|
|
2953
2898
|
}
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
Document.prototype.querySelector = querySelector;
|
|
2957
|
-
Document.prototype.querySelectorAll = querySelectorAll;
|
|
2958
|
-
Document.prototype.getElementById = function getElementById(key) {
|
|
2959
|
-
if (!getCurrentAppName() || isInvalidQuerySelectorKey(key)) {
|
|
2960
|
-
return globalEnv.rawGetElementById.call(this, key);
|
|
2899
|
+
else {
|
|
2900
|
+
this.dispatchMountedEvent();
|
|
2961
2901
|
}
|
|
2962
|
-
|
|
2963
|
-
|
|
2902
|
+
}
|
|
2903
|
+
/**
|
|
2904
|
+
* dispatch mounted event when app run finished
|
|
2905
|
+
*/
|
|
2906
|
+
dispatchMountedEvent() {
|
|
2907
|
+
if (appStates.UNMOUNT !== this.state) {
|
|
2908
|
+
this.state = appStates.MOUNTED;
|
|
2909
|
+
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.MOUNTED);
|
|
2964
2910
|
}
|
|
2965
|
-
|
|
2966
|
-
|
|
2911
|
+
}
|
|
2912
|
+
/**
|
|
2913
|
+
* unmount app
|
|
2914
|
+
* @param destroy completely destroy, delete cache resources
|
|
2915
|
+
* @param unmountcb callback of unmount
|
|
2916
|
+
*/
|
|
2917
|
+
unmount(destroy, unmountcb) {
|
|
2918
|
+
if (this.state === appStates.LOAD_SOURCE_ERROR) {
|
|
2919
|
+
destroy = true;
|
|
2967
2920
|
}
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2921
|
+
this.state = appStates.UNMOUNT;
|
|
2922
|
+
this.keepAliveState = null;
|
|
2923
|
+
this.keepAliveContainer = null;
|
|
2924
|
+
// result of unmount function
|
|
2925
|
+
let umdHookUnmountResult;
|
|
2926
|
+
/**
|
|
2927
|
+
* send an unmount event to the micro app or call umd unmount hook
|
|
2928
|
+
* before the sandbox is cleared
|
|
2929
|
+
*/
|
|
2930
|
+
if (this.umdHookUnmount) {
|
|
2931
|
+
try {
|
|
2932
|
+
umdHookUnmountResult = this.umdHookUnmount();
|
|
2933
|
+
}
|
|
2934
|
+
catch (e) {
|
|
2935
|
+
logError('an error occurred in the unmount function \n', this.name, e);
|
|
2936
|
+
}
|
|
2972
2937
|
}
|
|
2973
|
-
|
|
2974
|
-
|
|
2938
|
+
// dispatch unmount event to micro app
|
|
2939
|
+
dispatchCustomEventToMicroApp('unmount', this.name);
|
|
2940
|
+
this.handleUnmounted(destroy, umdHookUnmountResult, unmountcb);
|
|
2941
|
+
}
|
|
2942
|
+
/**
|
|
2943
|
+
* handle for promise umdHookUnmount
|
|
2944
|
+
* @param destroy completely destroy, delete cache resources
|
|
2945
|
+
* @param umdHookUnmountResult result of umdHookUnmount
|
|
2946
|
+
* @param unmountcb callback of unmount
|
|
2947
|
+
*/
|
|
2948
|
+
handleUnmounted(destroy, umdHookUnmountResult, unmountcb) {
|
|
2949
|
+
if (isPromise(umdHookUnmountResult)) {
|
|
2950
|
+
umdHookUnmountResult
|
|
2951
|
+
.then(() => this.actionsForUnmount(destroy, unmountcb))
|
|
2952
|
+
.catch(() => this.actionsForUnmount(destroy, unmountcb));
|
|
2975
2953
|
}
|
|
2976
|
-
|
|
2977
|
-
|
|
2954
|
+
else {
|
|
2955
|
+
this.actionsForUnmount(destroy, unmountcb);
|
|
2978
2956
|
}
|
|
2979
|
-
}
|
|
2980
|
-
|
|
2957
|
+
}
|
|
2958
|
+
/**
|
|
2959
|
+
* actions for unmount app
|
|
2960
|
+
* @param destroy completely destroy, delete cache resources
|
|
2961
|
+
* @param unmountcb callback of unmount
|
|
2962
|
+
*/
|
|
2963
|
+
actionsForUnmount(destroy, unmountcb) {
|
|
2981
2964
|
var _a;
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
isUniqueElement(key) ||
|
|
2985
|
-
isInvalidQuerySelectorKey(key) ||
|
|
2986
|
-
(!((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.inline) && /^script$/i.test(key))) {
|
|
2987
|
-
return globalEnv.rawGetElementsByTagName.call(this, key);
|
|
2988
|
-
}
|
|
2989
|
-
try {
|
|
2990
|
-
return querySelectorAll.call(this, key);
|
|
2991
|
-
}
|
|
2992
|
-
catch (_b) {
|
|
2993
|
-
return globalEnv.rawGetElementsByTagName.call(this, key);
|
|
2965
|
+
if (destroy) {
|
|
2966
|
+
this.actionsForCompletelyDestroy();
|
|
2994
2967
|
}
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
if (!getCurrentAppName() || isInvalidQuerySelectorKey(key)) {
|
|
2998
|
-
return globalEnv.rawGetElementsByName.call(this, key);
|
|
2968
|
+
else if (this.umdMode && this.container.childElementCount) {
|
|
2969
|
+
cloneContainer(this.container, this.source.html, false);
|
|
2999
2970
|
}
|
|
3000
|
-
|
|
3001
|
-
|
|
2971
|
+
// this.container maybe contains micro-app element, stop sandbox should exec after cloneContainer
|
|
2972
|
+
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.stop();
|
|
2973
|
+
if (!getActiveApps().length) {
|
|
2974
|
+
releasePatchSetAttribute();
|
|
3002
2975
|
}
|
|
3003
|
-
|
|
3004
|
-
|
|
2976
|
+
// dispatch unmount event to base app
|
|
2977
|
+
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.UNMOUNT);
|
|
2978
|
+
this.container.innerHTML = '';
|
|
2979
|
+
this.container = null;
|
|
2980
|
+
unmountcb && unmountcb();
|
|
2981
|
+
}
|
|
2982
|
+
// actions for completely destroy
|
|
2983
|
+
actionsForCompletelyDestroy() {
|
|
2984
|
+
if (!this.useSandbox && this.umdMode) {
|
|
2985
|
+
delete window[this.libraryName];
|
|
3005
2986
|
}
|
|
3006
|
-
|
|
3007
|
-
}
|
|
3008
|
-
function releasePatchDocument() {
|
|
3009
|
-
Document.prototype.createElement = globalEnv.rawCreateElement;
|
|
3010
|
-
Document.prototype.createElementNS = globalEnv.rawCreateElementNS;
|
|
3011
|
-
Document.prototype.createDocumentFragment = globalEnv.rawCreateDocumentFragment;
|
|
3012
|
-
Document.prototype.querySelector = globalEnv.rawQuerySelector;
|
|
3013
|
-
Document.prototype.querySelectorAll = globalEnv.rawQuerySelectorAll;
|
|
3014
|
-
Document.prototype.getElementById = globalEnv.rawGetElementById;
|
|
3015
|
-
Document.prototype.getElementsByClassName = globalEnv.rawGetElementsByClassName;
|
|
3016
|
-
Document.prototype.getElementsByTagName = globalEnv.rawGetElementsByTagName;
|
|
3017
|
-
Document.prototype.getElementsByName = globalEnv.rawGetElementsByName;
|
|
3018
|
-
}
|
|
3019
|
-
// release patch
|
|
3020
|
-
function releasePatches() {
|
|
3021
|
-
setCurrentAppName(null);
|
|
3022
|
-
releasePatchDocument();
|
|
3023
|
-
Element.prototype.setAttribute = globalEnv.rawSetAttribute;
|
|
3024
|
-
Element.prototype.appendChild = globalEnv.rawAppendChild;
|
|
3025
|
-
Element.prototype.insertBefore = globalEnv.rawInsertBefore;
|
|
3026
|
-
Element.prototype.replaceChild = globalEnv.rawReplaceChild;
|
|
3027
|
-
Element.prototype.removeChild = globalEnv.rawRemoveChild;
|
|
3028
|
-
Element.prototype.append = globalEnv.rawAppend;
|
|
3029
|
-
Element.prototype.prepend = globalEnv.rawPrepend;
|
|
3030
|
-
Element.prototype.cloneNode = globalEnv.rawCloneNode;
|
|
3031
|
-
}
|
|
3032
|
-
// Set the style of micro-app-head and micro-app-body
|
|
3033
|
-
let hasRejectMicroAppStyle = false;
|
|
3034
|
-
function rejectMicroAppStyle() {
|
|
3035
|
-
if (!hasRejectMicroAppStyle) {
|
|
3036
|
-
hasRejectMicroAppStyle = true;
|
|
3037
|
-
const style = pureCreateElement('style');
|
|
3038
|
-
style.setAttribute('type', 'text/css');
|
|
3039
|
-
style.textContent = `\n${microApp.tagName}, micro-app-body { display: block; } \nmicro-app-head { display: none; }`;
|
|
3040
|
-
globalEnv.rawDocument.head.appendChild(style);
|
|
2987
|
+
appInstanceMap.delete(this.name);
|
|
3041
2988
|
}
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
2989
|
+
// hidden app when disconnectedCallback called with keep-alive
|
|
2990
|
+
hiddenKeepAliveApp() {
|
|
2991
|
+
const oldContainer = this.container;
|
|
2992
|
+
cloneContainer(this.container, this.keepAliveContainer ? this.keepAliveContainer : (this.keepAliveContainer = document.createElement('div')), false);
|
|
2993
|
+
this.container = this.keepAliveContainer;
|
|
2994
|
+
this.keepAliveState = keepAliveStates.KEEP_ALIVE_HIDDEN;
|
|
2995
|
+
// event should dispatch before clone node
|
|
2996
|
+
// dispatch afterhidden event to micro-app
|
|
2997
|
+
dispatchCustomEventToMicroApp('appstate-change', this.name, {
|
|
2998
|
+
appState: 'afterhidden',
|
|
2999
|
+
});
|
|
3000
|
+
// dispatch afterhidden event to base app
|
|
3001
|
+
dispatchLifecyclesEvent(oldContainer, this.name, lifeCycles.AFTERHIDDEN);
|
|
3054
3002
|
}
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3003
|
+
// show app when connectedCallback called with keep-alive
|
|
3004
|
+
showKeepAliveApp(container) {
|
|
3005
|
+
// dispatch beforeshow event to micro-app
|
|
3006
|
+
dispatchCustomEventToMicroApp('appstate-change', this.name, {
|
|
3007
|
+
appState: 'beforeshow',
|
|
3008
|
+
});
|
|
3009
|
+
// dispatch beforeshow event to base app
|
|
3010
|
+
dispatchLifecyclesEvent(container, this.name, lifeCycles.BEFORESHOW);
|
|
3011
|
+
cloneContainer(this.container, container, false);
|
|
3012
|
+
this.container = container;
|
|
3013
|
+
this.keepAliveState = keepAliveStates.KEEP_ALIVE_SHOW;
|
|
3014
|
+
// dispatch aftershow event to micro-app
|
|
3015
|
+
dispatchCustomEventToMicroApp('appstate-change', this.name, {
|
|
3016
|
+
appState: 'aftershow',
|
|
3017
|
+
});
|
|
3018
|
+
// dispatch aftershow event to base app
|
|
3019
|
+
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.AFTERSHOW);
|
|
3060
3020
|
}
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3021
|
+
/**
|
|
3022
|
+
* app rendering error
|
|
3023
|
+
* @param e Error
|
|
3024
|
+
*/
|
|
3025
|
+
onerror(e) {
|
|
3026
|
+
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.ERROR, e);
|
|
3027
|
+
}
|
|
3028
|
+
// get app state
|
|
3029
|
+
getAppState() {
|
|
3030
|
+
return this.state;
|
|
3031
|
+
}
|
|
3032
|
+
// get keep-alive state
|
|
3033
|
+
getKeepAliveState() {
|
|
3034
|
+
return this.keepAliveState;
|
|
3035
|
+
}
|
|
3036
|
+
// get umd library, if it not exist, return empty object
|
|
3037
|
+
getUmdLibraryHooks() {
|
|
3038
|
+
var _a, _b;
|
|
3039
|
+
// after execScripts, the app maybe unmounted
|
|
3040
|
+
if (appStates.UNMOUNT !== this.state) {
|
|
3041
|
+
const global = ((_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow) !== null && _b !== void 0 ? _b : globalEnv.rawWindow);
|
|
3042
|
+
this.libraryName = getRootContainer(this.container).getAttribute('library') || `micro-app-${this.name}`;
|
|
3043
|
+
// do not use isObject
|
|
3044
|
+
return typeof global[this.libraryName] === 'object' ? global[this.libraryName] : {};
|
|
3045
|
+
}
|
|
3046
|
+
return {};
|
|
3066
3047
|
}
|
|
3067
3048
|
}
|
|
3068
3049
|
|
|
3069
|
-
// record all micro-app elements
|
|
3070
|
-
const elementInstanceMap = new Map();
|
|
3071
3050
|
/**
|
|
3072
3051
|
* define element
|
|
3073
3052
|
* @param tagName element name
|
|
@@ -3076,7 +3055,7 @@ function defineElement(tagName) {
|
|
|
3076
3055
|
class MicroAppElement extends HTMLElement {
|
|
3077
3056
|
constructor() {
|
|
3078
3057
|
super();
|
|
3079
|
-
this.
|
|
3058
|
+
this.isWaiting = false;
|
|
3080
3059
|
this.cacheData = null;
|
|
3081
3060
|
this.hasConnected = false;
|
|
3082
3061
|
this.appName = ''; // app name
|
|
@@ -3084,10 +3063,10 @@ function defineElement(tagName) {
|
|
|
3084
3063
|
this.ssrUrl = ''; // html path in ssr mode
|
|
3085
3064
|
this.version = version;
|
|
3086
3065
|
/**
|
|
3087
|
-
* handle for change of name an url after element
|
|
3066
|
+
* handle for change of name an url after element init
|
|
3088
3067
|
*/
|
|
3089
3068
|
this.handleAttributeUpdate = () => {
|
|
3090
|
-
this.
|
|
3069
|
+
this.isWaiting = false;
|
|
3091
3070
|
const formatAttrName = formatAppName(this.getAttribute('name'));
|
|
3092
3071
|
const formatAttrUrl = formatAppURL(this.getAttribute('url'), this.appName);
|
|
3093
3072
|
if (this.legalAttribute('name', formatAttrName) && this.legalAttribute('url', formatAttrUrl)) {
|
|
@@ -3122,10 +3101,7 @@ function defineElement(tagName) {
|
|
|
3122
3101
|
this.setAttribute('name', this.appName);
|
|
3123
3102
|
}
|
|
3124
3103
|
};
|
|
3125
|
-
|
|
3126
|
-
if (!this.querySelector('micro-app-head')) {
|
|
3127
|
-
this.performWhenFirstCreated();
|
|
3128
|
-
}
|
|
3104
|
+
patchSetAttribute();
|
|
3129
3105
|
}
|
|
3130
3106
|
static get observedAttributes() {
|
|
3131
3107
|
return ['name', 'url'];
|
|
@@ -3142,9 +3118,6 @@ function defineElement(tagName) {
|
|
|
3142
3118
|
// keep-alive: open keep-alive mode
|
|
3143
3119
|
connectedCallback() {
|
|
3144
3120
|
this.hasConnected = true;
|
|
3145
|
-
if (!elementInstanceMap.has(this)) {
|
|
3146
|
-
this.performWhenFirstCreated();
|
|
3147
|
-
}
|
|
3148
3121
|
defer(() => dispatchLifecyclesEvent(this, this.appName, lifeCycles.CREATED));
|
|
3149
3122
|
this.initialMount();
|
|
3150
3123
|
}
|
|
@@ -3155,12 +3128,7 @@ function defineElement(tagName) {
|
|
|
3155
3128
|
this.handleHiddenKeepAliveApp();
|
|
3156
3129
|
}
|
|
3157
3130
|
else {
|
|
3158
|
-
|
|
3159
|
-
this.handleUnmount(this.getDestroyCompatibleResult(), () => {
|
|
3160
|
-
if (elementInstanceMap.size === 0) {
|
|
3161
|
-
releasePatches();
|
|
3162
|
-
}
|
|
3163
|
-
});
|
|
3131
|
+
this.handleUnmount(this.getDestroyCompatibleResult());
|
|
3164
3132
|
}
|
|
3165
3133
|
}
|
|
3166
3134
|
attributeChangedCallback(attr, _oldVal, newVal) {
|
|
@@ -3189,8 +3157,8 @@ function defineElement(tagName) {
|
|
|
3189
3157
|
}
|
|
3190
3158
|
this.handleInitialNameAndUrl();
|
|
3191
3159
|
}
|
|
3192
|
-
else if (!this.
|
|
3193
|
-
this.
|
|
3160
|
+
else if (!this.isWaiting) {
|
|
3161
|
+
this.isWaiting = true;
|
|
3194
3162
|
defer(this.handleAttributeUpdate);
|
|
3195
3163
|
}
|
|
3196
3164
|
}
|
|
@@ -3199,15 +3167,6 @@ function defineElement(tagName) {
|
|
|
3199
3167
|
handleInitialNameAndUrl() {
|
|
3200
3168
|
this.hasConnected && this.initialMount();
|
|
3201
3169
|
}
|
|
3202
|
-
// Perform global initialization when the element count is 1
|
|
3203
|
-
performWhenFirstCreated() {
|
|
3204
|
-
if (elementInstanceMap.set(this, true).size === 1) {
|
|
3205
|
-
patchElementPrototypeMethods();
|
|
3206
|
-
rejectMicroAppStyle();
|
|
3207
|
-
releaseUnmountOfNestedApp();
|
|
3208
|
-
listenUmountOfNestedApp();
|
|
3209
|
-
}
|
|
3210
|
-
}
|
|
3211
3170
|
/**
|
|
3212
3171
|
* first mount of this app
|
|
3213
3172
|
*/
|
|
@@ -3369,7 +3328,7 @@ function defineElement(tagName) {
|
|
|
3369
3328
|
}
|
|
3370
3329
|
// show app when connectedCallback called with keep-alive
|
|
3371
3330
|
handleShowKeepAliveApp(app) {
|
|
3372
|
-
// must be
|
|
3331
|
+
// must be async
|
|
3373
3332
|
defer(() => { var _a; return app.showKeepAliveApp((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this); });
|
|
3374
3333
|
}
|
|
3375
3334
|
/**
|
|
@@ -3379,7 +3338,7 @@ function defineElement(tagName) {
|
|
|
3379
3338
|
*/
|
|
3380
3339
|
getDisposeResult(name) {
|
|
3381
3340
|
// @ts-ignore
|
|
3382
|
-
return (this.compatibleSpecialProperties(name) || microApp[name]) && this.
|
|
3341
|
+
return (this.compatibleSpecialProperties(name) || microApp[name]) && this.compatibleDisableSpecialProperties(name);
|
|
3383
3342
|
}
|
|
3384
3343
|
// compatible of disableScopecss & disableSandbox
|
|
3385
3344
|
compatibleSpecialProperties(name) {
|
|
@@ -3392,7 +3351,7 @@ function defineElement(tagName) {
|
|
|
3392
3351
|
return this.hasAttribute(name);
|
|
3393
3352
|
}
|
|
3394
3353
|
// compatible of disableScopecss & disableSandbox
|
|
3395
|
-
|
|
3354
|
+
compatibleDisableSpecialProperties(name) {
|
|
3396
3355
|
if (name === 'disableScopecss') {
|
|
3397
3356
|
return this.getAttribute('disableScopecss') !== 'false' && this.getAttribute('disable-scopecss') !== 'false';
|
|
3398
3357
|
}
|
|
@@ -3541,7 +3500,7 @@ function getGlobalAssets(assets) {
|
|
|
3541
3500
|
|
|
3542
3501
|
/**
|
|
3543
3502
|
* if app not prefetch & not unmount, then app is active
|
|
3544
|
-
* @param excludeHiddenApp exclude hidden keep-alive app
|
|
3503
|
+
* @param excludeHiddenApp exclude hidden keep-alive app, default is false
|
|
3545
3504
|
* @returns active apps
|
|
3546
3505
|
*/
|
|
3547
3506
|
function getActiveApps(excludeHiddenApp) {
|
|
@@ -3561,30 +3520,30 @@ function getAllApps() {
|
|
|
3561
3520
|
return Array.from(appInstanceMap.keys());
|
|
3562
3521
|
}
|
|
3563
3522
|
/**
|
|
3564
|
-
* unmount app by
|
|
3523
|
+
* unmount app by appName
|
|
3565
3524
|
* @param appName
|
|
3566
3525
|
* @param options unmountAppParams
|
|
3567
3526
|
* @returns Promise<void>
|
|
3568
3527
|
*/
|
|
3569
3528
|
function unmountApp(appName, options) {
|
|
3570
3529
|
const app = appInstanceMap.get(formatAppName(appName));
|
|
3571
|
-
return new Promise((
|
|
3530
|
+
return new Promise((resolve) => {
|
|
3572
3531
|
if (app) {
|
|
3573
3532
|
if (app.getAppState() === appStates.UNMOUNT || app.isPrefetch) {
|
|
3574
3533
|
if (options === null || options === void 0 ? void 0 : options.destroy) {
|
|
3575
3534
|
app.actionsForCompletelyDestroy();
|
|
3576
3535
|
}
|
|
3577
|
-
|
|
3536
|
+
resolve();
|
|
3578
3537
|
}
|
|
3579
3538
|
else if (app.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN) {
|
|
3580
3539
|
if (options === null || options === void 0 ? void 0 : options.destroy) {
|
|
3581
|
-
app.unmount(true,
|
|
3540
|
+
app.unmount(true, resolve);
|
|
3582
3541
|
}
|
|
3583
3542
|
else if (options === null || options === void 0 ? void 0 : options.clearAliveState) {
|
|
3584
|
-
app.unmount(false,
|
|
3543
|
+
app.unmount(false, resolve);
|
|
3585
3544
|
}
|
|
3586
3545
|
else {
|
|
3587
|
-
|
|
3546
|
+
resolve();
|
|
3588
3547
|
}
|
|
3589
3548
|
}
|
|
3590
3549
|
else {
|
|
@@ -3592,12 +3551,12 @@ function unmountApp(appName, options) {
|
|
|
3592
3551
|
const unmountHandler = () => {
|
|
3593
3552
|
container.removeEventListener('unmount', unmountHandler);
|
|
3594
3553
|
container.removeEventListener('afterhidden', afterhiddenHandler);
|
|
3595
|
-
|
|
3554
|
+
resolve();
|
|
3596
3555
|
};
|
|
3597
3556
|
const afterhiddenHandler = () => {
|
|
3598
3557
|
container.removeEventListener('unmount', unmountHandler);
|
|
3599
3558
|
container.removeEventListener('afterhidden', afterhiddenHandler);
|
|
3600
|
-
|
|
3559
|
+
resolve();
|
|
3601
3560
|
};
|
|
3602
3561
|
container.addEventListener('unmount', unmountHandler);
|
|
3603
3562
|
container.addEventListener('afterhidden', afterhiddenHandler);
|
|
@@ -3624,7 +3583,7 @@ function unmountApp(appName, options) {
|
|
|
3624
3583
|
}
|
|
3625
3584
|
else {
|
|
3626
3585
|
logWarn(`app ${appName} does not exist`);
|
|
3627
|
-
|
|
3586
|
+
resolve();
|
|
3628
3587
|
}
|
|
3629
3588
|
});
|
|
3630
3589
|
}
|