@micro-zoe/micro-app 1.0.0-alpha.5 → 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.5';
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) => {
@@ -531,7 +581,7 @@ class HTMLLoader {
531
581
  });
532
582
  }
533
583
  formatHTML(htmlUrl, htmlStr, appName) {
534
- return this.processHtml(htmlUrl, htmlStr, appName, microApp.plugins)
584
+ return this.processHtml(htmlUrl, htmlStr, appName, microApp.options.plugins)
535
585
  .replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
536
586
  return match
537
587
  .replace(/<head/i, '<micro-app-head')
@@ -553,7 +603,7 @@ class HTMLLoader {
553
603
  if (mergedPlugins.length > 0) {
554
604
  return mergedPlugins.reduce((preCode, plugin) => {
555
605
  if (isPlainObject(plugin) && isFunction(plugin.processHtml)) {
556
- return plugin.processHtml(preCode, url, plugin.options);
606
+ return plugin.processHtml(preCode, url);
557
607
  }
558
608
  return preCode;
559
609
  }, code);
@@ -591,11 +641,13 @@ class CSSParser {
591
641
  this.scopecssDisableSelectors = []; // disable or enable scopecss for specific selectors
592
642
  this.scopecssDisableNextLine = false; // use block comments /* scopecss-disable-next-line */ to disable scopecss on a specific line
593
643
  // https://developer.mozilla.org/en-US/docs/Web/API/CSSMediaRule
594
- this.mediaRule = this.createMatcherForAtRuleWithChildRule(/^@media *([^{]+)/, 'media');
644
+ this.mediaRule = this.createMatcherForRuleWithChildRule(/^@media *([^{]+)/, '@media');
595
645
  // https://developer.mozilla.org/en-US/docs/Web/API/CSSSupportsRule
596
- this.supportsRule = this.createMatcherForAtRuleWithChildRule(/^@supports *([^{]+)/, 'supports');
597
- this.documentRule = this.createMatcherForAtRuleWithChildRule(/^@([-\w]+)?document *([^{]+)/, 'document');
598
- 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')
599
651
  // https://developer.mozilla.org/en-US/docs/Web/API/CSSImportRule
600
652
  this.importRule = this.createMatcherForNoneBraceAtRule('import');
601
653
  // Removed in most browsers
@@ -716,6 +768,13 @@ class CSSParser {
716
768
  this.hostRule() ||
717
769
  this.fontFaceRule();
718
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
+ // }
719
778
  // https://developer.mozilla.org/en-US/docs/Web/API/CSSKeyframesRule
720
779
  keyframesRule() {
721
780
  if (!this.commonMatch(/^@([-\w]+)?keyframes\s*/))
@@ -769,17 +828,17 @@ class CSSParser {
769
828
  return false;
770
829
  return this.commonHandlerForAtRuleWithSelfRule('font-face');
771
830
  }
772
- // common matcher for @media, @supports, @document, @host
773
- createMatcherForAtRuleWithChildRule(reg, name) {
831
+ // common matcher for @media, @supports, @document, @host, :global
832
+ createMatcherForRuleWithChildRule(reg, name) {
774
833
  return () => {
775
834
  if (!this.commonMatch(reg))
776
835
  return false;
777
836
  if (!this.matchOpenBrace())
778
- return parseError(`@${name} missing '{'`, this.linkPath);
837
+ return parseError(`${name} missing '{'`, this.linkPath);
779
838
  this.matchComments();
780
839
  this.matchRules();
781
840
  if (!this.matchCloseBrace())
782
- return parseError(`@${name} missing '}'`, this.linkPath);
841
+ return parseError(`${name} missing '}'`, this.linkPath);
783
842
  this.matchLeadingSpaces();
784
843
  return true;
785
844
  };
@@ -906,20 +965,20 @@ let parser;
906
965
  * @param styleElement target style element
907
966
  * @param appName app name
908
967
  */
909
- function scopedCSS(styleElement, app) {
968
+ function scopedCSS(styleElement, app, linkPath) {
910
969
  if (app.scopecss) {
911
- const prefix = `${microApp.tagName}[name=${app.name}]`;
970
+ const prefix = createPrefix(app.name);
912
971
  if (!parser)
913
972
  parser = new CSSParser();
914
973
  if (styleElement.textContent) {
915
- commonAction(styleElement, app.name, prefix, app.url, styleElement.__MICRO_APP_LINK_PATH__);
974
+ commonAction(styleElement, app.name, prefix, app.url, linkPath);
916
975
  }
917
976
  else {
918
977
  const observer = new MutationObserver(function () {
919
978
  observer.disconnect();
920
979
  // styled-component will be ignore
921
980
  if (styleElement.textContent && !styleElement.hasAttribute('data-styled')) {
922
- commonAction(styleElement, app.name, prefix, app.url, styleElement.__MICRO_APP_LINK_PATH__);
981
+ commonAction(styleElement, app.name, prefix, app.url, linkPath);
923
982
  }
924
983
  });
925
984
  observer.observe(styleElement, { childList: true });
@@ -927,6 +986,10 @@ function scopedCSS(styleElement, app) {
927
986
  }
928
987
  return styleElement;
929
988
  }
989
+ function createPrefix(appName, reg = false) {
990
+ const regCharacter = reg ? '\\' : '';
991
+ return `${microApp.tagName}${regCharacter}[name=${appName}${regCharacter}]`;
992
+ }
930
993
 
931
994
  function eventHandler(event, element) {
932
995
  Object.defineProperties(event, {
@@ -968,8 +1031,76 @@ function dispatchOnErrorEvent(element) {
968
1031
  }
969
1032
  }
970
1033
 
971
- // Global links, reuse across apps
972
- 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
+ }
973
1104
  /**
974
1105
  * Extract link elements
975
1106
  * @param link link element
@@ -984,22 +1115,29 @@ function extractLinkFromHtml(link, parent, app, isDynamic = false) {
984
1115
  let replaceComment = null;
985
1116
  if (rel === 'stylesheet' && href) {
986
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);
987
1134
  if (!isDynamic) {
1135
+ app.source.links.add(href);
988
1136
  replaceComment = document.createComment(`link element with href=${href} move to micro-app-head as style element`);
989
- app.source.links.set(href, {
990
- code: '',
991
- placeholder: replaceComment,
992
- isGlobal: link.hasAttribute('global'),
993
- });
1137
+ linkInfo.appSpace[app.name].placeholder = replaceComment;
994
1138
  }
995
1139
  else {
996
- return {
997
- url: href,
998
- info: {
999
- code: '',
1000
- isGlobal: link.hasAttribute('global'),
1001
- }
1002
- };
1140
+ return { address: href, linkInfo };
1003
1141
  }
1004
1142
  }
1005
1143
  else if (rel && ['prefetch', 'preload', 'prerender', 'icon', 'apple-touch-icon'].includes(rel)) {
@@ -1028,79 +1166,138 @@ function extractLinkFromHtml(link, parent, app, isDynamic = false) {
1028
1166
  * @param app app
1029
1167
  * @param microAppHead micro-app-head
1030
1168
  */
1031
- function fetchLinksFromHtml(wrapElement, app, microAppHead) {
1032
- const linkEntries = Array.from(app.source.links.entries());
1033
- const fetchLinkPromise = linkEntries.map(([url]) => {
1034
- 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);
1035
1174
  });
1175
+ const fiberLinkTasks = app.isPrefetch || app.fiber ? [] : null;
1036
1176
  promiseStream(fetchLinkPromise, (res) => {
1037
- fetchLinkSuccess(linkEntries[res.index][0], linkEntries[res.index][1], res.data, microAppHead, app);
1177
+ injectFiberTask(fiberLinkTasks, () => fetchLinkSuccess(styleList[res.index], res.data, microAppHead, app));
1038
1178
  }, (err) => {
1039
1179
  logError(err, app.name);
1040
1180
  }, () => {
1041
- 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
+ }
1042
1195
  });
1043
1196
  }
1044
1197
  /**
1045
- * fetch link succeeded, replace placeholder with style tag
1046
- * @param url resource address
1047
- * @param info resource link info
1048
- * @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
1049
1207
  * @param microAppHead micro-app-head
1050
- * @param app app
1208
+ * @param app app instance
1051
1209
  */
1052
- function fetchLinkSuccess(url, info, data, microAppHead, app) {
1053
- if (info.isGlobal && !globalLinks.has(url)) {
1054
- 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;
1055
1237
  }
1056
- const styleLink = pureCreateElement('style');
1057
- styleLink.textContent = data;
1058
- styleLink.__MICRO_APP_LINK_PATH__ = url;
1059
- styleLink.setAttribute('data-origin-href', url);
1060
- if (info.placeholder.parentNode) {
1061
- 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
+ }
1062
1269
  }
1063
1270
  else {
1064
- microAppHead.appendChild(scopedCSS(styleLink, app));
1271
+ convertStyle.textContent = linkInfo.code;
1065
1272
  }
1066
- info.placeholder = null;
1067
- info.code = data;
1273
+ setConvertStyleAttr(convertStyle, attrs);
1068
1274
  }
1069
1275
  /**
1070
- * get css from dynamic link
1071
- * @param url link address
1072
- * @param info info
1276
+ * Handle css of dynamic link
1277
+ * @param address link address
1073
1278
  * @param app app
1279
+ * @param linkInfo linkInfo
1074
1280
  * @param originLink origin link element
1075
- * @param replaceStyle style element which replaced origin link
1076
1281
  */
1077
- function formatDynamicLink(url, info, app, originLink, replaceStyle) {
1078
- if (app.source.links.has(url)) {
1079
- replaceStyle.textContent = app.source.links.get(url).code;
1080
- scopedCSS(replaceStyle, app);
1081
- defer(() => dispatchOnLoadEvent(originLink));
1082
- 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);
1083
1290
  }
1084
- if (globalLinks.has(url)) {
1085
- const code = globalLinks.get(url);
1086
- info.code = code;
1087
- app.source.links.set(url, info);
1088
- replaceStyle.textContent = code;
1089
- scopedCSS(replaceStyle, app);
1090
- defer(() => dispatchOnLoadEvent(originLink));
1091
- 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
+ });
1092
1299
  }
1093
- fetchSource(url, app.name).then((data) => {
1094
- info.code = data;
1095
- app.source.links.set(url, info);
1096
- info.isGlobal && globalLinks.set(url, data);
1097
- replaceStyle.textContent = data;
1098
- scopedCSS(replaceStyle, app);
1099
- dispatchOnLoadEvent(originLink);
1100
- }).catch((err) => {
1101
- logError(err, app.name);
1102
- dispatchOnErrorEvent(originLink);
1103
- });
1300
+ return convertStyle;
1104
1301
  }
1105
1302
 
1106
1303
  class Adapter {
@@ -1176,10 +1373,13 @@ function throttleDeferForParentNode(proxyDocument) {
1176
1373
  }
1177
1374
  }
1178
1375
  function setRootParentNode(root, value) {
1179
- Object.defineProperty(root, 'parentNode', {
1180
- value,
1181
- configurable: true,
1182
- });
1376
+ const descriptor = Object.getOwnPropertyDescriptor(root, 'parentNode');
1377
+ if (!descriptor || descriptor.configurable) {
1378
+ Object.defineProperty(root, 'parentNode', {
1379
+ value,
1380
+ configurable: true,
1381
+ });
1382
+ }
1183
1383
  }
1184
1384
 
1185
1385
  // Record element and map element
@@ -1208,14 +1408,16 @@ function handleNewNode(parent, child, app) {
1208
1408
  dynamicElementInMicroAppMap.set(child, linkReplaceComment);
1209
1409
  return linkReplaceComment;
1210
1410
  }
1211
- else if (child.hasAttribute('ignore') || checkIgnoreUrl(child.getAttribute('href'), app.name)) {
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))) {
1212
1416
  return child;
1213
1417
  }
1214
- const { url, info, replaceComment } = extractLinkFromHtml(child, parent, app, true);
1215
- if (url && info) {
1216
- const replaceStyle = pureCreateElement('style');
1217
- replaceStyle.__MICRO_APP_LINK_PATH__ = url;
1218
- 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);
1219
1421
  dynamicElementInMicroAppMap.set(child, replaceStyle);
1220
1422
  return replaceStyle;
1221
1423
  }
@@ -1226,18 +1428,17 @@ function handleNewNode(parent, child, app) {
1226
1428
  return child;
1227
1429
  }
1228
1430
  else if (child instanceof HTMLScriptElement) {
1229
- const { replaceComment, url, info } = extractScriptElement(child, parent, app, true) || {};
1230
- if (url && info) {
1231
- if (!info.isExternal) { // inline script
1232
- const replaceElement = runScript(url, app, info, true);
1233
- dynamicElementInMicroAppMap.set(child, replaceElement);
1234
- return replaceElement;
1235
- }
1236
- else { // remote script
1237
- const replaceElement = runDynamicRemoteScript(url, info, app, child);
1238
- dynamicElementInMicroAppMap.set(child, replaceElement);
1239
- return replaceElement;
1240
- }
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;
1241
1442
  }
1242
1443
  else if (replaceComment) {
1243
1444
  dynamicElementInMicroAppMap.set(child, replaceComment);
@@ -1705,8 +1906,80 @@ function initGlobalEnv() {
1705
1906
  }
1706
1907
  }
1707
1908
 
1708
- // Global scripts, reuse across apps
1709
- 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
+ }
1710
1983
  /**
1711
1984
  * Extract script elements
1712
1985
  * @param script script element
@@ -1717,14 +1990,15 @@ const globalScripts = new Map();
1717
1990
  function extractScriptElement(script, parent, app, isDynamic = false) {
1718
1991
  let replaceComment = null;
1719
1992
  let src = script.getAttribute('src');
1720
- if (src) {
1993
+ if (src)
1721
1994
  src = CompletionPath(src, app.url);
1722
- }
1723
1995
  if (script.hasAttribute('exclude') || checkExcludeUrl(src, app.name)) {
1724
1996
  replaceComment = document.createComment('script element with exclude attribute removed by micro-app');
1725
1997
  }
1726
- else if ((script.type && !['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module', 'systemjs-module', 'systemjs-importmap'].includes(script.type)) ||
1727
- script.hasAttribute('ignore') || checkIgnoreUrl(src, app.name)) {
1998
+ else if ((script.type &&
1999
+ !scriptTypes.includes(script.type)) ||
2000
+ script.hasAttribute('ignore') ||
2001
+ checkIgnoreUrl(src, app.name)) {
1728
2002
  return null;
1729
2003
  }
1730
2004
  else if ((globalEnv.supportModuleScript && script.noModule) ||
@@ -1732,39 +2006,74 @@ function extractScriptElement(script, parent, app, isDynamic = false) {
1732
2006
  replaceComment = document.createComment(`${script.noModule ? 'noModule' : 'module'} script ignored by micro-app`);
1733
2007
  }
1734
2008
  else if (src) { // remote script
1735
- const info = {
1736
- code: '',
1737
- isExternal: true,
1738
- isDynamic: isDynamic,
2009
+ let scriptInfo = sourceCenter.script.getInfo(src);
2010
+ const appSpaceData = {
1739
2011
  async: script.hasAttribute('async'),
1740
2012
  defer: script.defer || script.type === 'module',
1741
2013
  module: script.type === 'module',
1742
- isGlobal: script.hasAttribute('global'),
2014
+ inline: script.hasAttribute('inline'),
2015
+ pure: script.hasAttribute('pure'),
2016
+ attrs: getAttributes(script),
1743
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);
1744
2037
  if (!isDynamic) {
1745
- app.source.scripts.set(src, info);
2038
+ app.source.scripts.add(src);
1746
2039
  replaceComment = document.createComment(`script with src='${src}' extract by micro-app`);
1747
2040
  }
1748
2041
  else {
1749
- return { url: src, info };
2042
+ return { address: src, scriptInfo };
1750
2043
  }
1751
2044
  }
1752
2045
  else if (script.textContent) { // inline script
1753
- const nonceStr = createNonceSrc();
1754
- 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 = {
1755
2056
  code: script.textContent,
1756
2057
  isExternal: false,
1757
- isDynamic: isDynamic,
1758
- async: false,
1759
- defer: script.type === 'module',
1760
- 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
+ }
1761
2068
  };
1762
2069
  if (!isDynamic) {
1763
- app.source.scripts.set(nonceStr, info);
2070
+ app.source.scripts.add(nonceStr);
2071
+ sourceCenter.script.setInfo(nonceStr, scriptInfo);
1764
2072
  replaceComment = document.createComment('inline script extract by micro-app');
1765
2073
  }
1766
2074
  else {
1767
- return { url: nonceStr, info };
2075
+ // Because each dynamic script is unique, it is not put into sourceCenter
2076
+ return { address: nonceStr, scriptInfo };
1768
2077
  }
1769
2078
  }
1770
2079
  else if (!isDynamic) {
@@ -1787,38 +2096,38 @@ function extractScriptElement(script, parent, app, isDynamic = false) {
1787
2096
  */
1788
2097
  function getAssetsPlugins(appName) {
1789
2098
  var _a, _b, _c;
1790
- const globalPlugins = ((_a = microApp.plugins) === null || _a === void 0 ? void 0 : _a.global) || [];
1791
- const modulePlugins = ((_c = (_b = microApp.plugins) === null || _b === void 0 ? void 0 : _b.modules) === null || _c === void 0 ? void 0 : _c[appName]) || [];
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]) || [];
1792
2101
  return [...globalPlugins, ...modulePlugins];
1793
2102
  }
1794
2103
  /**
1795
- * whether the url needs to be excluded
1796
- * @param url css or js link
2104
+ * whether the address needs to be excluded
2105
+ * @param address css or js link
1797
2106
  * @param plugins microApp plugins
1798
2107
  */
1799
- function checkExcludeUrl(url, appName) {
1800
- if (!url)
2108
+ function checkExcludeUrl(address, appName) {
2109
+ if (!address)
1801
2110
  return false;
1802
2111
  const plugins = getAssetsPlugins(appName) || [];
1803
2112
  return plugins.some(plugin => {
1804
2113
  if (!plugin.excludeChecker)
1805
2114
  return false;
1806
- return plugin.excludeChecker(url);
2115
+ return plugin.excludeChecker(address);
1807
2116
  });
1808
2117
  }
1809
2118
  /**
1810
- * whether the url needs to be ignore
1811
- * @param url css or js link
2119
+ * whether the address needs to be ignore
2120
+ * @param address css or js link
1812
2121
  * @param plugins microApp plugins
1813
2122
  */
1814
- function checkIgnoreUrl(url, appName) {
1815
- if (!url)
2123
+ function checkIgnoreUrl(address, appName) {
2124
+ if (!address)
1816
2125
  return false;
1817
2126
  const plugins = getAssetsPlugins(appName) || [];
1818
2127
  return plugins.some(plugin => {
1819
2128
  if (!plugin.ignoreChecker)
1820
2129
  return false;
1821
- return plugin.ignoreChecker(url);
2130
+ return plugin.ignoreChecker(address);
1822
2131
  });
1823
2132
  }
1824
2133
  /**
@@ -1827,28 +2136,31 @@ function checkIgnoreUrl(url, appName) {
1827
2136
  * @param app app
1828
2137
  */
1829
2138
  function fetchScriptsFromHtml(wrapElement, app) {
1830
- const scriptEntries = Array.from(app.source.scripts.entries());
2139
+ const scriptList = Array.from(app.source.scripts);
1831
2140
  const fetchScriptPromise = [];
1832
2141
  const fetchScriptPromiseInfo = [];
1833
- for (const [url, info] of scriptEntries) {
1834
- if (info.isExternal) {
1835
- const globalScriptText = globalScripts.get(url);
1836
- if (globalScriptText) {
1837
- info.code = globalScriptText;
1838
- }
1839
- else if ((!info.defer && !info.async) || app.isPrefetch) {
1840
- fetchScriptPromise.push(fetchSource(url, app.name));
1841
- fetchScriptPromiseInfo.push([url, info]);
1842
- }
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]);
1843
2148
  }
1844
2149
  }
2150
+ const fiberScriptTasks = app.isPrefetch || app.fiber ? [] : null;
1845
2151
  if (fetchScriptPromise.length) {
1846
2152
  promiseStream(fetchScriptPromise, (res) => {
1847
- 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));
1848
2154
  }, (err) => {
1849
2155
  logError(err, app.name);
1850
2156
  }, () => {
1851
- 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
+ }
1852
2164
  });
1853
2165
  }
1854
2166
  else {
@@ -1857,169 +2169,224 @@ function fetchScriptsFromHtml(wrapElement, app) {
1857
2169
  }
1858
2170
  /**
1859
2171
  * fetch js succeeded, record the code value
1860
- * @param url script address
1861
- * @param info resource script info
2172
+ * @param address script address
2173
+ * @param scriptInfo resource script info
1862
2174
  * @param data code
1863
2175
  */
1864
- function fetchScriptSuccess(url, info, data) {
1865
- if (info.isGlobal && !globalScripts.has(url)) {
1866
- 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
+ }
1867
2207
  }
1868
- info.code = data;
1869
2208
  }
1870
2209
  /**
1871
2210
  * Execute js in the mount lifecycle
1872
- * @param scriptList script list
1873
2211
  * @param app app
1874
2212
  * @param initHook callback for umd mode
1875
2213
  */
1876
- function execScripts(scriptList, app, initHook) {
1877
- 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);
1878
2217
  const deferScriptPromise = [];
1879
2218
  const deferScriptInfo = [];
1880
- for (const [url, info] of scriptListEntries) {
1881
- if (!info.isDynamic) {
1882
- // Notice the second render
1883
- if (info.defer || info.async) {
1884
- if (info.isExternal && !info.code) {
1885
- deferScriptPromise.push(fetchSource(url, app.name));
1886
- }
1887
- else {
1888
- deferScriptPromise.push(info.code);
1889
- }
1890
- deferScriptInfo.push([url, info]);
1891
- 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));
1892
2226
  }
1893
2227
  else {
1894
- runScript(url, app, info, false);
1895
- initHook(false);
2228
+ deferScriptPromise.push(scriptInfo.code);
1896
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
+ });
1897
2238
  }
1898
2239
  }
1899
2240
  if (deferScriptPromise.length) {
1900
2241
  promiseStream(deferScriptPromise, (res) => {
1901
- const info = deferScriptInfo[res.index][1];
1902
- info.code = info.code || res.data;
2242
+ const scriptInfo = deferScriptInfo[res.index][1];
2243
+ scriptInfo.code = scriptInfo.code || res.data;
1903
2244
  }, (err) => {
1904
2245
  initHook.errorCount = initHook.errorCount ? ++initHook.errorCount : 1;
1905
2246
  logError(err, app.name);
1906
2247
  }, () => {
1907
- deferScriptInfo.forEach(([url, info]) => {
1908
- if (info.code) {
1909
- runScript(url, app, info, false, initHook);
1910
- !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
+ });
1911
2254
  }
1912
2255
  });
1913
- initHook(isUndefined(initHook.moduleCount) ||
1914
- 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
+ }
1915
2273
  });
1916
2274
  }
1917
2275
  else {
1918
- initHook(true);
2276
+ if (fiberScriptTasks) {
2277
+ fiberScriptTasks.push(() => Promise.resolve(initHook(true)));
2278
+ serialExecFiberTasks(fiberScriptTasks);
2279
+ }
2280
+ else {
2281
+ initHook(true);
2282
+ }
1919
2283
  }
1920
2284
  }
1921
2285
  /**
1922
2286
  * run code
1923
- * @param url script address
2287
+ * @param address script address
1924
2288
  * @param app app
1925
- * @param info script info
1926
- * @param isDynamic dynamically created script
2289
+ * @param scriptInfo script info
1927
2290
  * @param callback callback of module script
1928
2291
  */
1929
- function runScript(url, app, info, isDynamic, callback) {
2292
+ function runScript(address, app, scriptInfo, callback, replaceElement) {
1930
2293
  var _a;
1931
2294
  try {
1932
- const code = bindScope(url, app, info.code, info);
1933
- if (app.inline || info.module) {
1934
- const scriptElement = pureCreateElement('script');
1935
- runCode2InlineScript(url, code, info.module, scriptElement, callback);
1936
- if (isDynamic)
1937
- return scriptElement;
1938
- // TEST IGNORE
1939
- (_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
+ }
1940
2316
  }
1941
2317
  else {
1942
- runCode2Function(code, info);
1943
- if (isDynamic)
1944
- return document.createComment('dynamic script extract by micro-app');
2318
+ runParsedFunction(app, scriptInfo);
1945
2319
  }
1946
2320
  }
1947
2321
  catch (e) {
1948
- console.error(`[micro-app from runScript] app ${app.name}: `, e);
2322
+ console.error(`[micro-app from ${replaceElement ? 'runDynamicScript' : 'runScript'}] app ${app.name}: `, e, address);
1949
2323
  }
1950
2324
  }
1951
2325
  /**
1952
2326
  * Get dynamically created remote script
1953
- * @param url script address
1954
- * @param info info
1955
- * @param app app
2327
+ * @param address script address
2328
+ * @param app app instance
2329
+ * @param scriptInfo scriptInfo
1956
2330
  * @param originScript origin script element
1957
2331
  */
1958
- 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');
1959
2334
  const dispatchScriptOnLoadEvent = () => dispatchOnLoadEvent(originScript);
1960
- // url is unique
1961
- if (app.source.scripts.has(url)) {
1962
- const existInfo = app.source.scripts.get(url);
1963
- !existInfo.module && defer(dispatchScriptOnLoadEvent);
1964
- /**
1965
- * TODO: 这里要改,当script初始化时动态创建远程script时,初次渲染和二次渲染的顺序不一致,会导致错误
1966
- * 1、url不存在缓存,初始化的时候肯定是要异步请求,那么执行顺序就会靠后,至少落后于html自带的script
1967
- * 2、url存在缓存,那么二次渲染的时候这里会同步执行,就会先于html自带的script执行
1968
- * 3、测试一下,初次渲染和二次渲染时,onload的执行时机,是在js执行完成,还是执行之前
1969
- * 4、将上述问题做成注释,方便后续阅读和理解
1970
- */
1971
- return runScript(url, app, existInfo, true, dispatchScriptOnLoadEvent);
1972
- }
1973
- if (globalScripts.has(url)) {
1974
- const code = globalScripts.get(url);
1975
- info.code = code;
1976
- app.source.scripts.set(url, info);
1977
- !info.module && defer(dispatchScriptOnLoadEvent);
1978
- return runScript(url, app, info, true, dispatchScriptOnLoadEvent);
1979
- }
1980
- let replaceElement;
1981
- if (app.inline || info.module) {
1982
- 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);
1983
2348
  }
1984
2349
  else {
1985
- 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
+ });
1986
2357
  }
1987
- fetchSource(url, app.name).then((code) => {
1988
- info.code = code;
1989
- app.source.scripts.set(url, info);
1990
- info.isGlobal && globalScripts.set(url, code);
1991
- try {
1992
- code = bindScope(url, app, code, info);
1993
- if (app.inline || info.module) {
1994
- runCode2InlineScript(url, code, info.module, replaceElement, dispatchScriptOnLoadEvent);
1995
- }
1996
- else {
1997
- runCode2Function(code, info);
1998
- }
1999
- }
2000
- catch (e) {
2001
- console.error(`[micro-app from runDynamicScript] app ${app.name}: `, e, url);
2002
- }
2003
- !info.module && dispatchScriptOnLoadEvent();
2004
- }).catch((err) => {
2005
- logError(err, app.name);
2006
- dispatchOnErrorEvent(originScript);
2007
- });
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);
2008
2369
  return replaceElement;
2009
2370
  }
2010
2371
  /**
2011
2372
  * common handle for inline script
2012
- * @param url script address
2373
+ * @param address script address
2013
2374
  * @param code bound code
2014
2375
  * @param module type='module' of script
2015
2376
  * @param scriptElement target script element
2377
+ * @param attrs attributes of script element
2016
2378
  * @param callback callback of module script
2017
2379
  */
2018
- function runCode2InlineScript(url, code, module, scriptElement, callback) {
2380
+ function runCode2InlineScript(address, code, module, scriptElement, attrs, callback) {
2019
2381
  if (module) {
2020
2382
  // module script is async, transform it to a blob for subsequent operations
2021
- const blob = new Blob([code], { type: 'text/javascript' });
2022
- 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
+ }
2023
2390
  scriptElement.setAttribute('type', 'module');
2024
2391
  if (callback) {
2025
2392
  callback.moduleCount && callback.moduleCount--;
@@ -2029,55 +2396,65 @@ function runCode2InlineScript(url, code, module, scriptElement, callback) {
2029
2396
  else {
2030
2397
  scriptElement.textContent = code;
2031
2398
  }
2032
- if (!url.startsWith('inline-')) {
2033
- scriptElement.setAttribute('data-origin-src', url);
2034
- }
2399
+ setConvertScriptAttr(scriptElement, attrs);
2035
2400
  }
2036
2401
  // init & run code2Function
2037
- function runCode2Function(code, info) {
2038
- if (!info.code2Function) {
2039
- 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);
2040
2406
  }
2041
- info.code2Function.call(window);
2407
+ appSpaceData.parsedFunction.call(window);
2042
2408
  }
2043
2409
  /**
2044
2410
  * bind js scope
2045
- * @param url script address
2046
2411
  * @param app app
2047
2412
  * @param code code
2048
- * @param info source script info
2413
+ * @param scriptInfo source script info
2049
2414
  */
2050
- function bindScope(url, app, code, info) {
2051
- // TODO: 增加缓存机制
2052
- if (isPlainObject(microApp.plugins)) {
2053
- 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);
2054
2419
  }
2055
- if (app.sandBox && !info.module) {
2056
- globalEnv.rawWindow.__MICRO_APP_PROXY_WINDOW__ = app.sandBox.proxyWindow;
2057
- 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__);`;
2058
2422
  }
2059
2423
  return code;
2060
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
+ }
2061
2439
  /**
2062
2440
  * Call the plugin to process the file
2063
- * @param url script address
2441
+ * @param address script address
2064
2442
  * @param code code
2065
2443
  * @param appName app name
2066
2444
  * @param plugins plugin list
2067
- * @param info source script info
2068
2445
  */
2069
- function usePlugins(url, code, appName, plugins, info) {
2446
+ function usePlugins(address, code, appName, plugins) {
2070
2447
  var _a;
2071
- const newCode = processCode(plugins.global, code, url, info);
2072
- 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);
2073
2450
  }
2074
- function processCode(configs, code, url, info) {
2451
+ function processCode(configs, code, address) {
2075
2452
  if (!isArray(configs)) {
2076
2453
  return code;
2077
2454
  }
2078
2455
  return configs.reduce((preCode, config) => {
2079
2456
  if (isPlainObject(config) && isFunction(config.loader)) {
2080
- return config.loader(preCode, url, config.options, info);
2457
+ return config.loader(preCode, address);
2081
2458
  }
2082
2459
  return preCode;
2083
2460
  }, code);
@@ -2098,10 +2475,10 @@ function getWrapElement(str) {
2098
2475
  * @param app app
2099
2476
  * @param microAppHead micro-app-head element
2100
2477
  */
2101
- function flatChildren(parent, app, microAppHead) {
2478
+ function flatChildren(parent, app, microAppHead, fiberStyleTasks) {
2102
2479
  const children = Array.from(parent.children);
2103
2480
  children.length && children.forEach((child) => {
2104
- flatChildren(child, app);
2481
+ flatChildren(child, app, microAppHead, fiberStyleTasks);
2105
2482
  });
2106
2483
  for (const dom of children) {
2107
2484
  if (dom instanceof HTMLLinkElement) {
@@ -2120,7 +2497,7 @@ function flatChildren(parent, app, microAppHead) {
2120
2497
  parent.replaceChild(document.createComment('style element with exclude attribute ignored by micro-app'), dom);
2121
2498
  }
2122
2499
  else if (app.scopecss && !dom.hasAttribute('ignore')) {
2123
- scopedCSS(dom, app);
2500
+ injectFiberTask(fiberStyleTasks, () => scopedCSS(dom, app));
2124
2501
  }
2125
2502
  }
2126
2503
  else if (dom instanceof HTMLScriptElement) {
@@ -2148,9 +2525,17 @@ function extractSourceDom(htmlStr, app) {
2148
2525
  app.onerror(new Error(msg));
2149
2526
  return logError(msg, app.name);
2150
2527
  }
2151
- 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);
2152
2534
  if (app.source.links.size) {
2153
- fetchLinksFromHtml(wrapElement, app, microAppHead);
2535
+ fetchLinksFromHtml(wrapElement, app, microAppHead, fiberStyleResult);
2536
+ }
2537
+ else if (fiberStyleResult) {
2538
+ fiberStyleResult.then(() => app.onLoad(wrapElement));
2154
2539
  }
2155
2540
  else {
2156
2541
  app.onLoad(wrapElement);
@@ -2989,9 +3374,19 @@ function addHistoryListener(appName) {
2989
3374
  function dispatchPopStateEventToMicroApp(appName, proxyWindow) {
2990
3375
  // create PopStateEvent named popstate-appName with sub app state
2991
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
+ // })
2992
3387
  globalEnv.rawWindow.dispatchEvent(newPopStateEvent);
2993
3388
  // call function window.onpopstate if it exists
2994
- typeof proxyWindow.onpopstate === 'function' && proxyWindow.onpopstate(newPopStateEvent);
3389
+ isFunction(proxyWindow.onpopstate) && proxyWindow.onpopstate(newPopStateEvent);
2995
3390
  }
2996
3391
  /**
2997
3392
  * dispatch formatted hashchange event to microApp
@@ -3006,7 +3401,7 @@ function dispatchHashChangeEventToMicroApp(appName, proxyWindow, oldHref) {
3006
3401
  });
3007
3402
  globalEnv.rawWindow.dispatchEvent(newHashChangeEvent);
3008
3403
  // call function window.onhashchange if it exists
3009
- typeof proxyWindow.onhashchange === 'function' && proxyWindow.onhashchange(newHashChangeEvent);
3404
+ isFunction(proxyWindow.onhashchange) && proxyWindow.onhashchange(newHashChangeEvent);
3010
3405
  }
3011
3406
  /**
3012
3407
  * dispatch native PopStateEvent, simulate location behavior
@@ -3163,7 +3558,6 @@ function reWriteHistoryMethod(method) {
3163
3558
  * 1. Exec after apply pushState/replaceState
3164
3559
  * 2. Unable to catch when base app navigate with location
3165
3560
  * 3. When in nest app, rawPushState/rawReplaceState has been modified by parent
3166
- * 4.
3167
3561
  */
3168
3562
  getActiveApps(true).forEach(appName => {
3169
3563
  const app = appInstanceMap.get(appName);
@@ -3273,7 +3667,7 @@ function createRouterApi() {
3273
3667
  removeDomScope();
3274
3668
  for (const guard of guards) {
3275
3669
  if (isFunction(guard)) {
3276
- guard(appName, to, from);
3670
+ guard(to, from, appName);
3277
3671
  }
3278
3672
  else if (isPlainObject(guard) && isFunction(guard[appName])) {
3279
3673
  guard[appName](to, from);
@@ -3614,7 +4008,12 @@ function createMicroRouter(appName, url) {
3614
4008
  microHistory: createMicroHistory(appName, microLocation),
3615
4009
  };
3616
4010
  }
3617
- // 当沙箱执行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
+ */
3618
4017
  function initRouteStateWithURL(appName, microLocation, defaultPage) {
3619
4018
  const microPath = getMicroPathFromURL(appName);
3620
4019
  if (microPath) {
@@ -3766,7 +4165,7 @@ function useMicroEventSource() {
3766
4165
  const { createMicroEventSource, clearMicroEventSource } = useMicroEventSource();
3767
4166
  const globalPropertyList = ['window', 'self', 'globalThis'];
3768
4167
  class SandBox {
3769
- constructor(appName, url, useMemoryRouter = true) {
4168
+ constructor(appName, url) {
3770
4169
  /**
3771
4170
  * Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
3772
4171
  * Fix https://github.com/micro-zoe/micro-app/issues/234
@@ -3791,26 +4190,36 @@ class SandBox {
3791
4190
  // Rewrite global event listener & timeout
3792
4191
  assign(this, effect(appName, this.microAppWindow));
3793
4192
  // inject global properties
3794
- this.initStaticGlobalKeys(this.microAppWindow, appName, url, useMemoryRouter);
4193
+ this.initStaticGlobalKeys(this.microAppWindow, appName, url);
3795
4194
  }
3796
- // TODO: 重构
3797
- start(umdMode = false, baseRoute = '', useMemoryRouter = true, defaultPage = '', disablePatchRequest = false) {
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, }) {
3798
4204
  if (!this.active) {
3799
4205
  this.active = true;
3800
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
+ }
3801
4210
  this.initRouteState(defaultPage);
3802
4211
  // unique listener of popstate event for sub app
3803
- this.removeHistoryListener = addHistoryListener(this.proxyWindow.__MICRO_APP_NAME__);
4212
+ this.removeHistoryListener = addHistoryListener(this.microAppWindow.__MICRO_APP_NAME__);
3804
4213
  }
3805
4214
  else {
3806
- 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;
3807
4216
  }
3808
4217
  /**
3809
4218
  * 1. prevent the key deleted during sandBox.stop after rewrite
3810
4219
  * 2. umd mode will not delete any keys during sandBox.stop
3811
4220
  */
3812
4221
  if (!umdMode) {
3813
- this.initGlobalKeysWhenStart(this.microAppWindow, this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.__MICRO_APP_URL__, disablePatchRequest);
4222
+ this.initGlobalKeysWhenStart(this.microAppWindow, this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__, disablePatchRequest);
3814
4223
  }
3815
4224
  if (++SandBox.activeCount === 1) {
3816
4225
  effectDocumentEvent();
@@ -3821,7 +4230,13 @@ class SandBox {
3821
4230
  fixBabelPolyfill6();
3822
4231
  }
3823
4232
  }
3824
- stop(umdMode, 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, }) {
3825
4240
  if (this.active) {
3826
4241
  this.releaseEffect();
3827
4242
  this.microAppWindow.microApp.clearDataListener();
@@ -3832,7 +4247,7 @@ class SandBox {
3832
4247
  this.removeHistoryListener();
3833
4248
  }
3834
4249
  if (clearEventSource) {
3835
- clearMicroEventSource(this.proxyWindow.__MICRO_APP_NAME__);
4250
+ clearMicroEventSource(this.microAppWindow.__MICRO_APP_NAME__);
3836
4251
  }
3837
4252
  /**
3838
4253
  * NOTE:
@@ -3883,9 +4298,9 @@ class SandBox {
3883
4298
  getSpecialProperties(appName) {
3884
4299
  var _a;
3885
4300
  this.scopeProperties = this.scopeProperties.concat(this.adapter.staticScopeProperties);
3886
- if (isPlainObject(microApp.plugins)) {
3887
- this.commonActionForSpecialProperties(microApp.plugins.global);
3888
- 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]);
3889
4304
  }
3890
4305
  }
3891
4306
  // common action for global plugins and module plugins
@@ -4003,7 +4418,7 @@ class SandBox {
4003
4418
  * @param url app url
4004
4419
  * @param useMemoryRouter whether use memory router
4005
4420
  */
4006
- initStaticGlobalKeys(microAppWindow, appName, url, useMemoryRouter) {
4421
+ initStaticGlobalKeys(microAppWindow, appName, url) {
4007
4422
  microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
4008
4423
  microAppWindow.__MICRO_APP_NAME__ = appName;
4009
4424
  microAppWindow.__MICRO_APP_URL__ = url;
@@ -4018,8 +4433,6 @@ class SandBox {
4018
4433
  });
4019
4434
  this.setProxyDocument(microAppWindow, appName);
4020
4435
  this.setMappingPropertiesWithRawDescriptor(microAppWindow);
4021
- if (useMemoryRouter)
4022
- this.setMicroAppRouter(microAppWindow, appName, url);
4023
4436
  }
4024
4437
  setProxyDocument(microAppWindow, appName) {
4025
4438
  const { proxyDocument, MicroDocument } = this.createProxyDocument(appName);
@@ -4197,17 +4610,19 @@ class SandBox {
4197
4610
  return element;
4198
4611
  };
4199
4612
  const proxyDocument = new Proxy(rawDocument, {
4200
- get(target, key) {
4613
+ get: (target, key) => {
4201
4614
  throttleDeferForSetAppName(appName);
4202
4615
  throttleDeferForParentNode(proxyDocument);
4203
4616
  if (key === 'createElement')
4204
4617
  return createElement;
4205
4618
  if (key === Symbol.toStringTag)
4206
4619
  return 'ProxyDocument';
4620
+ if (key === 'defaultView')
4621
+ return this.proxyWindow;
4207
4622
  const rawValue = Reflect.get(target, key);
4208
4623
  return isFunction(rawValue) ? bindFunctionToRawObject(rawDocument, rawValue, 'DOCUMENT') : rawValue;
4209
4624
  },
4210
- set(target, key, value) {
4625
+ set: (target, key, value) => {
4211
4626
  // Fix TypeError: Illegal invocation when set document.title
4212
4627
  Reflect.set(target, key, value);
4213
4628
  /**
@@ -4299,10 +4714,8 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
4299
4714
  });
4300
4715
  formatEventInfo(event, element);
4301
4716
  // global hooks
4302
- // @ts-ignore
4303
- if (isFunction((_a = microApp.lifeCycles) === null || _a === void 0 ? void 0 : _a[lifecycleName])) {
4304
- // @ts-ignore
4305
- 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);
4306
4719
  }
4307
4720
  element.dispatchEvent(event);
4308
4721
  }
@@ -4322,7 +4735,7 @@ function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
4322
4735
  // micro app instances
4323
4736
  const appInstanceMap = new Map();
4324
4737
  class CreateApp {
4325
- constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, useMemoryRouter, baseroute, keepRouteState, hiddenRouter, defaultPage, disablePatchRequest, }) {
4738
+ constructor({ name, url, container, scopecss, useSandbox, inline, esmodule, ssrUrl, isPrefetch, }) {
4326
4739
  this.state = appStates.CREATED;
4327
4740
  this.keepAliveState = null;
4328
4741
  this.keepAliveContainer = null;
@@ -4331,30 +4744,26 @@ class CreateApp {
4331
4744
  this.umdHookUnmount = null;
4332
4745
  this.libraryName = null;
4333
4746
  this.umdMode = false;
4334
- this.isPrefetch = false;
4335
- this.prefetchResolve = null;
4336
- this.container = null;
4337
4747
  this.sandBox = null;
4748
+ this.keepRouteState = false;
4749
+ this.fiber = false;
4750
+ this.useMemoryRouter = true;
4338
4751
  this.name = name;
4339
4752
  this.url = url;
4340
4753
  this.useSandbox = useSandbox;
4341
4754
  this.scopecss = this.useSandbox && scopecss;
4342
- this.useMemoryRouter = this.useSandbox && useMemoryRouter;
4343
- // 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 👇
4344
4758
  this.container = container !== null && container !== void 0 ? container : null;
4345
4759
  this.ssrUrl = ssrUrl !== null && ssrUrl !== void 0 ? ssrUrl : '';
4346
- this.inline = inline !== null && inline !== void 0 ? inline : false;
4347
- this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : '';
4348
- this.keepRouteState = keepRouteState !== null && keepRouteState !== void 0 ? keepRouteState : false;
4349
- this.hiddenRouter = hiddenRouter !== null && hiddenRouter !== void 0 ? hiddenRouter : false;
4350
- this.defaultPage = defaultPage !== null && defaultPage !== void 0 ? defaultPage : '';
4351
- this.disablePatchRequest = disablePatchRequest !== null && disablePatchRequest !== void 0 ? disablePatchRequest : false;
4352
- this.source = {
4353
- links: new Map(),
4354
- scripts: new Map(),
4355
- };
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() };
4356
4765
  this.loadSourceCode();
4357
- this.useSandbox && (this.sandBox = new SandBox(name, url, this.useMemoryRouter));
4766
+ this.useSandbox && (this.sandBox = new SandBox(name, url));
4358
4767
  }
4359
4768
  // Load resources
4360
4769
  loadSourceCode() {
@@ -4365,16 +4774,12 @@ class CreateApp {
4365
4774
  * When resource is loaded, mount app if it is not prefetch or unmount
4366
4775
  */
4367
4776
  onLoad(html) {
4368
- var _a;
4369
4777
  if (++this.loadSourceLevel === 2) {
4370
4778
  this.source.html = html;
4371
- if (this.isPrefetch) {
4372
- (_a = this.prefetchResolve) === null || _a === void 0 ? void 0 : _a.call(this);
4373
- this.prefetchResolve = null;
4374
- }
4375
- else if (appStates.UNMOUNT !== this.state) {
4376
- this.state = appStates.LOADED;
4377
- this.mount();
4779
+ this.state = appStates.LOADED;
4780
+ if (!this.isPrefetch && appStates.UNMOUNT !== this.state) {
4781
+ // @ts-ignore
4782
+ getRootContainer(this.container).mount(this);
4378
4783
  }
4379
4784
  }
4380
4785
  }
@@ -4384,10 +4789,6 @@ class CreateApp {
4384
4789
  */
4385
4790
  onLoadError(e) {
4386
4791
  this.loadSourceLevel = -1;
4387
- if (this.prefetchResolve) {
4388
- this.prefetchResolve();
4389
- this.prefetchResolve = null;
4390
- }
4391
4792
  if (appStates.UNMOUNT !== this.state) {
4392
4793
  this.onerror(e);
4393
4794
  this.state = appStates.LOAD_FAILED;
@@ -4401,15 +4802,16 @@ class CreateApp {
4401
4802
  * @param keepRouteState keep route state when unmount, default is false
4402
4803
  * @param disablePatchRequest prevent rewrite request method of child app
4403
4804
  */
4404
- mount(container, inline, baseroute, keepRouteState, defaultPage, hiddenRouter, disablePatchRequest) {
4405
- var _a, _b, _c;
4406
- this.inline = inline !== null && inline !== void 0 ? inline : this.inline;
4407
- this.keepRouteState = keepRouteState !== null && keepRouteState !== void 0 ? keepRouteState : this.keepRouteState;
4408
- this.container = (_a = this.container) !== null && _a !== void 0 ? _a : container;
4409
- this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : this.baseroute;
4410
- this.defaultPage = defaultPage !== null && defaultPage !== void 0 ? defaultPage : this.defaultPage;
4411
- this.hiddenRouter = hiddenRouter !== null && hiddenRouter !== void 0 ? hiddenRouter : this.hiddenRouter;
4412
- this.disablePatchRequest = disablePatchRequest !== null && disablePatchRequest !== void 0 ? disablePatchRequest : this.disablePatchRequest;
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
4413
4815
  if (this.loadSourceLevel !== 2) {
4414
4816
  this.state = appStates.LOADING;
4415
4817
  return;
@@ -4417,12 +4819,18 @@ class CreateApp {
4417
4819
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
4418
4820
  this.state = appStates.MOUNTING;
4419
4821
  cloneContainer(this.source.html, this.container, !this.umdMode);
4420
- (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.start(this.umdMode, this.baseroute, this.useMemoryRouter, this.defaultPage, this.disablePatchRequest);
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
+ });
4421
4829
  let umdHookMountResult; // result of mount function
4422
4830
  if (!this.umdMode) {
4423
4831
  let hasDispatchMountedEvent = false;
4424
4832
  // if all js are executed, param isFinished will be true
4425
- execScripts(this.source.scripts, this, (isFinished) => {
4833
+ execScripts(this, (isFinished) => {
4426
4834
  if (!this.umdMode) {
4427
4835
  const { mount, unmount } = this.getUmdLibraryHooks();
4428
4836
  /**
@@ -4452,7 +4860,7 @@ class CreateApp {
4452
4860
  });
4453
4861
  }
4454
4862
  else {
4455
- (_c = this.sandBox) === null || _c === void 0 ? void 0 : _c.rebuildUmdSnapshot();
4863
+ (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.rebuildUmdSnapshot();
4456
4864
  try {
4457
4865
  umdHookMountResult = this.umdHookMount();
4458
4866
  }
@@ -4554,7 +4962,11 @@ class CreateApp {
4554
4962
  * 1. if destroy is true, clear route state
4555
4963
  * 2. umd mode and keep-alive will not clear EventSource
4556
4964
  */
4557
- (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.stop(this.umdMode, 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
+ });
4558
4970
  if (!getActiveApps().length) {
4559
4971
  releasePatchSetAttribute();
4560
4972
  }
@@ -4653,7 +5065,8 @@ function defineElement(tagName) {
4653
5065
  super();
4654
5066
  this.isWaiting = false;
4655
5067
  this.cacheData = null;
4656
- this.hasConnected = false;
5068
+ this.connectedCount = 0;
5069
+ this.connectStateMap = new Map();
4657
5070
  this.appName = ''; // app name
4658
5071
  this.appUrl = ''; // app url
4659
5072
  this.ssrUrl = ''; // html path in ssr mode
@@ -4663,6 +5076,8 @@ function defineElement(tagName) {
4663
5076
  */
4664
5077
  this.handleAttributeUpdate = () => {
4665
5078
  this.isWaiting = false;
5079
+ if (!this.connectStateMap.get(this.connectedCount))
5080
+ return;
4666
5081
  const formatAttrName = formatAppName(this.getAttribute('name'));
4667
5082
  const formatAttrUrl = formatAppURL(this.getAttribute('url'), this.appName);
4668
5083
  if (this.legalAttribute('name', formatAttrName) && this.legalAttribute('url', formatAttrUrl)) {
@@ -4714,12 +5129,21 @@ function defineElement(tagName) {
4714
5129
  // baseRoute: route prefix, default is ''
4715
5130
  // keep-alive: open keep-alive mode
4716
5131
  connectedCallback() {
4717
- this.hasConnected = true;
4718
- defer(() => dispatchLifecyclesEvent(this, this.appName, lifeCycles.CREATED));
4719
- 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
+ });
4720
5144
  }
4721
5145
  disconnectedCallback() {
4722
- this.hasConnected = false;
5146
+ this.connectStateMap.set(this.connectedCount, false);
4723
5147
  const app = appInstanceMap.get(this.appName);
4724
5148
  if (app &&
4725
5149
  app.getAppState() !== appStates.UNMOUNT &&
@@ -4767,7 +5191,7 @@ function defineElement(tagName) {
4767
5191
  }
4768
5192
  // handle for connectedCallback run before attributeChangedCallback
4769
5193
  handleInitialNameAndUrl() {
4770
- this.hasConnected && this.initialMount();
5194
+ this.connectStateMap.get(this.connectedCount) && this.initialMount();
4771
5195
  }
4772
5196
  /**
4773
5197
  * first mount of this app
@@ -4782,26 +5206,35 @@ function defineElement(tagName) {
4782
5206
  if (appInstanceMap.has(this.appName)) {
4783
5207
  const app = appInstanceMap.get(this.appName);
4784
5208
  const existAppUrl = app.ssrUrl || app.url;
4785
- const activeAppUrl = this.ssrUrl || this.appUrl;
4786
- // keep-alive don't care about ssrUrl
4787
- // 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
+ */
4788
5216
  if (app.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN &&
4789
5217
  app.url === this.appUrl) {
4790
5218
  this.handleShowKeepAliveApp(app);
4791
5219
  }
4792
- else if (existAppUrl === activeAppUrl && (app.isPrefetch ||
4793
- 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())))) {
4794
5223
  this.handleAppMount(app);
4795
5224
  }
4796
5225
  else if (app.isPrefetch || app.getAppState() === appStates.UNMOUNT) {
4797
- /**
4798
- * url is different & old app is unmounted or prefetch, create new app to replace old one
4799
- */
4800
- 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
+ }
4801
5234
  this.handleCreateApp();
4802
5235
  }
4803
5236
  else {
4804
- 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);
4805
5238
  }
4806
5239
  }
4807
5240
  else {
@@ -4872,9 +5305,24 @@ function defineElement(tagName) {
4872
5305
  */
4873
5306
  handleAppMount(app) {
4874
5307
  app.isPrefetch = false;
4875
- defer(() => {
4876
- var _a;
4877
- 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'), this.getDisposeResult('disable-patch-request'));
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'),
4878
5326
  });
4879
5327
  }
4880
5328
  // create app instance
@@ -4887,22 +5335,16 @@ function defineElement(tagName) {
4887
5335
  if (appInstanceMap.has(this.appName)) {
4888
5336
  appInstanceMap.get(this.appName).actionsForCompletelyDestroy();
4889
5337
  }
4890
- const instance = new CreateApp({
5338
+ new CreateApp({
4891
5339
  name: this.appName,
4892
5340
  url: this.appUrl,
4893
- ssrUrl: this.ssrUrl,
4894
- container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
5341
+ scopecss: this.isScopecss(),
5342
+ useSandbox: this.isSandbox(),
4895
5343
  inline: this.getDisposeResult('inline'),
4896
- scopecss: !(this.getDisposeResult('disable-scopecss') || this.getDisposeResult('shadowDOM')),
4897
- useSandbox: !this.getDisposeResult('disable-sandbox'),
4898
- useMemoryRouter: !this.getDisposeResult('disable-memory-router'),
4899
- baseroute: this.getBaseRouteCompatible(),
4900
- keepRouteState: this.getDisposeResult('keep-router-state'),
4901
- defaultPage: this.getDefaultPageValue(),
4902
- hiddenRouter: this.getDisposeResult('hidden-router'),
4903
- disablePatchRequest: this.getDisposeResult('disable-patch-request'),
5344
+ esmodule: this.getDisposeResult('esmodule'),
5345
+ container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
5346
+ ssrUrl: this.ssrUrl,
4904
5347
  });
4905
- appInstanceMap.set(this.appName, instance);
4906
5348
  }
4907
5349
  /**
4908
5350
  * unmount app
@@ -4934,8 +5376,7 @@ function defineElement(tagName) {
4934
5376
  * @param name Configuration item name
4935
5377
  */
4936
5378
  getDisposeResult(name) {
4937
- // @ts-ignore
4938
- return (this.compatibleSpecialProperties(name) || !!microApp[name]) && this.compatibleDisableSpecialProperties(name);
5379
+ return (this.compatibleSpecialProperties(name) || !!microApp.options[name]) && this.compatibleDisableSpecialProperties(name);
4939
5380
  }
4940
5381
  // compatible of disableScopecss & disableSandbox
4941
5382
  compatibleSpecialProperties(name) {
@@ -4957,6 +5398,12 @@ function defineElement(tagName) {
4957
5398
  }
4958
5399
  return this.getAttribute(name) !== 'false';
4959
5400
  }
5401
+ isScopecss() {
5402
+ return !(this.getDisposeResult('disable-scopecss') || this.getDisposeResult('shadowDOM'));
5403
+ }
5404
+ isSandbox() {
5405
+ return !this.getDisposeResult('disable-sandbox');
5406
+ }
4960
5407
  /**
4961
5408
  * 2021-09-08
4962
5409
  * get baseRoute
@@ -5004,8 +5451,10 @@ function defineElement(tagName) {
5004
5451
  * get config of default page
5005
5452
  */
5006
5453
  getDefaultPageValue() {
5007
- var _a, _b, _c;
5008
- 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
+ '');
5009
5458
  }
5010
5459
  /**
5011
5460
  * Data from the base application
@@ -5062,33 +5511,40 @@ function preFetch(apps) {
5062
5511
  });
5063
5512
  }
5064
5513
  // sequential preload app
5065
- function preFetchInSerial(prefetchApp) {
5066
- return new Promise((resolve) => {
5067
- requestIdleCallback(() => {
5068
- var _a, _b, _c, _d, _e;
5069
- if (isPlainObject(prefetchApp) && navigator.onLine) {
5070
- prefetchApp.name = formatAppName(prefetchApp.name);
5071
- prefetchApp.url = formatAppURL(prefetchApp.url, prefetchApp.name);
5072
- if (prefetchApp.name && prefetchApp.url && !appInstanceMap.has(prefetchApp.name)) {
5073
- const app = new CreateApp({
5074
- name: prefetchApp.name,
5075
- url: prefetchApp.url,
5076
- scopecss: !((_b = (_a = prefetchApp['disable-scopecss']) !== null && _a !== void 0 ? _a : prefetchApp.disableScopecss) !== null && _b !== void 0 ? _b : microApp['disable-scopecss']),
5077
- useSandbox: !((_d = (_c = prefetchApp['disable-sandbox']) !== null && _c !== void 0 ? _c : prefetchApp.disableSandbox) !== null && _d !== void 0 ? _d : microApp['disable-sandbox']),
5078
- useMemoryRouter: !((_e = prefetchApp['disable-memory-router']) !== null && _e !== void 0 ? _e : microApp['disable-memory-router']),
5079
- });
5080
- app.isPrefetch = true;
5081
- app.prefetchResolve = resolve;
5082
- appInstanceMap.set(prefetchApp.name, app);
5083
- }
5084
- 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) => {
5085
5533
  resolve();
5086
- }
5534
+ oldOnload.call(app, html);
5535
+ };
5536
+ app.onLoadError = (e) => {
5537
+ resolve();
5538
+ oldOnLoadError.call(app, e);
5539
+ };
5087
5540
  }
5088
5541
  else {
5089
5542
  resolve();
5090
5543
  }
5091
- });
5544
+ }
5545
+ else {
5546
+ resolve();
5547
+ }
5092
5548
  });
5093
5549
  }
5094
5550
  /**
@@ -5098,21 +5554,35 @@ function preFetchInSerial(prefetchApp) {
5098
5554
  function getGlobalAssets(assets) {
5099
5555
  if (isPlainObject(assets)) {
5100
5556
  requestIdleCallback(() => {
5101
- fetchGlobalResources(assets.js, 'js', globalScripts);
5102
- fetchGlobalResources(assets.css, 'css', globalLinks);
5557
+ fetchGlobalResources(assets.js, 'js', sourceCenter.script);
5558
+ fetchGlobalResources(assets.css, 'css', sourceCenter.link);
5103
5559
  });
5104
5560
  }
5105
5561
  }
5106
5562
  // TODO: requestIdleCallback for every file
5107
- function fetchGlobalResources(resources, suffix, cache) {
5563
+ function fetchGlobalResources(resources, suffix, sourceHandler) {
5108
5564
  if (isArray(resources)) {
5109
- 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));
5110
5566
  const fetchResourcePromise = effectiveResource.map((path) => fetchSource(path));
5111
5567
  // fetch resource with stream
5112
5568
  promiseStream(fetchResourcePromise, (res) => {
5113
5569
  const path = effectiveResource[res.index];
5114
- if (!cache.has(path)) {
5115
- 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
+ }
5116
5586
  }
5117
5587
  }, (err) => {
5118
5588
  logError(err);
@@ -5217,6 +5687,7 @@ class MicroApp extends EventCenterForBaseApp {
5217
5687
  constructor() {
5218
5688
  super(...arguments);
5219
5689
  this.tagName = 'micro-app';
5690
+ this.options = {};
5220
5691
  this.preFetch = preFetch;
5221
5692
  this.router = router;
5222
5693
  }
@@ -5237,26 +5708,10 @@ class MicroApp extends EventCenterForBaseApp {
5237
5708
  return logWarn(`element ${this.tagName} is already defined`);
5238
5709
  }
5239
5710
  initGlobalEnv();
5240
- if (options && isPlainObject(options)) {
5241
- this.shadowDOM = options.shadowDOM;
5242
- this.destroy = options.destroy;
5243
- /**
5244
- * compatible with versions below 0.4.2 of destroy
5245
- * do not merge with the previous line
5246
- */
5247
- // @ts-ignore
5248
- this.destory = options.destory;
5249
- this.inline = options.inline;
5250
- this['disable-scopecss'] = (_a = options['disable-scopecss']) !== null && _a !== void 0 ? _a : options.disableScopecss;
5251
- this['disable-sandbox'] = (_b = options['disable-sandbox']) !== null && _b !== void 0 ? _b : options.disableSandbox;
5252
- this['disable-memory-router'] = options['disable-memory-router'];
5253
- this['disable-patch-request'] = options['disable-patch-request'];
5254
- this['keep-router-state'] = options['keep-router-state'];
5255
- this['hidden-router'] = options['hidden-router'];
5256
- this.esmodule = options.esmodule;
5257
- this.ssr = options.ssr;
5258
- isFunction(options.fetch) && (this.fetch = options.fetch);
5259
- 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;
5260
5715
  // load app assets when browser is idle
5261
5716
  options.preFetchApps && preFetch(options.preFetchApps);
5262
5717
  // load global assets when browser is idle
@@ -5272,7 +5727,6 @@ class MicroApp extends EventCenterForBaseApp {
5272
5727
  }
5273
5728
  }
5274
5729
  }
5275
- this.plugins = options.plugins;
5276
5730
  }
5277
5731
  }
5278
5732
  // define customElement after init