@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/lib/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- const version = '1.0.0-alpha.3';
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
- }, 50);
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.createMatcherForAtRuleWithChildRule(/^@media *([^{]+)/, 'media');
644
+ this.mediaRule = this.createMatcherForRuleWithChildRule(/^@media *([^{]+)/, '@media');
535
645
  // https://developer.mozilla.org/en-US/docs/Web/API/CSSSupportsRule
536
- this.supportsRule = this.createMatcherForAtRuleWithChildRule(/^@supports *([^{]+)/, 'supports');
537
- this.documentRule = this.createMatcherForAtRuleWithChildRule(/^@([-\w]+)?document *([^{]+)/, 'document');
538
- this.hostRule = this.createMatcherForAtRuleWithChildRule(/^@host\s*/, 'host');
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
- createMatcherForAtRuleWithChildRule(reg, name) {
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(`@${name} missing '{'`, this.linkPath);
837
+ return parseError(`${name} missing '{'`, this.linkPath);
719
838
  this.matchComments();
720
839
  this.matchRules();
721
840
  if (!this.matchCloseBrace())
722
- return parseError(`@${name} missing '}'`, this.linkPath);
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 = `${microApp.tagName}[name=${app.name}]`;
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, styleElement.__MICRO_APP_LINK_PATH__);
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, styleElement.__MICRO_APP_LINK_PATH__);
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
- // Global links, reuse across apps
912
- const globalLinks = new Map();
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.source.links.set(href, {
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 linkEntries = Array.from(app.source.links.entries());
973
- const fetchLinkPromise = linkEntries.map(([url]) => {
974
- return globalLinks.has(url) ? globalLinks.get(url) : fetchSource(url, app.name);
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
- fetchLinkSuccess(linkEntries[res.index][0], linkEntries[res.index][1], res.data, microAppHead, app);
1177
+ injectFiberTask(fiberLinkTasks, () => fetchLinkSuccess(styleList[res.index], res.data, microAppHead, app));
978
1178
  }, (err) => {
979
1179
  logError(err, app.name);
980
1180
  }, () => {
981
- app.onLoad(wrapElement);
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
- * fetch link succeeded, replace placeholder with style tag
986
- * @param url resource address
987
- * @param info resource link info
988
- * @param data code
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(url, info, data, microAppHead, app) {
993
- if (info.isGlobal && !globalLinks.has(url)) {
994
- globalLinks.set(url, data);
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
- const styleLink = pureCreateElement('style');
997
- styleLink.textContent = data;
998
- styleLink.__MICRO_APP_LINK_PATH__ = url;
999
- styleLink.setAttribute('data-origin-href', url);
1000
- if (info.placeholder.parentNode) {
1001
- info.placeholder.parentNode.replaceChild(scopedCSS(styleLink, app), info.placeholder);
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
- microAppHead.appendChild(scopedCSS(styleLink, app));
1271
+ convertStyle.textContent = linkInfo.code;
1005
1272
  }
1006
- info.placeholder = null;
1007
- info.code = data;
1273
+ setConvertStyleAttr(convertStyle, attrs);
1008
1274
  }
1009
1275
  /**
1010
- * get css from dynamic link
1011
- * @param url link address
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(url, info, app, originLink, replaceStyle) {
1018
- if (app.source.links.has(url)) {
1019
- replaceStyle.textContent = app.source.links.get(url).code;
1020
- scopedCSS(replaceStyle, app);
1021
- defer(() => dispatchOnLoadEvent(originLink));
1022
- return;
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
- if (globalLinks.has(url)) {
1025
- const code = globalLinks.get(url);
1026
- info.code = code;
1027
- app.source.links.set(url, info);
1028
- replaceStyle.textContent = code;
1029
- scopedCSS(replaceStyle, app);
1030
- defer(() => dispatchOnLoadEvent(originLink));
1031
- return;
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
- fetchSource(url, app.name).then((data) => {
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.defineProperty(root, 'parentNode', {
1120
- value,
1121
- configurable: true,
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 { url, info, replaceComment } = extractLinkFromHtml(child, parent, app, true);
1155
- if (url && info) {
1156
- const replaceStyle = pureCreateElement('style');
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
- const { replaceComment, url, info } = extractScriptElement(child, parent, app, true) || {};
1170
- if (url && info) {
1171
- if (!info.isExternal) { // inline script
1172
- const replaceElement = runScript(url, app, info, true);
1173
- dynamicElementInMicroAppMap.set(child, replaceElement);
1174
- return replaceElement;
1175
- }
1176
- else { // remote script
1177
- const replaceElement = runDynamicRemoteScript(url, info, app, child);
1178
- dynamicElementInMicroAppMap.set(child, replaceElement);
1179
- return replaceElement;
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
- // Global scripts, reuse across apps
1649
- const globalScripts = new Map();
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 (script.hasAttribute('exclude')) {
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 && !['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module'].includes(script.type)) ||
1664
- script.hasAttribute('ignore')) {
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
- src = CompletionPath(src, app.url);
1673
- const info = {
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
- isGlobal: script.hasAttribute('global'),
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.set(src, info);
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 { url: src, info };
2042
+ return { address: src, scriptInfo };
1688
2043
  }
1689
2044
  }
1690
2045
  else if (script.textContent) { // inline script
1691
- const nonceStr = createNonceSrc();
1692
- const info = {
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
- isDynamic: isDynamic,
1696
- async: false,
1697
- defer: script.type === 'module',
1698
- module: script.type === 'module',
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.set(nonceStr, info);
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
- return { url: nonceStr, info };
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 scriptEntries = Array.from(app.source.scripts.entries());
2139
+ const scriptList = Array.from(app.source.scripts);
1729
2140
  const fetchScriptPromise = [];
1730
2141
  const fetchScriptPromiseInfo = [];
1731
- for (const [url, info] of scriptEntries) {
1732
- if (info.isExternal) {
1733
- const globalScriptText = globalScripts.get(url);
1734
- if (globalScriptText) {
1735
- info.code = globalScriptText;
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
- app.onLoad(wrapElement);
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 url script address
1759
- * @param info resource script info
2172
+ * @param address script address
2173
+ * @param scriptInfo resource script info
1760
2174
  * @param data code
1761
2175
  */
1762
- function fetchScriptSuccess(url, info, data) {
1763
- if (info.isGlobal && !globalScripts.has(url)) {
1764
- globalScripts.set(url, data);
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(scriptList, app, initHook) {
1775
- const scriptListEntries = Array.from(scriptList.entries());
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 [url, info] of scriptListEntries) {
1779
- if (!info.isDynamic) {
1780
- // Notice the second render
1781
- if (info.defer || info.async) {
1782
- if (info.isExternal && !info.code) {
1783
- deferScriptPromise.push(fetchSource(url, app.name));
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
- runScript(url, app, info, false);
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 info = deferScriptInfo[res.index][1];
1800
- info.code = info.code || res.data;
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(([url, info]) => {
1806
- if (info.code) {
1807
- runScript(url, app, info, false, initHook);
1808
- !info.module && initHook(false);
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
- initHook(isUndefined(initHook.moduleCount) ||
1812
- initHook.errorCount === deferScriptPromise.length);
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
- initHook(true);
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 url script address
2287
+ * @param address script address
1822
2288
  * @param app app
1823
- * @param info script info
1824
- * @param isDynamic dynamically created script
2289
+ * @param scriptInfo script info
1825
2290
  * @param callback callback of module script
1826
2291
  */
1827
- function runScript(url, app, info, isDynamic, callback) {
2292
+ function runScript(address, app, scriptInfo, callback, replaceElement) {
1828
2293
  var _a;
1829
2294
  try {
1830
- const code = bindScope(url, app, info.code, info);
1831
- if (app.inline || info.module) {
1832
- const scriptElement = pureCreateElement('script');
1833
- runCode2InlineScript(url, code, info.module, scriptElement, callback);
1834
- if (isDynamic)
1835
- return scriptElement;
1836
- // TEST IGNORE
1837
- (_a = app.container) === null || _a === void 0 ? void 0 : _a.querySelector('micro-app-body').appendChild(scriptElement);
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
- runCode2Function(code, info);
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 url script address
1852
- * @param info info
1853
- * @param app app
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(url, info, app, originScript) {
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
- // url is unique
1859
- if (app.source.scripts.has(url)) {
1860
- const existInfo = app.source.scripts.get(url);
1861
- !existInfo.module && defer(dispatchScriptOnLoadEvent);
1862
- /**
1863
- * TODO: 这里要改,当script初始化时动态创建远程script时,初次渲染和二次渲染的顺序不一致,会导致错误
1864
- * 1、url不存在缓存,初始化的时候肯定是要异步请求,那么执行顺序就会靠后,至少落后于html自带的script
1865
- * 2、url存在缓存,那么二次渲染的时候这里会同步执行,就会先于html自带的script执行
1866
- * 3、测试一下,初次渲染和二次渲染时,onload的执行时机,是在js执行完成,还是执行之前
1867
- * 4、将上述问题做成注释,方便后续阅读和理解
1868
- */
1869
- return runScript(url, app, existInfo, true, dispatchScriptOnLoadEvent);
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
- replaceElement = document.createComment(`dynamic script with src='${url}' extract by micro-app`);
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
- fetchSource(url, app.name).then((code) => {
1886
- info.code = code;
1887
- app.source.scripts.set(url, info);
1888
- info.isGlobal && globalScripts.set(url, code);
1889
- try {
1890
- code = bindScope(url, app, code, info);
1891
- if (app.inline || info.module) {
1892
- runCode2InlineScript(url, code, info.module, replaceElement, dispatchScriptOnLoadEvent);
1893
- }
1894
- else {
1895
- runCode2Function(code, info);
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 url script address
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(url, code, module, scriptElement, callback) {
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
- const blob = new Blob([code], { type: 'text/javascript' });
1920
- scriptElement.src = URL.createObjectURL(blob);
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
- if (!url.startsWith('inline-')) {
1931
- scriptElement.setAttribute('data-origin-src', url);
1932
- }
2399
+ setConvertScriptAttr(scriptElement, attrs);
1933
2400
  }
1934
2401
  // init & run code2Function
1935
- function runCode2Function(code, info) {
1936
- if (!info.code2Function) {
1937
- info.code2Function = new Function(code);
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
- info.code2Function.call(window);
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 info source script info
2413
+ * @param scriptInfo source script info
1947
2414
  */
1948
- function bindScope(url, app, code, info) {
1949
- if (isPlainObject(microApp.plugins)) {
1950
- code = usePlugins(url, code, app.name, microApp.plugins, info);
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.sandBox && !info.module) {
1953
- globalEnv.rawWindow.__MICRO_APP_PROXY_WINDOW__ = app.sandBox.proxyWindow;
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 url script address
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(url, code, appName, plugins, info) {
2446
+ function usePlugins(address, code, appName, plugins) {
1967
2447
  var _a;
1968
- const newCode = processCode(plugins.global, code, url, info);
1969
- return processCode((_a = plugins.modules) === null || _a === void 0 ? void 0 : _a[appName], newCode, url, info);
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, url, info) {
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, url, config.options, info);
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
- flatChildren(wrapElement, app);
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
- appInstanceMap.forEach(app => {
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__ && appInstanceMap.clear();
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
- setCurrentAppName(null);
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
- typeof proxyWindow.onpopstate === 'function' && proxyWindow.onpopstate(newPopStateEvent);
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
- typeof proxyWindow.onhashchange === 'function' && proxyWindow.onhashchange(newHashChangeEvent);
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. Note the element binding
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(appName, to, from);
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
- * @param appName app name
3216
- * @param path page path
3726
+ * SetDefaultPageOptions {
3727
+ * @param name app name
3728
+ * @param path page path
3729
+ * }
3217
3730
  */
3218
- function setDefaultPage(appName, path) {
3219
- appName = formatAppName(appName);
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
- // 当沙箱执行start, 或者隐藏的keep-alive应用重新渲染时时才根据浏览器url更新location 或者 将参数更新到url上
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, useMemoryRouter = true) {
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, useMemoryRouter);
4193
+ this.initStaticGlobalKeys(this.microAppWindow, appName, url);
3670
4194
  }
3671
- start(baseRoute, useMemoryRouter = true, defaultPage = '') {
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.proxyWindow.__MICRO_APP_NAME__);
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__ = baseRoute;
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
- stop(keepRouteState, clearEventSource) {
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.proxyWindow.__MICRO_APP_NAME__);
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
- this.injectedKeys.forEach((key) => {
3712
- Reflect.deleteProperty(this.microAppWindow, key);
3713
- });
3714
- this.injectedKeys.clear();
3715
- this.escapeKeys.forEach((key) => {
3716
- Reflect.deleteProperty(globalEnv.rawWindow, key);
3717
- });
3718
- this.escapeKeys.clear();
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
- this.recordUmdInjectedValues.set(key, Reflect.get(this.microAppWindow, key));
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.forEach((value, key) => {
3740
- Reflect.set(this.proxyWindow, key, value);
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, useMemoryRouter) {
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
- this.patchRequestApi(microAppWindow, appName, url);
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
- // @ts-ignore
4154
- if (isFunction((_a = microApp.lifeCycles) === null || _a === void 0 ? void 0 : _a[lifecycleName])) {
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, ssrUrl, container, inline, scopecss, useSandbox, useMemoryRouter, baseroute, keepRouteState, hiddenRouter, defaultPage, }) {
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.useMemoryRouter = this.useSandbox && useMemoryRouter;
4194
- // optional during init base on prefetch 👇
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
- this.inline = inline !== null && inline !== void 0 ? inline : false;
4198
- this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : '';
4199
- this.keepRouteState = keepRouteState !== null && keepRouteState !== void 0 ? keepRouteState : false;
4200
- this.hiddenRouter = hiddenRouter !== null && hiddenRouter !== void 0 ? hiddenRouter : false;
4201
- this.defaultPage = defaultPage !== null && defaultPage !== void 0 ? defaultPage : '';
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, this.useMemoryRouter));
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
- extractHtml(this);
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
- if (this.isPrefetch) {
4222
- (_a = this.prefetchResolve) === null || _a === void 0 ? void 0 : _a.call(this);
4223
- this.prefetchResolve = null;
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, hiddenRouter) {
4254
- var _a, _b, _c;
4255
- this.inline = inline !== null && inline !== void 0 ? inline : this.inline;
4256
- this.keepRouteState = keepRouteState !== null && keepRouteState !== void 0 ? keepRouteState : this.keepRouteState;
4257
- this.container = (_a = this.container) !== null && _a !== void 0 ? _a : container;
4258
- this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : this.baseroute;
4259
- this.defaultPage = defaultPage !== null && defaultPage !== void 0 ? defaultPage : this.defaultPage;
4260
- this.hiddenRouter = hiddenRouter !== null && hiddenRouter !== void 0 ? hiddenRouter : this.hiddenRouter;
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
- (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.start(this.baseroute, this.useMemoryRouter, this.defaultPage);
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.source.scripts, this, (isFinished) => {
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
- (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordUmdSnapshot();
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
- (_c = this.sandBox) === null || _c === void 0 ? void 0 : _c.rebuildUmdSnapshot();
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
- (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.stop(this.keepRouteState && !destroy, !this.umdMode || destroy);
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
- // do not use isObject
4475
- return typeof global[this.libraryName] === 'object' ? global[this.libraryName] : {};
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.hasConnected = false;
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
- this.hasConnected = true;
4553
- defer(() => dispatchLifecyclesEvent(this, this.appName, lifeCycles.CREATED));
4554
- this.initialMount();
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.hasConnected = false;
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.hasConnected && this.initialMount();
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 activeAppUrl = this.ssrUrl || this.appUrl;
4621
- // keep-alive don't care about ssrUrl
4622
- // 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
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 === activeAppUrl && (app.isPrefetch ||
4628
- app.getAppState() === appStates.UNMOUNT)) {
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
- * url is different & old app is unmounted or prefetch, create new app to replace old one
4634
- */
4635
- logWarn(`the ${app.isPrefetch ? 'prefetch' : 'unmounted'} app with url: ${existAppUrl} is replaced by a new app`, this.appName);
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
- var _a;
4712
- return app.mount((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this, this.getDisposeResult('inline'), this.getBaseRouteCompatible(), this.getDisposeResult('keep-router-state'), this.getDefaultPageValue(), this.getDisposeResult('hidden-router'));
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
- const instance = new CreateApp({
5338
+ new CreateApp({
4726
5339
  name: this.appName,
4727
5340
  url: this.appUrl,
4728
- ssrUrl: this.ssrUrl,
4729
- container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
5341
+ scopecss: this.isScopecss(),
5342
+ useSandbox: this.isSandbox(),
4730
5343
  inline: this.getDisposeResult('inline'),
4731
- scopecss: !(this.getDisposeResult('disable-scopecss') || this.getDisposeResult('shadowDOM')),
4732
- useSandbox: !this.getDisposeResult('disable-sandbox'),
4733
- useMemoryRouter: !this.getDisposeResult('disable-memory-router'),
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
- // @ts-ignore
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
- var _a, _b, _c;
4842
- return (_c = (_b = (_a = router.getDefaultPage(this.appName)) !== null && _a !== void 0 ? _a : this.getAttribute('default-page')) !== null && _b !== void 0 ? _b : this.getAttribute('defaultPage')) !== null && _c !== void 0 ? _c : '';
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(prefetchApp) {
4900
- return new Promise((resolve) => {
4901
- requestIdleCallback(() => {
4902
- var _a, _b, _c, _d, _e;
4903
- if (isPlainObject(prefetchApp) && navigator.onLine) {
4904
- prefetchApp.name = formatAppName(prefetchApp.name);
4905
- prefetchApp.url = formatAppURL(prefetchApp.url, prefetchApp.name);
4906
- if (prefetchApp.name && prefetchApp.url && !appInstanceMap.has(prefetchApp.name)) {
4907
- const app = new CreateApp({
4908
- name: prefetchApp.name,
4909
- url: prefetchApp.url,
4910
- scopecss: !((_b = (_a = prefetchApp['disable-scopecss']) !== null && _a !== void 0 ? _a : prefetchApp.disableScopecss) !== null && _b !== void 0 ? _b : microApp['disable-scopecss']),
4911
- useSandbox: !((_d = (_c = prefetchApp['disable-sandbox']) !== null && _c !== void 0 ? _c : prefetchApp.disableSandbox) !== null && _d !== void 0 ? _d : microApp['disable-sandbox']),
4912
- useMemoryRouter: !((_e = prefetchApp['disable-memory-router']) !== null && _e !== void 0 ? _e : microApp['disable-memory-router']),
4913
- });
4914
- app.isPrefetch = true;
4915
- app.prefetchResolve = resolve;
4916
- appInstanceMap.set(prefetchApp.name, app);
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', globalScripts);
4936
- fetchGlobalResources(assets.css, 'css', globalLinks);
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, cache) {
5563
+ function fetchGlobalResources(resources, suffix, sourceHandler) {
4942
5564
  if (isArray(resources)) {
4943
- const effectiveResource = resources.filter((path) => isString(path) && path.includes(`.${suffix}`) && !cache.has(path));
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 (!cache.has(path)) {
4949
- cache.set(path, res.data);
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 (options && isPlainObject(options)) {
5075
- this.shadowDOM = options.shadowDOM;
5076
- this.destroy = options.destroy;
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