@micro-zoe/micro-app 1.0.0-alpha.3 → 1.0.0-alpha.6
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 +0 -4
- package/README.zh-cn.md +1 -1
- package/lib/index.d.ts +26 -15
- package/lib/index.esm.js +1103 -482
- 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 +3 -1
- package/typings/global.d.ts +117 -67
package/lib/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const version = '1.0.0-alpha.
|
|
1
|
+
const version = '1.0.0-alpha.6';
|
|
2
2
|
// do not use isUndefined
|
|
3
3
|
const isBrowser = typeof window !== 'undefined';
|
|
4
4
|
// do not use isUndefined
|
|
@@ -258,8 +258,19 @@ const requestIdleCallback = globalThis.requestIdleCallback ||
|
|
|
258
258
|
return Math.max(0, 50 - (Date.now() - lastTime));
|
|
259
259
|
},
|
|
260
260
|
});
|
|
261
|
-
},
|
|
261
|
+
}, 1);
|
|
262
262
|
};
|
|
263
|
+
/**
|
|
264
|
+
* Wrap requestIdleCallback with promise
|
|
265
|
+
* Exec callback when browser idle
|
|
266
|
+
*/
|
|
267
|
+
function promiseRequestIdle(callback) {
|
|
268
|
+
return new Promise((resolve) => {
|
|
269
|
+
requestIdleCallback(() => {
|
|
270
|
+
callback(resolve);
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
}
|
|
263
274
|
/**
|
|
264
275
|
* Record the currently running app.name
|
|
265
276
|
*/
|
|
@@ -433,6 +444,45 @@ function useMapRecord() {
|
|
|
433
444
|
}
|
|
434
445
|
};
|
|
435
446
|
}
|
|
447
|
+
function getAttributes(element) {
|
|
448
|
+
const attr = element.attributes;
|
|
449
|
+
const attrMap = new Map();
|
|
450
|
+
for (let i = 0; i < attr.length; i++) {
|
|
451
|
+
attrMap.set(attr[i].name, attr[i].value);
|
|
452
|
+
}
|
|
453
|
+
return attrMap;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* if fiberTasks exist, wrap callback with promiseRequestIdle
|
|
457
|
+
* if not, execute callback
|
|
458
|
+
* @param fiberTasks fiber task list
|
|
459
|
+
* @param callback action callback
|
|
460
|
+
*/
|
|
461
|
+
function injectFiberTask(fiberTasks, callback) {
|
|
462
|
+
if (fiberTasks) {
|
|
463
|
+
fiberTasks.push(() => promiseRequestIdle((resolve) => {
|
|
464
|
+
callback();
|
|
465
|
+
resolve();
|
|
466
|
+
}));
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
callback();
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* serial exec fiber task of link, style, script
|
|
474
|
+
* @param tasks task array or null
|
|
475
|
+
*/
|
|
476
|
+
function serialExecFiberTasks(tasks) {
|
|
477
|
+
return (tasks === null || tasks === void 0 ? void 0 : tasks.reduce((pre, next) => pre.then(next), Promise.resolve())) || null;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* inline script start with inline-xxx
|
|
481
|
+
* @param address source address
|
|
482
|
+
*/
|
|
483
|
+
function isInlineScript(address) {
|
|
484
|
+
return address.startsWith('inline-');
|
|
485
|
+
}
|
|
436
486
|
|
|
437
487
|
var ObservedAttrName;
|
|
438
488
|
(function (ObservedAttrName) {
|
|
@@ -493,8 +543,8 @@ function fetchSource(url, appName = null, options = {}) {
|
|
|
493
543
|
* baseApp: <script crossorigin src="https://sgm-static.jd.com/sgm-2.8.0.js" name="SGMH5" sid="6f88a6e4ba4b4ae5acef2ec22c075085" appKey="jdb-adminb2b-pc"></script>
|
|
494
544
|
*/
|
|
495
545
|
removeDomScope();
|
|
496
|
-
if (isFunction(microApp.fetch)) {
|
|
497
|
-
return microApp.fetch(url, options, appName);
|
|
546
|
+
if (isFunction(microApp.options.fetch)) {
|
|
547
|
+
return microApp.options.fetch(url, options, appName);
|
|
498
548
|
}
|
|
499
549
|
// Don’t use globalEnv.rawWindow.fetch, will cause sgm-2.8.0.js throw error in nest app
|
|
500
550
|
return window.fetch(url, options).then((res) => {
|
|
@@ -502,6 +552,66 @@ function fetchSource(url, appName = null, options = {}) {
|
|
|
502
552
|
});
|
|
503
553
|
}
|
|
504
554
|
|
|
555
|
+
class HTMLLoader {
|
|
556
|
+
static getInstance() {
|
|
557
|
+
if (!this.instance) {
|
|
558
|
+
this.instance = new HTMLLoader();
|
|
559
|
+
}
|
|
560
|
+
return this.instance;
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* run logic of load and format html
|
|
564
|
+
* @param successCb success callback
|
|
565
|
+
* @param errorCb error callback, type: (err: Error, meetFetchErr: boolean) => void
|
|
566
|
+
*/
|
|
567
|
+
run(app, successCb) {
|
|
568
|
+
const appName = app.name;
|
|
569
|
+
const htmlUrl = app.ssrUrl || app.url;
|
|
570
|
+
fetchSource(htmlUrl, appName, { cache: 'no-cache' }).then((htmlStr) => {
|
|
571
|
+
if (!htmlStr) {
|
|
572
|
+
const msg = 'html is empty, please check in detail';
|
|
573
|
+
app.onerror(new Error(msg));
|
|
574
|
+
return logError(msg, appName);
|
|
575
|
+
}
|
|
576
|
+
htmlStr = this.formatHTML(htmlUrl, htmlStr, appName);
|
|
577
|
+
successCb(htmlStr, app);
|
|
578
|
+
}).catch((e) => {
|
|
579
|
+
logError(`Failed to fetch data from ${app.url}, micro-app stop rendering`, appName, e);
|
|
580
|
+
app.onLoadError(e);
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
formatHTML(htmlUrl, htmlStr, appName) {
|
|
584
|
+
return this.processHtml(htmlUrl, htmlStr, appName, microApp.options.plugins)
|
|
585
|
+
.replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
|
|
586
|
+
return match
|
|
587
|
+
.replace(/<head/i, '<micro-app-head')
|
|
588
|
+
.replace(/<\/head>/i, '</micro-app-head>');
|
|
589
|
+
})
|
|
590
|
+
.replace(/<body[^>]*>[\s\S]*?<\/body>/i, (match) => {
|
|
591
|
+
return match
|
|
592
|
+
.replace(/<body/i, '<micro-app-body')
|
|
593
|
+
.replace(/<\/body>/i, '</micro-app-body>');
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
processHtml(url, code, appName, plugins) {
|
|
597
|
+
var _a;
|
|
598
|
+
if (!plugins)
|
|
599
|
+
return code;
|
|
600
|
+
const mergedPlugins = [];
|
|
601
|
+
plugins.global && mergedPlugins.push(...plugins.global);
|
|
602
|
+
((_a = plugins.modules) === null || _a === void 0 ? void 0 : _a[appName]) && mergedPlugins.push(...plugins.modules[appName]);
|
|
603
|
+
if (mergedPlugins.length > 0) {
|
|
604
|
+
return mergedPlugins.reduce((preCode, plugin) => {
|
|
605
|
+
if (isPlainObject(plugin) && isFunction(plugin.processHtml)) {
|
|
606
|
+
return plugin.processHtml(preCode, url);
|
|
607
|
+
}
|
|
608
|
+
return preCode;
|
|
609
|
+
}, code);
|
|
610
|
+
}
|
|
611
|
+
return code;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
505
615
|
// common reg
|
|
506
616
|
const rootSelectorREG = /(^|\s+)(html|:root)(?=[\s>~[.#:]+|$)/;
|
|
507
617
|
const bodySelectorREG = /(^|\s+)((html[\s>~]+body)|body)(?=[\s>~[.#:]+|$)/;
|
|
@@ -531,11 +641,13 @@ class CSSParser {
|
|
|
531
641
|
this.scopecssDisableSelectors = []; // disable or enable scopecss for specific selectors
|
|
532
642
|
this.scopecssDisableNextLine = false; // use block comments /* scopecss-disable-next-line */ to disable scopecss on a specific line
|
|
533
643
|
// https://developer.mozilla.org/en-US/docs/Web/API/CSSMediaRule
|
|
534
|
-
this.mediaRule = this.
|
|
644
|
+
this.mediaRule = this.createMatcherForRuleWithChildRule(/^@media *([^{]+)/, '@media');
|
|
535
645
|
// https://developer.mozilla.org/en-US/docs/Web/API/CSSSupportsRule
|
|
536
|
-
this.supportsRule = this.
|
|
537
|
-
this.documentRule = this.
|
|
538
|
-
this.hostRule = this.
|
|
646
|
+
this.supportsRule = this.createMatcherForRuleWithChildRule(/^@supports *([^{]+)/, '@supports');
|
|
647
|
+
this.documentRule = this.createMatcherForRuleWithChildRule(/^@([-\w]+)?document *([^{]+)/, '@document');
|
|
648
|
+
this.hostRule = this.createMatcherForRuleWithChildRule(/^@host\s*/, '@host');
|
|
649
|
+
// :global is CSS Modules rule, it will be converted to normal syntax
|
|
650
|
+
// private globalRule = this.createMatcherForRuleWithChildRule(/^:global([^{]*)/, ':global')
|
|
539
651
|
// https://developer.mozilla.org/en-US/docs/Web/API/CSSImportRule
|
|
540
652
|
this.importRule = this.createMatcherForNoneBraceAtRule('import');
|
|
541
653
|
// Removed in most browsers
|
|
@@ -656,6 +768,13 @@ class CSSParser {
|
|
|
656
768
|
this.hostRule() ||
|
|
657
769
|
this.fontFaceRule();
|
|
658
770
|
}
|
|
771
|
+
// :global is CSS Modules rule, it will be converted to normal syntax
|
|
772
|
+
// private matchGlobalRule (): boolean | void {
|
|
773
|
+
// if (this.cssText[0] !== ':') return false
|
|
774
|
+
// // reset scopecssDisableNextLine
|
|
775
|
+
// this.scopecssDisableNextLine = false
|
|
776
|
+
// return this.globalRule()
|
|
777
|
+
// }
|
|
659
778
|
// https://developer.mozilla.org/en-US/docs/Web/API/CSSKeyframesRule
|
|
660
779
|
keyframesRule() {
|
|
661
780
|
if (!this.commonMatch(/^@([-\w]+)?keyframes\s*/))
|
|
@@ -709,17 +828,17 @@ class CSSParser {
|
|
|
709
828
|
return false;
|
|
710
829
|
return this.commonHandlerForAtRuleWithSelfRule('font-face');
|
|
711
830
|
}
|
|
712
|
-
// common matcher for @media, @supports, @document, @host
|
|
713
|
-
|
|
831
|
+
// common matcher for @media, @supports, @document, @host, :global
|
|
832
|
+
createMatcherForRuleWithChildRule(reg, name) {
|
|
714
833
|
return () => {
|
|
715
834
|
if (!this.commonMatch(reg))
|
|
716
835
|
return false;
|
|
717
836
|
if (!this.matchOpenBrace())
|
|
718
|
-
return parseError(
|
|
837
|
+
return parseError(`${name} missing '{'`, this.linkPath);
|
|
719
838
|
this.matchComments();
|
|
720
839
|
this.matchRules();
|
|
721
840
|
if (!this.matchCloseBrace())
|
|
722
|
-
return parseError(
|
|
841
|
+
return parseError(`${name} missing '}'`, this.linkPath);
|
|
723
842
|
this.matchLeadingSpaces();
|
|
724
843
|
return true;
|
|
725
844
|
};
|
|
@@ -846,20 +965,20 @@ let parser;
|
|
|
846
965
|
* @param styleElement target style element
|
|
847
966
|
* @param appName app name
|
|
848
967
|
*/
|
|
849
|
-
function scopedCSS(styleElement, app) {
|
|
968
|
+
function scopedCSS(styleElement, app, linkPath) {
|
|
850
969
|
if (app.scopecss) {
|
|
851
|
-
const prefix =
|
|
970
|
+
const prefix = createPrefix(app.name);
|
|
852
971
|
if (!parser)
|
|
853
972
|
parser = new CSSParser();
|
|
854
973
|
if (styleElement.textContent) {
|
|
855
|
-
commonAction(styleElement, app.name, prefix, app.url,
|
|
974
|
+
commonAction(styleElement, app.name, prefix, app.url, linkPath);
|
|
856
975
|
}
|
|
857
976
|
else {
|
|
858
977
|
const observer = new MutationObserver(function () {
|
|
859
978
|
observer.disconnect();
|
|
860
979
|
// styled-component will be ignore
|
|
861
980
|
if (styleElement.textContent && !styleElement.hasAttribute('data-styled')) {
|
|
862
|
-
commonAction(styleElement, app.name, prefix, app.url,
|
|
981
|
+
commonAction(styleElement, app.name, prefix, app.url, linkPath);
|
|
863
982
|
}
|
|
864
983
|
});
|
|
865
984
|
observer.observe(styleElement, { childList: true });
|
|
@@ -867,6 +986,10 @@ function scopedCSS(styleElement, app) {
|
|
|
867
986
|
}
|
|
868
987
|
return styleElement;
|
|
869
988
|
}
|
|
989
|
+
function createPrefix(appName, reg = false) {
|
|
990
|
+
const regCharacter = reg ? '\\' : '';
|
|
991
|
+
return `${microApp.tagName}${regCharacter}[name=${appName}${regCharacter}]`;
|
|
992
|
+
}
|
|
870
993
|
|
|
871
994
|
function eventHandler(event, element) {
|
|
872
995
|
Object.defineProperties(event, {
|
|
@@ -908,8 +1031,76 @@ function dispatchOnErrorEvent(element) {
|
|
|
908
1031
|
}
|
|
909
1032
|
}
|
|
910
1033
|
|
|
911
|
-
|
|
912
|
-
|
|
1034
|
+
/**
|
|
1035
|
+
* SourceCenter is a resource management center
|
|
1036
|
+
* All html, js, css will be recorded and processed here
|
|
1037
|
+
* NOTE:
|
|
1038
|
+
* 1. All resources are global and shared between apps
|
|
1039
|
+
* 2. Pay attention to the case of html with parameters
|
|
1040
|
+
* 3. The resource is first processed by the plugin
|
|
1041
|
+
*/
|
|
1042
|
+
function createSourceCenter() {
|
|
1043
|
+
const linkList = new Map();
|
|
1044
|
+
const scriptList = new Map();
|
|
1045
|
+
// setInterval(() => {
|
|
1046
|
+
// console.log(linkList, scriptList)
|
|
1047
|
+
// }, 10000);
|
|
1048
|
+
function createSourceHandler(targetList) {
|
|
1049
|
+
return {
|
|
1050
|
+
setInfo(address, info) {
|
|
1051
|
+
targetList.set(address, info);
|
|
1052
|
+
},
|
|
1053
|
+
getInfo(address) {
|
|
1054
|
+
var _a;
|
|
1055
|
+
return (_a = targetList.get(address)) !== null && _a !== void 0 ? _a : null;
|
|
1056
|
+
},
|
|
1057
|
+
hasInfo(address) {
|
|
1058
|
+
return targetList.has(address);
|
|
1059
|
+
},
|
|
1060
|
+
deleteInfo(address) {
|
|
1061
|
+
return targetList.delete(address);
|
|
1062
|
+
}
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
return {
|
|
1066
|
+
link: createSourceHandler(linkList),
|
|
1067
|
+
script: Object.assign(Object.assign({}, createSourceHandler(scriptList)), { deleteInlineInfo(addressList) {
|
|
1068
|
+
addressList.forEach((address) => {
|
|
1069
|
+
if (isInlineScript(address)) {
|
|
1070
|
+
scriptList.delete(address);
|
|
1071
|
+
}
|
|
1072
|
+
});
|
|
1073
|
+
} }),
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
var sourceCenter = createSourceCenter();
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
*
|
|
1080
|
+
* @param appName app.name
|
|
1081
|
+
* @param linkInfo linkInfo of current address
|
|
1082
|
+
*/
|
|
1083
|
+
function getExistParseCode(appName, prefix, linkInfo) {
|
|
1084
|
+
const appSpace = linkInfo.appSpace;
|
|
1085
|
+
for (const item in appSpace) {
|
|
1086
|
+
if (item !== appName) {
|
|
1087
|
+
const appSpaceData = appSpace[item];
|
|
1088
|
+
if (appSpaceData.parsedCode) {
|
|
1089
|
+
return appSpaceData.parsedCode.replaceAll(new RegExp(createPrefix(item, true), 'g'), prefix);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
// transfer the attributes on the link to convertStyle
|
|
1095
|
+
function setConvertStyleAttr(convertStyle, attrs) {
|
|
1096
|
+
attrs.forEach((value, key) => {
|
|
1097
|
+
if (key === 'rel')
|
|
1098
|
+
return;
|
|
1099
|
+
if (key === 'href')
|
|
1100
|
+
key = 'data-origin-href';
|
|
1101
|
+
convertStyle.setAttribute(key, value);
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
913
1104
|
/**
|
|
914
1105
|
* Extract link elements
|
|
915
1106
|
* @param link link element
|
|
@@ -924,22 +1115,29 @@ function extractLinkFromHtml(link, parent, app, isDynamic = false) {
|
|
|
924
1115
|
let replaceComment = null;
|
|
925
1116
|
if (rel === 'stylesheet' && href) {
|
|
926
1117
|
href = CompletionPath(href, app.url);
|
|
1118
|
+
let linkInfo = sourceCenter.link.getInfo(href);
|
|
1119
|
+
const appSpaceData = {
|
|
1120
|
+
attrs: getAttributes(link),
|
|
1121
|
+
};
|
|
1122
|
+
if (!linkInfo) {
|
|
1123
|
+
linkInfo = {
|
|
1124
|
+
code: '',
|
|
1125
|
+
appSpace: {
|
|
1126
|
+
[app.name]: appSpaceData,
|
|
1127
|
+
}
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
else {
|
|
1131
|
+
linkInfo.appSpace[app.name] = linkInfo.appSpace[app.name] || appSpaceData;
|
|
1132
|
+
}
|
|
1133
|
+
sourceCenter.link.setInfo(href, linkInfo);
|
|
927
1134
|
if (!isDynamic) {
|
|
1135
|
+
app.source.links.add(href);
|
|
928
1136
|
replaceComment = document.createComment(`link element with href=${href} move to micro-app-head as style element`);
|
|
929
|
-
app.
|
|
930
|
-
code: '',
|
|
931
|
-
placeholder: replaceComment,
|
|
932
|
-
isGlobal: link.hasAttribute('global'),
|
|
933
|
-
});
|
|
1137
|
+
linkInfo.appSpace[app.name].placeholder = replaceComment;
|
|
934
1138
|
}
|
|
935
1139
|
else {
|
|
936
|
-
return {
|
|
937
|
-
url: href,
|
|
938
|
-
info: {
|
|
939
|
-
code: '',
|
|
940
|
-
isGlobal: link.hasAttribute('global'),
|
|
941
|
-
}
|
|
942
|
-
};
|
|
1140
|
+
return { address: href, linkInfo };
|
|
943
1141
|
}
|
|
944
1142
|
}
|
|
945
1143
|
else if (rel && ['prefetch', 'preload', 'prerender', 'icon', 'apple-touch-icon'].includes(rel)) {
|
|
@@ -968,79 +1166,138 @@ function extractLinkFromHtml(link, parent, app, isDynamic = false) {
|
|
|
968
1166
|
* @param app app
|
|
969
1167
|
* @param microAppHead micro-app-head
|
|
970
1168
|
*/
|
|
971
|
-
function fetchLinksFromHtml(wrapElement, app, microAppHead) {
|
|
972
|
-
const
|
|
973
|
-
const fetchLinkPromise =
|
|
974
|
-
|
|
1169
|
+
function fetchLinksFromHtml(wrapElement, app, microAppHead, fiberStyleResult) {
|
|
1170
|
+
const styleList = Array.from(app.source.links);
|
|
1171
|
+
const fetchLinkPromise = styleList.map((address) => {
|
|
1172
|
+
const linkInfo = sourceCenter.link.getInfo(address);
|
|
1173
|
+
return linkInfo.code ? linkInfo.code : fetchSource(address, app.name);
|
|
975
1174
|
});
|
|
1175
|
+
const fiberLinkTasks = app.isPrefetch || app.fiber ? [] : null;
|
|
976
1176
|
promiseStream(fetchLinkPromise, (res) => {
|
|
977
|
-
|
|
1177
|
+
injectFiberTask(fiberLinkTasks, () => fetchLinkSuccess(styleList[res.index], res.data, microAppHead, app));
|
|
978
1178
|
}, (err) => {
|
|
979
1179
|
logError(err, app.name);
|
|
980
1180
|
}, () => {
|
|
981
|
-
|
|
1181
|
+
if (fiberLinkTasks) {
|
|
1182
|
+
/**
|
|
1183
|
+
* 1. If fiberLinkTasks is not null, fiberStyleResult is not null
|
|
1184
|
+
* 2. Download link source while processing style
|
|
1185
|
+
* 3. Process style first, and then process link
|
|
1186
|
+
*/
|
|
1187
|
+
fiberStyleResult.then(() => {
|
|
1188
|
+
fiberLinkTasks.push(() => Promise.resolve(app.onLoad(wrapElement)));
|
|
1189
|
+
serialExecFiberTasks(fiberLinkTasks);
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
else {
|
|
1193
|
+
app.onLoad(wrapElement);
|
|
1194
|
+
}
|
|
982
1195
|
});
|
|
983
1196
|
}
|
|
984
1197
|
/**
|
|
985
|
-
*
|
|
986
|
-
*
|
|
987
|
-
*
|
|
988
|
-
*
|
|
1198
|
+
* Fetch link succeeded, replace placeholder with style tag
|
|
1199
|
+
* NOTE:
|
|
1200
|
+
* 1. Only exec when init, no longer exec when remount
|
|
1201
|
+
* 2. Only handler html link element, not dynamic link or style
|
|
1202
|
+
* 3. The same prefix can reuse parsedCode
|
|
1203
|
+
* 4. Async exec with requestIdleCallback in prefetch or fiber
|
|
1204
|
+
* 5. appSpace[app.name].placeholder/attrs must exist
|
|
1205
|
+
* @param address resource address
|
|
1206
|
+
* @param code link source code
|
|
989
1207
|
* @param microAppHead micro-app-head
|
|
990
|
-
* @param app app
|
|
1208
|
+
* @param app app instance
|
|
991
1209
|
*/
|
|
992
|
-
function fetchLinkSuccess(
|
|
993
|
-
|
|
994
|
-
|
|
1210
|
+
function fetchLinkSuccess(address, code, microAppHead, app) {
|
|
1211
|
+
/**
|
|
1212
|
+
* linkInfo must exist, but linkInfo.code not
|
|
1213
|
+
* so we set code to linkInfo.code
|
|
1214
|
+
*/
|
|
1215
|
+
const linkInfo = sourceCenter.link.getInfo(address);
|
|
1216
|
+
linkInfo.code = code;
|
|
1217
|
+
const appSpaceData = linkInfo.appSpace[app.name];
|
|
1218
|
+
const placeholder = appSpaceData.placeholder;
|
|
1219
|
+
/**
|
|
1220
|
+
* When prefetch app is replaced by a new app in the processing phase, since the linkInfo is common, when the linkInfo of the prefetch app is processed, it may have already been processed.
|
|
1221
|
+
* This causes placeholder to be possibly null
|
|
1222
|
+
* e.g.
|
|
1223
|
+
* 1. prefetch app.url different from <micro-app></micro-app>
|
|
1224
|
+
* 2. prefetch param different from <micro-app></micro-app>
|
|
1225
|
+
*/
|
|
1226
|
+
if (placeholder) {
|
|
1227
|
+
const convertStyle = pureCreateElement('style');
|
|
1228
|
+
handleConvertStyle(app, address, convertStyle, linkInfo, appSpaceData.attrs);
|
|
1229
|
+
if (placeholder.parentNode) {
|
|
1230
|
+
placeholder.parentNode.replaceChild(convertStyle, placeholder);
|
|
1231
|
+
}
|
|
1232
|
+
else {
|
|
1233
|
+
microAppHead.appendChild(convertStyle);
|
|
1234
|
+
}
|
|
1235
|
+
// clear placeholder
|
|
1236
|
+
appSpaceData.placeholder = null;
|
|
995
1237
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1238
|
+
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Get parsedCode, update convertStyle
|
|
1241
|
+
* Actions:
|
|
1242
|
+
* 1. get scope css (through scopedCSS or oldData)
|
|
1243
|
+
* 2. record parsedCode
|
|
1244
|
+
* 3. set parsedCode to convertStyle if need
|
|
1245
|
+
* @param app app instance
|
|
1246
|
+
* @param address resource address
|
|
1247
|
+
* @param convertStyle converted style
|
|
1248
|
+
* @param linkInfo linkInfo in sourceCenter
|
|
1249
|
+
* @param attrs attrs of link
|
|
1250
|
+
*/
|
|
1251
|
+
function handleConvertStyle(app, address, convertStyle, linkInfo, attrs) {
|
|
1252
|
+
if (app.scopecss) {
|
|
1253
|
+
const appSpaceData = linkInfo.appSpace[app.name];
|
|
1254
|
+
appSpaceData.prefix = appSpaceData.prefix || createPrefix(app.name);
|
|
1255
|
+
if (!appSpaceData.parsedCode) {
|
|
1256
|
+
const existParsedCode = getExistParseCode(app.name, appSpaceData.prefix, linkInfo);
|
|
1257
|
+
if (!existParsedCode) {
|
|
1258
|
+
convertStyle.textContent = linkInfo.code;
|
|
1259
|
+
scopedCSS(convertStyle, app, address);
|
|
1260
|
+
}
|
|
1261
|
+
else {
|
|
1262
|
+
convertStyle.textContent = existParsedCode;
|
|
1263
|
+
}
|
|
1264
|
+
appSpaceData.parsedCode = convertStyle.textContent;
|
|
1265
|
+
}
|
|
1266
|
+
else {
|
|
1267
|
+
convertStyle.textContent = appSpaceData.parsedCode;
|
|
1268
|
+
}
|
|
1002
1269
|
}
|
|
1003
1270
|
else {
|
|
1004
|
-
|
|
1271
|
+
convertStyle.textContent = linkInfo.code;
|
|
1005
1272
|
}
|
|
1006
|
-
|
|
1007
|
-
info.code = data;
|
|
1273
|
+
setConvertStyleAttr(convertStyle, attrs);
|
|
1008
1274
|
}
|
|
1009
1275
|
/**
|
|
1010
|
-
*
|
|
1011
|
-
* @param
|
|
1012
|
-
* @param info info
|
|
1276
|
+
* Handle css of dynamic link
|
|
1277
|
+
* @param address link address
|
|
1013
1278
|
* @param app app
|
|
1279
|
+
* @param linkInfo linkInfo
|
|
1014
1280
|
* @param originLink origin link element
|
|
1015
|
-
* @param replaceStyle style element which replaced origin link
|
|
1016
1281
|
*/
|
|
1017
|
-
function formatDynamicLink(
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1282
|
+
function formatDynamicLink(address, app, linkInfo, originLink) {
|
|
1283
|
+
const convertStyle = pureCreateElement('style');
|
|
1284
|
+
const handleDynamicLink = () => {
|
|
1285
|
+
handleConvertStyle(app, address, convertStyle, linkInfo, linkInfo.appSpace[app.name].attrs);
|
|
1286
|
+
dispatchOnLoadEvent(originLink);
|
|
1287
|
+
};
|
|
1288
|
+
if (linkInfo.code) {
|
|
1289
|
+
defer(handleDynamicLink);
|
|
1023
1290
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1291
|
+
else {
|
|
1292
|
+
fetchSource(address, app.name).then((data) => {
|
|
1293
|
+
linkInfo.code = data;
|
|
1294
|
+
handleDynamicLink();
|
|
1295
|
+
}).catch((err) => {
|
|
1296
|
+
logError(err, app.name);
|
|
1297
|
+
dispatchOnErrorEvent(originLink);
|
|
1298
|
+
});
|
|
1032
1299
|
}
|
|
1033
|
-
|
|
1034
|
-
info.code = data;
|
|
1035
|
-
app.source.links.set(url, info);
|
|
1036
|
-
info.isGlobal && globalLinks.set(url, data);
|
|
1037
|
-
replaceStyle.textContent = data;
|
|
1038
|
-
scopedCSS(replaceStyle, app);
|
|
1039
|
-
dispatchOnLoadEvent(originLink);
|
|
1040
|
-
}).catch((err) => {
|
|
1041
|
-
logError(err, app.name);
|
|
1042
|
-
dispatchOnErrorEvent(originLink);
|
|
1043
|
-
});
|
|
1300
|
+
return convertStyle;
|
|
1044
1301
|
}
|
|
1045
1302
|
|
|
1046
1303
|
class Adapter {
|
|
@@ -1116,10 +1373,13 @@ function throttleDeferForParentNode(proxyDocument) {
|
|
|
1116
1373
|
}
|
|
1117
1374
|
}
|
|
1118
1375
|
function setRootParentNode(root, value) {
|
|
1119
|
-
Object.
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1376
|
+
const descriptor = Object.getOwnPropertyDescriptor(root, 'parentNode');
|
|
1377
|
+
if (!descriptor || descriptor.configurable) {
|
|
1378
|
+
Object.defineProperty(root, 'parentNode', {
|
|
1379
|
+
value,
|
|
1380
|
+
configurable: true,
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1123
1383
|
}
|
|
1124
1384
|
|
|
1125
1385
|
// Record element and map element
|
|
@@ -1143,19 +1403,21 @@ function handleNewNode(parent, child, app) {
|
|
|
1143
1403
|
return child;
|
|
1144
1404
|
}
|
|
1145
1405
|
else if (child instanceof HTMLLinkElement) {
|
|
1146
|
-
if (child.hasAttribute('exclude')) {
|
|
1406
|
+
if (child.hasAttribute('exclude') || checkExcludeUrl(child.getAttribute('href'), app.name)) {
|
|
1147
1407
|
const linkReplaceComment = document.createComment('link element with exclude attribute ignored by micro-app');
|
|
1148
1408
|
dynamicElementInMicroAppMap.set(child, linkReplaceComment);
|
|
1149
1409
|
return linkReplaceComment;
|
|
1150
1410
|
}
|
|
1151
|
-
else if (child.hasAttribute('ignore')
|
|
1411
|
+
else if (child.hasAttribute('ignore') ||
|
|
1412
|
+
checkIgnoreUrl(child.getAttribute('href'), app.name) ||
|
|
1413
|
+
(child.href &&
|
|
1414
|
+
isFunction(microApp.options.excludeAssetFilter) &&
|
|
1415
|
+
microApp.options.excludeAssetFilter(child.href))) {
|
|
1152
1416
|
return child;
|
|
1153
1417
|
}
|
|
1154
|
-
const {
|
|
1155
|
-
if (
|
|
1156
|
-
const replaceStyle =
|
|
1157
|
-
replaceStyle.__MICRO_APP_LINK_PATH__ = url;
|
|
1158
|
-
formatDynamicLink(url, info, app, child, replaceStyle);
|
|
1418
|
+
const { address, linkInfo, replaceComment } = extractLinkFromHtml(child, parent, app, true);
|
|
1419
|
+
if (address && linkInfo) {
|
|
1420
|
+
const replaceStyle = formatDynamicLink(address, app, linkInfo, child);
|
|
1159
1421
|
dynamicElementInMicroAppMap.set(child, replaceStyle);
|
|
1160
1422
|
return replaceStyle;
|
|
1161
1423
|
}
|
|
@@ -1166,18 +1428,17 @@ function handleNewNode(parent, child, app) {
|
|
|
1166
1428
|
return child;
|
|
1167
1429
|
}
|
|
1168
1430
|
else if (child instanceof HTMLScriptElement) {
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
}
|
|
1431
|
+
if (child.src &&
|
|
1432
|
+
isFunction(microApp.options.excludeAssetFilter) &&
|
|
1433
|
+
microApp.options.excludeAssetFilter(child.src)) {
|
|
1434
|
+
return child;
|
|
1435
|
+
}
|
|
1436
|
+
const { replaceComment, address, scriptInfo } = extractScriptElement(child, parent, app, true) || {};
|
|
1437
|
+
if (address && scriptInfo) {
|
|
1438
|
+
// remote script or inline script
|
|
1439
|
+
const replaceElement = scriptInfo.isExternal ? runDynamicRemoteScript(address, app, scriptInfo, child) : runDynamicInlineScript(address, app, scriptInfo);
|
|
1440
|
+
dynamicElementInMicroAppMap.set(child, replaceElement);
|
|
1441
|
+
return replaceElement;
|
|
1181
1442
|
}
|
|
1182
1443
|
else if (replaceComment) {
|
|
1183
1444
|
dynamicElementInMicroAppMap.set(child, replaceComment);
|
|
@@ -1645,8 +1906,80 @@ function initGlobalEnv() {
|
|
|
1645
1906
|
}
|
|
1646
1907
|
}
|
|
1647
1908
|
|
|
1648
|
-
|
|
1649
|
-
|
|
1909
|
+
const scriptTypes = ['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module', 'systemjs-module', 'systemjs-importmap'];
|
|
1910
|
+
// whether use type='module' script
|
|
1911
|
+
function isTypeModule(app, scriptInfo) {
|
|
1912
|
+
return scriptInfo.appSpace[app.name].module && (!app.useSandbox || app.esmodule);
|
|
1913
|
+
}
|
|
1914
|
+
// special script element
|
|
1915
|
+
function isSpecialScript(app, scriptInfo) {
|
|
1916
|
+
const attrs = scriptInfo.appSpace[app.name].attrs;
|
|
1917
|
+
return attrs.has('id');
|
|
1918
|
+
}
|
|
1919
|
+
/**
|
|
1920
|
+
* whether to run js in inline mode
|
|
1921
|
+
* scene:
|
|
1922
|
+
* 1. inline config for app
|
|
1923
|
+
* 2. inline attr in script element
|
|
1924
|
+
* 3. module script
|
|
1925
|
+
* 4. script with special attr
|
|
1926
|
+
*/
|
|
1927
|
+
function isInlineMode(app, scriptInfo) {
|
|
1928
|
+
return (app.inline ||
|
|
1929
|
+
scriptInfo.appSpace[app.name].inline ||
|
|
1930
|
+
isTypeModule(app, scriptInfo) ||
|
|
1931
|
+
isSpecialScript(app, scriptInfo));
|
|
1932
|
+
}
|
|
1933
|
+
// Convert string code to function
|
|
1934
|
+
function code2Function(code) {
|
|
1935
|
+
return new Function(code);
|
|
1936
|
+
}
|
|
1937
|
+
/**
|
|
1938
|
+
* If the appSpace of the current js address has other app, try to reuse parsedFunction of other app
|
|
1939
|
+
* @param appName app.name
|
|
1940
|
+
* @param scriptInfo scriptInfo of current address
|
|
1941
|
+
* @param currentCode pure code of current address
|
|
1942
|
+
*/
|
|
1943
|
+
function getExistParseResult(appName, scriptInfo, currentCode) {
|
|
1944
|
+
const appSpace = scriptInfo.appSpace;
|
|
1945
|
+
for (const item in appSpace) {
|
|
1946
|
+
if (item !== appName) {
|
|
1947
|
+
const appSpaceData = appSpace[item];
|
|
1948
|
+
if (appSpaceData.parsedCode === currentCode && appSpaceData.parsedFunction) {
|
|
1949
|
+
return appSpaceData.parsedFunction;
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
/**
|
|
1955
|
+
* get parsedFunction from exist data or parsedCode
|
|
1956
|
+
* @returns parsedFunction
|
|
1957
|
+
*/
|
|
1958
|
+
function getParsedFunction(app, scriptInfo, parsedCode) {
|
|
1959
|
+
return getExistParseResult(app.name, scriptInfo, parsedCode) || code2Function(parsedCode);
|
|
1960
|
+
}
|
|
1961
|
+
// Prevent randomly created strings from repeating
|
|
1962
|
+
function getUniqueNonceSrc() {
|
|
1963
|
+
const nonceStr = createNonceSrc();
|
|
1964
|
+
if (sourceCenter.script.hasInfo(nonceStr)) {
|
|
1965
|
+
return getUniqueNonceSrc();
|
|
1966
|
+
}
|
|
1967
|
+
return nonceStr;
|
|
1968
|
+
}
|
|
1969
|
+
// transfer the attributes on the script to convertScript
|
|
1970
|
+
function setConvertScriptAttr(convertScript, attrs) {
|
|
1971
|
+
attrs.forEach((value, key) => {
|
|
1972
|
+
if ((key === 'type' && value === 'module') || key === 'defer' || key === 'async')
|
|
1973
|
+
return;
|
|
1974
|
+
if (key === 'src')
|
|
1975
|
+
key = 'data-origin-src';
|
|
1976
|
+
convertScript.setAttribute(key, value);
|
|
1977
|
+
});
|
|
1978
|
+
}
|
|
1979
|
+
// wrap code in sandbox
|
|
1980
|
+
function isWrapInSandBox(app, scriptInfo) {
|
|
1981
|
+
return app.useSandbox && !isTypeModule(app, scriptInfo);
|
|
1982
|
+
}
|
|
1650
1983
|
/**
|
|
1651
1984
|
* Extract script elements
|
|
1652
1985
|
* @param script script element
|
|
@@ -1657,11 +1990,15 @@ const globalScripts = new Map();
|
|
|
1657
1990
|
function extractScriptElement(script, parent, app, isDynamic = false) {
|
|
1658
1991
|
let replaceComment = null;
|
|
1659
1992
|
let src = script.getAttribute('src');
|
|
1660
|
-
if (
|
|
1993
|
+
if (src)
|
|
1994
|
+
src = CompletionPath(src, app.url);
|
|
1995
|
+
if (script.hasAttribute('exclude') || checkExcludeUrl(src, app.name)) {
|
|
1661
1996
|
replaceComment = document.createComment('script element with exclude attribute removed by micro-app');
|
|
1662
1997
|
}
|
|
1663
|
-
else if ((script.type &&
|
|
1664
|
-
script.
|
|
1998
|
+
else if ((script.type &&
|
|
1999
|
+
!scriptTypes.includes(script.type)) ||
|
|
2000
|
+
script.hasAttribute('ignore') ||
|
|
2001
|
+
checkIgnoreUrl(src, app.name)) {
|
|
1665
2002
|
return null;
|
|
1666
2003
|
}
|
|
1667
2004
|
else if ((globalEnv.supportModuleScript && script.noModule) ||
|
|
@@ -1669,40 +2006,74 @@ function extractScriptElement(script, parent, app, isDynamic = false) {
|
|
|
1669
2006
|
replaceComment = document.createComment(`${script.noModule ? 'noModule' : 'module'} script ignored by micro-app`);
|
|
1670
2007
|
}
|
|
1671
2008
|
else if (src) { // remote script
|
|
1672
|
-
|
|
1673
|
-
const
|
|
1674
|
-
code: '',
|
|
1675
|
-
isExternal: true,
|
|
1676
|
-
isDynamic: isDynamic,
|
|
2009
|
+
let scriptInfo = sourceCenter.script.getInfo(src);
|
|
2010
|
+
const appSpaceData = {
|
|
1677
2011
|
async: script.hasAttribute('async'),
|
|
1678
2012
|
defer: script.defer || script.type === 'module',
|
|
1679
2013
|
module: script.type === 'module',
|
|
1680
|
-
|
|
2014
|
+
inline: script.hasAttribute('inline'),
|
|
2015
|
+
pure: script.hasAttribute('pure'),
|
|
2016
|
+
attrs: getAttributes(script),
|
|
1681
2017
|
};
|
|
2018
|
+
if (!scriptInfo) {
|
|
2019
|
+
scriptInfo = {
|
|
2020
|
+
code: '',
|
|
2021
|
+
isExternal: true,
|
|
2022
|
+
appSpace: {
|
|
2023
|
+
[app.name]: appSpaceData,
|
|
2024
|
+
}
|
|
2025
|
+
};
|
|
2026
|
+
}
|
|
2027
|
+
else {
|
|
2028
|
+
/**
|
|
2029
|
+
* Reuse when appSpace exists
|
|
2030
|
+
* NOTE:
|
|
2031
|
+
* 1. The same static script, appSpace must be the same (in fact, it may be different when url change)
|
|
2032
|
+
* 2. The same dynamic script, appSpace may be the same, but we still reuse appSpace, which should pay attention
|
|
2033
|
+
*/
|
|
2034
|
+
scriptInfo.appSpace[app.name] = scriptInfo.appSpace[app.name] || appSpaceData;
|
|
2035
|
+
}
|
|
2036
|
+
sourceCenter.script.setInfo(src, scriptInfo);
|
|
1682
2037
|
if (!isDynamic) {
|
|
1683
|
-
app.source.scripts.
|
|
2038
|
+
app.source.scripts.add(src);
|
|
1684
2039
|
replaceComment = document.createComment(`script with src='${src}' extract by micro-app`);
|
|
1685
2040
|
}
|
|
1686
2041
|
else {
|
|
1687
|
-
return {
|
|
2042
|
+
return { address: src, scriptInfo };
|
|
1688
2043
|
}
|
|
1689
2044
|
}
|
|
1690
2045
|
else if (script.textContent) { // inline script
|
|
1691
|
-
|
|
1692
|
-
|
|
2046
|
+
/**
|
|
2047
|
+
* NOTE:
|
|
2048
|
+
* 1. Each inline script is unique
|
|
2049
|
+
* 2. Every dynamic created inline script will be re-executed
|
|
2050
|
+
* ACTION:
|
|
2051
|
+
* 1. Delete dynamic inline script info after exec
|
|
2052
|
+
* 2. Delete static inline script info when destroy
|
|
2053
|
+
*/
|
|
2054
|
+
const nonceStr = getUniqueNonceSrc();
|
|
2055
|
+
const scriptInfo = {
|
|
1693
2056
|
code: script.textContent,
|
|
1694
2057
|
isExternal: false,
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
2058
|
+
appSpace: {
|
|
2059
|
+
[app.name]: {
|
|
2060
|
+
async: false,
|
|
2061
|
+
defer: script.type === 'module',
|
|
2062
|
+
module: script.type === 'module',
|
|
2063
|
+
inline: script.hasAttribute('inline'),
|
|
2064
|
+
pure: script.hasAttribute('pure'),
|
|
2065
|
+
attrs: getAttributes(script),
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
1699
2068
|
};
|
|
1700
2069
|
if (!isDynamic) {
|
|
1701
|
-
app.source.scripts.
|
|
2070
|
+
app.source.scripts.add(nonceStr);
|
|
2071
|
+
sourceCenter.script.setInfo(nonceStr, scriptInfo);
|
|
1702
2072
|
replaceComment = document.createComment('inline script extract by micro-app');
|
|
1703
2073
|
}
|
|
1704
2074
|
else {
|
|
1705
|
-
|
|
2075
|
+
// Because each dynamic script is unique, it is not put into sourceCenter
|
|
2076
|
+
return { address: nonceStr, scriptInfo };
|
|
1706
2077
|
}
|
|
1707
2078
|
}
|
|
1708
2079
|
else if (!isDynamic) {
|
|
@@ -1719,34 +2090,77 @@ function extractScriptElement(script, parent, app, isDynamic = false) {
|
|
|
1719
2090
|
return parent.replaceChild(replaceComment, script);
|
|
1720
2091
|
}
|
|
1721
2092
|
}
|
|
2093
|
+
/**
|
|
2094
|
+
* get assets plugins
|
|
2095
|
+
* @param appName app name
|
|
2096
|
+
*/
|
|
2097
|
+
function getAssetsPlugins(appName) {
|
|
2098
|
+
var _a, _b, _c;
|
|
2099
|
+
const globalPlugins = ((_a = microApp.options.plugins) === null || _a === void 0 ? void 0 : _a.global) || [];
|
|
2100
|
+
const modulePlugins = ((_c = (_b = microApp.options.plugins) === null || _b === void 0 ? void 0 : _b.modules) === null || _c === void 0 ? void 0 : _c[appName]) || [];
|
|
2101
|
+
return [...globalPlugins, ...modulePlugins];
|
|
2102
|
+
}
|
|
2103
|
+
/**
|
|
2104
|
+
* whether the address needs to be excluded
|
|
2105
|
+
* @param address css or js link
|
|
2106
|
+
* @param plugins microApp plugins
|
|
2107
|
+
*/
|
|
2108
|
+
function checkExcludeUrl(address, appName) {
|
|
2109
|
+
if (!address)
|
|
2110
|
+
return false;
|
|
2111
|
+
const plugins = getAssetsPlugins(appName) || [];
|
|
2112
|
+
return plugins.some(plugin => {
|
|
2113
|
+
if (!plugin.excludeChecker)
|
|
2114
|
+
return false;
|
|
2115
|
+
return plugin.excludeChecker(address);
|
|
2116
|
+
});
|
|
2117
|
+
}
|
|
2118
|
+
/**
|
|
2119
|
+
* whether the address needs to be ignore
|
|
2120
|
+
* @param address css or js link
|
|
2121
|
+
* @param plugins microApp plugins
|
|
2122
|
+
*/
|
|
2123
|
+
function checkIgnoreUrl(address, appName) {
|
|
2124
|
+
if (!address)
|
|
2125
|
+
return false;
|
|
2126
|
+
const plugins = getAssetsPlugins(appName) || [];
|
|
2127
|
+
return plugins.some(plugin => {
|
|
2128
|
+
if (!plugin.ignoreChecker)
|
|
2129
|
+
return false;
|
|
2130
|
+
return plugin.ignoreChecker(address);
|
|
2131
|
+
});
|
|
2132
|
+
}
|
|
1722
2133
|
/**
|
|
1723
2134
|
* Get remote resources of script
|
|
1724
2135
|
* @param wrapElement htmlDom
|
|
1725
2136
|
* @param app app
|
|
1726
2137
|
*/
|
|
1727
2138
|
function fetchScriptsFromHtml(wrapElement, app) {
|
|
1728
|
-
const
|
|
2139
|
+
const scriptList = Array.from(app.source.scripts);
|
|
1729
2140
|
const fetchScriptPromise = [];
|
|
1730
2141
|
const fetchScriptPromiseInfo = [];
|
|
1731
|
-
for (const
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
else if ((!info.defer && !info.async) || app.isPrefetch) {
|
|
1738
|
-
fetchScriptPromise.push(fetchSource(url, app.name));
|
|
1739
|
-
fetchScriptPromiseInfo.push([url, info]);
|
|
1740
|
-
}
|
|
2142
|
+
for (const address of scriptList) {
|
|
2143
|
+
const scriptInfo = sourceCenter.script.getInfo(address);
|
|
2144
|
+
const appSpaceData = scriptInfo.appSpace[app.name];
|
|
2145
|
+
if ((!appSpaceData.defer && !appSpaceData.async) || app.isPrefetch) {
|
|
2146
|
+
fetchScriptPromise.push(scriptInfo.code ? scriptInfo.code : fetchSource(address, app.name));
|
|
2147
|
+
fetchScriptPromiseInfo.push([address, scriptInfo]);
|
|
1741
2148
|
}
|
|
1742
2149
|
}
|
|
2150
|
+
const fiberScriptTasks = app.isPrefetch || app.fiber ? [] : null;
|
|
1743
2151
|
if (fetchScriptPromise.length) {
|
|
1744
2152
|
promiseStream(fetchScriptPromise, (res) => {
|
|
1745
|
-
fetchScriptSuccess(fetchScriptPromiseInfo[res.index][0], fetchScriptPromiseInfo[res.index][1], res.data);
|
|
2153
|
+
injectFiberTask(fiberScriptTasks, () => fetchScriptSuccess(fetchScriptPromiseInfo[res.index][0], fetchScriptPromiseInfo[res.index][1], res.data, app));
|
|
1746
2154
|
}, (err) => {
|
|
1747
2155
|
logError(err, app.name);
|
|
1748
2156
|
}, () => {
|
|
1749
|
-
|
|
2157
|
+
if (fiberScriptTasks) {
|
|
2158
|
+
fiberScriptTasks.push(() => Promise.resolve(app.onLoad(wrapElement)));
|
|
2159
|
+
serialExecFiberTasks(fiberScriptTasks);
|
|
2160
|
+
}
|
|
2161
|
+
else {
|
|
2162
|
+
app.onLoad(wrapElement);
|
|
2163
|
+
}
|
|
1750
2164
|
});
|
|
1751
2165
|
}
|
|
1752
2166
|
else {
|
|
@@ -1755,169 +2169,224 @@ function fetchScriptsFromHtml(wrapElement, app) {
|
|
|
1755
2169
|
}
|
|
1756
2170
|
/**
|
|
1757
2171
|
* fetch js succeeded, record the code value
|
|
1758
|
-
* @param
|
|
1759
|
-
* @param
|
|
2172
|
+
* @param address script address
|
|
2173
|
+
* @param scriptInfo resource script info
|
|
1760
2174
|
* @param data code
|
|
1761
2175
|
*/
|
|
1762
|
-
function fetchScriptSuccess(
|
|
1763
|
-
|
|
1764
|
-
|
|
2176
|
+
function fetchScriptSuccess(address, scriptInfo, code, app) {
|
|
2177
|
+
// reset scriptInfo.code
|
|
2178
|
+
scriptInfo.code = code;
|
|
2179
|
+
/**
|
|
2180
|
+
* Pre parse script for prefetch, improve rendering performance
|
|
2181
|
+
* NOTE:
|
|
2182
|
+
* 1. if global parseResult exist, skip this step
|
|
2183
|
+
* 2. if app is inline or script is esmodule, skip this step
|
|
2184
|
+
* 3. if global parseResult not exist, the current script occupies the position, when js is reused, parseResult is reference
|
|
2185
|
+
*/
|
|
2186
|
+
if (app.isPrefetch) {
|
|
2187
|
+
const appSpaceData = scriptInfo.appSpace[app.name];
|
|
2188
|
+
/**
|
|
2189
|
+
* When prefetch app is replaced by a new app in the processing phase, since the scriptInfo is common, when the scriptInfo of the prefetch app is processed, it may have already been processed.
|
|
2190
|
+
* This causes parsedCode to already exist when preloading ends
|
|
2191
|
+
* e.g.
|
|
2192
|
+
* 1. prefetch app.url different from <micro-app></micro-app>
|
|
2193
|
+
* 2. prefetch param different from <micro-app></micro-app>
|
|
2194
|
+
*/
|
|
2195
|
+
if (!appSpaceData.parsedCode) {
|
|
2196
|
+
appSpaceData.parsedCode = bindScope(address, app, code, scriptInfo);
|
|
2197
|
+
appSpaceData.wrapInSandBox = isWrapInSandBox(app, scriptInfo);
|
|
2198
|
+
if (!isInlineMode(app, scriptInfo)) {
|
|
2199
|
+
try {
|
|
2200
|
+
appSpaceData.parsedFunction = getParsedFunction(app, scriptInfo, appSpaceData.parsedCode);
|
|
2201
|
+
}
|
|
2202
|
+
catch (err) {
|
|
2203
|
+
logWarn('Something went wrong while handling preloaded resources', app.name, '\n', err);
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
1765
2207
|
}
|
|
1766
|
-
info.code = data;
|
|
1767
2208
|
}
|
|
1768
2209
|
/**
|
|
1769
2210
|
* Execute js in the mount lifecycle
|
|
1770
|
-
* @param scriptList script list
|
|
1771
2211
|
* @param app app
|
|
1772
2212
|
* @param initHook callback for umd mode
|
|
1773
2213
|
*/
|
|
1774
|
-
function execScripts(
|
|
1775
|
-
const
|
|
2214
|
+
function execScripts(app, initHook) {
|
|
2215
|
+
const fiberScriptTasks = app.fiber ? [] : null;
|
|
2216
|
+
const scriptList = Array.from(app.source.scripts);
|
|
1776
2217
|
const deferScriptPromise = [];
|
|
1777
2218
|
const deferScriptInfo = [];
|
|
1778
|
-
for (const
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
else {
|
|
1786
|
-
deferScriptPromise.push(info.code);
|
|
1787
|
-
}
|
|
1788
|
-
deferScriptInfo.push([url, info]);
|
|
1789
|
-
info.module && (initHook.moduleCount = initHook.moduleCount ? ++initHook.moduleCount : 1);
|
|
2219
|
+
for (const address of scriptList) {
|
|
2220
|
+
const scriptInfo = sourceCenter.script.getInfo(address);
|
|
2221
|
+
const appSpaceData = scriptInfo.appSpace[app.name];
|
|
2222
|
+
// Notice the second render
|
|
2223
|
+
if (appSpaceData.defer || appSpaceData.async) {
|
|
2224
|
+
if (scriptInfo.isExternal && !scriptInfo.code) {
|
|
2225
|
+
deferScriptPromise.push(fetchSource(address, app.name));
|
|
1790
2226
|
}
|
|
1791
2227
|
else {
|
|
1792
|
-
|
|
1793
|
-
initHook(false);
|
|
2228
|
+
deferScriptPromise.push(scriptInfo.code);
|
|
1794
2229
|
}
|
|
2230
|
+
deferScriptInfo.push([address, scriptInfo]);
|
|
2231
|
+
isTypeModule(app, scriptInfo) && (initHook.moduleCount = initHook.moduleCount ? ++initHook.moduleCount : 1);
|
|
2232
|
+
}
|
|
2233
|
+
else {
|
|
2234
|
+
injectFiberTask(fiberScriptTasks, () => {
|
|
2235
|
+
runScript(address, app, scriptInfo);
|
|
2236
|
+
initHook(false);
|
|
2237
|
+
});
|
|
1795
2238
|
}
|
|
1796
2239
|
}
|
|
1797
2240
|
if (deferScriptPromise.length) {
|
|
1798
2241
|
promiseStream(deferScriptPromise, (res) => {
|
|
1799
|
-
const
|
|
1800
|
-
|
|
2242
|
+
const scriptInfo = deferScriptInfo[res.index][1];
|
|
2243
|
+
scriptInfo.code = scriptInfo.code || res.data;
|
|
1801
2244
|
}, (err) => {
|
|
1802
2245
|
initHook.errorCount = initHook.errorCount ? ++initHook.errorCount : 1;
|
|
1803
2246
|
logError(err, app.name);
|
|
1804
2247
|
}, () => {
|
|
1805
|
-
deferScriptInfo.forEach(([
|
|
1806
|
-
if (
|
|
1807
|
-
|
|
1808
|
-
|
|
2248
|
+
deferScriptInfo.forEach(([address, scriptInfo]) => {
|
|
2249
|
+
if (scriptInfo.code) {
|
|
2250
|
+
injectFiberTask(fiberScriptTasks, () => {
|
|
2251
|
+
runScript(address, app, scriptInfo, initHook);
|
|
2252
|
+
!isTypeModule(app, scriptInfo) && initHook(false);
|
|
2253
|
+
});
|
|
1809
2254
|
}
|
|
1810
2255
|
});
|
|
1811
|
-
|
|
1812
|
-
|
|
2256
|
+
/**
|
|
2257
|
+
* Fiber wraps js in requestIdleCallback and executes it in sequence
|
|
2258
|
+
* NOTE:
|
|
2259
|
+
* 1. In order to ensure the execution order, wait for all js loaded and then execute
|
|
2260
|
+
* 2. If js create a dynamic script, it may be errors in the execution order, because the subsequent js is wrapped in requestIdleCallback, even putting dynamic script in requestIdleCallback doesn't solve it
|
|
2261
|
+
*
|
|
2262
|
+
* BUG: NOTE.2 - execution order problem
|
|
2263
|
+
*/
|
|
2264
|
+
if (fiberScriptTasks) {
|
|
2265
|
+
fiberScriptTasks.push(() => Promise.resolve(initHook(isUndefined(initHook.moduleCount) ||
|
|
2266
|
+
initHook.errorCount === deferScriptPromise.length)));
|
|
2267
|
+
serialExecFiberTasks(fiberScriptTasks);
|
|
2268
|
+
}
|
|
2269
|
+
else {
|
|
2270
|
+
initHook(isUndefined(initHook.moduleCount) ||
|
|
2271
|
+
initHook.errorCount === deferScriptPromise.length);
|
|
2272
|
+
}
|
|
1813
2273
|
});
|
|
1814
2274
|
}
|
|
1815
2275
|
else {
|
|
1816
|
-
|
|
2276
|
+
if (fiberScriptTasks) {
|
|
2277
|
+
fiberScriptTasks.push(() => Promise.resolve(initHook(true)));
|
|
2278
|
+
serialExecFiberTasks(fiberScriptTasks);
|
|
2279
|
+
}
|
|
2280
|
+
else {
|
|
2281
|
+
initHook(true);
|
|
2282
|
+
}
|
|
1817
2283
|
}
|
|
1818
2284
|
}
|
|
1819
2285
|
/**
|
|
1820
2286
|
* run code
|
|
1821
|
-
* @param
|
|
2287
|
+
* @param address script address
|
|
1822
2288
|
* @param app app
|
|
1823
|
-
* @param
|
|
1824
|
-
* @param isDynamic dynamically created script
|
|
2289
|
+
* @param scriptInfo script info
|
|
1825
2290
|
* @param callback callback of module script
|
|
1826
2291
|
*/
|
|
1827
|
-
function runScript(
|
|
2292
|
+
function runScript(address, app, scriptInfo, callback, replaceElement) {
|
|
1828
2293
|
var _a;
|
|
1829
2294
|
try {
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
2295
|
+
actionsBeforeRunScript(app);
|
|
2296
|
+
const appSpaceData = scriptInfo.appSpace[app.name];
|
|
2297
|
+
const wrapInSandBox = isWrapInSandBox(app, scriptInfo);
|
|
2298
|
+
/**
|
|
2299
|
+
* NOTE:
|
|
2300
|
+
* 1. plugins and wrapCode will only be executed once
|
|
2301
|
+
* 2. if parsedCode not exist, parsedFunction is not exist
|
|
2302
|
+
* 3. if parsedCode exist, parsedFunction does not necessarily exist
|
|
2303
|
+
*/
|
|
2304
|
+
if (!appSpaceData.parsedCode || appSpaceData.wrapInSandBox !== wrapInSandBox) {
|
|
2305
|
+
appSpaceData.parsedCode = bindScope(address, app, scriptInfo.code, scriptInfo);
|
|
2306
|
+
appSpaceData.wrapInSandBox = wrapInSandBox;
|
|
2307
|
+
appSpaceData.parsedFunction = null;
|
|
2308
|
+
}
|
|
2309
|
+
if (isInlineMode(app, scriptInfo)) {
|
|
2310
|
+
const scriptElement = replaceElement || pureCreateElement('script');
|
|
2311
|
+
runCode2InlineScript(address, appSpaceData.parsedCode, isTypeModule(app, scriptInfo), scriptElement, appSpaceData.attrs, callback);
|
|
2312
|
+
if (!replaceElement) {
|
|
2313
|
+
// TEST IGNORE
|
|
2314
|
+
(_a = app.container) === null || _a === void 0 ? void 0 : _a.querySelector('micro-app-body').appendChild(scriptElement);
|
|
2315
|
+
}
|
|
1838
2316
|
}
|
|
1839
2317
|
else {
|
|
1840
|
-
|
|
1841
|
-
if (isDynamic)
|
|
1842
|
-
return document.createComment('dynamic script extract by micro-app');
|
|
2318
|
+
runParsedFunction(app, scriptInfo);
|
|
1843
2319
|
}
|
|
1844
2320
|
}
|
|
1845
2321
|
catch (e) {
|
|
1846
|
-
console.error(`[micro-app from runScript] app ${app.name}: `, e);
|
|
2322
|
+
console.error(`[micro-app from ${replaceElement ? 'runDynamicScript' : 'runScript'}] app ${app.name}: `, e, address);
|
|
1847
2323
|
}
|
|
1848
2324
|
}
|
|
1849
2325
|
/**
|
|
1850
2326
|
* Get dynamically created remote script
|
|
1851
|
-
* @param
|
|
1852
|
-
* @param
|
|
1853
|
-
* @param
|
|
2327
|
+
* @param address script address
|
|
2328
|
+
* @param app app instance
|
|
2329
|
+
* @param scriptInfo scriptInfo
|
|
1854
2330
|
* @param originScript origin script element
|
|
1855
2331
|
*/
|
|
1856
|
-
function runDynamicRemoteScript(
|
|
2332
|
+
function runDynamicRemoteScript(address, app, scriptInfo, originScript) {
|
|
2333
|
+
const replaceElement = isInlineMode(app, scriptInfo) ? pureCreateElement('script') : document.createComment('dynamic script extract by micro-app');
|
|
1857
2334
|
const dispatchScriptOnLoadEvent = () => dispatchOnLoadEvent(originScript);
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
if (globalScripts.has(url)) {
|
|
1872
|
-
const code = globalScripts.get(url);
|
|
1873
|
-
info.code = code;
|
|
1874
|
-
app.source.scripts.set(url, info);
|
|
1875
|
-
!info.module && defer(dispatchScriptOnLoadEvent);
|
|
1876
|
-
return runScript(url, app, info, true, dispatchScriptOnLoadEvent);
|
|
1877
|
-
}
|
|
1878
|
-
let replaceElement;
|
|
1879
|
-
if (app.inline || info.module) {
|
|
1880
|
-
replaceElement = pureCreateElement('script');
|
|
2335
|
+
const runDynamicScript = () => {
|
|
2336
|
+
const descriptor = Object.getOwnPropertyDescriptor(globalEnv.rawDocument, 'currentScript');
|
|
2337
|
+
if (!descriptor || descriptor.configurable) {
|
|
2338
|
+
Object.defineProperty(globalEnv.rawDocument, 'currentScript', {
|
|
2339
|
+
value: originScript,
|
|
2340
|
+
configurable: true,
|
|
2341
|
+
});
|
|
2342
|
+
}
|
|
2343
|
+
runScript(address, app, scriptInfo, dispatchScriptOnLoadEvent, replaceElement);
|
|
2344
|
+
!isTypeModule(app, scriptInfo) && dispatchScriptOnLoadEvent();
|
|
2345
|
+
};
|
|
2346
|
+
if (scriptInfo.code) {
|
|
2347
|
+
defer(runDynamicScript);
|
|
1881
2348
|
}
|
|
1882
2349
|
else {
|
|
1883
|
-
|
|
2350
|
+
fetchSource(address, app.name).then((code) => {
|
|
2351
|
+
scriptInfo.code = code;
|
|
2352
|
+
runDynamicScript();
|
|
2353
|
+
}).catch((err) => {
|
|
2354
|
+
logError(err, app.name);
|
|
2355
|
+
dispatchOnErrorEvent(originScript);
|
|
2356
|
+
});
|
|
1884
2357
|
}
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
}
|
|
1897
|
-
}
|
|
1898
|
-
catch (e) {
|
|
1899
|
-
console.error(`[micro-app from runDynamicScript] app ${app.name}: `, e, url);
|
|
1900
|
-
}
|
|
1901
|
-
!info.module && dispatchOnLoadEvent(originScript);
|
|
1902
|
-
}).catch((err) => {
|
|
1903
|
-
logError(err, app.name);
|
|
1904
|
-
dispatchOnErrorEvent(originScript);
|
|
1905
|
-
});
|
|
2358
|
+
return replaceElement;
|
|
2359
|
+
}
|
|
2360
|
+
/**
|
|
2361
|
+
* Get dynamically created inline script
|
|
2362
|
+
* @param address script address
|
|
2363
|
+
* @param app app instance
|
|
2364
|
+
* @param scriptInfo scriptInfo
|
|
2365
|
+
*/
|
|
2366
|
+
function runDynamicInlineScript(address, app, scriptInfo) {
|
|
2367
|
+
const replaceElement = isInlineMode(app, scriptInfo) ? pureCreateElement('script') : document.createComment('dynamic script extract by micro-app');
|
|
2368
|
+
runScript(address, app, scriptInfo, void 0, replaceElement);
|
|
1906
2369
|
return replaceElement;
|
|
1907
2370
|
}
|
|
1908
2371
|
/**
|
|
1909
2372
|
* common handle for inline script
|
|
1910
|
-
* @param
|
|
2373
|
+
* @param address script address
|
|
1911
2374
|
* @param code bound code
|
|
1912
2375
|
* @param module type='module' of script
|
|
1913
2376
|
* @param scriptElement target script element
|
|
2377
|
+
* @param attrs attributes of script element
|
|
1914
2378
|
* @param callback callback of module script
|
|
1915
2379
|
*/
|
|
1916
|
-
function runCode2InlineScript(
|
|
2380
|
+
function runCode2InlineScript(address, code, module, scriptElement, attrs, callback) {
|
|
1917
2381
|
if (module) {
|
|
1918
2382
|
// module script is async, transform it to a blob for subsequent operations
|
|
1919
|
-
|
|
1920
|
-
|
|
2383
|
+
if (isInlineScript(address)) {
|
|
2384
|
+
const blob = new Blob([code], { type: 'text/javascript' });
|
|
2385
|
+
scriptElement.src = URL.createObjectURL(blob);
|
|
2386
|
+
}
|
|
2387
|
+
else {
|
|
2388
|
+
scriptElement.src = address;
|
|
2389
|
+
}
|
|
1921
2390
|
scriptElement.setAttribute('type', 'module');
|
|
1922
2391
|
if (callback) {
|
|
1923
2392
|
callback.moduleCount && callback.moduleCount--;
|
|
@@ -1927,54 +2396,65 @@ function runCode2InlineScript(url, code, module, scriptElement, callback) {
|
|
|
1927
2396
|
else {
|
|
1928
2397
|
scriptElement.textContent = code;
|
|
1929
2398
|
}
|
|
1930
|
-
|
|
1931
|
-
scriptElement.setAttribute('data-origin-src', url);
|
|
1932
|
-
}
|
|
2399
|
+
setConvertScriptAttr(scriptElement, attrs);
|
|
1933
2400
|
}
|
|
1934
2401
|
// init & run code2Function
|
|
1935
|
-
function
|
|
1936
|
-
|
|
1937
|
-
|
|
2402
|
+
function runParsedFunction(app, scriptInfo) {
|
|
2403
|
+
const appSpaceData = scriptInfo.appSpace[app.name];
|
|
2404
|
+
if (!appSpaceData.parsedFunction) {
|
|
2405
|
+
appSpaceData.parsedFunction = getParsedFunction(app, scriptInfo, appSpaceData.parsedCode);
|
|
1938
2406
|
}
|
|
1939
|
-
|
|
2407
|
+
appSpaceData.parsedFunction.call(window);
|
|
1940
2408
|
}
|
|
1941
2409
|
/**
|
|
1942
2410
|
* bind js scope
|
|
1943
|
-
* @param url script address
|
|
1944
2411
|
* @param app app
|
|
1945
2412
|
* @param code code
|
|
1946
|
-
* @param
|
|
2413
|
+
* @param scriptInfo source script info
|
|
1947
2414
|
*/
|
|
1948
|
-
function bindScope(
|
|
1949
|
-
|
|
1950
|
-
|
|
2415
|
+
function bindScope(address, app, code, scriptInfo) {
|
|
2416
|
+
// TODO: cache
|
|
2417
|
+
if (isPlainObject(microApp.options.plugins)) {
|
|
2418
|
+
code = usePlugins(address, code, app.name, microApp.options.plugins);
|
|
1951
2419
|
}
|
|
1952
|
-
if (app
|
|
1953
|
-
|
|
1954
|
-
return `;(function(proxyWindow){with(proxyWindow.__MICRO_APP_WINDOW__){(function(${globalKeyToBeCached}){;${code}\n}).call(proxyWindow,${globalKeyToBeCached})}})(window.__MICRO_APP_PROXY_WINDOW__);`;
|
|
2420
|
+
if (isWrapInSandBox(app, scriptInfo)) {
|
|
2421
|
+
return `;(function(proxyWindow){with(proxyWindow.__MICRO_APP_WINDOW__){(function(${globalKeyToBeCached}){;${code}\n${isInlineScript(address) ? '' : `//# sourceURL=${address}\n`}}).call(proxyWindow,${globalKeyToBeCached})}})(window.__MICRO_APP_PROXY_WINDOW__);`;
|
|
1955
2422
|
}
|
|
1956
2423
|
return code;
|
|
1957
2424
|
}
|
|
2425
|
+
/**
|
|
2426
|
+
* actions before run script
|
|
2427
|
+
*/
|
|
2428
|
+
function actionsBeforeRunScript(app) {
|
|
2429
|
+
setActiveProxyWindow(app);
|
|
2430
|
+
}
|
|
2431
|
+
/**
|
|
2432
|
+
* set active sandBox.proxyWindow to window.__MICRO_APP_PROXY_WINDOW__
|
|
2433
|
+
*/
|
|
2434
|
+
function setActiveProxyWindow(app) {
|
|
2435
|
+
if (app.sandBox) {
|
|
2436
|
+
globalEnv.rawWindow.__MICRO_APP_PROXY_WINDOW__ = app.sandBox.proxyWindow;
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
1958
2439
|
/**
|
|
1959
2440
|
* Call the plugin to process the file
|
|
1960
|
-
* @param
|
|
2441
|
+
* @param address script address
|
|
1961
2442
|
* @param code code
|
|
1962
2443
|
* @param appName app name
|
|
1963
2444
|
* @param plugins plugin list
|
|
1964
|
-
* @param info source script info
|
|
1965
2445
|
*/
|
|
1966
|
-
function usePlugins(
|
|
2446
|
+
function usePlugins(address, code, appName, plugins) {
|
|
1967
2447
|
var _a;
|
|
1968
|
-
const newCode = processCode(plugins.global, code,
|
|
1969
|
-
return processCode((_a = plugins.modules) === null || _a === void 0 ? void 0 : _a[appName], newCode,
|
|
2448
|
+
const newCode = processCode(plugins.global, code, address);
|
|
2449
|
+
return processCode((_a = plugins.modules) === null || _a === void 0 ? void 0 : _a[appName], newCode, address);
|
|
1970
2450
|
}
|
|
1971
|
-
function processCode(configs, code,
|
|
2451
|
+
function processCode(configs, code, address) {
|
|
1972
2452
|
if (!isArray(configs)) {
|
|
1973
2453
|
return code;
|
|
1974
2454
|
}
|
|
1975
2455
|
return configs.reduce((preCode, config) => {
|
|
1976
2456
|
if (isPlainObject(config) && isFunction(config.loader)) {
|
|
1977
|
-
return config.loader(preCode,
|
|
2457
|
+
return config.loader(preCode, address);
|
|
1978
2458
|
}
|
|
1979
2459
|
return preCode;
|
|
1980
2460
|
}, code);
|
|
@@ -1995,17 +2475,17 @@ function getWrapElement(str) {
|
|
|
1995
2475
|
* @param app app
|
|
1996
2476
|
* @param microAppHead micro-app-head element
|
|
1997
2477
|
*/
|
|
1998
|
-
function flatChildren(parent, app, microAppHead) {
|
|
2478
|
+
function flatChildren(parent, app, microAppHead, fiberStyleTasks) {
|
|
1999
2479
|
const children = Array.from(parent.children);
|
|
2000
2480
|
children.length && children.forEach((child) => {
|
|
2001
|
-
flatChildren(child, app);
|
|
2481
|
+
flatChildren(child, app, microAppHead, fiberStyleTasks);
|
|
2002
2482
|
});
|
|
2003
2483
|
for (const dom of children) {
|
|
2004
2484
|
if (dom instanceof HTMLLinkElement) {
|
|
2005
|
-
if (dom.hasAttribute('exclude')) {
|
|
2485
|
+
if (dom.hasAttribute('exclude') || checkExcludeUrl(dom.getAttribute('href'), app.name)) {
|
|
2006
2486
|
parent.replaceChild(document.createComment('link element with exclude attribute ignored by micro-app'), dom);
|
|
2007
2487
|
}
|
|
2008
|
-
else if (!dom.hasAttribute('ignore')) {
|
|
2488
|
+
else if (!(dom.hasAttribute('ignore') || checkIgnoreUrl(dom.getAttribute('href'), app.name))) {
|
|
2009
2489
|
extractLinkFromHtml(dom, parent, app);
|
|
2010
2490
|
}
|
|
2011
2491
|
else if (dom.hasAttribute('href')) {
|
|
@@ -2017,7 +2497,7 @@ function flatChildren(parent, app, microAppHead) {
|
|
|
2017
2497
|
parent.replaceChild(document.createComment('style element with exclude attribute ignored by micro-app'), dom);
|
|
2018
2498
|
}
|
|
2019
2499
|
else if (app.scopecss && !dom.hasAttribute('ignore')) {
|
|
2020
|
-
scopedCSS(dom, app);
|
|
2500
|
+
injectFiberTask(fiberStyleTasks, () => scopedCSS(dom, app));
|
|
2021
2501
|
}
|
|
2022
2502
|
}
|
|
2023
2503
|
else if (dom instanceof HTMLScriptElement) {
|
|
@@ -2045,9 +2525,17 @@ function extractSourceDom(htmlStr, app) {
|
|
|
2045
2525
|
app.onerror(new Error(msg));
|
|
2046
2526
|
return logError(msg, app.name);
|
|
2047
2527
|
}
|
|
2048
|
-
|
|
2528
|
+
const fiberStyleTasks = app.isPrefetch || app.fiber ? [] : null;
|
|
2529
|
+
flatChildren(wrapElement, app, microAppHead, fiberStyleTasks);
|
|
2530
|
+
/**
|
|
2531
|
+
* Style and link are parallel, because it takes a lot of time for link to request resources. During this period, style processing can be performed to improve efficiency.
|
|
2532
|
+
*/
|
|
2533
|
+
const fiberStyleResult = serialExecFiberTasks(fiberStyleTasks);
|
|
2049
2534
|
if (app.source.links.size) {
|
|
2050
|
-
fetchLinksFromHtml(wrapElement, app, microAppHead);
|
|
2535
|
+
fetchLinksFromHtml(wrapElement, app, microAppHead, fiberStyleResult);
|
|
2536
|
+
}
|
|
2537
|
+
else if (fiberStyleResult) {
|
|
2538
|
+
fiberStyleResult.then(() => app.onLoad(wrapElement));
|
|
2051
2539
|
}
|
|
2052
2540
|
else {
|
|
2053
2541
|
app.onLoad(wrapElement);
|
|
@@ -2059,34 +2547,6 @@ function extractSourceDom(htmlStr, app) {
|
|
|
2059
2547
|
app.onLoad(wrapElement);
|
|
2060
2548
|
}
|
|
2061
2549
|
}
|
|
2062
|
-
/**
|
|
2063
|
-
* Get and format html
|
|
2064
|
-
* @param app app
|
|
2065
|
-
*/
|
|
2066
|
-
function extractHtml(app) {
|
|
2067
|
-
fetchSource(app.ssrUrl || app.url, app.name, { cache: 'no-cache' }).then((htmlStr) => {
|
|
2068
|
-
if (!htmlStr) {
|
|
2069
|
-
const msg = 'html is empty, please check in detail';
|
|
2070
|
-
app.onerror(new Error(msg));
|
|
2071
|
-
return logError(msg, app.name);
|
|
2072
|
-
}
|
|
2073
|
-
htmlStr = htmlStr
|
|
2074
|
-
.replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
|
|
2075
|
-
return match
|
|
2076
|
-
.replace(/<head/i, '<micro-app-head')
|
|
2077
|
-
.replace(/<\/head>/i, '</micro-app-head>');
|
|
2078
|
-
})
|
|
2079
|
-
.replace(/<body[^>]*>[\s\S]*?<\/body>/i, (match) => {
|
|
2080
|
-
return match
|
|
2081
|
-
.replace(/<body/i, '<micro-app-body')
|
|
2082
|
-
.replace(/<\/body>/i, '</micro-app-body>');
|
|
2083
|
-
});
|
|
2084
|
-
extractSourceDom(htmlStr, app);
|
|
2085
|
-
}).catch((e) => {
|
|
2086
|
-
logError(`Failed to fetch data from ${app.url}, micro-app stop rendering`, app.name, e);
|
|
2087
|
-
app.onLoadError(e);
|
|
2088
|
-
});
|
|
2089
|
-
}
|
|
2090
2550
|
|
|
2091
2551
|
class EventCenter {
|
|
2092
2552
|
constructor() {
|
|
@@ -2369,12 +2829,39 @@ function rebuildDataCenterSnapshot(microAppEventCenter) {
|
|
|
2369
2829
|
}
|
|
2370
2830
|
}
|
|
2371
2831
|
|
|
2832
|
+
// 管理 app 的单例
|
|
2833
|
+
class AppManager {
|
|
2834
|
+
constructor() {
|
|
2835
|
+
// Todo: appInstanceMap 由 AppManager 来创建,不再由 create_app 管理
|
|
2836
|
+
this.appInstanceMap = appInstanceMap;
|
|
2837
|
+
}
|
|
2838
|
+
static getInstance() {
|
|
2839
|
+
if (!this.instance) {
|
|
2840
|
+
this.instance = new AppManager();
|
|
2841
|
+
}
|
|
2842
|
+
return this.instance;
|
|
2843
|
+
}
|
|
2844
|
+
get(appName) {
|
|
2845
|
+
return this.appInstanceMap.get(appName);
|
|
2846
|
+
}
|
|
2847
|
+
set(appName, app) {
|
|
2848
|
+
this.appInstanceMap.set(appName, app);
|
|
2849
|
+
}
|
|
2850
|
+
getAll() {
|
|
2851
|
+
return Array.from(this.appInstanceMap.values());
|
|
2852
|
+
}
|
|
2853
|
+
clear() {
|
|
2854
|
+
this.appInstanceMap.clear();
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2372
2858
|
function unmountNestedApp() {
|
|
2373
|
-
|
|
2859
|
+
releaseUnmountOfNestedApp();
|
|
2860
|
+
AppManager.getInstance().getAll().forEach(app => {
|
|
2374
2861
|
// @ts-ignore
|
|
2375
2862
|
app.container && getRootContainer(app.container).disconnectedCallback();
|
|
2376
2863
|
});
|
|
2377
|
-
!window.__MICRO_APP_UMD_MODE__ &&
|
|
2864
|
+
!window.__MICRO_APP_UMD_MODE__ && AppManager.getInstance().clear();
|
|
2378
2865
|
}
|
|
2379
2866
|
// release listener
|
|
2380
2867
|
function releaseUnmountOfNestedApp() {
|
|
@@ -2586,6 +3073,13 @@ function effect(appName, microAppWindow) {
|
|
|
2586
3073
|
let umdIntervalIdMap = new Map();
|
|
2587
3074
|
let umdTimeoutIdMap = new Map();
|
|
2588
3075
|
let umdOnClickHandler;
|
|
3076
|
+
const clearUmdSnapshotData = () => {
|
|
3077
|
+
umdWindowListenerMap.clear();
|
|
3078
|
+
umdIntervalIdMap.clear();
|
|
3079
|
+
umdTimeoutIdMap.clear();
|
|
3080
|
+
umdDocumentListenerMap.clear();
|
|
3081
|
+
umdOnClickHandler = null;
|
|
3082
|
+
};
|
|
2589
3083
|
// record event and timer before exec umdMountHook
|
|
2590
3084
|
const recordUmdEffect = () => {
|
|
2591
3085
|
// record window event
|
|
@@ -2631,13 +3125,12 @@ function effect(appName, microAppWindow) {
|
|
|
2631
3125
|
// rebuild onclick event
|
|
2632
3126
|
umdOnClickHandler && documentClickListMap.set(appName, umdOnClickHandler);
|
|
2633
3127
|
// rebuild document event
|
|
2634
|
-
setCurrentAppName(appName);
|
|
2635
3128
|
umdDocumentListenerMap.forEach((listenerList, type) => {
|
|
2636
3129
|
for (const listener of listenerList) {
|
|
2637
3130
|
document.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
|
|
2638
3131
|
}
|
|
2639
3132
|
});
|
|
2640
|
-
|
|
3133
|
+
clearUmdSnapshotData();
|
|
2641
3134
|
};
|
|
2642
3135
|
// release all event listener & interval & timeout when unmount app
|
|
2643
3136
|
const releaseEffect = () => {
|
|
@@ -2881,9 +3374,19 @@ function addHistoryListener(appName) {
|
|
|
2881
3374
|
function dispatchPopStateEventToMicroApp(appName, proxyWindow) {
|
|
2882
3375
|
// create PopStateEvent named popstate-appName with sub app state
|
|
2883
3376
|
const newPopStateEvent = new PopStateEvent(formatEventName$1('popstate', appName), { state: getMicroState(appName) });
|
|
3377
|
+
/**
|
|
3378
|
+
* angular14 takes e.type as type judgment
|
|
3379
|
+
* when e.type is popstate-appName popstate event will be invalid
|
|
3380
|
+
*/
|
|
3381
|
+
// Object.defineProperty(newPopStateEvent, 'type', {
|
|
3382
|
+
// value: 'popstate',
|
|
3383
|
+
// writable: true,
|
|
3384
|
+
// configurable: true,
|
|
3385
|
+
// enumerable: true,
|
|
3386
|
+
// })
|
|
2884
3387
|
globalEnv.rawWindow.dispatchEvent(newPopStateEvent);
|
|
2885
3388
|
// call function window.onpopstate if it exists
|
|
2886
|
-
|
|
3389
|
+
isFunction(proxyWindow.onpopstate) && proxyWindow.onpopstate(newPopStateEvent);
|
|
2887
3390
|
}
|
|
2888
3391
|
/**
|
|
2889
3392
|
* dispatch formatted hashchange event to microApp
|
|
@@ -2898,7 +3401,7 @@ function dispatchHashChangeEventToMicroApp(appName, proxyWindow, oldHref) {
|
|
|
2898
3401
|
});
|
|
2899
3402
|
globalEnv.rawWindow.dispatchEvent(newHashChangeEvent);
|
|
2900
3403
|
// call function window.onhashchange if it exists
|
|
2901
|
-
|
|
3404
|
+
isFunction(proxyWindow.onhashchange) && proxyWindow.onhashchange(newHashChangeEvent);
|
|
2902
3405
|
}
|
|
2903
3406
|
/**
|
|
2904
3407
|
* dispatch native PopStateEvent, simulate location behavior
|
|
@@ -2974,6 +3477,15 @@ function createMicroHistory(appName, microLocation) {
|
|
|
2974
3477
|
}
|
|
2975
3478
|
const rawValue = Reflect.get(target, key);
|
|
2976
3479
|
return isFunction(rawValue) ? bindFunctionToRawObject(target, rawValue, 'HISTORY') : rawValue;
|
|
3480
|
+
},
|
|
3481
|
+
set(target, key, value) {
|
|
3482
|
+
Reflect.set(target, key, value);
|
|
3483
|
+
/**
|
|
3484
|
+
* If the set() method returns false, and the assignment happened in strict-mode code, a TypeError will be thrown.
|
|
3485
|
+
* e.g. history.state = {}
|
|
3486
|
+
* TypeError: 'set' on proxy: trap returned falsish for property 'state'
|
|
3487
|
+
*/
|
|
3488
|
+
return true;
|
|
2977
3489
|
}
|
|
2978
3490
|
});
|
|
2979
3491
|
}
|
|
@@ -3046,7 +3558,6 @@ function reWriteHistoryMethod(method) {
|
|
|
3046
3558
|
* 1. Exec after apply pushState/replaceState
|
|
3047
3559
|
* 2. Unable to catch when base app navigate with location
|
|
3048
3560
|
* 3. When in nest app, rawPushState/rawReplaceState has been modified by parent
|
|
3049
|
-
* 4.
|
|
3050
3561
|
*/
|
|
3051
3562
|
getActiveApps(true).forEach(appName => {
|
|
3052
3563
|
const app = appInstanceMap.get(appName);
|
|
@@ -3145,7 +3656,7 @@ function createRouterApi() {
|
|
|
3145
3656
|
* NOTE:
|
|
3146
3657
|
* 1. Modify browser url first, and then run guards,
|
|
3147
3658
|
* consistent with the browser forward & back button
|
|
3148
|
-
* 2.
|
|
3659
|
+
* 2. Prevent the element binding
|
|
3149
3660
|
* @param appName app name
|
|
3150
3661
|
* @param to target location
|
|
3151
3662
|
* @param from old location
|
|
@@ -3156,7 +3667,7 @@ function createRouterApi() {
|
|
|
3156
3667
|
removeDomScope();
|
|
3157
3668
|
for (const guard of guards) {
|
|
3158
3669
|
if (isFunction(guard)) {
|
|
3159
|
-
guard(
|
|
3670
|
+
guard(to, from, appName);
|
|
3160
3671
|
}
|
|
3161
3672
|
else if (isPlainObject(guard) && isFunction(guard[appName])) {
|
|
3162
3673
|
guard[appName](to, from);
|
|
@@ -3212,12 +3723,14 @@ function createRouterApi() {
|
|
|
3212
3723
|
const defaultPageRecord = useMapRecord();
|
|
3213
3724
|
/**
|
|
3214
3725
|
* defaultPage only effect when mount, and has lower priority than query on browser url
|
|
3215
|
-
*
|
|
3216
|
-
*
|
|
3726
|
+
* SetDefaultPageOptions {
|
|
3727
|
+
* @param name app name
|
|
3728
|
+
* @param path page path
|
|
3729
|
+
* }
|
|
3217
3730
|
*/
|
|
3218
|
-
function setDefaultPage(
|
|
3219
|
-
appName = formatAppName(
|
|
3220
|
-
if (!appName || !path) {
|
|
3731
|
+
function setDefaultPage(options) {
|
|
3732
|
+
const appName = formatAppName(options.name);
|
|
3733
|
+
if (!appName || !options.path) {
|
|
3221
3734
|
if (process.env.NODE_ENV !== 'production') {
|
|
3222
3735
|
if (!appName) {
|
|
3223
3736
|
logWarn(`setDefaultPage: invalid appName "${appName}"`);
|
|
@@ -3228,7 +3741,7 @@ function createRouterApi() {
|
|
|
3228
3741
|
}
|
|
3229
3742
|
return noopFalse;
|
|
3230
3743
|
}
|
|
3231
|
-
return defaultPageRecord.add(appName, path);
|
|
3744
|
+
return defaultPageRecord.add(appName, options.path);
|
|
3232
3745
|
}
|
|
3233
3746
|
function removeDefaultPage(appName) {
|
|
3234
3747
|
appName = formatAppName(appName);
|
|
@@ -3254,6 +3767,10 @@ function createRouterApi() {
|
|
|
3254
3767
|
removeDomScope();
|
|
3255
3768
|
const rawValue = Reflect.get(target, key);
|
|
3256
3769
|
return isFunction(rawValue) ? bindFunctionToRawObject(target, rawValue, 'BASEROUTER') : rawValue;
|
|
3770
|
+
},
|
|
3771
|
+
set(target, key, value) {
|
|
3772
|
+
Reflect.set(target, key, value);
|
|
3773
|
+
return true;
|
|
3257
3774
|
}
|
|
3258
3775
|
});
|
|
3259
3776
|
}
|
|
@@ -3491,7 +4008,12 @@ function createMicroRouter(appName, url) {
|
|
|
3491
4008
|
microHistory: createMicroHistory(appName, microLocation),
|
|
3492
4009
|
};
|
|
3493
4010
|
}
|
|
3494
|
-
|
|
4011
|
+
/**
|
|
4012
|
+
* When the sandbox executes start, or the hidden keep-alive application is re-rendered, the location is updated according to the browser url or attach router info to browser url
|
|
4013
|
+
* @param appName app.name
|
|
4014
|
+
* @param microLocation MicroLocation for sandbox
|
|
4015
|
+
* @param defaultPage default page
|
|
4016
|
+
*/
|
|
3495
4017
|
function initRouteStateWithURL(appName, microLocation, defaultPage) {
|
|
3496
4018
|
const microPath = getMicroPathFromURL(appName);
|
|
3497
4019
|
if (microPath) {
|
|
@@ -3643,7 +4165,7 @@ function useMicroEventSource() {
|
|
|
3643
4165
|
const { createMicroEventSource, clearMicroEventSource } = useMicroEventSource();
|
|
3644
4166
|
const globalPropertyList = ['window', 'self', 'globalThis'];
|
|
3645
4167
|
class SandBox {
|
|
3646
|
-
constructor(appName, url
|
|
4168
|
+
constructor(appName, url) {
|
|
3647
4169
|
/**
|
|
3648
4170
|
* Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
|
|
3649
4171
|
* Fix https://github.com/micro-zoe/micro-app/issues/234
|
|
@@ -3655,6 +4177,8 @@ class SandBox {
|
|
|
3655
4177
|
this.injectedKeys = new Set();
|
|
3656
4178
|
// Properties escape to rawWindow, cleared when unmount
|
|
3657
4179
|
this.escapeKeys = new Set();
|
|
4180
|
+
// record injected values before the first execution of umdHookMount and rebuild before remount umd app
|
|
4181
|
+
// private recordUmdInjectedValues?: Map<PropertyKey, unknown>
|
|
3658
4182
|
// sandbox state
|
|
3659
4183
|
this.active = false;
|
|
3660
4184
|
this.microAppWindow = {}; // Proxy target
|
|
@@ -3666,21 +4190,37 @@ class SandBox {
|
|
|
3666
4190
|
// Rewrite global event listener & timeout
|
|
3667
4191
|
assign(this, effect(appName, this.microAppWindow));
|
|
3668
4192
|
// inject global properties
|
|
3669
|
-
this.initStaticGlobalKeys(this.microAppWindow, appName, url
|
|
4193
|
+
this.initStaticGlobalKeys(this.microAppWindow, appName, url);
|
|
3670
4194
|
}
|
|
3671
|
-
|
|
4195
|
+
/**
|
|
4196
|
+
* open sandbox and perform some initial actions
|
|
4197
|
+
* @param umdMode is umd mode
|
|
4198
|
+
* @param baseroute base route for child
|
|
4199
|
+
* @param useMemoryRouter use virtual router
|
|
4200
|
+
* @param defaultPage default page when mount child base on virtual router
|
|
4201
|
+
* @param disablePatchRequest prevent patchRequestApi
|
|
4202
|
+
*/
|
|
4203
|
+
start({ umdMode, baseroute, useMemoryRouter, defaultPage, disablePatchRequest, }) {
|
|
3672
4204
|
if (!this.active) {
|
|
3673
4205
|
this.active = true;
|
|
3674
4206
|
if (useMemoryRouter) {
|
|
4207
|
+
if (isUndefined(this.microAppWindow.location)) {
|
|
4208
|
+
this.setMicroAppRouter(this.microAppWindow, this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__);
|
|
4209
|
+
}
|
|
3675
4210
|
this.initRouteState(defaultPage);
|
|
3676
4211
|
// unique listener of popstate event for sub app
|
|
3677
|
-
this.removeHistoryListener = addHistoryListener(this.
|
|
4212
|
+
this.removeHistoryListener = addHistoryListener(this.microAppWindow.__MICRO_APP_NAME__);
|
|
3678
4213
|
}
|
|
3679
4214
|
else {
|
|
3680
|
-
this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ =
|
|
4215
|
+
this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute;
|
|
4216
|
+
}
|
|
4217
|
+
/**
|
|
4218
|
+
* 1. prevent the key deleted during sandBox.stop after rewrite
|
|
4219
|
+
* 2. umd mode will not delete any keys during sandBox.stop
|
|
4220
|
+
*/
|
|
4221
|
+
if (!umdMode) {
|
|
4222
|
+
this.initGlobalKeysWhenStart(this.microAppWindow, this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__, disablePatchRequest);
|
|
3681
4223
|
}
|
|
3682
|
-
// prevent the key deleted during sandBox.stop after rewrite
|
|
3683
|
-
this.initGlobalKeysWhenStart(this.microAppWindow, this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.__MICRO_APP_URL__);
|
|
3684
4224
|
if (++SandBox.activeCount === 1) {
|
|
3685
4225
|
effectDocumentEvent();
|
|
3686
4226
|
patchElementPrototypeMethods();
|
|
@@ -3690,7 +4230,13 @@ class SandBox {
|
|
|
3690
4230
|
fixBabelPolyfill6();
|
|
3691
4231
|
}
|
|
3692
4232
|
}
|
|
3693
|
-
|
|
4233
|
+
/**
|
|
4234
|
+
* close sandbox and perform some clean up actions
|
|
4235
|
+
* @param umdMode is umd mode
|
|
4236
|
+
* @param keepRouteState prevent reset route
|
|
4237
|
+
* @param clearEventSource clear MicroEventSource when destroy
|
|
4238
|
+
*/
|
|
4239
|
+
stop({ umdMode, keepRouteState, clearEventSource, }) {
|
|
3694
4240
|
if (this.active) {
|
|
3695
4241
|
this.releaseEffect();
|
|
3696
4242
|
this.microAppWindow.microApp.clearDataListener();
|
|
@@ -3701,21 +4247,24 @@ class SandBox {
|
|
|
3701
4247
|
this.removeHistoryListener();
|
|
3702
4248
|
}
|
|
3703
4249
|
if (clearEventSource) {
|
|
3704
|
-
clearMicroEventSource(this.
|
|
4250
|
+
clearMicroEventSource(this.microAppWindow.__MICRO_APP_NAME__);
|
|
3705
4251
|
}
|
|
3706
4252
|
/**
|
|
3707
4253
|
* NOTE:
|
|
3708
4254
|
* 1. injectedKeys and escapeKeys must be placed at the back
|
|
3709
4255
|
* 2. if key in initial microAppWindow, and then rewrite, this key will be delete from microAppWindow when stop, and lost when restart
|
|
4256
|
+
* 3. umd mode will not delete global keys
|
|
3710
4257
|
*/
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
4258
|
+
if (!umdMode) {
|
|
4259
|
+
this.injectedKeys.forEach((key) => {
|
|
4260
|
+
Reflect.deleteProperty(this.microAppWindow, key);
|
|
4261
|
+
});
|
|
4262
|
+
this.injectedKeys.clear();
|
|
4263
|
+
this.escapeKeys.forEach((key) => {
|
|
4264
|
+
Reflect.deleteProperty(globalEnv.rawWindow, key);
|
|
4265
|
+
});
|
|
4266
|
+
this.escapeKeys.clear();
|
|
4267
|
+
}
|
|
3719
4268
|
if (--SandBox.activeCount === 0) {
|
|
3720
4269
|
releaseEffectDocumentEvent();
|
|
3721
4270
|
releasePatches();
|
|
@@ -3726,19 +4275,19 @@ class SandBox {
|
|
|
3726
4275
|
}
|
|
3727
4276
|
// record umd snapshot before the first execution of umdHookMount
|
|
3728
4277
|
recordUmdSnapshot() {
|
|
3729
|
-
this.microAppWindow.__MICRO_APP_UMD_MODE__ = true
|
|
4278
|
+
// this.microAppWindow.__MICRO_APP_UMD_MODE__ = true
|
|
3730
4279
|
this.recordUmdEffect();
|
|
3731
4280
|
recordDataCenterSnapshot(this.microAppWindow.microApp);
|
|
3732
|
-
this.recordUmdInjectedValues = new Map()
|
|
3733
|
-
this.injectedKeys.forEach((key) => {
|
|
3734
|
-
|
|
3735
|
-
})
|
|
4281
|
+
// this.recordUmdInjectedValues = new Map<PropertyKey, unknown>()
|
|
4282
|
+
// this.injectedKeys.forEach((key: PropertyKey) => {
|
|
4283
|
+
// this.recordUmdInjectedValues!.set(key, Reflect.get(this.microAppWindow, key))
|
|
4284
|
+
// })
|
|
3736
4285
|
}
|
|
3737
4286
|
// rebuild umd snapshot before remount umd app
|
|
3738
4287
|
rebuildUmdSnapshot() {
|
|
3739
|
-
this.recordUmdInjectedValues
|
|
3740
|
-
|
|
3741
|
-
})
|
|
4288
|
+
// this.recordUmdInjectedValues!.forEach((value: unknown, key: PropertyKey) => {
|
|
4289
|
+
// Reflect.set(this.proxyWindow, key, value)
|
|
4290
|
+
// })
|
|
3742
4291
|
this.rebuildUmdEffect();
|
|
3743
4292
|
rebuildDataCenterSnapshot(this.microAppWindow.microApp);
|
|
3744
4293
|
}
|
|
@@ -3749,9 +4298,9 @@ class SandBox {
|
|
|
3749
4298
|
getSpecialProperties(appName) {
|
|
3750
4299
|
var _a;
|
|
3751
4300
|
this.scopeProperties = this.scopeProperties.concat(this.adapter.staticScopeProperties);
|
|
3752
|
-
if (isPlainObject(microApp.plugins)) {
|
|
3753
|
-
this.commonActionForSpecialProperties(microApp.plugins.global);
|
|
3754
|
-
this.commonActionForSpecialProperties((_a = microApp.plugins.modules) === null || _a === void 0 ? void 0 : _a[appName]);
|
|
4301
|
+
if (isPlainObject(microApp.options.plugins)) {
|
|
4302
|
+
this.commonActionForSpecialProperties(microApp.options.plugins.global);
|
|
4303
|
+
this.commonActionForSpecialProperties((_a = microApp.options.plugins.modules) === null || _a === void 0 ? void 0 : _a[appName]);
|
|
3755
4304
|
}
|
|
3756
4305
|
}
|
|
3757
4306
|
// common action for global plugins and module plugins
|
|
@@ -3869,7 +4418,7 @@ class SandBox {
|
|
|
3869
4418
|
* @param url app url
|
|
3870
4419
|
* @param useMemoryRouter whether use memory router
|
|
3871
4420
|
*/
|
|
3872
|
-
initStaticGlobalKeys(microAppWindow, appName, url
|
|
4421
|
+
initStaticGlobalKeys(microAppWindow, appName, url) {
|
|
3873
4422
|
microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
|
|
3874
4423
|
microAppWindow.__MICRO_APP_NAME__ = appName;
|
|
3875
4424
|
microAppWindow.__MICRO_APP_URL__ = url;
|
|
@@ -3884,8 +4433,6 @@ class SandBox {
|
|
|
3884
4433
|
});
|
|
3885
4434
|
this.setProxyDocument(microAppWindow, appName);
|
|
3886
4435
|
this.setMappingPropertiesWithRawDescriptor(microAppWindow);
|
|
3887
|
-
if (useMemoryRouter)
|
|
3888
|
-
this.setMicroAppRouter(microAppWindow, appName, url);
|
|
3889
4436
|
}
|
|
3890
4437
|
setProxyDocument(microAppWindow, appName) {
|
|
3891
4438
|
const { proxyDocument, MicroDocument } = this.createProxyDocument(appName);
|
|
@@ -3942,11 +4489,13 @@ class SandBox {
|
|
|
3942
4489
|
* @param microAppWindow micro window
|
|
3943
4490
|
* @param appName app name
|
|
3944
4491
|
* @param url app url
|
|
4492
|
+
* @param disablePatchRequest prevent rewrite request method of child app
|
|
3945
4493
|
*/
|
|
3946
|
-
initGlobalKeysWhenStart(microAppWindow, appName, url) {
|
|
4494
|
+
initGlobalKeysWhenStart(microAppWindow, appName, url, disablePatchRequest) {
|
|
3947
4495
|
microAppWindow.hasOwnProperty = (key) => rawHasOwnProperty.call(microAppWindow, key) || rawHasOwnProperty.call(globalEnv.rawWindow, key);
|
|
3948
4496
|
this.setHijackProperty(microAppWindow, appName);
|
|
3949
|
-
|
|
4497
|
+
if (!disablePatchRequest)
|
|
4498
|
+
this.patchRequestApi(microAppWindow, appName, url);
|
|
3950
4499
|
}
|
|
3951
4500
|
// set hijack Properties to microAppWindow
|
|
3952
4501
|
setHijackProperty(microAppWindow, appName) {
|
|
@@ -4061,16 +4610,26 @@ class SandBox {
|
|
|
4061
4610
|
return element;
|
|
4062
4611
|
};
|
|
4063
4612
|
const proxyDocument = new Proxy(rawDocument, {
|
|
4064
|
-
get(target, key) {
|
|
4613
|
+
get: (target, key) => {
|
|
4065
4614
|
throttleDeferForSetAppName(appName);
|
|
4066
4615
|
throttleDeferForParentNode(proxyDocument);
|
|
4067
4616
|
if (key === 'createElement')
|
|
4068
4617
|
return createElement;
|
|
4069
4618
|
if (key === Symbol.toStringTag)
|
|
4070
4619
|
return 'ProxyDocument';
|
|
4620
|
+
if (key === 'defaultView')
|
|
4621
|
+
return this.proxyWindow;
|
|
4071
4622
|
const rawValue = Reflect.get(target, key);
|
|
4072
4623
|
return isFunction(rawValue) ? bindFunctionToRawObject(rawDocument, rawValue, 'DOCUMENT') : rawValue;
|
|
4073
4624
|
},
|
|
4625
|
+
set: (target, key, value) => {
|
|
4626
|
+
// Fix TypeError: Illegal invocation when set document.title
|
|
4627
|
+
Reflect.set(target, key, value);
|
|
4628
|
+
/**
|
|
4629
|
+
* If the set method returns false, and the assignment happened in strict-mode code, a TypeError will be thrown.
|
|
4630
|
+
*/
|
|
4631
|
+
return true;
|
|
4632
|
+
}
|
|
4074
4633
|
});
|
|
4075
4634
|
class MicroDocument {
|
|
4076
4635
|
static [Symbol.hasInstance](target) {
|
|
@@ -4094,11 +4653,16 @@ class SandBox {
|
|
|
4094
4653
|
* B.prototype.__proto__ === A.prototype // true
|
|
4095
4654
|
*/
|
|
4096
4655
|
Object.setPrototypeOf(MicroDocument, rawRootDocument);
|
|
4656
|
+
// Object.create(rawRootDocument.prototype) will cause MicroDocument and proxyDocument methods not same when exec Document.prototype.xxx = xxx in child app
|
|
4097
4657
|
Object.setPrototypeOf(MicroDocument.prototype, new Proxy(rawRootDocument.prototype, {
|
|
4098
4658
|
get(target, key) {
|
|
4099
4659
|
throttleDeferForSetAppName(appName);
|
|
4100
4660
|
const rawValue = Reflect.get(target, key);
|
|
4101
4661
|
return isFunction(rawValue) ? bindFunctionToRawObject(rawDocument, rawValue, 'DOCUMENT') : rawValue;
|
|
4662
|
+
},
|
|
4663
|
+
set(target, key, value) {
|
|
4664
|
+
Reflect.set(target, key, value);
|
|
4665
|
+
return true;
|
|
4102
4666
|
}
|
|
4103
4667
|
}));
|
|
4104
4668
|
return {
|
|
@@ -4150,10 +4714,8 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
|
|
|
4150
4714
|
});
|
|
4151
4715
|
formatEventInfo(event, element);
|
|
4152
4716
|
// global hooks
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
// @ts-ignore
|
|
4156
|
-
microApp.lifeCycles[lifecycleName](event);
|
|
4717
|
+
if (isFunction((_a = microApp.options.lifeCycles) === null || _a === void 0 ? void 0 : _a[lifecycleName])) {
|
|
4718
|
+
microApp.options.lifeCycles[lifecycleName](event);
|
|
4157
4719
|
}
|
|
4158
4720
|
element.dispatchEvent(event);
|
|
4159
4721
|
}
|
|
@@ -4173,7 +4735,7 @@ function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
|
|
|
4173
4735
|
// micro app instances
|
|
4174
4736
|
const appInstanceMap = new Map();
|
|
4175
4737
|
class CreateApp {
|
|
4176
|
-
constructor({ name, url,
|
|
4738
|
+
constructor({ name, url, container, scopecss, useSandbox, inline, esmodule, ssrUrl, isPrefetch, }) {
|
|
4177
4739
|
this.state = appStates.CREATED;
|
|
4178
4740
|
this.keepAliveState = null;
|
|
4179
4741
|
this.keepAliveContainer = null;
|
|
@@ -4182,49 +4744,42 @@ class CreateApp {
|
|
|
4182
4744
|
this.umdHookUnmount = null;
|
|
4183
4745
|
this.libraryName = null;
|
|
4184
4746
|
this.umdMode = false;
|
|
4185
|
-
this.isPrefetch = false;
|
|
4186
|
-
this.prefetchResolve = null;
|
|
4187
|
-
this.container = null;
|
|
4188
4747
|
this.sandBox = null;
|
|
4748
|
+
this.keepRouteState = false;
|
|
4749
|
+
this.fiber = false;
|
|
4750
|
+
this.useMemoryRouter = true;
|
|
4189
4751
|
this.name = name;
|
|
4190
4752
|
this.url = url;
|
|
4191
4753
|
this.useSandbox = useSandbox;
|
|
4192
4754
|
this.scopecss = this.useSandbox && scopecss;
|
|
4193
|
-
this.
|
|
4194
|
-
|
|
4755
|
+
this.inline = inline !== null && inline !== void 0 ? inline : false;
|
|
4756
|
+
this.esmodule = esmodule !== null && esmodule !== void 0 ? esmodule : false;
|
|
4757
|
+
// not exist when prefetch 👇
|
|
4195
4758
|
this.container = container !== null && container !== void 0 ? container : null;
|
|
4196
4759
|
this.ssrUrl = ssrUrl !== null && ssrUrl !== void 0 ? ssrUrl : '';
|
|
4197
|
-
|
|
4198
|
-
this.
|
|
4199
|
-
|
|
4200
|
-
this.
|
|
4201
|
-
this.
|
|
4202
|
-
this.source = {
|
|
4203
|
-
links: new Map(),
|
|
4204
|
-
scripts: new Map(),
|
|
4205
|
-
};
|
|
4760
|
+
// not exist when normal 👇
|
|
4761
|
+
this.isPrefetch = isPrefetch !== null && isPrefetch !== void 0 ? isPrefetch : false;
|
|
4762
|
+
// init actions
|
|
4763
|
+
appInstanceMap.set(this.name, this);
|
|
4764
|
+
this.source = { html: null, links: new Set(), scripts: new Set() };
|
|
4206
4765
|
this.loadSourceCode();
|
|
4207
|
-
this.useSandbox && (this.sandBox = new SandBox(name, url
|
|
4766
|
+
this.useSandbox && (this.sandBox = new SandBox(name, url));
|
|
4208
4767
|
}
|
|
4209
4768
|
// Load resources
|
|
4210
4769
|
loadSourceCode() {
|
|
4211
4770
|
this.state = appStates.LOADING;
|
|
4212
|
-
|
|
4771
|
+
HTMLLoader.getInstance().run(this, extractSourceDom);
|
|
4213
4772
|
}
|
|
4214
4773
|
/**
|
|
4215
4774
|
* When resource is loaded, mount app if it is not prefetch or unmount
|
|
4216
4775
|
*/
|
|
4217
4776
|
onLoad(html) {
|
|
4218
|
-
var _a;
|
|
4219
4777
|
if (++this.loadSourceLevel === 2) {
|
|
4220
4778
|
this.source.html = html;
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
else if (appStates.UNMOUNT !== this.state) {
|
|
4226
|
-
this.state = appStates.LOADED;
|
|
4227
|
-
this.mount();
|
|
4779
|
+
this.state = appStates.LOADED;
|
|
4780
|
+
if (!this.isPrefetch && appStates.UNMOUNT !== this.state) {
|
|
4781
|
+
// @ts-ignore
|
|
4782
|
+
getRootContainer(this.container).mount(this);
|
|
4228
4783
|
}
|
|
4229
4784
|
}
|
|
4230
4785
|
}
|
|
@@ -4234,10 +4789,6 @@ class CreateApp {
|
|
|
4234
4789
|
*/
|
|
4235
4790
|
onLoadError(e) {
|
|
4236
4791
|
this.loadSourceLevel = -1;
|
|
4237
|
-
if (this.prefetchResolve) {
|
|
4238
|
-
this.prefetchResolve();
|
|
4239
|
-
this.prefetchResolve = null;
|
|
4240
|
-
}
|
|
4241
4792
|
if (appStates.UNMOUNT !== this.state) {
|
|
4242
4793
|
this.onerror(e);
|
|
4243
4794
|
this.state = appStates.LOAD_FAILED;
|
|
@@ -4249,15 +4800,18 @@ class CreateApp {
|
|
|
4249
4800
|
* @param inline js runs in inline mode
|
|
4250
4801
|
* @param baseroute route prefix, default is ''
|
|
4251
4802
|
* @param keepRouteState keep route state when unmount, default is false
|
|
4803
|
+
* @param disablePatchRequest prevent rewrite request method of child app
|
|
4252
4804
|
*/
|
|
4253
|
-
mount(container, inline, baseroute, keepRouteState, defaultPage,
|
|
4254
|
-
var _a, _b
|
|
4255
|
-
this.
|
|
4256
|
-
this.
|
|
4257
|
-
this.
|
|
4258
|
-
this.
|
|
4259
|
-
this.
|
|
4260
|
-
|
|
4805
|
+
mount({ container, inline, esmodule, useMemoryRouter, baseroute, keepRouteState, defaultPage, disablePatchRequest, fiber, }) {
|
|
4806
|
+
var _a, _b;
|
|
4807
|
+
this.container = container;
|
|
4808
|
+
this.inline = inline;
|
|
4809
|
+
this.esmodule = esmodule;
|
|
4810
|
+
this.keepRouteState = keepRouteState;
|
|
4811
|
+
this.fiber = fiber;
|
|
4812
|
+
// use in sandbox/effect
|
|
4813
|
+
this.useMemoryRouter = useMemoryRouter;
|
|
4814
|
+
// this.hiddenRouter = hiddenRouter ?? this.hiddenRouter
|
|
4261
4815
|
if (this.loadSourceLevel !== 2) {
|
|
4262
4816
|
this.state = appStates.LOADING;
|
|
4263
4817
|
return;
|
|
@@ -4265,21 +4819,32 @@ class CreateApp {
|
|
|
4265
4819
|
dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
|
|
4266
4820
|
this.state = appStates.MOUNTING;
|
|
4267
4821
|
cloneContainer(this.source.html, this.container, !this.umdMode);
|
|
4268
|
-
(
|
|
4822
|
+
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.start({
|
|
4823
|
+
umdMode: this.umdMode,
|
|
4824
|
+
baseroute,
|
|
4825
|
+
useMemoryRouter,
|
|
4826
|
+
defaultPage,
|
|
4827
|
+
disablePatchRequest,
|
|
4828
|
+
});
|
|
4269
4829
|
let umdHookMountResult; // result of mount function
|
|
4270
4830
|
if (!this.umdMode) {
|
|
4271
4831
|
let hasDispatchMountedEvent = false;
|
|
4272
4832
|
// if all js are executed, param isFinished will be true
|
|
4273
|
-
execScripts(this
|
|
4274
|
-
var _a;
|
|
4833
|
+
execScripts(this, (isFinished) => {
|
|
4275
4834
|
if (!this.umdMode) {
|
|
4276
4835
|
const { mount, unmount } = this.getUmdLibraryHooks();
|
|
4836
|
+
/**
|
|
4837
|
+
* umdHookUnmount can works in non UMD mode
|
|
4838
|
+
* register with window.unmount
|
|
4839
|
+
*/
|
|
4840
|
+
this.umdHookUnmount = unmount;
|
|
4277
4841
|
// if mount & unmount is function, the sub app is umd mode
|
|
4278
4842
|
if (isFunction(mount) && isFunction(unmount)) {
|
|
4279
4843
|
this.umdHookMount = mount;
|
|
4280
|
-
this.umdHookUnmount = unmount;
|
|
4281
4844
|
this.umdMode = true;
|
|
4282
|
-
(
|
|
4845
|
+
if (this.sandBox)
|
|
4846
|
+
this.sandBox.proxyWindow.__MICRO_APP_UMD_MODE__ = true;
|
|
4847
|
+
// this.sandBox?.recordUmdSnapshot()
|
|
4283
4848
|
try {
|
|
4284
4849
|
umdHookMountResult = this.umdHookMount();
|
|
4285
4850
|
}
|
|
@@ -4295,7 +4860,7 @@ class CreateApp {
|
|
|
4295
4860
|
});
|
|
4296
4861
|
}
|
|
4297
4862
|
else {
|
|
4298
|
-
(
|
|
4863
|
+
(_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.rebuildUmdSnapshot();
|
|
4299
4864
|
try {
|
|
4300
4865
|
umdHookMountResult = this.umdHookMount();
|
|
4301
4866
|
}
|
|
@@ -4347,7 +4912,7 @@ class CreateApp {
|
|
|
4347
4912
|
* send an unmount event to the micro app or call umd unmount hook
|
|
4348
4913
|
* before the sandbox is cleared
|
|
4349
4914
|
*/
|
|
4350
|
-
if (this.umdHookUnmount) {
|
|
4915
|
+
if (isFunction(this.umdHookUnmount)) {
|
|
4351
4916
|
try {
|
|
4352
4917
|
umdHookUnmountResult = this.umdHookUnmount();
|
|
4353
4918
|
}
|
|
@@ -4381,20 +4946,27 @@ class CreateApp {
|
|
|
4381
4946
|
* @param unmountcb callback of unmount
|
|
4382
4947
|
*/
|
|
4383
4948
|
actionsForUnmount(destroy, unmountcb) {
|
|
4384
|
-
var _a;
|
|
4949
|
+
var _a, _b;
|
|
4385
4950
|
if (destroy) {
|
|
4386
4951
|
this.actionsForCompletelyDestroy();
|
|
4387
4952
|
}
|
|
4388
4953
|
else if (this.umdMode && this.container.childElementCount) {
|
|
4389
4954
|
cloneContainer(this.container, this.source.html, false);
|
|
4390
4955
|
}
|
|
4956
|
+
if (this.umdMode) {
|
|
4957
|
+
(_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordUmdSnapshot();
|
|
4958
|
+
}
|
|
4391
4959
|
/**
|
|
4392
4960
|
* this.container maybe contains micro-app element, stop sandbox should exec after cloneContainer
|
|
4393
4961
|
* NOTE:
|
|
4394
4962
|
* 1. if destroy is true, clear route state
|
|
4395
4963
|
* 2. umd mode and keep-alive will not clear EventSource
|
|
4396
4964
|
*/
|
|
4397
|
-
(
|
|
4965
|
+
(_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.stop({
|
|
4966
|
+
umdMode: this.umdMode,
|
|
4967
|
+
keepRouteState: this.keepRouteState && !destroy,
|
|
4968
|
+
clearEventSource: !this.umdMode || destroy,
|
|
4969
|
+
});
|
|
4398
4970
|
if (!getActiveApps().length) {
|
|
4399
4971
|
releasePatchSetAttribute();
|
|
4400
4972
|
}
|
|
@@ -4466,13 +5038,18 @@ class CreateApp {
|
|
|
4466
5038
|
}
|
|
4467
5039
|
// get umd library, if it not exist, return empty object
|
|
4468
5040
|
getUmdLibraryHooks() {
|
|
4469
|
-
var _a, _b;
|
|
5041
|
+
var _a, _b, _c, _d;
|
|
4470
5042
|
// after execScripts, the app maybe unmounted
|
|
4471
5043
|
if (appStates.UNMOUNT !== this.state) {
|
|
4472
5044
|
const global = ((_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow) !== null && _b !== void 0 ? _b : globalEnv.rawWindow);
|
|
4473
5045
|
this.libraryName = getRootContainer(this.container).getAttribute('library') || `micro-app-${this.name}`;
|
|
4474
|
-
|
|
4475
|
-
|
|
5046
|
+
if (isObject(global[this.libraryName])) {
|
|
5047
|
+
return global[this.libraryName];
|
|
5048
|
+
}
|
|
5049
|
+
return {
|
|
5050
|
+
mount: (_c = this.sandBox) === null || _c === void 0 ? void 0 : _c.proxyWindow.mount,
|
|
5051
|
+
unmount: (_d = this.sandBox) === null || _d === void 0 ? void 0 : _d.proxyWindow.unmount,
|
|
5052
|
+
};
|
|
4476
5053
|
}
|
|
4477
5054
|
return {};
|
|
4478
5055
|
}
|
|
@@ -4488,7 +5065,8 @@ function defineElement(tagName) {
|
|
|
4488
5065
|
super();
|
|
4489
5066
|
this.isWaiting = false;
|
|
4490
5067
|
this.cacheData = null;
|
|
4491
|
-
this.
|
|
5068
|
+
this.connectedCount = 0;
|
|
5069
|
+
this.connectStateMap = new Map();
|
|
4492
5070
|
this.appName = ''; // app name
|
|
4493
5071
|
this.appUrl = ''; // app url
|
|
4494
5072
|
this.ssrUrl = ''; // html path in ssr mode
|
|
@@ -4498,6 +5076,8 @@ function defineElement(tagName) {
|
|
|
4498
5076
|
*/
|
|
4499
5077
|
this.handleAttributeUpdate = () => {
|
|
4500
5078
|
this.isWaiting = false;
|
|
5079
|
+
if (!this.connectStateMap.get(this.connectedCount))
|
|
5080
|
+
return;
|
|
4501
5081
|
const formatAttrName = formatAppName(this.getAttribute('name'));
|
|
4502
5082
|
const formatAttrUrl = formatAppURL(this.getAttribute('url'), this.appName);
|
|
4503
5083
|
if (this.legalAttribute('name', formatAttrName) && this.legalAttribute('url', formatAttrUrl)) {
|
|
@@ -4549,12 +5129,21 @@ function defineElement(tagName) {
|
|
|
4549
5129
|
// baseRoute: route prefix, default is ''
|
|
4550
5130
|
// keep-alive: open keep-alive mode
|
|
4551
5131
|
connectedCallback() {
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
5132
|
+
const cacheCount = ++this.connectedCount;
|
|
5133
|
+
this.connectStateMap.set(cacheCount, true);
|
|
5134
|
+
/**
|
|
5135
|
+
* In some special scenes, such as vue's keep-alive, the micro-app will be inserted and deleted twice in an instant
|
|
5136
|
+
* So we execute the mount method async and record connectState to prevent repeated rendering
|
|
5137
|
+
*/
|
|
5138
|
+
defer(() => {
|
|
5139
|
+
if (this.connectStateMap.get(cacheCount)) {
|
|
5140
|
+
dispatchLifecyclesEvent(this, this.appName, lifeCycles.CREATED);
|
|
5141
|
+
this.initialMount();
|
|
5142
|
+
}
|
|
5143
|
+
});
|
|
4555
5144
|
}
|
|
4556
5145
|
disconnectedCallback() {
|
|
4557
|
-
this.
|
|
5146
|
+
this.connectStateMap.set(this.connectedCount, false);
|
|
4558
5147
|
const app = appInstanceMap.get(this.appName);
|
|
4559
5148
|
if (app &&
|
|
4560
5149
|
app.getAppState() !== appStates.UNMOUNT &&
|
|
@@ -4602,7 +5191,7 @@ function defineElement(tagName) {
|
|
|
4602
5191
|
}
|
|
4603
5192
|
// handle for connectedCallback run before attributeChangedCallback
|
|
4604
5193
|
handleInitialNameAndUrl() {
|
|
4605
|
-
this.
|
|
5194
|
+
this.connectStateMap.get(this.connectedCount) && this.initialMount();
|
|
4606
5195
|
}
|
|
4607
5196
|
/**
|
|
4608
5197
|
* first mount of this app
|
|
@@ -4617,26 +5206,35 @@ function defineElement(tagName) {
|
|
|
4617
5206
|
if (appInstanceMap.has(this.appName)) {
|
|
4618
5207
|
const app = appInstanceMap.get(this.appName);
|
|
4619
5208
|
const existAppUrl = app.ssrUrl || app.url;
|
|
4620
|
-
const
|
|
4621
|
-
|
|
4622
|
-
|
|
5209
|
+
const targetAppUrl = this.ssrUrl || this.appUrl;
|
|
5210
|
+
/**
|
|
5211
|
+
* NOTE:
|
|
5212
|
+
* 1. keep-alive don't care about ssrUrl
|
|
5213
|
+
* 2. Even if the keep-alive app is pushed into the background, it is still active and cannot be replaced. Otherwise, it is difficult for developers to troubleshoot in case of conflict and will leave developers at a loss
|
|
5214
|
+
* 3. When scopecss, useSandbox of prefetch app different from target app, delete prefetch app and create new one
|
|
5215
|
+
*/
|
|
4623
5216
|
if (app.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN &&
|
|
4624
5217
|
app.url === this.appUrl) {
|
|
4625
5218
|
this.handleShowKeepAliveApp(app);
|
|
4626
5219
|
}
|
|
4627
|
-
else if (existAppUrl ===
|
|
4628
|
-
app.
|
|
5220
|
+
else if (existAppUrl === targetAppUrl && (app.getAppState() === appStates.UNMOUNT ||
|
|
5221
|
+
(app.isPrefetch && (app.scopecss === this.isScopecss() &&
|
|
5222
|
+
app.useSandbox === this.isSandbox())))) {
|
|
4629
5223
|
this.handleAppMount(app);
|
|
4630
5224
|
}
|
|
4631
5225
|
else if (app.isPrefetch || app.getAppState() === appStates.UNMOUNT) {
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
5226
|
+
if (process.env.NODE_ENV !== 'production' &&
|
|
5227
|
+
app.scopecss === this.isScopecss() &&
|
|
5228
|
+
app.useSandbox === this.isSandbox()) {
|
|
5229
|
+
/**
|
|
5230
|
+
* url is different & old app is unmounted or prefetch, create new app to replace old one
|
|
5231
|
+
*/
|
|
5232
|
+
logWarn(`the ${app.isPrefetch ? 'prefetch' : 'unmounted'} app with url: ${existAppUrl} replaced by a new app with url: ${targetAppUrl}`, this.appName);
|
|
5233
|
+
}
|
|
4636
5234
|
this.handleCreateApp();
|
|
4637
5235
|
}
|
|
4638
5236
|
else {
|
|
4639
|
-
logError(`app name conflict, an app named ${this.appName} is running`, this.appName);
|
|
5237
|
+
logError(`app name conflict, an app named: ${this.appName} with url: ${existAppUrl} is running`, this.appName);
|
|
4640
5238
|
}
|
|
4641
5239
|
}
|
|
4642
5240
|
else {
|
|
@@ -4707,9 +5305,24 @@ function defineElement(tagName) {
|
|
|
4707
5305
|
*/
|
|
4708
5306
|
handleAppMount(app) {
|
|
4709
5307
|
app.isPrefetch = false;
|
|
4710
|
-
defer(() =>
|
|
4711
|
-
|
|
4712
|
-
|
|
5308
|
+
defer(() => this.mount(app));
|
|
5309
|
+
}
|
|
5310
|
+
/**
|
|
5311
|
+
* public mount action for micro_app_element & create_app
|
|
5312
|
+
*/
|
|
5313
|
+
mount(app) {
|
|
5314
|
+
var _a;
|
|
5315
|
+
app.mount({
|
|
5316
|
+
container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
|
|
5317
|
+
inline: this.getDisposeResult('inline'),
|
|
5318
|
+
esmodule: this.getDisposeResult('esmodule'),
|
|
5319
|
+
useMemoryRouter: !this.getDisposeResult('disable-memory-router'),
|
|
5320
|
+
baseroute: this.getBaseRouteCompatible(),
|
|
5321
|
+
keepRouteState: this.getDisposeResult('keep-router-state'),
|
|
5322
|
+
defaultPage: this.getDefaultPageValue(),
|
|
5323
|
+
hiddenRouter: this.getDisposeResult('hidden-router'),
|
|
5324
|
+
disablePatchRequest: this.getDisposeResult('disable-patch-request'),
|
|
5325
|
+
fiber: this.getDisposeResult('fiber'),
|
|
4713
5326
|
});
|
|
4714
5327
|
}
|
|
4715
5328
|
// create app instance
|
|
@@ -4722,21 +5335,16 @@ function defineElement(tagName) {
|
|
|
4722
5335
|
if (appInstanceMap.has(this.appName)) {
|
|
4723
5336
|
appInstanceMap.get(this.appName).actionsForCompletelyDestroy();
|
|
4724
5337
|
}
|
|
4725
|
-
|
|
5338
|
+
new CreateApp({
|
|
4726
5339
|
name: this.appName,
|
|
4727
5340
|
url: this.appUrl,
|
|
4728
|
-
|
|
4729
|
-
|
|
5341
|
+
scopecss: this.isScopecss(),
|
|
5342
|
+
useSandbox: this.isSandbox(),
|
|
4730
5343
|
inline: this.getDisposeResult('inline'),
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
baseroute: this.getBaseRouteCompatible(),
|
|
4735
|
-
keepRouteState: this.getDisposeResult('keep-router-state'),
|
|
4736
|
-
defaultPage: this.getDefaultPageValue(),
|
|
4737
|
-
hiddenRouter: this.getDisposeResult('hidden-router'),
|
|
5344
|
+
esmodule: this.getDisposeResult('esmodule'),
|
|
5345
|
+
container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
|
|
5346
|
+
ssrUrl: this.ssrUrl,
|
|
4738
5347
|
});
|
|
4739
|
-
appInstanceMap.set(this.appName, instance);
|
|
4740
5348
|
}
|
|
4741
5349
|
/**
|
|
4742
5350
|
* unmount app
|
|
@@ -4768,8 +5376,7 @@ function defineElement(tagName) {
|
|
|
4768
5376
|
* @param name Configuration item name
|
|
4769
5377
|
*/
|
|
4770
5378
|
getDisposeResult(name) {
|
|
4771
|
-
|
|
4772
|
-
return (this.compatibleSpecialProperties(name) || !!microApp[name]) && this.compatibleDisableSpecialProperties(name);
|
|
5379
|
+
return (this.compatibleSpecialProperties(name) || !!microApp.options[name]) && this.compatibleDisableSpecialProperties(name);
|
|
4773
5380
|
}
|
|
4774
5381
|
// compatible of disableScopecss & disableSandbox
|
|
4775
5382
|
compatibleSpecialProperties(name) {
|
|
@@ -4791,6 +5398,12 @@ function defineElement(tagName) {
|
|
|
4791
5398
|
}
|
|
4792
5399
|
return this.getAttribute(name) !== 'false';
|
|
4793
5400
|
}
|
|
5401
|
+
isScopecss() {
|
|
5402
|
+
return !(this.getDisposeResult('disable-scopecss') || this.getDisposeResult('shadowDOM'));
|
|
5403
|
+
}
|
|
5404
|
+
isSandbox() {
|
|
5405
|
+
return !this.getDisposeResult('disable-sandbox');
|
|
5406
|
+
}
|
|
4794
5407
|
/**
|
|
4795
5408
|
* 2021-09-08
|
|
4796
5409
|
* get baseRoute
|
|
@@ -4838,8 +5451,10 @@ function defineElement(tagName) {
|
|
|
4838
5451
|
* get config of default page
|
|
4839
5452
|
*/
|
|
4840
5453
|
getDefaultPageValue() {
|
|
4841
|
-
|
|
4842
|
-
|
|
5454
|
+
return (router.getDefaultPage(this.appName) ||
|
|
5455
|
+
this.getAttribute('default-page') ||
|
|
5456
|
+
this.getAttribute('defaultPage') ||
|
|
5457
|
+
'');
|
|
4843
5458
|
}
|
|
4844
5459
|
/**
|
|
4845
5460
|
* Data from the base application
|
|
@@ -4896,33 +5511,40 @@ function preFetch(apps) {
|
|
|
4896
5511
|
});
|
|
4897
5512
|
}
|
|
4898
5513
|
// sequential preload app
|
|
4899
|
-
function preFetchInSerial(
|
|
4900
|
-
return
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
else {
|
|
5514
|
+
function preFetchInSerial(options) {
|
|
5515
|
+
return promiseRequestIdle((resolve) => {
|
|
5516
|
+
var _a, _b, _c, _d, _e, _f;
|
|
5517
|
+
if (isPlainObject(options) && navigator.onLine) {
|
|
5518
|
+
options.name = formatAppName(options.name);
|
|
5519
|
+
options.url = formatAppURL(options.url, options.name);
|
|
5520
|
+
if (options.name && options.url && !appInstanceMap.has(options.name)) {
|
|
5521
|
+
const app = new CreateApp({
|
|
5522
|
+
name: options.name,
|
|
5523
|
+
url: options.url,
|
|
5524
|
+
scopecss: !((_b = (_a = options['disable-scopecss']) !== null && _a !== void 0 ? _a : options.disableScopecss) !== null && _b !== void 0 ? _b : microApp.options['disable-scopecss']),
|
|
5525
|
+
useSandbox: !((_d = (_c = options['disable-sandbox']) !== null && _c !== void 0 ? _c : options.disableSandbox) !== null && _d !== void 0 ? _d : microApp.options['disable-sandbox']),
|
|
5526
|
+
inline: (_e = options.inline) !== null && _e !== void 0 ? _e : microApp.options.inline,
|
|
5527
|
+
esmodule: (_f = options.esmodule) !== null && _f !== void 0 ? _f : microApp.options.esmodule,
|
|
5528
|
+
isPrefetch: true,
|
|
5529
|
+
});
|
|
5530
|
+
const oldOnload = app.onLoad;
|
|
5531
|
+
const oldOnLoadError = app.onLoadError;
|
|
5532
|
+
app.onLoad = (html) => {
|
|
4919
5533
|
resolve();
|
|
4920
|
-
|
|
5534
|
+
oldOnload.call(app, html);
|
|
5535
|
+
};
|
|
5536
|
+
app.onLoadError = (e) => {
|
|
5537
|
+
resolve();
|
|
5538
|
+
oldOnLoadError.call(app, e);
|
|
5539
|
+
};
|
|
4921
5540
|
}
|
|
4922
5541
|
else {
|
|
4923
5542
|
resolve();
|
|
4924
5543
|
}
|
|
4925
|
-
}
|
|
5544
|
+
}
|
|
5545
|
+
else {
|
|
5546
|
+
resolve();
|
|
5547
|
+
}
|
|
4926
5548
|
});
|
|
4927
5549
|
}
|
|
4928
5550
|
/**
|
|
@@ -4932,21 +5554,35 @@ function preFetchInSerial(prefetchApp) {
|
|
|
4932
5554
|
function getGlobalAssets(assets) {
|
|
4933
5555
|
if (isPlainObject(assets)) {
|
|
4934
5556
|
requestIdleCallback(() => {
|
|
4935
|
-
fetchGlobalResources(assets.js, 'js',
|
|
4936
|
-
fetchGlobalResources(assets.css, 'css',
|
|
5557
|
+
fetchGlobalResources(assets.js, 'js', sourceCenter.script);
|
|
5558
|
+
fetchGlobalResources(assets.css, 'css', sourceCenter.link);
|
|
4937
5559
|
});
|
|
4938
5560
|
}
|
|
4939
5561
|
}
|
|
4940
5562
|
// TODO: requestIdleCallback for every file
|
|
4941
|
-
function fetchGlobalResources(resources, suffix,
|
|
5563
|
+
function fetchGlobalResources(resources, suffix, sourceHandler) {
|
|
4942
5564
|
if (isArray(resources)) {
|
|
4943
|
-
const effectiveResource = resources.filter((path) => isString(path) && path.includes(`.${suffix}`) && !
|
|
5565
|
+
const effectiveResource = resources.filter((path) => isString(path) && path.includes(`.${suffix}`) && !sourceHandler.hasInfo(path));
|
|
4944
5566
|
const fetchResourcePromise = effectiveResource.map((path) => fetchSource(path));
|
|
4945
5567
|
// fetch resource with stream
|
|
4946
5568
|
promiseStream(fetchResourcePromise, (res) => {
|
|
4947
5569
|
const path = effectiveResource[res.index];
|
|
4948
|
-
if (
|
|
4949
|
-
|
|
5570
|
+
if (suffix === 'js') {
|
|
5571
|
+
if (!sourceHandler.hasInfo(path)) {
|
|
5572
|
+
sourceHandler.setInfo(path, {
|
|
5573
|
+
code: res.data,
|
|
5574
|
+
isExternal: false,
|
|
5575
|
+
appSpace: {},
|
|
5576
|
+
});
|
|
5577
|
+
}
|
|
5578
|
+
}
|
|
5579
|
+
else {
|
|
5580
|
+
if (!sourceHandler.hasInfo(path)) {
|
|
5581
|
+
sourceHandler.setInfo(path, {
|
|
5582
|
+
code: res.data,
|
|
5583
|
+
appSpace: {}
|
|
5584
|
+
});
|
|
5585
|
+
}
|
|
4950
5586
|
}
|
|
4951
5587
|
}, (err) => {
|
|
4952
5588
|
logError(err);
|
|
@@ -5051,6 +5687,7 @@ class MicroApp extends EventCenterForBaseApp {
|
|
|
5051
5687
|
constructor() {
|
|
5052
5688
|
super(...arguments);
|
|
5053
5689
|
this.tagName = 'micro-app';
|
|
5690
|
+
this.options = {};
|
|
5054
5691
|
this.preFetch = preFetch;
|
|
5055
5692
|
this.router = router;
|
|
5056
5693
|
}
|
|
@@ -5071,25 +5708,10 @@ class MicroApp extends EventCenterForBaseApp {
|
|
|
5071
5708
|
return logWarn(`element ${this.tagName} is already defined`);
|
|
5072
5709
|
}
|
|
5073
5710
|
initGlobalEnv();
|
|
5074
|
-
if (
|
|
5075
|
-
this.
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
* compatible with versions below 0.4.2 of destroy
|
|
5079
|
-
* do not merge with the previous line
|
|
5080
|
-
*/
|
|
5081
|
-
// @ts-ignore
|
|
5082
|
-
this.destory = options.destory;
|
|
5083
|
-
this.inline = options.inline;
|
|
5084
|
-
this['disable-scopecss'] = (_a = options['disable-scopecss']) !== null && _a !== void 0 ? _a : options.disableScopecss;
|
|
5085
|
-
this['disable-sandbox'] = (_b = options['disable-sandbox']) !== null && _b !== void 0 ? _b : options.disableSandbox;
|
|
5086
|
-
this['disable-memory-router'] = options['disable-memory-router'];
|
|
5087
|
-
this['keep-router-state'] = options['keep-router-state'];
|
|
5088
|
-
this['hidden-router'] = options['hidden-router'];
|
|
5089
|
-
this.esmodule = options.esmodule;
|
|
5090
|
-
this.ssr = options.ssr;
|
|
5091
|
-
isFunction(options.fetch) && (this.fetch = options.fetch);
|
|
5092
|
-
isPlainObject(options.lifeCycles) && (this.lifeCycles = options.lifeCycles);
|
|
5711
|
+
if (isPlainObject(options)) {
|
|
5712
|
+
this.options = options;
|
|
5713
|
+
options['disable-scopecss'] = (_a = options['disable-scopecss']) !== null && _a !== void 0 ? _a : options.disableScopecss;
|
|
5714
|
+
options['disable-sandbox'] = (_b = options['disable-sandbox']) !== null && _b !== void 0 ? _b : options.disableSandbox;
|
|
5093
5715
|
// load app assets when browser is idle
|
|
5094
5716
|
options.preFetchApps && preFetch(options.preFetchApps);
|
|
5095
5717
|
// load global assets when browser is idle
|
|
@@ -5105,7 +5727,6 @@ class MicroApp extends EventCenterForBaseApp {
|
|
|
5105
5727
|
}
|
|
5106
5728
|
}
|
|
5107
5729
|
}
|
|
5108
|
-
this.plugins = options.plugins;
|
|
5109
5730
|
}
|
|
5110
5731
|
}
|
|
5111
5732
|
// define customElement after init
|