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