@micro-zoe/micro-app 1.0.0-alpha.5 → 1.0.0-alpha.8

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.8';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -33,6 +33,10 @@ function isString(target) {
33
33
  function isBoolean(target) {
34
34
  return typeof target === 'boolean';
35
35
  }
36
+ // is Number
37
+ function isNumber(target) {
38
+ return typeof target === 'number';
39
+ }
36
40
  // is function
37
41
  function isFunction(target) {
38
42
  return typeof target === 'function';
@@ -72,6 +76,12 @@ function isShadowRoot(target) {
72
76
  function isURL(target) {
73
77
  return target instanceof URL;
74
78
  }
79
+ function isElement(target) {
80
+ return target instanceof Element;
81
+ }
82
+ function isNode(target) {
83
+ return target instanceof Node;
84
+ }
75
85
  // is ProxyDocument
76
86
  function isProxyDocument(target) {
77
87
  return toString.call(target) === '[object ProxyDocument]';
@@ -258,8 +268,19 @@ const requestIdleCallback = globalThis.requestIdleCallback ||
258
268
  return Math.max(0, 50 - (Date.now() - lastTime));
259
269
  },
260
270
  });
261
- }, 50);
271
+ }, 1);
262
272
  };
273
+ /**
274
+ * Wrap requestIdleCallback with promise
275
+ * Exec callback when browser idle
276
+ */
277
+ function promiseRequestIdle(callback) {
278
+ return new Promise((resolve) => {
279
+ requestIdleCallback(() => {
280
+ callback(resolve);
281
+ });
282
+ });
283
+ }
263
284
  /**
264
285
  * Record the currently running app.name
265
286
  */
@@ -433,6 +454,59 @@ function useMapRecord() {
433
454
  }
434
455
  };
435
456
  }
457
+ function getAttributes(element) {
458
+ const attr = element.attributes;
459
+ const attrMap = new Map();
460
+ for (let i = 0; i < attr.length; i++) {
461
+ attrMap.set(attr[i].name, attr[i].value);
462
+ }
463
+ return attrMap;
464
+ }
465
+ /**
466
+ * if fiberTasks exist, wrap callback with promiseRequestIdle
467
+ * if not, execute callback
468
+ * @param fiberTasks fiber task list
469
+ * @param callback action callback
470
+ */
471
+ function injectFiberTask(fiberTasks, callback) {
472
+ if (fiberTasks) {
473
+ fiberTasks.push(() => promiseRequestIdle((resolve) => {
474
+ callback();
475
+ resolve();
476
+ }));
477
+ }
478
+ else {
479
+ callback();
480
+ }
481
+ }
482
+ /**
483
+ * serial exec fiber task of link, style, script
484
+ * @param tasks task array or null
485
+ */
486
+ function serialExecFiberTasks(tasks) {
487
+ return (tasks === null || tasks === void 0 ? void 0 : tasks.reduce((pre, next) => pre.then(next), Promise.resolve())) || null;
488
+ }
489
+ /**
490
+ * inline script start with inline-xxx
491
+ * @param address source address
492
+ */
493
+ function isInlineScript(address) {
494
+ return address.startsWith('inline-');
495
+ }
496
+ /**
497
+ * call function with try catch
498
+ * @param fn target function
499
+ * @param appName app.name
500
+ * @param args arguments
501
+ */
502
+ function callFnWithTryCatch(fn, appName, msgSuffix, ...args) {
503
+ try {
504
+ isFunction(fn) && fn(...args);
505
+ }
506
+ catch (e) {
507
+ logError(`an error occurred in app ${appName} ${msgSuffix} \n`, null, e);
508
+ }
509
+ }
436
510
 
437
511
  var ObservedAttrName;
438
512
  (function (ObservedAttrName) {
@@ -463,12 +537,39 @@ var lifeCycles;
463
537
  lifeCycles["AFTERSHOW"] = "aftershow";
464
538
  lifeCycles["AFTERHIDDEN"] = "afterhidden";
465
539
  })(lifeCycles || (lifeCycles = {}));
540
+ // global event of child app
541
+ var microGlobalEvent;
542
+ (function (microGlobalEvent) {
543
+ microGlobalEvent["ONMOUNT"] = "onmount";
544
+ microGlobalEvent["ONUNMOUNT"] = "onunmount";
545
+ })(microGlobalEvent || (microGlobalEvent = {}));
466
546
  // keep-alive status
467
547
  var keepAliveStates;
468
548
  (function (keepAliveStates) {
469
549
  keepAliveStates["KEEP_ALIVE_SHOW"] = "keep_alive_show";
470
550
  keepAliveStates["KEEP_ALIVE_HIDDEN"] = "keep_alive_hidden";
471
551
  })(keepAliveStates || (keepAliveStates = {}));
552
+ // micro-app config
553
+ var MicroAppConfig;
554
+ (function (MicroAppConfig) {
555
+ MicroAppConfig["DESTROY"] = "destroy";
556
+ MicroAppConfig["DESTORY"] = "destory";
557
+ MicroAppConfig["INLINE"] = "inline";
558
+ MicroAppConfig["DISABLESCOPECSS"] = "disableScopecss";
559
+ MicroAppConfig["DISABLESANDBOX"] = "disableSandbox";
560
+ MicroAppConfig["DISABLE_SCOPECSS"] = "disable-scopecss";
561
+ MicroAppConfig["DISABLE_SANDBOX"] = "disable-sandbox";
562
+ MicroAppConfig["DISABLE_MEMORY_ROUTER"] = "disable-memory-router";
563
+ MicroAppConfig["DISABLE_PATCH_REQUEST"] = "disable-patch-request";
564
+ MicroAppConfig["KEEP_ROUTER_STATE"] = "keep-router-state";
565
+ MicroAppConfig["HIDDEN_ROUTER"] = "hidden-router";
566
+ MicroAppConfig["KEEP_ALIVE"] = "keep-alive";
567
+ MicroAppConfig["CLEAR_DATA"] = "clear-data";
568
+ MicroAppConfig["ESMODULE"] = "esmodule";
569
+ MicroAppConfig["SSR"] = "ssr";
570
+ MicroAppConfig["FIBER"] = "fiber";
571
+ })(MicroAppConfig || (MicroAppConfig = {}));
572
+ const PREFETCH_LEVEL = [1, 2, 3];
472
573
  /**
473
574
  * global key must be static key, they can not rewrite
474
575
  * e.g.
@@ -493,8 +594,8 @@ function fetchSource(url, appName = null, options = {}) {
493
594
  * baseApp: <script crossorigin src="https://sgm-static.jd.com/sgm-2.8.0.js" name="SGMH5" sid="6f88a6e4ba4b4ae5acef2ec22c075085" appKey="jdb-adminb2b-pc"></script>
494
595
  */
495
596
  removeDomScope();
496
- if (isFunction(microApp.fetch)) {
497
- return microApp.fetch(url, options, appName);
597
+ if (isFunction(microApp.options.fetch)) {
598
+ return microApp.options.fetch(url, options, appName);
498
599
  }
499
600
  // Don’t use globalEnv.rawWindow.fetch, will cause sgm-2.8.0.js throw error in nest app
500
601
  return window.fetch(url, options).then((res) => {
@@ -531,7 +632,7 @@ class HTMLLoader {
531
632
  });
532
633
  }
533
634
  formatHTML(htmlUrl, htmlStr, appName) {
534
- return this.processHtml(htmlUrl, htmlStr, appName, microApp.plugins)
635
+ return this.processHtml(htmlUrl, htmlStr, appName, microApp.options.plugins)
535
636
  .replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
536
637
  return match
537
638
  .replace(/<head/i, '<micro-app-head')
@@ -553,7 +654,7 @@ class HTMLLoader {
553
654
  if (mergedPlugins.length > 0) {
554
655
  return mergedPlugins.reduce((preCode, plugin) => {
555
656
  if (isPlainObject(plugin) && isFunction(plugin.processHtml)) {
556
- return plugin.processHtml(preCode, url, plugin.options);
657
+ return plugin.processHtml(preCode, url);
557
658
  }
558
659
  return preCode;
559
660
  }, code);
@@ -591,11 +692,13 @@ class CSSParser {
591
692
  this.scopecssDisableSelectors = []; // disable or enable scopecss for specific selectors
592
693
  this.scopecssDisableNextLine = false; // use block comments /* scopecss-disable-next-line */ to disable scopecss on a specific line
593
694
  // https://developer.mozilla.org/en-US/docs/Web/API/CSSMediaRule
594
- this.mediaRule = this.createMatcherForAtRuleWithChildRule(/^@media *([^{]+)/, 'media');
695
+ this.mediaRule = this.createMatcherForRuleWithChildRule(/^@media *([^{]+)/, '@media');
595
696
  // 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');
697
+ this.supportsRule = this.createMatcherForRuleWithChildRule(/^@supports *([^{]+)/, '@supports');
698
+ this.documentRule = this.createMatcherForRuleWithChildRule(/^@([-\w]+)?document *([^{]+)/, '@document');
699
+ this.hostRule = this.createMatcherForRuleWithChildRule(/^@host\s*/, '@host');
700
+ // :global is CSS Modules rule, it will be converted to normal syntax
701
+ // private globalRule = this.createMatcherForRuleWithChildRule(/^:global([^{]*)/, ':global')
599
702
  // https://developer.mozilla.org/en-US/docs/Web/API/CSSImportRule
600
703
  this.importRule = this.createMatcherForNoneBraceAtRule('import');
601
704
  // Removed in most browsers
@@ -716,6 +819,13 @@ class CSSParser {
716
819
  this.hostRule() ||
717
820
  this.fontFaceRule();
718
821
  }
822
+ // :global is CSS Modules rule, it will be converted to normal syntax
823
+ // private matchGlobalRule (): boolean | void {
824
+ // if (this.cssText[0] !== ':') return false
825
+ // // reset scopecssDisableNextLine
826
+ // this.scopecssDisableNextLine = false
827
+ // return this.globalRule()
828
+ // }
719
829
  // https://developer.mozilla.org/en-US/docs/Web/API/CSSKeyframesRule
720
830
  keyframesRule() {
721
831
  if (!this.commonMatch(/^@([-\w]+)?keyframes\s*/))
@@ -769,17 +879,17 @@ class CSSParser {
769
879
  return false;
770
880
  return this.commonHandlerForAtRuleWithSelfRule('font-face');
771
881
  }
772
- // common matcher for @media, @supports, @document, @host
773
- createMatcherForAtRuleWithChildRule(reg, name) {
882
+ // common matcher for @media, @supports, @document, @host, :global
883
+ createMatcherForRuleWithChildRule(reg, name) {
774
884
  return () => {
775
885
  if (!this.commonMatch(reg))
776
886
  return false;
777
887
  if (!this.matchOpenBrace())
778
- return parseError(`@${name} missing '{'`, this.linkPath);
888
+ return parseError(`${name} missing '{'`, this.linkPath);
779
889
  this.matchComments();
780
890
  this.matchRules();
781
891
  if (!this.matchCloseBrace())
782
- return parseError(`@${name} missing '}'`, this.linkPath);
892
+ return parseError(`${name} missing '}'`, this.linkPath);
783
893
  this.matchLeadingSpaces();
784
894
  return true;
785
895
  };
@@ -906,20 +1016,20 @@ let parser;
906
1016
  * @param styleElement target style element
907
1017
  * @param appName app name
908
1018
  */
909
- function scopedCSS(styleElement, app) {
1019
+ function scopedCSS(styleElement, app, linkPath) {
910
1020
  if (app.scopecss) {
911
- const prefix = `${microApp.tagName}[name=${app.name}]`;
1021
+ const prefix = createPrefix(app.name);
912
1022
  if (!parser)
913
1023
  parser = new CSSParser();
914
1024
  if (styleElement.textContent) {
915
- commonAction(styleElement, app.name, prefix, app.url, styleElement.__MICRO_APP_LINK_PATH__);
1025
+ commonAction(styleElement, app.name, prefix, app.url, linkPath);
916
1026
  }
917
1027
  else {
918
1028
  const observer = new MutationObserver(function () {
919
1029
  observer.disconnect();
920
1030
  // styled-component will be ignore
921
1031
  if (styleElement.textContent && !styleElement.hasAttribute('data-styled')) {
922
- commonAction(styleElement, app.name, prefix, app.url, styleElement.__MICRO_APP_LINK_PATH__);
1032
+ commonAction(styleElement, app.name, prefix, app.url, linkPath);
923
1033
  }
924
1034
  });
925
1035
  observer.observe(styleElement, { childList: true });
@@ -927,6 +1037,10 @@ function scopedCSS(styleElement, app) {
927
1037
  }
928
1038
  return styleElement;
929
1039
  }
1040
+ function createPrefix(appName, reg = false) {
1041
+ const regCharacter = reg ? '\\' : '';
1042
+ return `${microApp.tagName}${regCharacter}[name=${appName}${regCharacter}]`;
1043
+ }
930
1044
 
931
1045
  function eventHandler(event, element) {
932
1046
  Object.defineProperties(event, {
@@ -968,8 +1082,76 @@ function dispatchOnErrorEvent(element) {
968
1082
  }
969
1083
  }
970
1084
 
971
- // Global links, reuse across apps
972
- const globalLinks = new Map();
1085
+ /**
1086
+ * SourceCenter is a resource management center
1087
+ * All html, js, css will be recorded and processed here
1088
+ * NOTE:
1089
+ * 1. All resources are global and shared between apps
1090
+ * 2. Pay attention to the case of html with parameters
1091
+ * 3. The resource is first processed by the plugin
1092
+ */
1093
+ function createSourceCenter() {
1094
+ const linkList = new Map();
1095
+ const scriptList = new Map();
1096
+ // setInterval(() => {
1097
+ // console.log(linkList, scriptList)
1098
+ // }, 10000);
1099
+ function createSourceHandler(targetList) {
1100
+ return {
1101
+ setInfo(address, info) {
1102
+ targetList.set(address, info);
1103
+ },
1104
+ getInfo(address) {
1105
+ var _a;
1106
+ return (_a = targetList.get(address)) !== null && _a !== void 0 ? _a : null;
1107
+ },
1108
+ hasInfo(address) {
1109
+ return targetList.has(address);
1110
+ },
1111
+ deleteInfo(address) {
1112
+ return targetList.delete(address);
1113
+ }
1114
+ };
1115
+ }
1116
+ return {
1117
+ link: createSourceHandler(linkList),
1118
+ script: Object.assign(Object.assign({}, createSourceHandler(scriptList)), { deleteInlineInfo(addressList) {
1119
+ addressList.forEach((address) => {
1120
+ if (isInlineScript(address)) {
1121
+ scriptList.delete(address);
1122
+ }
1123
+ });
1124
+ } }),
1125
+ };
1126
+ }
1127
+ var sourceCenter = createSourceCenter();
1128
+
1129
+ /**
1130
+ *
1131
+ * @param appName app.name
1132
+ * @param linkInfo linkInfo of current address
1133
+ */
1134
+ function getExistParseCode(appName, prefix, linkInfo) {
1135
+ const appSpace = linkInfo.appSpace;
1136
+ for (const item in appSpace) {
1137
+ if (item !== appName) {
1138
+ const appSpaceData = appSpace[item];
1139
+ if (appSpaceData.parsedCode) {
1140
+ return appSpaceData.parsedCode.replaceAll(new RegExp(createPrefix(item, true), 'g'), prefix);
1141
+ }
1142
+ }
1143
+ }
1144
+ }
1145
+ // transfer the attributes on the link to convertStyle
1146
+ function setConvertStyleAttr(convertStyle, attrs) {
1147
+ attrs.forEach((value, key) => {
1148
+ if (key === 'rel')
1149
+ return;
1150
+ if (key === 'href')
1151
+ key = 'data-origin-href';
1152
+ convertStyle.setAttribute(key, value);
1153
+ });
1154
+ }
973
1155
  /**
974
1156
  * Extract link elements
975
1157
  * @param link link element
@@ -984,22 +1166,29 @@ function extractLinkFromHtml(link, parent, app, isDynamic = false) {
984
1166
  let replaceComment = null;
985
1167
  if (rel === 'stylesheet' && href) {
986
1168
  href = CompletionPath(href, app.url);
1169
+ let linkInfo = sourceCenter.link.getInfo(href);
1170
+ const appSpaceData = {
1171
+ attrs: getAttributes(link),
1172
+ };
1173
+ if (!linkInfo) {
1174
+ linkInfo = {
1175
+ code: '',
1176
+ appSpace: {
1177
+ [app.name]: appSpaceData,
1178
+ }
1179
+ };
1180
+ }
1181
+ else {
1182
+ linkInfo.appSpace[app.name] = linkInfo.appSpace[app.name] || appSpaceData;
1183
+ }
1184
+ sourceCenter.link.setInfo(href, linkInfo);
987
1185
  if (!isDynamic) {
1186
+ app.source.links.add(href);
988
1187
  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
- });
1188
+ linkInfo.appSpace[app.name].placeholder = replaceComment;
994
1189
  }
995
1190
  else {
996
- return {
997
- url: href,
998
- info: {
999
- code: '',
1000
- isGlobal: link.hasAttribute('global'),
1001
- }
1002
- };
1191
+ return { address: href, linkInfo };
1003
1192
  }
1004
1193
  }
1005
1194
  else if (rel && ['prefetch', 'preload', 'prerender', 'icon', 'apple-touch-icon'].includes(rel)) {
@@ -1028,79 +1217,138 @@ function extractLinkFromHtml(link, parent, app, isDynamic = false) {
1028
1217
  * @param app app
1029
1218
  * @param microAppHead micro-app-head
1030
1219
  */
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);
1220
+ function fetchLinksFromHtml(wrapElement, app, microAppHead, fiberStyleResult) {
1221
+ const styleList = Array.from(app.source.links);
1222
+ const fetchLinkPromise = styleList.map((address) => {
1223
+ const linkInfo = sourceCenter.link.getInfo(address);
1224
+ return linkInfo.code ? linkInfo.code : fetchSource(address, app.name);
1035
1225
  });
1226
+ const fiberLinkTasks = app.isPrefetch || app.fiber ? [] : null;
1036
1227
  promiseStream(fetchLinkPromise, (res) => {
1037
- fetchLinkSuccess(linkEntries[res.index][0], linkEntries[res.index][1], res.data, microAppHead, app);
1228
+ injectFiberTask(fiberLinkTasks, () => fetchLinkSuccess(styleList[res.index], res.data, microAppHead, app));
1038
1229
  }, (err) => {
1039
1230
  logError(err, app.name);
1040
1231
  }, () => {
1041
- app.onLoad(wrapElement);
1232
+ if (fiberLinkTasks) {
1233
+ /**
1234
+ * 1. If fiberLinkTasks is not null, fiberStyleResult is not null
1235
+ * 2. Download link source while processing style
1236
+ * 3. Process style first, and then process link
1237
+ */
1238
+ fiberStyleResult.then(() => {
1239
+ fiberLinkTasks.push(() => Promise.resolve(app.onLoad(wrapElement)));
1240
+ serialExecFiberTasks(fiberLinkTasks);
1241
+ });
1242
+ }
1243
+ else {
1244
+ app.onLoad(wrapElement);
1245
+ }
1042
1246
  });
1043
1247
  }
1044
1248
  /**
1045
- * fetch link succeeded, replace placeholder with style tag
1046
- * @param url resource address
1047
- * @param info resource link info
1048
- * @param data code
1249
+ * Fetch link succeeded, replace placeholder with style tag
1250
+ * NOTE:
1251
+ * 1. Only exec when init, no longer exec when remount
1252
+ * 2. Only handler html link element, not dynamic link or style
1253
+ * 3. The same prefix can reuse parsedCode
1254
+ * 4. Async exec with requestIdleCallback in prefetch or fiber
1255
+ * 5. appSpace[app.name].placeholder/attrs must exist
1256
+ * @param address resource address
1257
+ * @param code link source code
1049
1258
  * @param microAppHead micro-app-head
1050
- * @param app app
1259
+ * @param app app instance
1051
1260
  */
1052
- function fetchLinkSuccess(url, info, data, microAppHead, app) {
1053
- if (info.isGlobal && !globalLinks.has(url)) {
1054
- globalLinks.set(url, data);
1261
+ function fetchLinkSuccess(address, code, microAppHead, app) {
1262
+ /**
1263
+ * linkInfo must exist, but linkInfo.code not
1264
+ * so we set code to linkInfo.code
1265
+ */
1266
+ const linkInfo = sourceCenter.link.getInfo(address);
1267
+ linkInfo.code = code;
1268
+ const appSpaceData = linkInfo.appSpace[app.name];
1269
+ const placeholder = appSpaceData.placeholder;
1270
+ /**
1271
+ * 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.
1272
+ * This causes placeholder to be possibly null
1273
+ * e.g.
1274
+ * 1. prefetch app.url different from <micro-app></micro-app>
1275
+ * 2. prefetch param different from <micro-app></micro-app>
1276
+ */
1277
+ if (placeholder) {
1278
+ const convertStyle = pureCreateElement('style');
1279
+ handleConvertStyle(app, address, convertStyle, linkInfo, appSpaceData.attrs);
1280
+ if (placeholder.parentNode) {
1281
+ placeholder.parentNode.replaceChild(convertStyle, placeholder);
1282
+ }
1283
+ else {
1284
+ microAppHead.appendChild(convertStyle);
1285
+ }
1286
+ // clear placeholder
1287
+ appSpaceData.placeholder = null;
1055
1288
  }
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);
1289
+ }
1290
+ /**
1291
+ * Get parsedCode, update convertStyle
1292
+ * Actions:
1293
+ * 1. get scope css (through scopedCSS or oldData)
1294
+ * 2. record parsedCode
1295
+ * 3. set parsedCode to convertStyle if need
1296
+ * @param app app instance
1297
+ * @param address resource address
1298
+ * @param convertStyle converted style
1299
+ * @param linkInfo linkInfo in sourceCenter
1300
+ * @param attrs attrs of link
1301
+ */
1302
+ function handleConvertStyle(app, address, convertStyle, linkInfo, attrs) {
1303
+ if (app.scopecss) {
1304
+ const appSpaceData = linkInfo.appSpace[app.name];
1305
+ appSpaceData.prefix = appSpaceData.prefix || createPrefix(app.name);
1306
+ if (!appSpaceData.parsedCode) {
1307
+ const existParsedCode = getExistParseCode(app.name, appSpaceData.prefix, linkInfo);
1308
+ if (!existParsedCode) {
1309
+ convertStyle.textContent = linkInfo.code;
1310
+ scopedCSS(convertStyle, app, address);
1311
+ }
1312
+ else {
1313
+ convertStyle.textContent = existParsedCode;
1314
+ }
1315
+ appSpaceData.parsedCode = convertStyle.textContent;
1316
+ }
1317
+ else {
1318
+ convertStyle.textContent = appSpaceData.parsedCode;
1319
+ }
1062
1320
  }
1063
1321
  else {
1064
- microAppHead.appendChild(scopedCSS(styleLink, app));
1322
+ convertStyle.textContent = linkInfo.code;
1065
1323
  }
1066
- info.placeholder = null;
1067
- info.code = data;
1324
+ setConvertStyleAttr(convertStyle, attrs);
1068
1325
  }
1069
1326
  /**
1070
- * get css from dynamic link
1071
- * @param url link address
1072
- * @param info info
1327
+ * Handle css of dynamic link
1328
+ * @param address link address
1073
1329
  * @param app app
1330
+ * @param linkInfo linkInfo
1074
1331
  * @param originLink origin link element
1075
- * @param replaceStyle style element which replaced origin link
1076
1332
  */
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;
1333
+ function formatDynamicLink(address, app, linkInfo, originLink) {
1334
+ const convertStyle = pureCreateElement('style');
1335
+ const handleDynamicLink = () => {
1336
+ handleConvertStyle(app, address, convertStyle, linkInfo, linkInfo.appSpace[app.name].attrs);
1337
+ dispatchOnLoadEvent(originLink);
1338
+ };
1339
+ if (linkInfo.code) {
1340
+ defer(handleDynamicLink);
1083
1341
  }
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;
1342
+ else {
1343
+ fetchSource(address, app.name).then((data) => {
1344
+ linkInfo.code = data;
1345
+ handleDynamicLink();
1346
+ }).catch((err) => {
1347
+ logError(err, app.name);
1348
+ dispatchOnErrorEvent(originLink);
1349
+ });
1092
1350
  }
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
- });
1351
+ return convertStyle;
1104
1352
  }
1105
1353
 
1106
1354
  class Adapter {
@@ -1122,10 +1370,9 @@ class Adapter {
1122
1370
  ];
1123
1371
  this.injectReactHRMProperty();
1124
1372
  }
1125
- // TODO: __DEV__ process.env.NODE_ENV !== 'production'
1126
1373
  // adapter for react
1127
1374
  injectReactHRMProperty() {
1128
- if (process.env.NODE_ENV !== 'production') {
1375
+ if ((process.env.NODE_ENV !== 'production')) {
1129
1376
  // react child in non-react env
1130
1377
  this.staticEscapeProperties.push('__REACT_ERROR_OVERLAY_GLOBAL_HOOK__');
1131
1378
  // in react parent
@@ -1149,7 +1396,7 @@ function fixBabelPolyfill6() {
1149
1396
  */
1150
1397
  function fixReactHMRConflict(app) {
1151
1398
  var _a;
1152
- if (process.env.NODE_ENV !== 'production') {
1399
+ if ((process.env.NODE_ENV !== 'production')) {
1153
1400
  const rawReactErrorHook = globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__;
1154
1401
  const childReactErrorHook = (_a = app.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__;
1155
1402
  if (rawReactErrorHook && childReactErrorHook) {
@@ -1169,17 +1416,25 @@ function fixReactHMRConflict(app) {
1169
1416
  function throttleDeferForParentNode(proxyDocument) {
1170
1417
  const html = globalEnv.rawDocument.firstElementChild;
1171
1418
  if (html && html.parentNode !== proxyDocument) {
1172
- setRootParentNode(html, proxyDocument);
1419
+ setParentNode(html, proxyDocument);
1173
1420
  defer(() => {
1174
- setRootParentNode(html, globalEnv.rawDocument);
1421
+ setParentNode(html, globalEnv.rawDocument);
1175
1422
  });
1176
1423
  }
1177
1424
  }
1178
- function setRootParentNode(root, value) {
1179
- Object.defineProperty(root, 'parentNode', {
1180
- value,
1181
- configurable: true,
1182
- });
1425
+ /**
1426
+ * Modify the point of parentNode
1427
+ * @param target target Node
1428
+ * @param value parentNode
1429
+ */
1430
+ function setParentNode(target, value) {
1431
+ const descriptor = Object.getOwnPropertyDescriptor(target, 'parentNode');
1432
+ if (!descriptor || descriptor.configurable) {
1433
+ rawDefineProperty(target, 'parentNode', {
1434
+ value,
1435
+ configurable: true,
1436
+ });
1437
+ }
1183
1438
  }
1184
1439
 
1185
1440
  // Record element and map element
@@ -1208,14 +1463,16 @@ function handleNewNode(parent, child, app) {
1208
1463
  dynamicElementInMicroAppMap.set(child, linkReplaceComment);
1209
1464
  return linkReplaceComment;
1210
1465
  }
1211
- else if (child.hasAttribute('ignore') || checkIgnoreUrl(child.getAttribute('href'), app.name)) {
1466
+ else if (child.hasAttribute('ignore') ||
1467
+ checkIgnoreUrl(child.getAttribute('href'), app.name) ||
1468
+ (child.href &&
1469
+ isFunction(microApp.options.excludeAssetFilter) &&
1470
+ microApp.options.excludeAssetFilter(child.href))) {
1212
1471
  return child;
1213
1472
  }
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);
1473
+ const { address, linkInfo, replaceComment } = extractLinkFromHtml(child, parent, app, true);
1474
+ if (address && linkInfo) {
1475
+ const replaceStyle = formatDynamicLink(address, app, linkInfo, child);
1219
1476
  dynamicElementInMicroAppMap.set(child, replaceStyle);
1220
1477
  return replaceStyle;
1221
1478
  }
@@ -1226,18 +1483,17 @@ function handleNewNode(parent, child, app) {
1226
1483
  return child;
1227
1484
  }
1228
1485
  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
- }
1486
+ if (child.src &&
1487
+ isFunction(microApp.options.excludeAssetFilter) &&
1488
+ microApp.options.excludeAssetFilter(child.src)) {
1489
+ return child;
1490
+ }
1491
+ const { replaceComment, address, scriptInfo } = extractScriptElement(child, parent, app, true) || {};
1492
+ if (address && scriptInfo) {
1493
+ // remote script or inline script
1494
+ const replaceElement = scriptInfo.isExternal ? runDynamicRemoteScript(address, app, scriptInfo, child) : runDynamicInlineScript(address, app, scriptInfo);
1495
+ dynamicElementInMicroAppMap.set(child, replaceElement);
1496
+ return replaceElement;
1241
1497
  }
1242
1498
  else if (replaceComment) {
1243
1499
  dynamicElementInMicroAppMap.set(child, replaceComment);
@@ -1256,37 +1512,56 @@ function handleNewNode(parent, child, app) {
1256
1512
  * @param passiveChild second param of insertBefore and replaceChild
1257
1513
  */
1258
1514
  function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
1259
- const hijackElement = getHijackElement(parent, app);
1515
+ const hijackParent = getHijackParent(parent, app);
1260
1516
  /**
1261
1517
  * If passiveChild is not the child node, insertBefore replaceChild will have a problem, at this time, it will be degraded to appendChild
1262
1518
  * E.g: document.head.insertBefore(targetChild, document.head.childNodes[0])
1263
1519
  */
1264
- if (hijackElement) {
1520
+ if (hijackParent) {
1521
+ /**
1522
+ * WARNING:
1523
+ * Verifying that the parentNode of the targetChild points to document.body will cause other problems ?
1524
+ */
1525
+ if (hijackParent.tagName === 'MICRO-APP-BODY' && rawMethod !== globalEnv.rawRemoveChild) {
1526
+ const descriptor = Object.getOwnPropertyDescriptor(targetChild, 'parentNode');
1527
+ if (!descriptor || descriptor.configurable) {
1528
+ rawDefineProperty(targetChild, 'parentNode', {
1529
+ configurable: true,
1530
+ get() {
1531
+ /**
1532
+ * When operate child from parentNode async, may have been unmount
1533
+ * e.g.
1534
+ * target.parentNode.remove(target)
1535
+ */
1536
+ return !app.container ? hijackParent : document.body;
1537
+ },
1538
+ });
1539
+ }
1540
+ }
1265
1541
  /**
1266
1542
  * 1. If passiveChild exists, it must be insertBefore or replaceChild
1267
1543
  * 2. When removeChild, targetChild may not be in microAppHead or head
1268
1544
  */
1269
- if (passiveChild && !hijackElement.contains(passiveChild)) {
1270
- return globalEnv.rawAppendChild.call(hijackElement, targetChild);
1545
+ if (passiveChild && !hijackParent.contains(passiveChild)) {
1546
+ return globalEnv.rawAppendChild.call(hijackParent, targetChild);
1271
1547
  }
1272
- else if (rawMethod === globalEnv.rawRemoveChild && !hijackElement.contains(targetChild)) {
1548
+ else if (rawMethod === globalEnv.rawRemoveChild && !hijackParent.contains(targetChild)) {
1273
1549
  if (parent.contains(targetChild)) {
1274
1550
  return rawMethod.call(parent, targetChild);
1275
1551
  }
1276
1552
  return targetChild;
1277
1553
  }
1278
- // TODO: __DEV__
1279
- if (process.env.NODE_ENV !== 'production' &&
1554
+ if ((process.env.NODE_ENV !== 'production') &&
1280
1555
  targetChild instanceof HTMLIFrameElement &&
1281
1556
  rawMethod === globalEnv.rawAppendChild) {
1282
1557
  fixReactHMRConflict(app);
1283
1558
  }
1284
- return invokeRawMethod(rawMethod, hijackElement, targetChild, passiveChild);
1559
+ return invokeRawMethod(rawMethod, hijackParent, targetChild, passiveChild);
1285
1560
  }
1286
1561
  return invokeRawMethod(rawMethod, parent, targetChild, passiveChild);
1287
1562
  }
1288
1563
  // head/body map to micro-app-head/micro-app-body
1289
- function getHijackElement(node, app) {
1564
+ function getHijackParent(node, app) {
1290
1565
  var _a, _b;
1291
1566
  if (node === document.head) {
1292
1567
  return (_a = app === null || app === void 0 ? void 0 : app.container) === null || _a === void 0 ? void 0 : _a.querySelector('micro-app-head');
@@ -1319,13 +1594,13 @@ function getMappingNode(node) {
1319
1594
  */
1320
1595
  function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
1321
1596
  const currentAppName = getCurrentAppName();
1322
- if (newChild instanceof Node &&
1597
+ if (isNode(newChild) &&
1323
1598
  (newChild.__MICRO_APP_NAME__ ||
1324
1599
  (currentAppName && !newChild.__PURE_ELEMENT__))) {
1325
1600
  newChild.__MICRO_APP_NAME__ = newChild.__MICRO_APP_NAME__ || currentAppName;
1326
1601
  const app = appInstanceMap.get(newChild.__MICRO_APP_NAME__);
1327
1602
  if (app === null || app === void 0 ? void 0 : app.container) {
1328
- if (newChild instanceof Element) {
1603
+ if (isElement(newChild)) {
1329
1604
  if (/^(img|script)$/i.test(newChild.tagName)) {
1330
1605
  if (newChild.hasAttribute('src')) {
1331
1606
  globalEnv.rawSetAttribute.call(newChild, 'src', CompletionPath(newChild.getAttribute('src'), app.url));
@@ -1345,7 +1620,7 @@ function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
1345
1620
  }
1346
1621
  }
1347
1622
  else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
1348
- if (!(newChild instanceof Node) && currentAppName) {
1623
+ if (!isNode(newChild) && currentAppName) {
1349
1624
  const app = appInstanceMap.get(currentAppName);
1350
1625
  if (app === null || app === void 0 ? void 0 : app.container) {
1351
1626
  if (parent === document.head) {
@@ -1392,7 +1667,6 @@ function patchElementPrototypeMethods() {
1392
1667
  };
1393
1668
  // prototype methods of delete element👇
1394
1669
  Element.prototype.removeChild = function removeChild(oldChild) {
1395
- var _a;
1396
1670
  if (oldChild === null || oldChild === void 0 ? void 0 : oldChild.__MICRO_APP_NAME__) {
1397
1671
  const app = appInstanceMap.get(oldChild.__MICRO_APP_NAME__);
1398
1672
  if (app === null || app === void 0 ? void 0 : app.container) {
@@ -1401,8 +1675,8 @@ function patchElementPrototypeMethods() {
1401
1675
  try {
1402
1676
  return globalEnv.rawRemoveChild.call(this, oldChild);
1403
1677
  }
1404
- catch (_b) {
1405
- return (_a = oldChild === null || oldChild === void 0 ? void 0 : oldChild.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(oldChild);
1678
+ catch (_a) {
1679
+ return ((oldChild === null || oldChild === void 0 ? void 0 : oldChild.parentNode) && globalEnv.rawRemoveChild.call(oldChild.parentNode, oldChild));
1406
1680
  }
1407
1681
  }
1408
1682
  return globalEnv.rawRemoveChild.call(this, oldChild);
@@ -1705,8 +1979,80 @@ function initGlobalEnv() {
1705
1979
  }
1706
1980
  }
1707
1981
 
1708
- // Global scripts, reuse across apps
1709
- const globalScripts = new Map();
1982
+ const scriptTypes = ['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module', 'systemjs-module', 'systemjs-importmap'];
1983
+ // whether use type='module' script
1984
+ function isTypeModule(app, scriptInfo) {
1985
+ return scriptInfo.appSpace[app.name].module && (!app.useSandbox || app.esmodule);
1986
+ }
1987
+ // special script element
1988
+ function isSpecialScript(app, scriptInfo) {
1989
+ const attrs = scriptInfo.appSpace[app.name].attrs;
1990
+ return attrs.has('id');
1991
+ }
1992
+ /**
1993
+ * whether to run js in inline mode
1994
+ * scene:
1995
+ * 1. inline config for app
1996
+ * 2. inline attr in script element
1997
+ * 3. module script
1998
+ * 4. script with special attr
1999
+ */
2000
+ function isInlineMode(app, scriptInfo) {
2001
+ return (app.inline ||
2002
+ scriptInfo.appSpace[app.name].inline ||
2003
+ isTypeModule(app, scriptInfo) ||
2004
+ isSpecialScript(app, scriptInfo));
2005
+ }
2006
+ // Convert string code to function
2007
+ function code2Function(code) {
2008
+ return new Function(code);
2009
+ }
2010
+ /**
2011
+ * If the appSpace of the current js address has other app, try to reuse parsedFunction of other app
2012
+ * @param appName app.name
2013
+ * @param scriptInfo scriptInfo of current address
2014
+ * @param currentCode pure code of current address
2015
+ */
2016
+ function getExistParseResult(appName, scriptInfo, currentCode) {
2017
+ const appSpace = scriptInfo.appSpace;
2018
+ for (const item in appSpace) {
2019
+ if (item !== appName) {
2020
+ const appSpaceData = appSpace[item];
2021
+ if (appSpaceData.parsedCode === currentCode && appSpaceData.parsedFunction) {
2022
+ return appSpaceData.parsedFunction;
2023
+ }
2024
+ }
2025
+ }
2026
+ }
2027
+ /**
2028
+ * get parsedFunction from exist data or parsedCode
2029
+ * @returns parsedFunction
2030
+ */
2031
+ function getParsedFunction(app, scriptInfo, parsedCode) {
2032
+ return getExistParseResult(app.name, scriptInfo, parsedCode) || code2Function(parsedCode);
2033
+ }
2034
+ // Prevent randomly created strings from repeating
2035
+ function getUniqueNonceSrc() {
2036
+ const nonceStr = createNonceSrc();
2037
+ if (sourceCenter.script.hasInfo(nonceStr)) {
2038
+ return getUniqueNonceSrc();
2039
+ }
2040
+ return nonceStr;
2041
+ }
2042
+ // transfer the attributes on the script to convertScript
2043
+ function setConvertScriptAttr(convertScript, attrs) {
2044
+ attrs.forEach((value, key) => {
2045
+ if ((key === 'type' && value === 'module') || key === 'defer' || key === 'async')
2046
+ return;
2047
+ if (key === 'src')
2048
+ key = 'data-origin-src';
2049
+ convertScript.setAttribute(key, value);
2050
+ });
2051
+ }
2052
+ // wrap code in sandbox
2053
+ function isWrapInSandBox(app, scriptInfo) {
2054
+ return app.useSandbox && !isTypeModule(app, scriptInfo);
2055
+ }
1710
2056
  /**
1711
2057
  * Extract script elements
1712
2058
  * @param script script element
@@ -1717,14 +2063,15 @@ const globalScripts = new Map();
1717
2063
  function extractScriptElement(script, parent, app, isDynamic = false) {
1718
2064
  let replaceComment = null;
1719
2065
  let src = script.getAttribute('src');
1720
- if (src) {
2066
+ if (src)
1721
2067
  src = CompletionPath(src, app.url);
1722
- }
1723
2068
  if (script.hasAttribute('exclude') || checkExcludeUrl(src, app.name)) {
1724
2069
  replaceComment = document.createComment('script element with exclude attribute removed by micro-app');
1725
2070
  }
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)) {
2071
+ else if ((script.type &&
2072
+ !scriptTypes.includes(script.type)) ||
2073
+ script.hasAttribute('ignore') ||
2074
+ checkIgnoreUrl(src, app.name)) {
1728
2075
  return null;
1729
2076
  }
1730
2077
  else if ((globalEnv.supportModuleScript && script.noModule) ||
@@ -1732,39 +2079,74 @@ function extractScriptElement(script, parent, app, isDynamic = false) {
1732
2079
  replaceComment = document.createComment(`${script.noModule ? 'noModule' : 'module'} script ignored by micro-app`);
1733
2080
  }
1734
2081
  else if (src) { // remote script
1735
- const info = {
1736
- code: '',
1737
- isExternal: true,
1738
- isDynamic: isDynamic,
2082
+ let scriptInfo = sourceCenter.script.getInfo(src);
2083
+ const appSpaceData = {
1739
2084
  async: script.hasAttribute('async'),
1740
2085
  defer: script.defer || script.type === 'module',
1741
2086
  module: script.type === 'module',
1742
- isGlobal: script.hasAttribute('global'),
2087
+ inline: script.hasAttribute('inline'),
2088
+ pure: script.hasAttribute('pure'),
2089
+ attrs: getAttributes(script),
1743
2090
  };
2091
+ if (!scriptInfo) {
2092
+ scriptInfo = {
2093
+ code: '',
2094
+ isExternal: true,
2095
+ appSpace: {
2096
+ [app.name]: appSpaceData,
2097
+ }
2098
+ };
2099
+ }
2100
+ else {
2101
+ /**
2102
+ * Reuse when appSpace exists
2103
+ * NOTE:
2104
+ * 1. The same static script, appSpace must be the same (in fact, it may be different when url change)
2105
+ * 2. The same dynamic script, appSpace may be the same, but we still reuse appSpace, which should pay attention
2106
+ */
2107
+ scriptInfo.appSpace[app.name] = scriptInfo.appSpace[app.name] || appSpaceData;
2108
+ }
2109
+ sourceCenter.script.setInfo(src, scriptInfo);
1744
2110
  if (!isDynamic) {
1745
- app.source.scripts.set(src, info);
2111
+ app.source.scripts.add(src);
1746
2112
  replaceComment = document.createComment(`script with src='${src}' extract by micro-app`);
1747
2113
  }
1748
2114
  else {
1749
- return { url: src, info };
2115
+ return { address: src, scriptInfo };
1750
2116
  }
1751
2117
  }
1752
2118
  else if (script.textContent) { // inline script
1753
- const nonceStr = createNonceSrc();
1754
- const info = {
2119
+ /**
2120
+ * NOTE:
2121
+ * 1. Each inline script is unique
2122
+ * 2. Every dynamic created inline script will be re-executed
2123
+ * ACTION:
2124
+ * 1. Delete dynamic inline script info after exec
2125
+ * 2. Delete static inline script info when destroy
2126
+ */
2127
+ const nonceStr = getUniqueNonceSrc();
2128
+ const scriptInfo = {
1755
2129
  code: script.textContent,
1756
2130
  isExternal: false,
1757
- isDynamic: isDynamic,
1758
- async: false,
1759
- defer: script.type === 'module',
1760
- module: script.type === 'module',
2131
+ appSpace: {
2132
+ [app.name]: {
2133
+ async: false,
2134
+ defer: script.type === 'module',
2135
+ module: script.type === 'module',
2136
+ inline: script.hasAttribute('inline'),
2137
+ pure: script.hasAttribute('pure'),
2138
+ attrs: getAttributes(script),
2139
+ }
2140
+ }
1761
2141
  };
1762
2142
  if (!isDynamic) {
1763
- app.source.scripts.set(nonceStr, info);
2143
+ app.source.scripts.add(nonceStr);
2144
+ sourceCenter.script.setInfo(nonceStr, scriptInfo);
1764
2145
  replaceComment = document.createComment('inline script extract by micro-app');
1765
2146
  }
1766
2147
  else {
1767
- return { url: nonceStr, info };
2148
+ // Because each dynamic script is unique, it is not put into sourceCenter
2149
+ return { address: nonceStr, scriptInfo };
1768
2150
  }
1769
2151
  }
1770
2152
  else if (!isDynamic) {
@@ -1787,38 +2169,38 @@ function extractScriptElement(script, parent, app, isDynamic = false) {
1787
2169
  */
1788
2170
  function getAssetsPlugins(appName) {
1789
2171
  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]) || [];
2172
+ const globalPlugins = ((_a = microApp.options.plugins) === null || _a === void 0 ? void 0 : _a.global) || [];
2173
+ const modulePlugins = ((_c = (_b = microApp.options.plugins) === null || _b === void 0 ? void 0 : _b.modules) === null || _c === void 0 ? void 0 : _c[appName]) || [];
1792
2174
  return [...globalPlugins, ...modulePlugins];
1793
2175
  }
1794
2176
  /**
1795
- * whether the url needs to be excluded
1796
- * @param url css or js link
2177
+ * whether the address needs to be excluded
2178
+ * @param address css or js link
1797
2179
  * @param plugins microApp plugins
1798
2180
  */
1799
- function checkExcludeUrl(url, appName) {
1800
- if (!url)
2181
+ function checkExcludeUrl(address, appName) {
2182
+ if (!address)
1801
2183
  return false;
1802
2184
  const plugins = getAssetsPlugins(appName) || [];
1803
2185
  return plugins.some(plugin => {
1804
2186
  if (!plugin.excludeChecker)
1805
2187
  return false;
1806
- return plugin.excludeChecker(url);
2188
+ return plugin.excludeChecker(address);
1807
2189
  });
1808
2190
  }
1809
2191
  /**
1810
- * whether the url needs to be ignore
1811
- * @param url css or js link
2192
+ * whether the address needs to be ignore
2193
+ * @param address css or js link
1812
2194
  * @param plugins microApp plugins
1813
2195
  */
1814
- function checkIgnoreUrl(url, appName) {
1815
- if (!url)
2196
+ function checkIgnoreUrl(address, appName) {
2197
+ if (!address)
1816
2198
  return false;
1817
2199
  const plugins = getAssetsPlugins(appName) || [];
1818
2200
  return plugins.some(plugin => {
1819
2201
  if (!plugin.ignoreChecker)
1820
2202
  return false;
1821
- return plugin.ignoreChecker(url);
2203
+ return plugin.ignoreChecker(address);
1822
2204
  });
1823
2205
  }
1824
2206
  /**
@@ -1827,28 +2209,31 @@ function checkIgnoreUrl(url, appName) {
1827
2209
  * @param app app
1828
2210
  */
1829
2211
  function fetchScriptsFromHtml(wrapElement, app) {
1830
- const scriptEntries = Array.from(app.source.scripts.entries());
2212
+ const scriptList = Array.from(app.source.scripts);
1831
2213
  const fetchScriptPromise = [];
1832
2214
  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
- }
2215
+ for (const address of scriptList) {
2216
+ const scriptInfo = sourceCenter.script.getInfo(address);
2217
+ const appSpaceData = scriptInfo.appSpace[app.name];
2218
+ if ((!appSpaceData.defer && !appSpaceData.async) || (app.isPrefetch && !app.isPrerender)) {
2219
+ fetchScriptPromise.push(scriptInfo.code ? scriptInfo.code : fetchSource(address, app.name));
2220
+ fetchScriptPromiseInfo.push([address, scriptInfo]);
1843
2221
  }
1844
2222
  }
2223
+ const fiberScriptTasks = app.isPrefetch || app.fiber ? [] : null;
1845
2224
  if (fetchScriptPromise.length) {
1846
2225
  promiseStream(fetchScriptPromise, (res) => {
1847
- fetchScriptSuccess(fetchScriptPromiseInfo[res.index][0], fetchScriptPromiseInfo[res.index][1], res.data);
2226
+ injectFiberTask(fiberScriptTasks, () => fetchScriptSuccess(fetchScriptPromiseInfo[res.index][0], fetchScriptPromiseInfo[res.index][1], res.data, app));
1848
2227
  }, (err) => {
1849
2228
  logError(err, app.name);
1850
2229
  }, () => {
1851
- app.onLoad(wrapElement);
2230
+ if (fiberScriptTasks) {
2231
+ fiberScriptTasks.push(() => Promise.resolve(app.onLoad(wrapElement)));
2232
+ serialExecFiberTasks(fiberScriptTasks);
2233
+ }
2234
+ else {
2235
+ app.onLoad(wrapElement);
2236
+ }
1852
2237
  });
1853
2238
  }
1854
2239
  else {
@@ -1857,169 +2242,224 @@ function fetchScriptsFromHtml(wrapElement, app) {
1857
2242
  }
1858
2243
  /**
1859
2244
  * fetch js succeeded, record the code value
1860
- * @param url script address
1861
- * @param info resource script info
2245
+ * @param address script address
2246
+ * @param scriptInfo resource script info
1862
2247
  * @param data code
1863
2248
  */
1864
- function fetchScriptSuccess(url, info, data) {
1865
- if (info.isGlobal && !globalScripts.has(url)) {
1866
- globalScripts.set(url, data);
2249
+ function fetchScriptSuccess(address, scriptInfo, code, app) {
2250
+ // reset scriptInfo.code
2251
+ scriptInfo.code = code;
2252
+ /**
2253
+ * Pre parse script for prefetch, improve rendering performance
2254
+ * NOTE:
2255
+ * 1. if global parseResult exist, skip this step
2256
+ * 2. if app is inline or script is esmodule, skip this step
2257
+ * 3. if global parseResult not exist, the current script occupies the position, when js is reused, parseResult is reference
2258
+ */
2259
+ if (app.isPrefetch && app.prefetchLevel === 2) {
2260
+ const appSpaceData = scriptInfo.appSpace[app.name];
2261
+ /**
2262
+ * 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.
2263
+ * This causes parsedCode to already exist when preloading ends
2264
+ * e.g.
2265
+ * 1. prefetch app.url different from <micro-app></micro-app>
2266
+ * 2. prefetch param different from <micro-app></micro-app>
2267
+ */
2268
+ if (!appSpaceData.parsedCode) {
2269
+ appSpaceData.parsedCode = bindScope(address, app, code, scriptInfo);
2270
+ appSpaceData.wrapInSandBox = isWrapInSandBox(app, scriptInfo);
2271
+ if (!isInlineMode(app, scriptInfo)) {
2272
+ try {
2273
+ appSpaceData.parsedFunction = getParsedFunction(app, scriptInfo, appSpaceData.parsedCode);
2274
+ }
2275
+ catch (err) {
2276
+ logWarn('Something went wrong while handling preloaded resources', app.name, '\n', err);
2277
+ }
2278
+ }
2279
+ }
1867
2280
  }
1868
- info.code = data;
1869
2281
  }
1870
2282
  /**
1871
2283
  * Execute js in the mount lifecycle
1872
- * @param scriptList script list
1873
2284
  * @param app app
1874
2285
  * @param initHook callback for umd mode
1875
2286
  */
1876
- function execScripts(scriptList, app, initHook) {
1877
- const scriptListEntries = Array.from(scriptList.entries());
2287
+ function execScripts(app, initHook) {
2288
+ const fiberScriptTasks = app.fiber ? [] : null;
2289
+ const scriptList = Array.from(app.source.scripts);
1878
2290
  const deferScriptPromise = [];
1879
2291
  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);
2292
+ for (const address of scriptList) {
2293
+ const scriptInfo = sourceCenter.script.getInfo(address);
2294
+ const appSpaceData = scriptInfo.appSpace[app.name];
2295
+ // Notice the second render
2296
+ if (appSpaceData.defer || appSpaceData.async) {
2297
+ if (scriptInfo.isExternal && !scriptInfo.code) {
2298
+ deferScriptPromise.push(fetchSource(address, app.name));
1892
2299
  }
1893
2300
  else {
1894
- runScript(url, app, info, false);
1895
- initHook(false);
2301
+ deferScriptPromise.push(scriptInfo.code);
1896
2302
  }
2303
+ deferScriptInfo.push([address, scriptInfo]);
2304
+ isTypeModule(app, scriptInfo) && (initHook.moduleCount = initHook.moduleCount ? ++initHook.moduleCount : 1);
2305
+ }
2306
+ else {
2307
+ injectFiberTask(fiberScriptTasks, () => {
2308
+ runScript(address, app, scriptInfo);
2309
+ initHook(false);
2310
+ });
1897
2311
  }
1898
2312
  }
1899
2313
  if (deferScriptPromise.length) {
1900
2314
  promiseStream(deferScriptPromise, (res) => {
1901
- const info = deferScriptInfo[res.index][1];
1902
- info.code = info.code || res.data;
2315
+ const scriptInfo = deferScriptInfo[res.index][1];
2316
+ scriptInfo.code = scriptInfo.code || res.data;
1903
2317
  }, (err) => {
1904
2318
  initHook.errorCount = initHook.errorCount ? ++initHook.errorCount : 1;
1905
2319
  logError(err, app.name);
1906
2320
  }, () => {
1907
- deferScriptInfo.forEach(([url, info]) => {
1908
- if (info.code) {
1909
- runScript(url, app, info, false, initHook);
1910
- !info.module && initHook(false);
2321
+ deferScriptInfo.forEach(([address, scriptInfo]) => {
2322
+ if (scriptInfo.code) {
2323
+ injectFiberTask(fiberScriptTasks, () => {
2324
+ runScript(address, app, scriptInfo, initHook);
2325
+ !isTypeModule(app, scriptInfo) && initHook(false);
2326
+ });
1911
2327
  }
1912
2328
  });
1913
- initHook(isUndefined(initHook.moduleCount) ||
1914
- initHook.errorCount === deferScriptPromise.length);
2329
+ /**
2330
+ * Fiber wraps js in requestIdleCallback and executes it in sequence
2331
+ * NOTE:
2332
+ * 1. In order to ensure the execution order, wait for all js loaded and then execute
2333
+ * 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
2334
+ *
2335
+ * BUG: NOTE.2 - execution order problem
2336
+ */
2337
+ if (fiberScriptTasks) {
2338
+ fiberScriptTasks.push(() => Promise.resolve(initHook(isUndefined(initHook.moduleCount) ||
2339
+ initHook.errorCount === deferScriptPromise.length)));
2340
+ serialExecFiberTasks(fiberScriptTasks);
2341
+ }
2342
+ else {
2343
+ initHook(isUndefined(initHook.moduleCount) ||
2344
+ initHook.errorCount === deferScriptPromise.length);
2345
+ }
1915
2346
  });
1916
2347
  }
1917
2348
  else {
1918
- initHook(true);
2349
+ if (fiberScriptTasks) {
2350
+ fiberScriptTasks.push(() => Promise.resolve(initHook(true)));
2351
+ serialExecFiberTasks(fiberScriptTasks);
2352
+ }
2353
+ else {
2354
+ initHook(true);
2355
+ }
1919
2356
  }
1920
2357
  }
1921
2358
  /**
1922
2359
  * run code
1923
- * @param url script address
2360
+ * @param address script address
1924
2361
  * @param app app
1925
- * @param info script info
1926
- * @param isDynamic dynamically created script
2362
+ * @param scriptInfo script info
1927
2363
  * @param callback callback of module script
1928
2364
  */
1929
- function runScript(url, app, info, isDynamic, callback) {
2365
+ function runScript(address, app, scriptInfo, callback, replaceElement) {
1930
2366
  var _a;
1931
2367
  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);
2368
+ actionsBeforeRunScript(app);
2369
+ const appSpaceData = scriptInfo.appSpace[app.name];
2370
+ const wrapInSandBox = isWrapInSandBox(app, scriptInfo);
2371
+ /**
2372
+ * NOTE:
2373
+ * 1. plugins and wrapCode will only be executed once
2374
+ * 2. if parsedCode not exist, parsedFunction is not exist
2375
+ * 3. if parsedCode exist, parsedFunction does not necessarily exist
2376
+ */
2377
+ if (!appSpaceData.parsedCode || appSpaceData.wrapInSandBox !== wrapInSandBox) {
2378
+ appSpaceData.parsedCode = bindScope(address, app, scriptInfo.code, scriptInfo);
2379
+ appSpaceData.wrapInSandBox = wrapInSandBox;
2380
+ appSpaceData.parsedFunction = null;
2381
+ }
2382
+ if (isInlineMode(app, scriptInfo)) {
2383
+ const scriptElement = replaceElement || pureCreateElement('script');
2384
+ runCode2InlineScript(address, appSpaceData.parsedCode, isTypeModule(app, scriptInfo), scriptElement, appSpaceData.attrs, callback);
2385
+ if (!replaceElement) {
2386
+ // TEST IGNORE
2387
+ (_a = app.container) === null || _a === void 0 ? void 0 : _a.querySelector('micro-app-body').appendChild(scriptElement);
2388
+ }
1940
2389
  }
1941
2390
  else {
1942
- runCode2Function(code, info);
1943
- if (isDynamic)
1944
- return document.createComment('dynamic script extract by micro-app');
2391
+ runParsedFunction(app, scriptInfo);
1945
2392
  }
1946
2393
  }
1947
2394
  catch (e) {
1948
- console.error(`[micro-app from runScript] app ${app.name}: `, e);
2395
+ console.error(`[micro-app from ${replaceElement ? 'runDynamicScript' : 'runScript'}] app ${app.name}: `, e, address);
1949
2396
  }
1950
2397
  }
1951
2398
  /**
1952
2399
  * Get dynamically created remote script
1953
- * @param url script address
1954
- * @param info info
1955
- * @param app app
2400
+ * @param address script address
2401
+ * @param app app instance
2402
+ * @param scriptInfo scriptInfo
1956
2403
  * @param originScript origin script element
1957
2404
  */
1958
- function runDynamicRemoteScript(url, info, app, originScript) {
2405
+ function runDynamicRemoteScript(address, app, scriptInfo, originScript) {
2406
+ const replaceElement = isInlineMode(app, scriptInfo) ? pureCreateElement('script') : document.createComment('dynamic script extract by micro-app');
1959
2407
  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');
2408
+ const runDynamicScript = () => {
2409
+ const descriptor = Object.getOwnPropertyDescriptor(globalEnv.rawDocument, 'currentScript');
2410
+ if (!descriptor || descriptor.configurable) {
2411
+ Object.defineProperty(globalEnv.rawDocument, 'currentScript', {
2412
+ value: originScript,
2413
+ configurable: true,
2414
+ });
2415
+ }
2416
+ runScript(address, app, scriptInfo, dispatchScriptOnLoadEvent, replaceElement);
2417
+ !isTypeModule(app, scriptInfo) && dispatchScriptOnLoadEvent();
2418
+ };
2419
+ if (scriptInfo.code) {
2420
+ defer(runDynamicScript);
1983
2421
  }
1984
2422
  else {
1985
- replaceElement = document.createComment(`dynamic script with src='${url}' extract by micro-app`);
2423
+ fetchSource(address, app.name).then((code) => {
2424
+ scriptInfo.code = code;
2425
+ runDynamicScript();
2426
+ }).catch((err) => {
2427
+ logError(err, app.name);
2428
+ dispatchOnErrorEvent(originScript);
2429
+ });
1986
2430
  }
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
- });
2431
+ return replaceElement;
2432
+ }
2433
+ /**
2434
+ * Get dynamically created inline script
2435
+ * @param address script address
2436
+ * @param app app instance
2437
+ * @param scriptInfo scriptInfo
2438
+ */
2439
+ function runDynamicInlineScript(address, app, scriptInfo) {
2440
+ const replaceElement = isInlineMode(app, scriptInfo) ? pureCreateElement('script') : document.createComment('dynamic script extract by micro-app');
2441
+ runScript(address, app, scriptInfo, void 0, replaceElement);
2008
2442
  return replaceElement;
2009
2443
  }
2010
2444
  /**
2011
2445
  * common handle for inline script
2012
- * @param url script address
2446
+ * @param address script address
2013
2447
  * @param code bound code
2014
2448
  * @param module type='module' of script
2015
2449
  * @param scriptElement target script element
2450
+ * @param attrs attributes of script element
2016
2451
  * @param callback callback of module script
2017
2452
  */
2018
- function runCode2InlineScript(url, code, module, scriptElement, callback) {
2453
+ function runCode2InlineScript(address, code, module, scriptElement, attrs, callback) {
2019
2454
  if (module) {
2020
2455
  // 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);
2456
+ if (isInlineScript(address)) {
2457
+ const blob = new Blob([code], { type: 'text/javascript' });
2458
+ scriptElement.src = URL.createObjectURL(blob);
2459
+ }
2460
+ else {
2461
+ scriptElement.src = address;
2462
+ }
2023
2463
  scriptElement.setAttribute('type', 'module');
2024
2464
  if (callback) {
2025
2465
  callback.moduleCount && callback.moduleCount--;
@@ -2029,55 +2469,65 @@ function runCode2InlineScript(url, code, module, scriptElement, callback) {
2029
2469
  else {
2030
2470
  scriptElement.textContent = code;
2031
2471
  }
2032
- if (!url.startsWith('inline-')) {
2033
- scriptElement.setAttribute('data-origin-src', url);
2034
- }
2472
+ setConvertScriptAttr(scriptElement, attrs);
2035
2473
  }
2036
2474
  // init & run code2Function
2037
- function runCode2Function(code, info) {
2038
- if (!info.code2Function) {
2039
- info.code2Function = new Function(code);
2475
+ function runParsedFunction(app, scriptInfo) {
2476
+ const appSpaceData = scriptInfo.appSpace[app.name];
2477
+ if (!appSpaceData.parsedFunction) {
2478
+ appSpaceData.parsedFunction = getParsedFunction(app, scriptInfo, appSpaceData.parsedCode);
2040
2479
  }
2041
- info.code2Function.call(window);
2480
+ appSpaceData.parsedFunction.call(window);
2042
2481
  }
2043
2482
  /**
2044
2483
  * bind js scope
2045
- * @param url script address
2046
2484
  * @param app app
2047
2485
  * @param code code
2048
- * @param info source script info
2486
+ * @param scriptInfo source script info
2049
2487
  */
2050
- function bindScope(url, app, code, info) {
2051
- // TODO: 增加缓存机制
2052
- if (isPlainObject(microApp.plugins)) {
2053
- code = usePlugins(url, code, app.name, microApp.plugins, info);
2488
+ function bindScope(address, app, code, scriptInfo) {
2489
+ // TODO: cache
2490
+ if (isPlainObject(microApp.options.plugins)) {
2491
+ code = usePlugins(address, code, app.name, microApp.options.plugins);
2054
2492
  }
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__);`;
2493
+ if (isWrapInSandBox(app, scriptInfo)) {
2494
+ 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
2495
  }
2059
2496
  return code;
2060
2497
  }
2498
+ /**
2499
+ * actions before run script
2500
+ */
2501
+ function actionsBeforeRunScript(app) {
2502
+ setActiveProxyWindow(app);
2503
+ }
2504
+ /**
2505
+ * set active sandBox.proxyWindow to window.__MICRO_APP_PROXY_WINDOW__
2506
+ */
2507
+ function setActiveProxyWindow(app) {
2508
+ if (app.sandBox) {
2509
+ globalEnv.rawWindow.__MICRO_APP_PROXY_WINDOW__ = app.sandBox.proxyWindow;
2510
+ }
2511
+ }
2061
2512
  /**
2062
2513
  * Call the plugin to process the file
2063
- * @param url script address
2514
+ * @param address script address
2064
2515
  * @param code code
2065
2516
  * @param appName app name
2066
2517
  * @param plugins plugin list
2067
- * @param info source script info
2068
2518
  */
2069
- function usePlugins(url, code, appName, plugins, info) {
2519
+ function usePlugins(address, code, appName, plugins) {
2070
2520
  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);
2521
+ const newCode = processCode(plugins.global, code, address);
2522
+ return processCode((_a = plugins.modules) === null || _a === void 0 ? void 0 : _a[appName], newCode, address);
2073
2523
  }
2074
- function processCode(configs, code, url, info) {
2524
+ function processCode(configs, code, address) {
2075
2525
  if (!isArray(configs)) {
2076
2526
  return code;
2077
2527
  }
2078
2528
  return configs.reduce((preCode, config) => {
2079
2529
  if (isPlainObject(config) && isFunction(config.loader)) {
2080
- return config.loader(preCode, url, config.options, info);
2530
+ return config.loader(preCode, address);
2081
2531
  }
2082
2532
  return preCode;
2083
2533
  }, code);
@@ -2098,10 +2548,10 @@ function getWrapElement(str) {
2098
2548
  * @param app app
2099
2549
  * @param microAppHead micro-app-head element
2100
2550
  */
2101
- function flatChildren(parent, app, microAppHead) {
2551
+ function flatChildren(parent, app, microAppHead, fiberStyleTasks) {
2102
2552
  const children = Array.from(parent.children);
2103
2553
  children.length && children.forEach((child) => {
2104
- flatChildren(child, app);
2554
+ flatChildren(child, app, microAppHead, fiberStyleTasks);
2105
2555
  });
2106
2556
  for (const dom of children) {
2107
2557
  if (dom instanceof HTMLLinkElement) {
@@ -2120,7 +2570,7 @@ function flatChildren(parent, app, microAppHead) {
2120
2570
  parent.replaceChild(document.createComment('style element with exclude attribute ignored by micro-app'), dom);
2121
2571
  }
2122
2572
  else if (app.scopecss && !dom.hasAttribute('ignore')) {
2123
- scopedCSS(dom, app);
2573
+ injectFiberTask(fiberStyleTasks, () => scopedCSS(dom, app));
2124
2574
  }
2125
2575
  }
2126
2576
  else if (dom instanceof HTMLScriptElement) {
@@ -2148,9 +2598,17 @@ function extractSourceDom(htmlStr, app) {
2148
2598
  app.onerror(new Error(msg));
2149
2599
  return logError(msg, app.name);
2150
2600
  }
2151
- flatChildren(wrapElement, app);
2601
+ const fiberStyleTasks = app.isPrefetch || app.fiber ? [] : null;
2602
+ flatChildren(wrapElement, app, microAppHead, fiberStyleTasks);
2603
+ /**
2604
+ * 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.
2605
+ */
2606
+ const fiberStyleResult = serialExecFiberTasks(fiberStyleTasks);
2152
2607
  if (app.source.links.size) {
2153
- fetchLinksFromHtml(wrapElement, app, microAppHead);
2608
+ fetchLinksFromHtml(wrapElement, app, microAppHead, fiberStyleResult);
2609
+ }
2610
+ else if (fiberStyleResult) {
2611
+ fiberStyleResult.then(() => app.onLoad(wrapElement));
2154
2612
  }
2155
2613
  else {
2156
2614
  app.onLoad(wrapElement);
@@ -2166,6 +2624,39 @@ function extractSourceDom(htmlStr, app) {
2166
2624
  class EventCenter {
2167
2625
  constructor() {
2168
2626
  this.eventList = new Map();
2627
+ this.queue = [];
2628
+ this.recordStep = {};
2629
+ // run task
2630
+ this.process = () => {
2631
+ var _a, _b;
2632
+ let name;
2633
+ const temRecordStep = this.recordStep;
2634
+ const queue = this.queue;
2635
+ this.recordStep = {};
2636
+ this.queue = [];
2637
+ while (name = queue.shift()) {
2638
+ const eventInfo = this.eventList.get(name);
2639
+ // clear tempData, force before exec nextStep
2640
+ const tempData = eventInfo.tempData;
2641
+ const force = eventInfo.force;
2642
+ eventInfo.tempData = null;
2643
+ eventInfo.force = false;
2644
+ let resArr;
2645
+ if (force || !this.isEqual(eventInfo.data, tempData)) {
2646
+ eventInfo.data = tempData || eventInfo.data;
2647
+ for (const f of eventInfo.callbacks) {
2648
+ const res = f(eventInfo.data);
2649
+ res && (resArr !== null && resArr !== void 0 ? resArr : (resArr = [])).push(res);
2650
+ }
2651
+ (_b = (_a = temRecordStep[name]).dispatchDataEvent) === null || _b === void 0 ? void 0 : _b.call(_a);
2652
+ /**
2653
+ * WARING:
2654
+ * If data of other app is sent in nextStep, it may cause confusion of tempData and force
2655
+ */
2656
+ temRecordStep[name].nextStepList.forEach((nextStep) => nextStep(resArr));
2657
+ }
2658
+ }
2659
+ };
2169
2660
  }
2170
2661
  // whether the name is legal
2171
2662
  isLegalName(name) {
@@ -2175,6 +2666,39 @@ class EventCenter {
2175
2666
  }
2176
2667
  return true;
2177
2668
  }
2669
+ // add appName to queue
2670
+ enqueue(name, nextStep, dispatchDataEvent) {
2671
+ // this.nextStepList.push(nextStep)
2672
+ if (this.recordStep[name]) {
2673
+ this.recordStep[name].nextStepList.push(nextStep);
2674
+ dispatchDataEvent && (this.recordStep[name].dispatchDataEvent = dispatchDataEvent);
2675
+ }
2676
+ else {
2677
+ this.recordStep[name] = {
2678
+ nextStepList: [nextStep],
2679
+ dispatchDataEvent,
2680
+ };
2681
+ }
2682
+ /**
2683
+ * The micro task is executed async when the second render of child.
2684
+ * We should ensure that the data changes are executed before binding the listening function
2685
+ */
2686
+ (!this.queue.includes(name) && this.queue.push(name) === 1) && defer(this.process);
2687
+ }
2688
+ /**
2689
+ * In react, each setState will trigger setData, so we need a filter operation to avoid repeated trigger
2690
+ */
2691
+ isEqual(oldData, newData) {
2692
+ if (!newData || Object.keys(oldData).length !== Object.keys(newData).length)
2693
+ return false;
2694
+ for (const key in oldData) {
2695
+ if (Object.prototype.hasOwnProperty.call(oldData, key)) {
2696
+ if (oldData[key] !== newData[key])
2697
+ return false;
2698
+ }
2699
+ }
2700
+ return true;
2701
+ }
2178
2702
  /**
2179
2703
  * add listener
2180
2704
  * @param name event name
@@ -2194,7 +2718,10 @@ class EventCenter {
2194
2718
  };
2195
2719
  this.eventList.set(name, eventInfo);
2196
2720
  }
2197
- else if (autoTrigger && Object.getOwnPropertyNames(eventInfo.data).length) {
2721
+ else if (autoTrigger &&
2722
+ Object.keys(eventInfo.data).length &&
2723
+ (!this.queue.includes(name) ||
2724
+ this.isEqual(eventInfo.data, eventInfo.tempData))) {
2198
2725
  // auto trigger when data not null
2199
2726
  f(eventInfo.data);
2200
2727
  }
@@ -2215,21 +2742,27 @@ class EventCenter {
2215
2742
  }
2216
2743
  }
2217
2744
  }
2745
+ /**
2746
+ * clearData
2747
+ */
2748
+ clearData(name) {
2749
+ if (this.isLegalName(name)) {
2750
+ const eventInfo = this.eventList.get(name);
2751
+ if (eventInfo) {
2752
+ eventInfo.data = {};
2753
+ }
2754
+ }
2755
+ }
2218
2756
  // dispatch data
2219
- dispatch(name, data) {
2757
+ dispatch(name, data, nextStep, force, dispatchDataEvent) {
2220
2758
  if (this.isLegalName(name)) {
2221
2759
  if (!isPlainObject(data)) {
2222
2760
  return logError('event-center: data must be object');
2223
2761
  }
2224
2762
  let eventInfo = this.eventList.get(name);
2225
2763
  if (eventInfo) {
2226
- // Update when the data is not equal
2227
- if (eventInfo.data !== data) {
2228
- eventInfo.data = data;
2229
- for (const f of eventInfo.callbacks) {
2230
- f(data);
2231
- }
2232
- }
2764
+ eventInfo.tempData = assign({}, eventInfo.tempData || eventInfo.data, data);
2765
+ !eventInfo.force && (eventInfo.force = !!force);
2233
2766
  }
2234
2767
  else {
2235
2768
  eventInfo = {
@@ -2237,7 +2770,13 @@ class EventCenter {
2237
2770
  callbacks: new Set(),
2238
2771
  };
2239
2772
  this.eventList.set(name, eventInfo);
2773
+ /**
2774
+ * When sent data to parent, eventInfo probably does not exist, because parent may listen to datachange
2775
+ */
2776
+ eventInfo.force = true;
2240
2777
  }
2778
+ // add to queue, event eventInfo is null
2779
+ this.enqueue(name, nextStep, dispatchDataEvent);
2241
2780
  }
2242
2781
  }
2243
2782
  // get data
@@ -2286,10 +2825,13 @@ class EventCenterForGlobal {
2286
2825
  * dispatch global data
2287
2826
  * @param data data
2288
2827
  */
2289
- setGlobalData(data) {
2828
+ setGlobalData(data, nextStep, force) {
2290
2829
  // clear dom scope before dispatch global data, apply to micro app
2291
2830
  removeDomScope();
2292
- eventCenter.dispatch('global', data);
2831
+ eventCenter.dispatch('global', data, (resArr) => isFunction(nextStep) && nextStep(resArr), force);
2832
+ }
2833
+ forceSetGlobalData(data, nextStep) {
2834
+ this.setGlobalData(data, nextStep, true);
2293
2835
  }
2294
2836
  /**
2295
2837
  * get global data
@@ -2297,6 +2839,12 @@ class EventCenterForGlobal {
2297
2839
  getGlobalData() {
2298
2840
  return eventCenter.getData('global');
2299
2841
  }
2842
+ /**
2843
+ * clear global data
2844
+ */
2845
+ clearGlobalData() {
2846
+ eventCenter.clearData('global');
2847
+ }
2300
2848
  /**
2301
2849
  * clear all listener of global data
2302
2850
  * if appName exists, only the specified functions is cleared
@@ -2347,8 +2895,19 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
2347
2895
  * @param appName app.name
2348
2896
  * @param data data
2349
2897
  */
2350
- setData(appName, data) {
2351
- eventCenter.dispatch(formatEventName(formatAppName(appName), true), data);
2898
+ setData(appName, data, nextStep, force) {
2899
+ eventCenter.dispatch(formatEventName(formatAppName(appName), true), data, (resArr) => isFunction(nextStep) && nextStep(resArr), force);
2900
+ }
2901
+ forceSetData(appName, data, nextStep) {
2902
+ this.setData(appName, data, nextStep, true);
2903
+ }
2904
+ /**
2905
+ * clear data from base app
2906
+ * @param appName app.name
2907
+ * @param fromBaseApp whether clear data from child app, default is true
2908
+ */
2909
+ clearData(appName, fromBaseApp = true) {
2910
+ eventCenter.clearData(formatEventName(formatAppName(appName), fromBaseApp));
2352
2911
  }
2353
2912
  /**
2354
2913
  * clear all listener for specified micro app
@@ -2384,25 +2943,36 @@ class EventCenterForMicroApp extends EventCenterForGlobal {
2384
2943
  /**
2385
2944
  * get data from base app
2386
2945
  */
2387
- getData() {
2388
- return eventCenter.getData(formatEventName(this.appName, true));
2946
+ getData(fromBaseApp = true) {
2947
+ return eventCenter.getData(formatEventName(this.appName, fromBaseApp));
2389
2948
  }
2390
2949
  /**
2391
2950
  * dispatch data to base app
2392
2951
  * @param data data
2393
2952
  */
2394
- dispatch(data) {
2953
+ dispatch(data, nextStep, force) {
2395
2954
  removeDomScope();
2396
- eventCenter.dispatch(formatEventName(this.appName, false), data);
2397
- const app = appInstanceMap.get(this.appName);
2398
- if ((app === null || app === void 0 ? void 0 : app.container) && isPlainObject(data)) {
2399
- const event = new CustomEvent('datachange', {
2400
- detail: {
2401
- data,
2402
- }
2403
- });
2404
- getRootContainer(app.container).dispatchEvent(event);
2405
- }
2955
+ eventCenter.dispatch(formatEventName(this.appName, false), data, (resArr) => isFunction(nextStep) && nextStep(resArr), force, () => {
2956
+ const app = appInstanceMap.get(this.appName);
2957
+ if ((app === null || app === void 0 ? void 0 : app.container) && isPlainObject(data)) {
2958
+ const event = new CustomEvent('datachange', {
2959
+ detail: {
2960
+ data: eventCenter.getData(formatEventName(this.appName, false))
2961
+ }
2962
+ });
2963
+ getRootContainer(app.container).dispatchEvent(event);
2964
+ }
2965
+ });
2966
+ }
2967
+ forceDispatch(data, nextStep) {
2968
+ this.dispatch(data, nextStep, true);
2969
+ }
2970
+ /**
2971
+ * clear data from child app
2972
+ * @param fromBaseApp whether clear data from base app, default is false
2973
+ */
2974
+ clearData(fromBaseApp = false) {
2975
+ eventCenter.clearData(formatEventName(this.appName, fromBaseApp));
2406
2976
  }
2407
2977
  /**
2408
2978
  * clear all listeners
@@ -2592,12 +3162,14 @@ function effectDocumentEvent() {
2592
3162
  const { rawDocument, rawDocumentAddEventListener, rawDocumentRemoveEventListener, } = globalEnv;
2593
3163
  !hasRewriteDocumentOnClick && overwriteDocumentOnClick();
2594
3164
  document.addEventListener = function (type, listener, options) {
2595
- var _a;
2596
3165
  const appName = getCurrentAppName();
2597
3166
  /**
2598
3167
  * ignore bound function of document event in umd mode, used to solve problem of react global events
3168
+ * update in 2022-09-02:
3169
+ * boundFunction is no longer exclude, because events in UMD mode will not cleared from v1.0.0-alpha.4
3170
+ * if (appName && !(appInstanceMap.get(appName)?.umdMode && isBoundFunction(listener))) {
2599
3171
  */
2600
- if (appName && !(((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.umdMode) && isBoundFunction(listener))) {
3172
+ if (appName) {
2601
3173
  const appListenersMap = documentEventListenerMap.get(appName);
2602
3174
  if (appListenersMap) {
2603
3175
  const appListenerList = appListenersMap.get(type);
@@ -2616,9 +3188,13 @@ function effectDocumentEvent() {
2616
3188
  rawDocumentAddEventListener.call(rawDocument, type, listener, options);
2617
3189
  };
2618
3190
  document.removeEventListener = function (type, listener, options) {
2619
- var _a;
2620
3191
  const appName = getCurrentAppName();
2621
- if (appName && !(((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.umdMode) && isBoundFunction(listener))) {
3192
+ /**
3193
+ * update in 2022-09-02:
3194
+ * boundFunction is no longer exclude, because events in UMD mode will not cleared from v1.0.0-alpha.4
3195
+ * if (appName && !(appInstanceMap.get(appName)?.umdMode && isBoundFunction(listener))) {
3196
+ */
3197
+ if (appName) {
2622
3198
  const appListenersMap = documentEventListenerMap.get(appName);
2623
3199
  if (appListenersMap) {
2624
3200
  const appListenerList = appListenersMap.get(type);
@@ -2683,69 +3259,80 @@ function effect(appName, microAppWindow) {
2683
3259
  timeoutIdMap.delete(timeoutId);
2684
3260
  rawClearTimeout.call(rawWindow, timeoutId);
2685
3261
  };
2686
- const umdWindowListenerMap = new Map();
2687
- const umdDocumentListenerMap = new Map();
2688
- let umdIntervalIdMap = new Map();
2689
- let umdTimeoutIdMap = new Map();
2690
- let umdOnClickHandler;
2691
- const clearUmdSnapshotData = () => {
2692
- umdWindowListenerMap.clear();
2693
- umdIntervalIdMap.clear();
2694
- umdTimeoutIdMap.clear();
2695
- umdDocumentListenerMap.clear();
2696
- umdOnClickHandler = null;
3262
+ const sstWindowListenerMap = new Map();
3263
+ const sstDocumentListenerMap = new Map();
3264
+ let sstIntervalIdMap = new Map();
3265
+ let sstTimeoutIdMap = new Map();
3266
+ let sstOnClickHandler;
3267
+ const clearSnapshotData = () => {
3268
+ sstWindowListenerMap.clear();
3269
+ sstIntervalIdMap.clear();
3270
+ sstTimeoutIdMap.clear();
3271
+ sstDocumentListenerMap.clear();
3272
+ sstOnClickHandler = null;
2697
3273
  };
2698
- // record event and timer before exec umdMountHook
2699
- const recordUmdEffect = () => {
3274
+ /**
3275
+ * record event and timer
3276
+ * Scenes:
3277
+ * 1. exec umdMountHook in umd mode
3278
+ * 2. hidden keep-alive app
3279
+ * 3. after init prerender app
3280
+ */
3281
+ const recordEffect = () => {
2700
3282
  // record window event
2701
3283
  eventListenerMap.forEach((listenerList, type) => {
2702
3284
  if (listenerList.size) {
2703
- umdWindowListenerMap.set(type, new Set(listenerList));
3285
+ sstWindowListenerMap.set(type, new Set(listenerList));
2704
3286
  }
2705
3287
  });
2706
3288
  // record timers
2707
3289
  if (intervalIdMap.size) {
2708
- umdIntervalIdMap = new Map(intervalIdMap);
3290
+ sstIntervalIdMap = new Map(intervalIdMap);
2709
3291
  }
2710
3292
  if (timeoutIdMap.size) {
2711
- umdTimeoutIdMap = new Map(timeoutIdMap);
3293
+ sstTimeoutIdMap = new Map(timeoutIdMap);
2712
3294
  }
2713
3295
  // record onclick handler
2714
- umdOnClickHandler = documentClickListMap.get(appName);
3296
+ sstOnClickHandler = documentClickListMap.get(appName);
2715
3297
  // record document event
2716
3298
  const documentAppListenersMap = documentEventListenerMap.get(appName);
2717
3299
  if (documentAppListenersMap) {
2718
3300
  documentAppListenersMap.forEach((listenerList, type) => {
2719
3301
  if (listenerList.size) {
2720
- umdDocumentListenerMap.set(type, new Set(listenerList));
3302
+ sstDocumentListenerMap.set(type, new Set(listenerList));
2721
3303
  }
2722
3304
  });
2723
3305
  }
2724
3306
  };
2725
- // rebuild event and timer before remount umd app
2726
- const rebuildUmdEffect = () => {
3307
+ // rebuild event and timer before remount app
3308
+ const rebuildEffect = () => {
2727
3309
  // rebuild window event
2728
- umdWindowListenerMap.forEach((listenerList, type) => {
3310
+ sstWindowListenerMap.forEach((listenerList, type) => {
2729
3311
  for (const listener of listenerList) {
2730
3312
  microAppWindow.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
2731
3313
  }
2732
3314
  });
2733
3315
  // rebuild timer
2734
- umdIntervalIdMap.forEach((info) => {
3316
+ sstIntervalIdMap.forEach((info) => {
2735
3317
  microAppWindow.setInterval(info.handler, info.timeout, ...info.args);
2736
3318
  });
2737
- umdTimeoutIdMap.forEach((info) => {
3319
+ sstTimeoutIdMap.forEach((info) => {
2738
3320
  microAppWindow.setTimeout(info.handler, info.timeout, ...info.args);
2739
3321
  });
2740
3322
  // rebuild onclick event
2741
- umdOnClickHandler && documentClickListMap.set(appName, umdOnClickHandler);
2742
- // rebuild document event
2743
- umdDocumentListenerMap.forEach((listenerList, type) => {
3323
+ sstOnClickHandler && documentClickListMap.set(appName, sstOnClickHandler);
3324
+ /**
3325
+ * rebuild document event
3326
+ * WARNING!!: do not delete setCurrentAppName & removeDomScope
3327
+ */
3328
+ setCurrentAppName(appName);
3329
+ sstDocumentListenerMap.forEach((listenerList, type) => {
2744
3330
  for (const listener of listenerList) {
2745
3331
  document.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
2746
3332
  }
2747
3333
  });
2748
- clearUmdSnapshotData();
3334
+ removeDomScope();
3335
+ clearSnapshotData();
2749
3336
  };
2750
3337
  // release all event listener & interval & timeout when unmount app
2751
3338
  const releaseEffect = () => {
@@ -2785,8 +3372,8 @@ function effect(appName, microAppWindow) {
2785
3372
  }
2786
3373
  };
2787
3374
  return {
2788
- recordUmdEffect,
2789
- rebuildUmdEffect,
3375
+ recordEffect,
3376
+ rebuildEffect,
2790
3377
  releaseEffect,
2791
3378
  };
2792
3379
  }
@@ -2812,7 +3399,6 @@ function removeMicroState(appName, rawState) {
2812
3399
  delete rawState.microAppState;
2813
3400
  }
2814
3401
  }
2815
- // 生成新的state对象
2816
3402
  return assign({}, rawState);
2817
3403
  }
2818
3404
  // get micro app state form origin state
@@ -2824,12 +3410,15 @@ const ENC_AD_RE = /&/g; // %M1
2824
3410
  const ENC_EQ_RE = /=/g; // %M2
2825
3411
  const DEC_AD_RE = /%M1/g; // &
2826
3412
  const DEC_EQ_RE = /%M2/g; // =
3413
+ // encode path with special symbol
2827
3414
  function encodeMicroPath(path) {
2828
3415
  return encodeURIComponent(commonDecode(path).replace(ENC_AD_RE, '%M1').replace(ENC_EQ_RE, '%M2'));
2829
3416
  }
3417
+ // decode path
2830
3418
  function decodeMicroPath(path) {
2831
3419
  return commonDecode(path).replace(DEC_AD_RE, '&').replace(DEC_EQ_RE, '=');
2832
3420
  }
3421
+ // Recursively resolve address
2833
3422
  function commonDecode(path) {
2834
3423
  try {
2835
3424
  const decPath = decodeURIComponent(path);
@@ -2841,12 +3430,15 @@ function commonDecode(path) {
2841
3430
  return path;
2842
3431
  }
2843
3432
  }
2844
- // 格式化query参数key,防止与原有参数的冲突
3433
+ // Format the query parameter key to prevent conflicts with the original parameters
2845
3434
  function formatQueryAppName(appName) {
2846
3435
  // return `app-${appName}`
2847
3436
  return appName;
2848
3437
  }
2849
- // 根据浏览器url参数,获取当前子应用的path
3438
+ /**
3439
+ * Get app fullPath from browser url
3440
+ * @param appName app.name
3441
+ */
2850
3442
  function getMicroPathFromURL(appName) {
2851
3443
  var _a, _b;
2852
3444
  const rawLocation = globalEnv.rawWindow.location;
@@ -2854,15 +3446,23 @@ function getMicroPathFromURL(appName) {
2854
3446
  const microPath = ((_a = queryObject.hashQuery) === null || _a === void 0 ? void 0 : _a[formatQueryAppName(appName)]) || ((_b = queryObject.searchQuery) === null || _b === void 0 ? void 0 : _b[formatQueryAppName(appName)]);
2855
3447
  return isString(microPath) ? decodeMicroPath(microPath) : null;
2856
3448
  }
2857
- // 将name=encodeUrl地址插入到浏览器url上
3449
+ /**
3450
+ * Attach child app fullPath to browser url
3451
+ * @param appName app.name
3452
+ * @param microLocation location of child app
3453
+ */
2858
3454
  function setMicroPathToURL(appName, microLocation) {
2859
3455
  let { pathname, search, hash } = globalEnv.rawWindow.location;
2860
3456
  const queryObject = getQueryObjectFromURL(search, hash);
2861
3457
  const encodedMicroPath = encodeMicroPath(microLocation.pathname +
2862
3458
  microLocation.search +
2863
3459
  microLocation.hash);
2864
- let isAttach2Hash = false; // 基座是否是hash模式,这个其实也不准,只是表示参数加到了hash上
2865
- // hash存在且search不存在,则认为是hash路由
3460
+ /**
3461
+ * Is parent is hash router
3462
+ * In fact, this is not true. It just means that the parameter is added to the hash
3463
+ */
3464
+ let isAttach2Hash = false;
3465
+ // If hash exists and search does not exist, it is considered as a hash route
2866
3466
  if (hash && !search) {
2867
3467
  isAttach2Hash = true;
2868
3468
  if (queryObject.hashQuery) {
@@ -2892,7 +3492,11 @@ function setMicroPathToURL(appName, microLocation) {
2892
3492
  isAttach2Hash,
2893
3493
  };
2894
3494
  }
2895
- // 将name=encodeUrl的参数从浏览器url上删除
3495
+ /**
3496
+ * Delete child app fullPath from browser url
3497
+ * @param appName app.name
3498
+ * @param targetLocation target Location, default is rawLocation
3499
+ */
2896
3500
  function removeMicroPathFromURL(appName, targetLocation) {
2897
3501
  var _a, _b, _c, _d;
2898
3502
  let { pathname, search, hash } = targetLocation || globalEnv.rawWindow.location;
@@ -2915,7 +3519,7 @@ function removeMicroPathFromURL(appName, targetLocation) {
2915
3519
  };
2916
3520
  }
2917
3521
  /**
2918
- * 根据location获取query对象
3522
+ * Format search, hash to object
2919
3523
  */
2920
3524
  function getQueryObjectFromURL(search, hash) {
2921
3525
  const queryObject = {};
@@ -2937,6 +3541,18 @@ function getNoHashMicroPathFromURL(appName, baseUrl) {
2937
3541
  const formatLocation = createURL(microPath, baseUrl);
2938
3542
  return formatLocation.origin + formatLocation.pathname + formatLocation.search;
2939
3543
  }
3544
+ /**
3545
+ * Effect app is an app that can perform route navigation
3546
+ * NOTE: Invalid app action
3547
+ * 1. prevent update browser url, dispatch popStateEvent, reload browser
3548
+ * 2. It can update path with pushState/replaceState
3549
+ * 3. Can not update path outside (with router api)
3550
+ * 3. Can not update path by location
3551
+ */
3552
+ function isEffectiveApp(appName) {
3553
+ const app = appInstanceMap.get(appName);
3554
+ return !!(app && !app.isPrefetch);
3555
+ }
2940
3556
 
2941
3557
  /**
2942
3558
  * dispatch PopStateEvent & HashChangeEvent to child app
@@ -2953,7 +3569,8 @@ function addHistoryListener(appName) {
2953
3569
  * 1. unmount app & hidden keep-alive app will not receive popstate event
2954
3570
  * 2. filter out onlyForBrowser
2955
3571
  */
2956
- if (getActiveApps(true).includes(appName) && !e.onlyForBrowser) {
3572
+ if (getActiveApps({ excludeHiddenApp: true, excludePreRender: true }).includes(appName) &&
3573
+ !e.onlyForBrowser) {
2957
3574
  const microPath = getMicroPathFromURL(appName);
2958
3575
  const app = appInstanceMap.get(appName);
2959
3576
  const proxyWindow = app.sandBox.proxyWindow;
@@ -2989,9 +3606,19 @@ function addHistoryListener(appName) {
2989
3606
  function dispatchPopStateEventToMicroApp(appName, proxyWindow) {
2990
3607
  // create PopStateEvent named popstate-appName with sub app state
2991
3608
  const newPopStateEvent = new PopStateEvent(formatEventName$1('popstate', appName), { state: getMicroState(appName) });
3609
+ /**
3610
+ * angular14 takes e.type as type judgment
3611
+ * when e.type is popstate-appName popstate event will be invalid
3612
+ */
3613
+ // Object.defineProperty(newPopStateEvent, 'type', {
3614
+ // value: 'popstate',
3615
+ // writable: true,
3616
+ // configurable: true,
3617
+ // enumerable: true,
3618
+ // })
2992
3619
  globalEnv.rawWindow.dispatchEvent(newPopStateEvent);
2993
3620
  // call function window.onpopstate if it exists
2994
- typeof proxyWindow.onpopstate === 'function' && proxyWindow.onpopstate(newPopStateEvent);
3621
+ isFunction(proxyWindow.onpopstate) && proxyWindow.onpopstate(newPopStateEvent);
2995
3622
  }
2996
3623
  /**
2997
3624
  * dispatch formatted hashchange event to microApp
@@ -3006,7 +3633,7 @@ function dispatchHashChangeEventToMicroApp(appName, proxyWindow, oldHref) {
3006
3633
  });
3007
3634
  globalEnv.rawWindow.dispatchEvent(newHashChangeEvent);
3008
3635
  // call function window.onhashchange if it exists
3009
- typeof proxyWindow.onhashchange === 'function' && proxyWindow.onhashchange(newHashChangeEvent);
3636
+ isFunction(proxyWindow.onhashchange) && proxyWindow.onhashchange(newHashChangeEvent);
3010
3637
  }
3011
3638
  /**
3012
3639
  * dispatch native PopStateEvent, simulate location behavior
@@ -3031,15 +3658,18 @@ function dispatchNativeHashChangeEvent(oldHref) {
3031
3658
  }
3032
3659
  /**
3033
3660
  * dispatch popstate & hashchange event to browser
3661
+ * @param appName app.name
3034
3662
  * @param onlyForBrowser only dispatch event to browser
3035
3663
  * @param oldHref old href of rawWindow.location
3036
3664
  */
3037
- function dispatchNativeEvent(onlyForBrowser, oldHref) {
3665
+ function dispatchNativeEvent(appName, onlyForBrowser, oldHref) {
3038
3666
  // clear element scope before dispatch global event
3039
3667
  removeDomScope();
3040
- dispatchNativePopStateEvent(onlyForBrowser);
3041
- if (oldHref) {
3042
- dispatchNativeHashChangeEvent(oldHref);
3668
+ if (isEffectiveApp(appName)) {
3669
+ dispatchNativePopStateEvent(onlyForBrowser);
3670
+ if (oldHref) {
3671
+ dispatchNativeHashChangeEvent(oldHref);
3672
+ }
3043
3673
  }
3044
3674
  }
3045
3675
 
@@ -3056,7 +3686,7 @@ function createMicroHistory(appName, microLocation) {
3056
3686
  if (isString(rests[2]) || isURL(rests[2])) {
3057
3687
  const targetLocation = createURL(rests[2], microLocation.href);
3058
3688
  if (targetLocation.origin === microLocation.origin) {
3059
- navigateWithNativeEvent(methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rests[0]), rests[1]);
3689
+ navigateWithNativeEvent(appName, methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rests[0]), rests[1]);
3060
3690
  const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3061
3691
  if (targetFullPath !== microLocation.fullPath) {
3062
3692
  updateMicroLocation(appName, targetFullPath, microLocation);
@@ -3064,7 +3694,7 @@ function createMicroHistory(appName, microLocation) {
3064
3694
  return void 0;
3065
3695
  }
3066
3696
  }
3067
- nativeHistoryNavigate(methodName, rests[2], rests[0], rests[1]);
3697
+ nativeHistoryNavigate(appName, methodName, rests[2], rests[0], rests[1]);
3068
3698
  };
3069
3699
  }
3070
3700
  const pushState = getMicroHistoryMethod('pushState');
@@ -3096,14 +3726,17 @@ function createMicroHistory(appName, microLocation) {
3096
3726
  }
3097
3727
  /**
3098
3728
  * navigate to new path base on native method of history
3729
+ * @param appName app.name
3099
3730
  * @param methodName pushState/replaceState
3100
3731
  * @param fullPath full path
3101
3732
  * @param state history.state, default is null
3102
3733
  * @param title history.title, default is ''
3103
3734
  */
3104
- function nativeHistoryNavigate(methodName, fullPath, state = null, title = '') {
3105
- const method = methodName === 'pushState' ? globalEnv.rawPushState : globalEnv.rawReplaceState;
3106
- method.call(globalEnv.rawWindow.history, state, title, fullPath);
3735
+ function nativeHistoryNavigate(appName, methodName, fullPath, state = null, title = '') {
3736
+ if (isEffectiveApp(appName)) {
3737
+ const method = methodName === 'pushState' ? globalEnv.rawPushState : globalEnv.rawReplaceState;
3738
+ method.call(globalEnv.rawWindow.history, state, title, fullPath);
3739
+ }
3107
3740
  }
3108
3741
  /**
3109
3742
  * Navigate to new path, and dispatch native popStateEvent/hashChangeEvent to browser
@@ -3112,29 +3745,33 @@ function nativeHistoryNavigate(methodName, fullPath, state = null, title = '') {
3112
3745
  * 2. proxyHistory.pushState/replaceState with limited popstateEvent
3113
3746
  * 3. api microApp.router.push/replace
3114
3747
  * 4. proxyLocation.hash = xxx
3748
+ * @param appName app.name
3115
3749
  * @param methodName pushState/replaceState
3116
3750
  * @param result result of add/remove microApp path on browser url
3117
3751
  * @param onlyForBrowser only dispatch event to browser
3118
3752
  * @param state history.state, not required
3119
3753
  * @param title history.title, not required
3120
3754
  */
3121
- function navigateWithNativeEvent(methodName, result, onlyForBrowser, state, title) {
3122
- const rawLocation = globalEnv.rawWindow.location;
3123
- const oldFullPath = rawLocation.pathname + rawLocation.search + rawLocation.hash;
3124
- const oldHref = result.isAttach2Hash && oldFullPath !== result.fullPath ? rawLocation.href : null;
3125
- // navigate with native history method
3126
- nativeHistoryNavigate(methodName, result.fullPath, state, title);
3127
- if (oldFullPath !== result.fullPath)
3128
- dispatchNativeEvent(onlyForBrowser, oldHref);
3755
+ function navigateWithNativeEvent(appName, methodName, result, onlyForBrowser, state, title) {
3756
+ if (isEffectiveApp(appName)) {
3757
+ const rawLocation = globalEnv.rawWindow.location;
3758
+ const oldFullPath = rawLocation.pathname + rawLocation.search + rawLocation.hash;
3759
+ const oldHref = result.isAttach2Hash && oldFullPath !== result.fullPath ? rawLocation.href : null;
3760
+ // navigate with native history method
3761
+ nativeHistoryNavigate(appName, methodName, result.fullPath, state, title);
3762
+ if (oldFullPath !== result.fullPath)
3763
+ dispatchNativeEvent(appName, onlyForBrowser, oldHref);
3764
+ }
3129
3765
  }
3130
3766
  /**
3131
3767
  * update browser url when mount/unmount/hidden/show/attachToURL/attachAllToURL
3132
3768
  * just attach microRoute info to browser, dispatch event to base app(exclude child)
3769
+ * @param appName app.name
3133
3770
  * @param result result of add/remove microApp path on browser url
3134
3771
  * @param state history.state
3135
3772
  */
3136
- function attachRouteToBrowserURL(result, state) {
3137
- navigateWithNativeEvent('replaceState', result, true, state);
3773
+ function attachRouteToBrowserURL(appName, result, state) {
3774
+ navigateWithNativeEvent(appName, 'replaceState', result, true, state);
3138
3775
  }
3139
3776
  /**
3140
3777
  * When path is same, keep the microAppState in history.state
@@ -3163,12 +3800,14 @@ function reWriteHistoryMethod(method) {
3163
3800
  * 1. Exec after apply pushState/replaceState
3164
3801
  * 2. Unable to catch when base app navigate with location
3165
3802
  * 3. When in nest app, rawPushState/rawReplaceState has been modified by parent
3166
- * 4.
3167
3803
  */
3168
- getActiveApps(true).forEach(appName => {
3804
+ getActiveApps({
3805
+ excludeHiddenApp: true,
3806
+ excludePreRender: true,
3807
+ }).forEach(appName => {
3169
3808
  const app = appInstanceMap.get(appName);
3170
3809
  if (app.sandBox && app.useMemoryRouter && !getMicroPathFromURL(appName)) {
3171
- attachRouteToBrowserURL(setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName)));
3810
+ attachRouteToBrowserURL(appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName)));
3172
3811
  }
3173
3812
  });
3174
3813
  // fix bug for nest app
@@ -3200,7 +3839,7 @@ function createRouterApi() {
3200
3839
  * @param state to.state
3201
3840
  */
3202
3841
  function navigateWithRawHistory(appName, methodName, targetLocation, state) {
3203
- navigateWithNativeEvent(methodName, setMicroPathToURL(appName, targetLocation), false, setMicroState(appName, state !== null && state !== void 0 ? state : null));
3842
+ navigateWithNativeEvent(appName, methodName, setMicroPathToURL(appName, targetLocation), false, setMicroState(appName, state !== null && state !== void 0 ? state : null));
3204
3843
  // clear element scope after navigate
3205
3844
  removeDomScope();
3206
3845
  }
@@ -3221,7 +3860,7 @@ function createRouterApi() {
3221
3860
  return logError(`navigation failed, memory router of app ${appName} is closed`);
3222
3861
  }
3223
3862
  // active apps, include hidden keep-alive app
3224
- if (getActiveApps().includes(appName)) {
3863
+ if (getActiveApps({ excludePreRender: true }).includes(appName)) {
3225
3864
  const microLocation = app.sandBox.proxyWindow.location;
3226
3865
  const targetLocation = createURL(to.path, microLocation.href);
3227
3866
  // Only get path data, even if the origin is different from microApp
@@ -3273,7 +3912,7 @@ function createRouterApi() {
3273
3912
  removeDomScope();
3274
3913
  for (const guard of guards) {
3275
3914
  if (isFunction(guard)) {
3276
- guard(appName, to, from);
3915
+ guard(to, from, appName);
3277
3916
  }
3278
3917
  else if (isPlainObject(guard) && isFunction(guard[appName])) {
3279
3918
  guard[appName](to, from);
@@ -3304,7 +3943,7 @@ function createRouterApi() {
3304
3943
  function commonHandlerForAttachToURL(appName) {
3305
3944
  const app = appInstanceMap.get(appName);
3306
3945
  if (app.sandBox && app.useMemoryRouter) {
3307
- attachRouteToBrowserURL(setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName)));
3946
+ attachRouteToBrowserURL(appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName)));
3308
3947
  }
3309
3948
  }
3310
3949
  /**
@@ -3319,10 +3958,14 @@ function createRouterApi() {
3319
3958
  }
3320
3959
  /**
3321
3960
  * Attach all active app router info to browser url
3322
- * exclude hidden keep-alive app
3961
+ * @param includeHiddenApp include hidden keep-alive app
3962
+ * @param includePreRender include preRender app
3323
3963
  */
3324
- function attachAllToURL(includeHiddenApp = false) {
3325
- getActiveApps(!includeHiddenApp).forEach(appName => commonHandlerForAttachToURL(appName));
3964
+ function attachAllToURL({ includeHiddenApp = false, includePreRender = false, }) {
3965
+ getActiveApps({
3966
+ excludeHiddenApp: !includeHiddenApp,
3967
+ excludePreRender: !includePreRender,
3968
+ }).forEach(appName => commonHandlerForAttachToURL(appName));
3326
3969
  }
3327
3970
  function createDefaultPageApi() {
3328
3971
  // defaultPage data
@@ -3337,7 +3980,7 @@ function createRouterApi() {
3337
3980
  function setDefaultPage(options) {
3338
3981
  const appName = formatAppName(options.name);
3339
3982
  if (!appName || !options.path) {
3340
- if (process.env.NODE_ENV !== 'production') {
3983
+ if ((process.env.NODE_ENV !== 'production')) {
3341
3984
  if (!appName) {
3342
3985
  logWarn(`setDefaultPage: invalid appName "${appName}"`);
3343
3986
  }
@@ -3380,7 +4023,7 @@ function createRouterApi() {
3380
4023
  }
3381
4024
  });
3382
4025
  }
3383
- else if (process.env.NODE_ENV !== 'production') {
4026
+ else if ((process.env.NODE_ENV !== 'production')) {
3384
4027
  logWarn('setBaseAppRouter: Invalid base router');
3385
4028
  }
3386
4029
  }
@@ -3448,13 +4091,13 @@ function createMicroLocation(appName, url) {
3448
4091
  if (targetLocation.hash !== shadowLocation.hash) {
3449
4092
  if (setMicroPathResult.isAttach2Hash)
3450
4093
  oldHref = rawLocation.href;
3451
- nativeHistoryNavigate(methodName, setMicroPathResult.fullPath);
4094
+ nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath);
3452
4095
  }
3453
4096
  if (targetLocation.hash) {
3454
- dispatchNativeEvent(false, oldHref);
4097
+ dispatchNativeEvent(appName, false, oldHref);
3455
4098
  }
3456
4099
  else {
3457
- rawLocation.reload();
4100
+ rawReload();
3458
4101
  }
3459
4102
  return void 0;
3460
4103
  /**
@@ -3463,8 +4106,8 @@ function createMicroLocation(appName, url) {
3463
4106
  */
3464
4107
  }
3465
4108
  else if (setMicroPathResult.isAttach2Hash) {
3466
- nativeHistoryNavigate(methodName, setMicroPathResult.fullPath);
3467
- rawLocation.reload();
4109
+ nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath);
4110
+ rawReload();
3468
4111
  return void 0;
3469
4112
  }
3470
4113
  value = setMicroPathResult.fullPath;
@@ -3494,7 +4137,7 @@ function createMicroLocation(appName, url) {
3494
4137
  // When the browser url has a hash value, the same pathname/search will not refresh browser
3495
4138
  if (targetLocation[key] === shadowLocation[key] && shadowLocation.hash) {
3496
4139
  // The href has not changed, not need to dispatch hashchange event
3497
- dispatchNativeEvent(false);
4140
+ dispatchNativeEvent(appName, false);
3498
4141
  }
3499
4142
  else {
3500
4143
  /**
@@ -3503,19 +4146,24 @@ function createMicroLocation(appName, url) {
3503
4146
  * pathname: /path ==> /path#hash, /path ==> /path?query
3504
4147
  * search: ?query ==> ?query#hash
3505
4148
  */
3506
- nativeHistoryNavigate(targetLocation[key] === shadowLocation[key] ? 'replaceState' : 'pushState', setMicroPathToURL(appName, targetLocation).fullPath);
3507
- rawLocation.reload();
4149
+ nativeHistoryNavigate(appName, targetLocation[key] === shadowLocation[key] ? 'replaceState' : 'pushState', setMicroPathToURL(appName, targetLocation).fullPath);
4150
+ rawReload();
3508
4151
  }
3509
4152
  }
4153
+ function rawReload() {
4154
+ isEffectiveApp(appName) && rawLocation.reload();
4155
+ }
3510
4156
  /**
3511
4157
  * Special processing for four keys: href, pathname, search and hash
3512
4158
  * They take values from shadowLocation, and require special operations when assigning values
3513
4159
  */
3514
4160
  rawDefineProperties(microLocation, {
3515
4161
  href: createPropertyDescriptor(() => shadowLocation.href, (value) => {
3516
- const targetPath = commonHandler(value, 'pushState');
3517
- if (targetPath)
3518
- rawLocation.href = targetPath;
4162
+ if (isEffectiveApp(appName)) {
4163
+ const targetPath = commonHandler(value, 'pushState');
4164
+ if (targetPath)
4165
+ rawLocation.href = targetPath;
4166
+ }
3519
4167
  }),
3520
4168
  pathname: createPropertyDescriptor(() => shadowLocation.pathname, (value) => {
3521
4169
  const targetPath = ('/' + value).replace(/^\/+/, '/') + shadowLocation.search + shadowLocation.hash;
@@ -3530,16 +4178,18 @@ function createMicroLocation(appName, url) {
3530
4178
  const targetLocation = createURL(targetPath, url);
3531
4179
  // The same hash will not trigger popStateEvent
3532
4180
  if (targetLocation.hash !== shadowLocation.hash) {
3533
- navigateWithNativeEvent('pushState', setMicroPathToURL(appName, targetLocation), false);
4181
+ navigateWithNativeEvent(appName, 'pushState', setMicroPathToURL(appName, targetLocation), false);
3534
4182
  }
3535
4183
  }),
3536
4184
  fullPath: createPropertyDescriptor(() => shadowLocation.pathname + shadowLocation.search + shadowLocation.hash, noop),
3537
4185
  });
3538
4186
  const createLocationMethod = (locationMethodName) => {
3539
4187
  return function (value) {
3540
- const targetPath = commonHandler(value, locationMethodName === 'assign' ? 'pushState' : 'replaceState');
3541
- if (targetPath)
3542
- rawLocation[locationMethodName](targetPath);
4188
+ if (isEffectiveApp(appName)) {
4189
+ const targetPath = commonHandler(value, locationMethodName === 'assign' ? 'pushState' : 'replaceState');
4190
+ if (targetPath)
4191
+ rawLocation[locationMethodName](targetPath);
4192
+ }
3543
4193
  };
3544
4194
  };
3545
4195
  return assign(microLocation, {
@@ -3614,7 +4264,12 @@ function createMicroRouter(appName, url) {
3614
4264
  microHistory: createMicroHistory(appName, microLocation),
3615
4265
  };
3616
4266
  }
3617
- // 当沙箱执行start, 或者隐藏的keep-alive应用重新渲染时时才根据浏览器url更新location 或者 将参数更新到url上
4267
+ /**
4268
+ * 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
4269
+ * @param appName app.name
4270
+ * @param microLocation MicroLocation for sandbox
4271
+ * @param defaultPage default page
4272
+ */
3618
4273
  function initRouteStateWithURL(appName, microLocation, defaultPage) {
3619
4274
  const microPath = getMicroPathFromURL(appName);
3620
4275
  if (microPath) {
@@ -3633,7 +4288,7 @@ function updateBrowserURLWithLocation(appName, microLocation, defaultPage) {
3633
4288
  if (defaultPage)
3634
4289
  updateMicroLocation(appName, defaultPage, microLocation, 'prevent');
3635
4290
  // attach microApp route info to browser URL
3636
- attachRouteToBrowserURL(setMicroPathToURL(appName, microLocation), setMicroState(appName, null));
4291
+ attachRouteToBrowserURL(appName, setMicroPathToURL(appName, microLocation), setMicroState(appName, null));
3637
4292
  // trigger guards after change browser URL
3638
4293
  autoTriggerNavigationGuard(appName, microLocation);
3639
4294
  }
@@ -3657,7 +4312,7 @@ function clearRouteStateFromURL(appName, url, microLocation, keepRouteState) {
3657
4312
  * called on sandbox.stop or hidden of keep-alive app
3658
4313
  */
3659
4314
  function removeStateAndPathFromBrowser(appName) {
3660
- attachRouteToBrowserURL(removeMicroPathFromURL(appName), removeMicroState(appName, globalEnv.rawWindow.history.state));
4315
+ attachRouteToBrowserURL(appName, removeMicroPathFromURL(appName), removeMicroState(appName, globalEnv.rawWindow.history.state));
3661
4316
  }
3662
4317
 
3663
4318
  /**
@@ -3766,7 +4421,7 @@ function useMicroEventSource() {
3766
4421
  const { createMicroEventSource, clearMicroEventSource } = useMicroEventSource();
3767
4422
  const globalPropertyList = ['window', 'self', 'globalThis'];
3768
4423
  class SandBox {
3769
- constructor(appName, url, useMemoryRouter = true) {
4424
+ constructor(appName, url) {
3770
4425
  /**
3771
4426
  * Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
3772
4427
  * Fix https://github.com/micro-zoe/micro-app/issues/234
@@ -3789,28 +4444,38 @@ class SandBox {
3789
4444
  // create proxyWindow with Proxy(microAppWindow)
3790
4445
  this.proxyWindow = this.createProxyWindow(appName);
3791
4446
  // Rewrite global event listener & timeout
3792
- assign(this, effect(appName, this.microAppWindow));
4447
+ this.effectController = effect(appName, this.microAppWindow);
3793
4448
  // inject global properties
3794
- this.initStaticGlobalKeys(this.microAppWindow, appName, url, useMemoryRouter);
4449
+ this.initStaticGlobalKeys(this.microAppWindow, appName, url);
3795
4450
  }
3796
- // TODO: 重构
3797
- start(umdMode = false, baseRoute = '', useMemoryRouter = true, defaultPage = '', disablePatchRequest = false) {
4451
+ /**
4452
+ * open sandbox and perform some initial actions
4453
+ * @param umdMode is umd mode
4454
+ * @param baseroute base route for child
4455
+ * @param useMemoryRouter use virtual router
4456
+ * @param defaultPage default page when mount child base on virtual router
4457
+ * @param disablePatchRequest prevent patchRequestApi
4458
+ */
4459
+ start({ umdMode, baseroute, useMemoryRouter, defaultPage, disablePatchRequest, }) {
3798
4460
  if (!this.active) {
3799
4461
  this.active = true;
3800
4462
  if (useMemoryRouter) {
4463
+ if (isUndefined(this.microAppWindow.location)) {
4464
+ this.setMicroAppRouter(this.microAppWindow, this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__);
4465
+ }
3801
4466
  this.initRouteState(defaultPage);
3802
4467
  // unique listener of popstate event for sub app
3803
- this.removeHistoryListener = addHistoryListener(this.proxyWindow.__MICRO_APP_NAME__);
4468
+ this.removeHistoryListener = addHistoryListener(this.microAppWindow.__MICRO_APP_NAME__);
3804
4469
  }
3805
4470
  else {
3806
- this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseRoute;
4471
+ this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute;
3807
4472
  }
3808
4473
  /**
3809
4474
  * 1. prevent the key deleted during sandBox.stop after rewrite
3810
4475
  * 2. umd mode will not delete any keys during sandBox.stop
3811
4476
  */
3812
4477
  if (!umdMode) {
3813
- this.initGlobalKeysWhenStart(this.microAppWindow, this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.__MICRO_APP_URL__, disablePatchRequest);
4478
+ this.initGlobalKeysWhenStart(this.microAppWindow, this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__, disablePatchRequest);
3814
4479
  }
3815
4480
  if (++SandBox.activeCount === 1) {
3816
4481
  effectDocumentEvent();
@@ -3821,18 +4486,24 @@ class SandBox {
3821
4486
  fixBabelPolyfill6();
3822
4487
  }
3823
4488
  }
3824
- stop(umdMode, keepRouteState, clearEventSource) {
4489
+ /**
4490
+ * close sandbox and perform some clean up actions
4491
+ * @param umdMode is umd mode
4492
+ * @param keepRouteState prevent reset route
4493
+ * @param clearEventSource clear MicroEventSource when destroy
4494
+ * @param clearData clear data from base app
4495
+ */
4496
+ stop({ umdMode, keepRouteState, clearEventSource, clearData, }) {
3825
4497
  if (this.active) {
3826
- this.releaseEffect();
3827
- this.microAppWindow.microApp.clearDataListener();
3828
- this.microAppWindow.microApp.clearGlobalDataListener();
4498
+ // clear global event, timeout, data listener
4499
+ this.releaseGlobalEffect(clearData);
3829
4500
  if (this.removeHistoryListener) {
3830
4501
  this.clearRouteState(keepRouteState);
3831
4502
  // release listener of popstate
3832
4503
  this.removeHistoryListener();
3833
4504
  }
3834
4505
  if (clearEventSource) {
3835
- clearMicroEventSource(this.proxyWindow.__MICRO_APP_NAME__);
4506
+ clearMicroEventSource(this.microAppWindow.__MICRO_APP_NAME__);
3836
4507
  }
3837
4508
  /**
3838
4509
  * NOTE:
@@ -3858,10 +4529,33 @@ class SandBox {
3858
4529
  this.active = false;
3859
4530
  }
3860
4531
  }
3861
- // record umd snapshot before the first execution of umdHookMount
3862
- recordUmdSnapshot() {
4532
+ /**
4533
+ * clear global event, timeout, data listener
4534
+ * Scenes:
4535
+ * 1. unmount of normal/umd app
4536
+ * 2. hidden keep-alive app
4537
+ * 3. after init prerender app
4538
+ * @param clearData clear data from base app
4539
+ */
4540
+ releaseGlobalEffect(clearData = false) {
4541
+ this.effectController.releaseEffect();
4542
+ this.microAppWindow.microApp.clearDataListener();
4543
+ this.microAppWindow.microApp.clearGlobalDataListener();
4544
+ if (clearData) {
4545
+ microApp.clearData(this.microAppWindow.__MICRO_APP_NAME__);
4546
+ this.microAppWindow.microApp.clearData();
4547
+ }
4548
+ }
4549
+ /**
4550
+ * record umd snapshot before the first execution of umdHookMount
4551
+ * Scenes:
4552
+ * 1. exec umdMountHook in umd mode
4553
+ * 2. hidden keep-alive app
4554
+ * 3. after init prerender app
4555
+ */
4556
+ recordEffectSnapshot() {
3863
4557
  // this.microAppWindow.__MICRO_APP_UMD_MODE__ = true
3864
- this.recordUmdEffect();
4558
+ this.effectController.recordEffect();
3865
4559
  recordDataCenterSnapshot(this.microAppWindow.microApp);
3866
4560
  // this.recordUmdInjectedValues = new Map<PropertyKey, unknown>()
3867
4561
  // this.injectedKeys.forEach((key: PropertyKey) => {
@@ -3869,13 +4563,17 @@ class SandBox {
3869
4563
  // })
3870
4564
  }
3871
4565
  // rebuild umd snapshot before remount umd app
3872
- rebuildUmdSnapshot() {
4566
+ rebuildEffectSnapshot() {
3873
4567
  // this.recordUmdInjectedValues!.forEach((value: unknown, key: PropertyKey) => {
3874
4568
  // Reflect.set(this.proxyWindow, key, value)
3875
4569
  // })
3876
- this.rebuildUmdEffect();
4570
+ this.effectController.rebuildEffect();
3877
4571
  rebuildDataCenterSnapshot(this.microAppWindow.microApp);
3878
4572
  }
4573
+ // set __MICRO_APP_PRE_RENDER__ state
4574
+ setPreRenderState(state) {
4575
+ this.microAppWindow.__MICRO_APP_PRE_RENDER__ = state;
4576
+ }
3879
4577
  /**
3880
4578
  * get scopeProperties and escapeProperties from plugins & adapter
3881
4579
  * @param appName app name
@@ -3883,9 +4581,9 @@ class SandBox {
3883
4581
  getSpecialProperties(appName) {
3884
4582
  var _a;
3885
4583
  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]);
4584
+ if (isPlainObject(microApp.options.plugins)) {
4585
+ this.commonActionForSpecialProperties(microApp.options.plugins.global);
4586
+ this.commonActionForSpecialProperties((_a = microApp.options.plugins.modules) === null || _a === void 0 ? void 0 : _a[appName]);
3889
4587
  }
3890
4588
  }
3891
4589
  // common action for global plugins and module plugins
@@ -4003,12 +4701,13 @@ class SandBox {
4003
4701
  * @param url app url
4004
4702
  * @param useMemoryRouter whether use memory router
4005
4703
  */
4006
- initStaticGlobalKeys(microAppWindow, appName, url, useMemoryRouter) {
4704
+ initStaticGlobalKeys(microAppWindow, appName, url) {
4007
4705
  microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
4008
4706
  microAppWindow.__MICRO_APP_NAME__ = appName;
4009
4707
  microAppWindow.__MICRO_APP_URL__ = url;
4010
4708
  microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
4011
4709
  microAppWindow.__MICRO_APP_WINDOW__ = microAppWindow;
4710
+ microAppWindow.__MICRO_APP_PRE_RENDER__ = false;
4012
4711
  microAppWindow.rawWindow = globalEnv.rawWindow;
4013
4712
  microAppWindow.rawDocument = globalEnv.rawDocument;
4014
4713
  microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
@@ -4018,8 +4717,6 @@ class SandBox {
4018
4717
  });
4019
4718
  this.setProxyDocument(microAppWindow, appName);
4020
4719
  this.setMappingPropertiesWithRawDescriptor(microAppWindow);
4021
- if (useMemoryRouter)
4022
- this.setMicroAppRouter(microAppWindow, appName, url);
4023
4720
  }
4024
4721
  setProxyDocument(microAppWindow, appName) {
4025
4722
  const { proxyDocument, MicroDocument } = this.createProxyDocument(appName);
@@ -4197,17 +4894,19 @@ class SandBox {
4197
4894
  return element;
4198
4895
  };
4199
4896
  const proxyDocument = new Proxy(rawDocument, {
4200
- get(target, key) {
4897
+ get: (target, key) => {
4201
4898
  throttleDeferForSetAppName(appName);
4202
4899
  throttleDeferForParentNode(proxyDocument);
4203
4900
  if (key === 'createElement')
4204
4901
  return createElement;
4205
4902
  if (key === Symbol.toStringTag)
4206
4903
  return 'ProxyDocument';
4904
+ if (key === 'defaultView')
4905
+ return this.proxyWindow;
4207
4906
  const rawValue = Reflect.get(target, key);
4208
4907
  return isFunction(rawValue) ? bindFunctionToRawObject(rawDocument, rawValue, 'DOCUMENT') : rawValue;
4209
4908
  },
4210
- set(target, key, value) {
4909
+ set: (target, key, value) => {
4211
4910
  // Fix TypeError: Illegal invocation when set document.title
4212
4911
  Reflect.set(target, key, value);
4213
4912
  /**
@@ -4299,10 +4998,8 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
4299
4998
  });
4300
4999
  formatEventInfo(event, element);
4301
5000
  // 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);
5001
+ if (isFunction((_a = microApp.options.lifeCycles) === null || _a === void 0 ? void 0 : _a[lifecycleName])) {
5002
+ microApp.options.lifeCycles[lifecycleName](event);
4306
5003
  }
4307
5004
  element.dispatchEvent(event);
4308
5005
  }
@@ -4322,7 +5019,7 @@ function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
4322
5019
  // micro app instances
4323
5020
  const appInstanceMap = new Map();
4324
5021
  class CreateApp {
4325
- constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, useMemoryRouter, baseroute, keepRouteState, hiddenRouter, defaultPage, disablePatchRequest, }) {
5022
+ constructor({ name, url, container, scopecss, useSandbox, inline, esmodule, ssrUrl, isPrefetch, prefetchLevel, }) {
4326
5023
  this.state = appStates.CREATED;
4327
5024
  this.keepAliveState = null;
4328
5025
  this.keepAliveContainer = null;
@@ -4331,30 +5028,27 @@ class CreateApp {
4331
5028
  this.umdHookUnmount = null;
4332
5029
  this.libraryName = null;
4333
5030
  this.umdMode = false;
4334
- this.isPrefetch = false;
4335
- this.prefetchResolve = null;
4336
- this.container = null;
4337
5031
  this.sandBox = null;
5032
+ this.fiber = false;
5033
+ this.useMemoryRouter = true;
4338
5034
  this.name = name;
4339
5035
  this.url = url;
4340
5036
  this.useSandbox = useSandbox;
4341
5037
  this.scopecss = this.useSandbox && scopecss;
4342
- this.useMemoryRouter = this.useSandbox && useMemoryRouter;
4343
- // optional during init base on prefetch 👇
5038
+ this.inline = inline !== null && inline !== void 0 ? inline : false;
5039
+ this.esmodule = esmodule !== null && esmodule !== void 0 ? esmodule : false;
5040
+ // not exist when prefetch 👇
4344
5041
  this.container = container !== null && container !== void 0 ? container : null;
4345
5042
  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
- };
5043
+ // exist only prefetch 👇
5044
+ this.isPrefetch = isPrefetch !== null && isPrefetch !== void 0 ? isPrefetch : false;
5045
+ this.isPrerender = prefetchLevel === 3;
5046
+ this.prefetchLevel = prefetchLevel;
5047
+ // init actions
5048
+ appInstanceMap.set(this.name, this);
5049
+ this.source = { html: null, links: new Set(), scripts: new Set() };
4356
5050
  this.loadSourceCode();
4357
- this.useSandbox && (this.sandBox = new SandBox(name, url, this.useMemoryRouter));
5051
+ this.useSandbox && (this.sandBox = new SandBox(name, url));
4358
5052
  }
4359
5053
  // Load resources
4360
5054
  loadSourceCode() {
@@ -4364,17 +5058,45 @@ class CreateApp {
4364
5058
  /**
4365
5059
  * When resource is loaded, mount app if it is not prefetch or unmount
4366
5060
  */
4367
- onLoad(html) {
5061
+ onLoad(html, defaultPage, disablePatchRequest) {
4368
5062
  var _a;
4369
5063
  if (++this.loadSourceLevel === 2) {
4370
5064
  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;
5065
+ this.state = appStates.LOADED;
5066
+ if (!this.isPrefetch && appStates.UNMOUNT !== this.state) {
5067
+ getRootContainer(this.container).mount(this);
4374
5068
  }
4375
- else if (appStates.UNMOUNT !== this.state) {
4376
- this.state = appStates.LOADED;
4377
- this.mount();
5069
+ else if (this.isPrerender) {
5070
+ /**
5071
+ * PreRender is an option of prefetch, it will render app during prefetch
5072
+ * Limit:
5073
+ * 1. fiber forced on
5074
+ * 2. only virtual router support
5075
+ *
5076
+ * NOTE: (4P: not - update browser url, dispatch popstateEvent, reload window, dispatch lifecycle event)
5077
+ * 1. pushState/replaceState in child can update microLocation, but will not attach router info to browser url
5078
+ * 2. prevent dispatch popstate/hashchange event to browser
5079
+ * 3. all navigation actions of location are invalid (In the future, we can consider update microLocation without trigger browser reload)
5080
+ * 4. lifecycle event will not trigger when prerender
5081
+ *
5082
+ * Special scenes
5083
+ * 1. unmount prerender app when loading
5084
+ * 2. unmount prerender app when exec js
5085
+ * 2. unmount prerender app after exec js
5086
+ */
5087
+ const container = pureCreateElement('div');
5088
+ container.setAttribute('prerender', 'true');
5089
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.setPreRenderState(true);
5090
+ this.mount({
5091
+ container,
5092
+ inline: this.inline,
5093
+ useMemoryRouter: true,
5094
+ baseroute: '',
5095
+ fiber: true,
5096
+ esmodule: this.esmodule,
5097
+ defaultPage: defaultPage !== null && defaultPage !== void 0 ? defaultPage : '',
5098
+ disablePatchRequest: disablePatchRequest !== null && disablePatchRequest !== void 0 ? disablePatchRequest : false,
5099
+ });
4378
5100
  }
4379
5101
  }
4380
5102
  }
@@ -4384,10 +5106,6 @@ class CreateApp {
4384
5106
  */
4385
5107
  onLoadError(e) {
4386
5108
  this.loadSourceLevel = -1;
4387
- if (this.prefetchResolve) {
4388
- this.prefetchResolve();
4389
- this.prefetchResolve = null;
4390
- }
4391
5109
  if (appStates.UNMOUNT !== this.state) {
4392
5110
  this.onerror(e);
4393
5111
  this.state = appStates.LOAD_FAILED;
@@ -4396,33 +5114,98 @@ class CreateApp {
4396
5114
  /**
4397
5115
  * mount app
4398
5116
  * @param container app container
4399
- * @param inline js runs in inline mode
5117
+ * @param inline run js in inline mode
5118
+ * @param useMemoryRouter use virtual router
5119
+ * @param defaultPage default page of virtual router
4400
5120
  * @param baseroute route prefix, default is ''
4401
- * @param keepRouteState keep route state when unmount, default is false
4402
5121
  * @param disablePatchRequest prevent rewrite request method of child app
5122
+ * @param fiber run js in fiber mode
5123
+ * @param esmodule support type='module' script
4403
5124
  */
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;
5125
+ mount({ container, inline, useMemoryRouter, defaultPage, baseroute, disablePatchRequest, fiber, esmodule, }) {
5126
+ var _a, _b, _c, _d, _e, _f;
4413
5127
  if (this.loadSourceLevel !== 2) {
5128
+ /**
5129
+ * unmount prefetch app when loading source, when mount again before loading end,
5130
+ * isPrefetch & isPrerender will be reset, and this.container sill be null
5131
+ * so we should set this.container
5132
+ */
5133
+ this.container = container;
5134
+ // mount before prerender exec mount (loading source), set isPrerender to false
5135
+ this.isPrerender = false;
5136
+ // reset app state to LOADING
4414
5137
  this.state = appStates.LOADING;
4415
5138
  return;
4416
5139
  }
4417
- dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
5140
+ /**
5141
+ * Mount app with prerender, this.container is empty
5142
+ * When rendering again, identify prerender by this.container
5143
+ * Transfer the contents of div to the <micro-app> tag
5144
+ *
5145
+ * Special scenes:
5146
+ * 1. mount before prerender exec mount (loading source)
5147
+ * 2. mount when prerender js executing
5148
+ * 3. mount after prerender js exec end
5149
+ *
5150
+ * TODO: test shadowDOM
5151
+ */
5152
+ if (this.container instanceof HTMLDivElement &&
5153
+ this.container.hasAttribute('prerender')) {
5154
+ /**
5155
+ * rebuild effect event of window, document, data center
5156
+ * explain:
5157
+ * 1. rebuild before exec mount, do nothing
5158
+ * 2. rebuild when js executing, recovery recorded effect event, because prerender fiber mode
5159
+ * 3. rebuild after js exec end, normal recovery effect event
5160
+ */
5161
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.rebuildEffectSnapshot();
5162
+ // current this.container is <div prerender='true'></div>
5163
+ cloneContainer(this.container, container, false);
5164
+ /**
5165
+ * set this.container to <micro-app></micro-app>
5166
+ * NOTE:
5167
+ * must before exec this.preRenderEvent?.forEach((cb) => cb())
5168
+ */
5169
+ this.container = container;
5170
+ (_b = this.preRenderEvent) === null || _b === void 0 ? void 0 : _b.forEach((cb) => cb());
5171
+ // reset isPrerender config
5172
+ this.isPrerender = false;
5173
+ this.preRenderEvent = undefined;
5174
+ // attach router info to browser url
5175
+ router.attachToURL(this.name);
5176
+ return (_c = this.sandBox) === null || _c === void 0 ? void 0 : _c.setPreRenderState(false);
5177
+ }
5178
+ this.container = container;
5179
+ this.inline = inline;
5180
+ this.esmodule = esmodule;
5181
+ this.fiber = fiber;
5182
+ // use in sandbox/effect
5183
+ this.useMemoryRouter = useMemoryRouter;
5184
+ // this.hiddenRouter = hiddenRouter ?? this.hiddenRouter
5185
+ const dispatchBeforeMount = () => {
5186
+ dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
5187
+ };
5188
+ if (this.isPrerender) {
5189
+ ((_d = this.preRenderEvent) !== null && _d !== void 0 ? _d : (this.preRenderEvent = [])).push(dispatchBeforeMount);
5190
+ }
5191
+ else {
5192
+ dispatchBeforeMount();
5193
+ }
4418
5194
  this.state = appStates.MOUNTING;
4419
5195
  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);
5196
+ (_e = this.sandBox) === null || _e === void 0 ? void 0 : _e.start({
5197
+ umdMode: this.umdMode,
5198
+ baseroute,
5199
+ useMemoryRouter,
5200
+ defaultPage,
5201
+ disablePatchRequest,
5202
+ });
4421
5203
  let umdHookMountResult; // result of mount function
4422
5204
  if (!this.umdMode) {
4423
5205
  let hasDispatchMountedEvent = false;
4424
5206
  // if all js are executed, param isFinished will be true
4425
- execScripts(this.source.scripts, this, (isFinished) => {
5207
+ execScripts(this, (isFinished) => {
5208
+ var _a;
4426
5209
  if (!this.umdMode) {
4427
5210
  const { mount, unmount } = this.getUmdLibraryHooks();
4428
5211
  /**
@@ -4436,9 +5219,9 @@ class CreateApp {
4436
5219
  this.umdMode = true;
4437
5220
  if (this.sandBox)
4438
5221
  this.sandBox.proxyWindow.__MICRO_APP_UMD_MODE__ = true;
4439
- // this.sandBox?.recordUmdSnapshot()
5222
+ // this.sandBox?.recordEffectSnapshot()
4440
5223
  try {
4441
- umdHookMountResult = this.umdHookMount();
5224
+ umdHookMountResult = this.umdHookMount(microApp.getData(this.name, true));
4442
5225
  }
4443
5226
  catch (e) {
4444
5227
  logError('an error occurred in the mount function \n', this.name, e);
@@ -4447,12 +5230,19 @@ class CreateApp {
4447
5230
  }
4448
5231
  if (!hasDispatchMountedEvent && (isFinished === true || this.umdMode)) {
4449
5232
  hasDispatchMountedEvent = true;
4450
- this.handleMounted(umdHookMountResult);
5233
+ const dispatchMounted = () => this.handleMounted(umdHookMountResult);
5234
+ if (this.isPrerender) {
5235
+ ((_a = this.preRenderEvent) !== null && _a !== void 0 ? _a : (this.preRenderEvent = [])).push(dispatchMounted);
5236
+ this.recordAndReleaseEffect();
5237
+ }
5238
+ else {
5239
+ dispatchMounted();
5240
+ }
4451
5241
  }
4452
5242
  });
4453
5243
  }
4454
5244
  else {
4455
- (_c = this.sandBox) === null || _c === void 0 ? void 0 : _c.rebuildUmdSnapshot();
5245
+ (_f = this.sandBox) === null || _f === void 0 ? void 0 : _f.rebuildEffectSnapshot();
4456
5246
  try {
4457
5247
  umdHookMountResult = this.umdHookMount();
4458
5248
  }
@@ -4482,6 +5272,9 @@ class CreateApp {
4482
5272
  dispatchMountedEvent() {
4483
5273
  if (appStates.UNMOUNT !== this.state) {
4484
5274
  this.state = appStates.MOUNTED;
5275
+ // call window.onmount of child app
5276
+ callFnWithTryCatch(this.getGlobalEventListener(microGlobalEvent.ONMOUNT), this.name, `window.${microGlobalEvent.ONMOUNT}`, microApp.getData(this.name, true));
5277
+ // dispatch event mounted to parent
4485
5278
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.MOUNTED);
4486
5279
  }
4487
5280
  }
@@ -4489,9 +5282,11 @@ class CreateApp {
4489
5282
  * unmount app
4490
5283
  * NOTE: Do not add any params on account of unmountApp
4491
5284
  * @param destroy completely destroy, delete cache resources
5285
+ * @param clearData clear data of dateCenter
5286
+ * @param keepRouteState keep route state when unmount, default is false
4492
5287
  * @param unmountcb callback of unmount
4493
5288
  */
4494
- unmount(destroy, unmountcb) {
5289
+ unmount({ destroy, clearData, keepRouteState, unmountcb, }) {
4495
5290
  if (this.state === appStates.LOAD_FAILED) {
4496
5291
  destroy = true;
4497
5292
  }
@@ -4506,38 +5301,50 @@ class CreateApp {
4506
5301
  */
4507
5302
  if (isFunction(this.umdHookUnmount)) {
4508
5303
  try {
4509
- umdHookUnmountResult = this.umdHookUnmount();
5304
+ umdHookUnmountResult = this.umdHookUnmount(microApp.getData(this.name, true));
4510
5305
  }
4511
5306
  catch (e) {
4512
5307
  logError('an error occurred in the unmount function \n', this.name, e);
4513
5308
  }
4514
5309
  }
5310
+ // call window.onunmount of child app
5311
+ callFnWithTryCatch(this.getGlobalEventListener(microGlobalEvent.ONUNMOUNT), this.name, `window.${microGlobalEvent.ONUNMOUNT}`);
4515
5312
  // dispatch unmount event to micro app
4516
5313
  dispatchCustomEventToMicroApp('unmount', this.name);
4517
- this.handleUnmounted(destroy, umdHookUnmountResult, unmountcb);
5314
+ this.handleUnmounted(destroy, clearData, keepRouteState, umdHookUnmountResult, unmountcb);
4518
5315
  }
4519
5316
  /**
4520
5317
  * handle for promise umdHookUnmount
4521
5318
  * @param destroy completely destroy, delete cache resources
5319
+ * @param clearData clear data of dateCenter
5320
+ * @param keepRouteState keep route state when unmount, default is false
4522
5321
  * @param umdHookUnmountResult result of umdHookUnmount
4523
5322
  * @param unmountcb callback of unmount
4524
5323
  */
4525
- handleUnmounted(destroy, umdHookUnmountResult, unmountcb) {
5324
+ handleUnmounted(destroy, clearData, keepRouteState, umdHookUnmountResult, unmountcb) {
5325
+ const unmountParam = {
5326
+ destroy,
5327
+ clearData,
5328
+ keepRouteState,
5329
+ unmountcb,
5330
+ };
4526
5331
  if (isPromise(umdHookUnmountResult)) {
4527
5332
  umdHookUnmountResult
4528
- .then(() => this.actionsForUnmount(destroy, unmountcb))
4529
- .catch(() => this.actionsForUnmount(destroy, unmountcb));
5333
+ .then(() => this.actionsForUnmount(unmountParam))
5334
+ .catch(() => this.actionsForUnmount(unmountParam));
4530
5335
  }
4531
5336
  else {
4532
- this.actionsForUnmount(destroy, unmountcb);
5337
+ this.actionsForUnmount(unmountParam);
4533
5338
  }
4534
5339
  }
4535
5340
  /**
4536
5341
  * actions for unmount app
4537
5342
  * @param destroy completely destroy, delete cache resources
5343
+ * @param clearData clear data of dateCenter
5344
+ * @param keepRouteState keep route state when unmount, default is false
4538
5345
  * @param unmountcb callback of unmount
4539
5346
  */
4540
- actionsForUnmount(destroy, unmountcb) {
5347
+ actionsForUnmount({ destroy, clearData, keepRouteState, unmountcb }) {
4541
5348
  var _a, _b;
4542
5349
  if (destroy) {
4543
5350
  this.actionsForCompletelyDestroy();
@@ -4546,7 +5353,7 @@ class CreateApp {
4546
5353
  cloneContainer(this.container, this.source.html, false);
4547
5354
  }
4548
5355
  if (this.umdMode) {
4549
- (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordUmdSnapshot();
5356
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordEffectSnapshot();
4550
5357
  }
4551
5358
  /**
4552
5359
  * this.container maybe contains micro-app element, stop sandbox should exec after cloneContainer
@@ -4554,25 +5361,36 @@ class CreateApp {
4554
5361
  * 1. if destroy is true, clear route state
4555
5362
  * 2. umd mode and keep-alive will not clear EventSource
4556
5363
  */
4557
- (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.stop(this.umdMode, this.keepRouteState && !destroy, !this.umdMode || destroy);
5364
+ (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.stop({
5365
+ umdMode: this.umdMode,
5366
+ keepRouteState: keepRouteState && !destroy,
5367
+ clearEventSource: !this.umdMode || destroy,
5368
+ clearData: clearData || destroy,
5369
+ });
4558
5370
  if (!getActiveApps().length) {
4559
5371
  releasePatchSetAttribute();
4560
5372
  }
4561
5373
  // dispatch unmount event to base app
4562
5374
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.UNMOUNT);
5375
+ this.resetConfig();
5376
+ unmountcb && unmountcb();
5377
+ }
5378
+ resetConfig() {
4563
5379
  this.container.innerHTML = '';
4564
5380
  this.container = null;
4565
- unmountcb && unmountcb();
5381
+ this.isPrerender = false;
5382
+ this.preRenderEvent = undefined;
4566
5383
  }
4567
5384
  // actions for completely destroy
4568
5385
  actionsForCompletelyDestroy() {
4569
5386
  if (!this.useSandbox && this.umdMode) {
4570
5387
  delete window[this.libraryName];
4571
5388
  }
5389
+ sourceCenter.script.deleteInlineInfo(this.source.scripts);
4572
5390
  appInstanceMap.delete(this.name);
4573
5391
  }
4574
5392
  // hidden app when disconnectedCallback called with keep-alive
4575
- hiddenKeepAliveApp() {
5393
+ hiddenKeepAliveApp(callback) {
4576
5394
  var _a;
4577
5395
  const oldContainer = this.container;
4578
5396
  cloneContainer(this.container, this.keepAliveContainer ? this.keepAliveContainer : (this.keepAliveContainer = document.createElement('div')), false);
@@ -4587,10 +5405,13 @@ class CreateApp {
4587
5405
  dispatchLifecyclesEvent(oldContainer, this.name, lifeCycles.AFTERHIDDEN);
4588
5406
  // called after lifeCyclesEvent
4589
5407
  (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.removeRouteInfoForKeepAliveApp();
5408
+ this.recordAndReleaseEffect();
5409
+ callback && callback();
4590
5410
  }
4591
5411
  // show app when connectedCallback called with keep-alive
4592
5412
  showKeepAliveApp(container) {
4593
- var _a;
5413
+ var _a, _b;
5414
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.rebuildEffectSnapshot();
4594
5415
  // dispatch beforeShow event to micro-app
4595
5416
  dispatchCustomEventToMicroApp('appstate-change', this.name, {
4596
5417
  appState: 'beforeshow',
@@ -4601,7 +5422,7 @@ class CreateApp {
4601
5422
  this.container = container;
4602
5423
  this.keepAliveState = keepAliveStates.KEEP_ALIVE_SHOW;
4603
5424
  // called before lifeCyclesEvent
4604
- (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.setRouteInfoForKeepAliveApp();
5425
+ (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.setRouteInfoForKeepAliveApp();
4605
5426
  // dispatch afterShow event to micro-app
4606
5427
  dispatchCustomEventToMicroApp('appstate-change', this.name, {
4607
5428
  appState: 'aftershow',
@@ -4641,6 +5462,23 @@ class CreateApp {
4641
5462
  }
4642
5463
  return {};
4643
5464
  }
5465
+ getGlobalEventListener(eventName) {
5466
+ var _a;
5467
+ // @ts-ignore
5468
+ const listener = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow[eventName];
5469
+ return isFunction(listener) ? listener : null;
5470
+ }
5471
+ /**
5472
+ * Record global effect and then release (effect: global event, timeout, data listener)
5473
+ * Scenes:
5474
+ * 1. hidden keep-alive app
5475
+ * 2. after init prerender app
5476
+ */
5477
+ recordAndReleaseEffect() {
5478
+ var _a, _b;
5479
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordEffectSnapshot();
5480
+ (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.releaseGlobalEffect();
5481
+ }
4644
5482
  }
4645
5483
 
4646
5484
  /**
@@ -4653,7 +5491,8 @@ function defineElement(tagName) {
4653
5491
  super();
4654
5492
  this.isWaiting = false;
4655
5493
  this.cacheData = null;
4656
- this.hasConnected = false;
5494
+ this.connectedCount = 0;
5495
+ this.connectStateMap = new Map();
4657
5496
  this.appName = ''; // app name
4658
5497
  this.appUrl = ''; // app url
4659
5498
  this.ssrUrl = ''; // html path in ssr mode
@@ -4663,6 +5502,8 @@ function defineElement(tagName) {
4663
5502
  */
4664
5503
  this.handleAttributeUpdate = () => {
4665
5504
  this.isWaiting = false;
5505
+ if (!this.connectStateMap.get(this.connectedCount))
5506
+ return;
4666
5507
  const formatAttrName = formatAppName(this.getAttribute('name'));
4667
5508
  const formatAttrUrl = formatAppURL(this.getAttribute('url'), this.appName);
4668
5509
  if (this.legalAttribute('name', formatAttrName) && this.legalAttribute('url', formatAttrUrl)) {
@@ -4673,7 +5514,7 @@ function defineElement(tagName) {
4673
5514
  keepAliveStates.KEEP_ALIVE_HIDDEN !== existApp.getKeepAliveState() &&
4674
5515
  !existApp.isPrefetch) {
4675
5516
  this.setAttribute('name', this.appName);
4676
- return logError(`app name conflict, an app named ${formatAttrName} is running`, this.appName);
5517
+ return logError(`app name conflict, an app named ${formatAttrName} is running`);
4677
5518
  }
4678
5519
  }
4679
5520
  if (formatAttrName !== this.appName || formatAttrUrl !== this.appUrl) {
@@ -4714,22 +5555,56 @@ function defineElement(tagName) {
4714
5555
  // baseRoute: route prefix, default is ''
4715
5556
  // keep-alive: open keep-alive mode
4716
5557
  connectedCallback() {
4717
- this.hasConnected = true;
4718
- defer(() => dispatchLifecyclesEvent(this, this.appName, lifeCycles.CREATED));
4719
- this.initialMount();
5558
+ const cacheCount = ++this.connectedCount;
5559
+ this.connectStateMap.set(cacheCount, true);
5560
+ /**
5561
+ * In some special scenes, such as vue's keep-alive, the micro-app will be inserted and deleted twice in an instant
5562
+ * So we execute the mount method async and record connectState to prevent repeated rendering
5563
+ */
5564
+ defer(() => {
5565
+ if (this.connectStateMap.get(cacheCount)) {
5566
+ dispatchLifecyclesEvent(this, this.appName, lifeCycles.CREATED);
5567
+ this.handleConnected();
5568
+ }
5569
+ });
4720
5570
  }
4721
5571
  disconnectedCallback() {
4722
- this.hasConnected = false;
5572
+ this.connectStateMap.set(this.connectedCount, false);
5573
+ this.handleDisconnected();
5574
+ }
5575
+ /**
5576
+ * Re render app from the command line
5577
+ * MicroAppElement.reload(destroy)
5578
+ */
5579
+ reload(destroy) {
5580
+ return new Promise((resolve) => {
5581
+ const handleAfterReload = () => {
5582
+ this.removeEventListener(lifeCycles.MOUNTED, handleAfterReload);
5583
+ this.removeEventListener(lifeCycles.AFTERSHOW, handleAfterReload);
5584
+ resolve(true);
5585
+ };
5586
+ this.addEventListener(lifeCycles.MOUNTED, handleAfterReload);
5587
+ this.addEventListener(lifeCycles.AFTERSHOW, handleAfterReload);
5588
+ this.handleDisconnected(destroy, () => {
5589
+ this.handleConnected();
5590
+ });
5591
+ });
5592
+ }
5593
+ /**
5594
+ * common action for unmount
5595
+ * @param destroy reload param
5596
+ */
5597
+ handleDisconnected(destroy = false, callback) {
4723
5598
  const app = appInstanceMap.get(this.appName);
4724
5599
  if (app &&
4725
5600
  app.getAppState() !== appStates.UNMOUNT &&
4726
5601
  app.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN) {
4727
5602
  // keep-alive
4728
- if (this.getKeepAliveModeResult()) {
4729
- this.handleHiddenKeepAliveApp();
5603
+ if (this.getKeepAliveModeResult() && !destroy) {
5604
+ this.handleHiddenKeepAliveApp(callback);
4730
5605
  }
4731
5606
  else {
4732
- this.handleUnmount(this.getDestroyCompatibleResult());
5607
+ this.handleUnmount(destroy || this.getDestroyCompatibleResult(), callback);
4733
5608
  }
4734
5609
  }
4735
5610
  }
@@ -4767,12 +5642,12 @@ function defineElement(tagName) {
4767
5642
  }
4768
5643
  // handle for connectedCallback run before attributeChangedCallback
4769
5644
  handleInitialNameAndUrl() {
4770
- this.hasConnected && this.initialMount();
5645
+ this.connectStateMap.get(this.connectedCount) && this.handleConnected();
4771
5646
  }
4772
5647
  /**
4773
5648
  * first mount of this app
4774
5649
  */
4775
- initialMount() {
5650
+ handleConnected() {
4776
5651
  if (!this.appName || !this.appUrl)
4777
5652
  return;
4778
5653
  if (this.getDisposeResult('shadowDOM') && !this.shadowRoot && isFunction(this.attachShadow)) {
@@ -4782,26 +5657,35 @@ function defineElement(tagName) {
4782
5657
  if (appInstanceMap.has(this.appName)) {
4783
5658
  const app = appInstanceMap.get(this.appName);
4784
5659
  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
5660
+ const targetAppUrl = this.ssrUrl || this.appUrl;
5661
+ /**
5662
+ * NOTE:
5663
+ * 1. keep-alive don't care about ssrUrl
5664
+ * 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
5665
+ * 3. When scopecss, useSandbox of prefetch app different from target app, delete prefetch app and create new one
5666
+ */
4788
5667
  if (app.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN &&
4789
5668
  app.url === this.appUrl) {
4790
5669
  this.handleShowKeepAliveApp(app);
4791
5670
  }
4792
- else if (existAppUrl === activeAppUrl && (app.isPrefetch ||
4793
- app.getAppState() === appStates.UNMOUNT)) {
5671
+ else if (existAppUrl === targetAppUrl && (app.getAppState() === appStates.UNMOUNT ||
5672
+ (app.isPrefetch && (app.scopecss === this.isScopecss() &&
5673
+ app.useSandbox === this.isSandbox())))) {
4794
5674
  this.handleAppMount(app);
4795
5675
  }
4796
5676
  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);
5677
+ if ((process.env.NODE_ENV !== 'production') &&
5678
+ app.scopecss === this.isScopecss() &&
5679
+ app.useSandbox === this.isSandbox()) {
5680
+ /**
5681
+ * url is different & old app is unmounted or prefetch, create new app to replace old one
5682
+ */
5683
+ logWarn(`the ${app.isPrefetch ? 'prefetch' : 'unmounted'} app with url: ${existAppUrl} replaced by a new app with url: ${targetAppUrl}`, this.appName);
5684
+ }
4801
5685
  this.handleCreateApp();
4802
5686
  }
4803
5687
  else {
4804
- logError(`app name conflict, an app named ${this.appName} is running`, this.appName);
5688
+ logError(`app name conflict, an app named: ${this.appName} with url: ${existAppUrl} is running`);
4805
5689
  }
4806
5690
  }
4807
5691
  else {
@@ -4836,7 +5720,7 @@ function defineElement(tagName) {
4836
5720
  }
4837
5721
  else {
4838
5722
  // the hidden keep-alive app is still active
4839
- logError(`app name conflict, an app named ${this.appName} is running`, this.appName);
5723
+ logError(`app name conflict, an app named ${this.appName} is running`);
4840
5724
  }
4841
5725
  }
4842
5726
  else if (existApp.url === this.appUrl && existApp.ssrUrl === this.ssrUrl) {
@@ -4863,20 +5747,6 @@ function defineElement(tagName) {
4863
5747
  }
4864
5748
  return true;
4865
5749
  }
4866
- /**
4867
- * mount app
4868
- * some serious note before mount:
4869
- * 1. is prefetch ?
4870
- * 2. is remount in another container ?
4871
- * 3. is remount with change properties of the container ?
4872
- */
4873
- handleAppMount(app) {
4874
- 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'));
4878
- });
4879
- }
4880
5750
  // create app instance
4881
5751
  handleCreateApp() {
4882
5752
  var _a;
@@ -4887,41 +5757,68 @@ function defineElement(tagName) {
4887
5757
  if (appInstanceMap.has(this.appName)) {
4888
5758
  appInstanceMap.get(this.appName).actionsForCompletelyDestroy();
4889
5759
  }
4890
- const instance = new CreateApp({
5760
+ new CreateApp({
4891
5761
  name: this.appName,
4892
5762
  url: this.appUrl,
5763
+ scopecss: this.isScopecss(),
5764
+ useSandbox: this.isSandbox(),
5765
+ inline: this.getDisposeResult('inline'),
5766
+ esmodule: this.getDisposeResult('esmodule'),
5767
+ container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
4893
5768
  ssrUrl: this.ssrUrl,
5769
+ });
5770
+ }
5771
+ /**
5772
+ * mount app
5773
+ * some serious note before mount:
5774
+ * 1. is prefetch ?
5775
+ * 2. is remount in another container ?
5776
+ * 3. is remount with change properties of the container ?
5777
+ */
5778
+ handleAppMount(app) {
5779
+ app.isPrefetch = false;
5780
+ defer(() => this.mount(app));
5781
+ }
5782
+ /**
5783
+ * public mount action for micro_app_element & create_app
5784
+ */
5785
+ mount(app) {
5786
+ var _a;
5787
+ app.mount({
4894
5788
  container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
4895
5789
  inline: this.getDisposeResult('inline'),
4896
- scopecss: !(this.getDisposeResult('disable-scopecss') || this.getDisposeResult('shadowDOM')),
4897
- useSandbox: !this.getDisposeResult('disable-sandbox'),
4898
5790
  useMemoryRouter: !this.getDisposeResult('disable-memory-router'),
4899
- baseroute: this.getBaseRouteCompatible(),
4900
- keepRouteState: this.getDisposeResult('keep-router-state'),
4901
5791
  defaultPage: this.getDefaultPageValue(),
4902
- hiddenRouter: this.getDisposeResult('hidden-router'),
5792
+ baseroute: this.getBaseRouteCompatible(),
4903
5793
  disablePatchRequest: this.getDisposeResult('disable-patch-request'),
5794
+ fiber: this.getDisposeResult('fiber'),
5795
+ esmodule: this.getDisposeResult('esmodule'),
4904
5796
  });
4905
- appInstanceMap.set(this.appName, instance);
4906
5797
  }
4907
5798
  /**
4908
5799
  * unmount app
4909
5800
  * @param destroy delete cache resources when unmount
4910
5801
  */
4911
- handleUnmount(destroy, unmountCb) {
5802
+ handleUnmount(destroy, unmountcb) {
4912
5803
  const app = appInstanceMap.get(this.appName);
4913
5804
  if (app &&
4914
5805
  app.getAppState() !== appStates.UNMOUNT) {
4915
- app.unmount(destroy, unmountCb);
5806
+ app.unmount({
5807
+ destroy,
5808
+ clearData: this.getDisposeResult('clear-data'),
5809
+ keepRouteState: this.getDisposeResult('keep-router-state'),
5810
+ unmountcb,
5811
+ });
4916
5812
  }
4917
5813
  }
4918
5814
  // hidden app when disconnectedCallback called with keep-alive
4919
- handleHiddenKeepAliveApp() {
5815
+ handleHiddenKeepAliveApp(callback) {
4920
5816
  const app = appInstanceMap.get(this.appName);
4921
5817
  if (app &&
4922
5818
  app.getAppState() !== appStates.UNMOUNT &&
4923
- app.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN)
4924
- app.hiddenKeepAliveApp();
5819
+ app.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN) {
5820
+ app.hiddenKeepAliveApp(callback);
5821
+ }
4925
5822
  }
4926
5823
  // show app when connectedCallback called with keep-alive
4927
5824
  handleShowKeepAliveApp(app) {
@@ -4934,8 +5831,7 @@ function defineElement(tagName) {
4934
5831
  * @param name Configuration item name
4935
5832
  */
4936
5833
  getDisposeResult(name) {
4937
- // @ts-ignore
4938
- return (this.compatibleSpecialProperties(name) || !!microApp[name]) && this.compatibleDisableSpecialProperties(name);
5834
+ return (this.compatibleSpecialProperties(name) || !!microApp.options[name]) && this.compatibleDisableSpecialProperties(name);
4939
5835
  }
4940
5836
  // compatible of disableScopecss & disableSandbox
4941
5837
  compatibleSpecialProperties(name) {
@@ -4957,6 +5853,12 @@ function defineElement(tagName) {
4957
5853
  }
4958
5854
  return this.getAttribute(name) !== 'false';
4959
5855
  }
5856
+ isScopecss() {
5857
+ return !(this.getDisposeResult('disable-scopecss') || this.getDisposeResult('shadowDOM'));
5858
+ }
5859
+ isSandbox() {
5860
+ return !this.getDisposeResult('disable-sandbox');
5861
+ }
4960
5862
  /**
4961
5863
  * 2021-09-08
4962
5864
  * get baseRoute
@@ -5004,8 +5906,10 @@ function defineElement(tagName) {
5004
5906
  * get config of default page
5005
5907
  */
5006
5908
  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 : '';
5909
+ return (router.getDefaultPage(this.appName) ||
5910
+ this.getAttribute('default-page') ||
5911
+ this.getAttribute('defaultPage') ||
5912
+ '');
5009
5913
  }
5010
5914
  /**
5011
5915
  * Data from the base application
@@ -5031,7 +5935,7 @@ function defineElement(tagName) {
5031
5935
  return null;
5032
5936
  }
5033
5937
  }
5034
- window.customElements.define(tagName, MicroAppElement);
5938
+ globalEnv.rawWindow.customElements.define(tagName, MicroAppElement);
5035
5939
  }
5036
5940
 
5037
5941
  /**
@@ -5050,45 +5954,71 @@ function defineElement(tagName) {
5050
5954
  * 2: disableScopecss, disableSandbox, disableMemoryRouter must be same with micro-app element, if conflict, the one who executes first shall prevail
5051
5955
  * @param apps micro apps
5052
5956
  */
5053
- function preFetch(apps) {
5957
+ function preFetch(apps, delay) {
5054
5958
  if (!isBrowser) {
5055
5959
  return logError('preFetch is only supported in browser environment');
5056
5960
  }
5057
5961
  requestIdleCallback(() => {
5058
- isFunction(apps) && (apps = apps());
5059
- if (isArray(apps)) {
5060
- apps.reduce((pre, next) => pre.then(() => preFetchInSerial(next)), Promise.resolve());
5061
- }
5962
+ const delayTime = delay !== null && delay !== void 0 ? delay : microApp.options.prefetchDelay;
5963
+ setTimeout(() => {
5964
+ // releasePrefetchEffect()
5965
+ preFetchInSerial(apps);
5966
+ }, isNumber(delayTime) ? delayTime : 3000);
5062
5967
  });
5968
+ // const handleOnLoad = (): void => {
5969
+ // releasePrefetchEffect()
5970
+ // requestIdleCallback(() => {
5971
+ // preFetchInSerial(apps)
5972
+ // })
5973
+ // }
5974
+ // const releasePrefetchEffect = (): void => {
5975
+ // window.removeEventListener('load', handleOnLoad)
5976
+ // clearTimeout(preFetchTime)
5977
+ // }
5978
+ // window.addEventListener('load', handleOnLoad)
5979
+ }
5980
+ function preFetchInSerial(apps) {
5981
+ isFunction(apps) && (apps = apps());
5982
+ if (isArray(apps)) {
5983
+ apps.reduce((pre, next) => pre.then(() => preFetchAction(next)), Promise.resolve());
5984
+ }
5063
5985
  }
5064
5986
  // 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 {
5987
+ function preFetchAction(options) {
5988
+ return promiseRequestIdle((resolve) => {
5989
+ var _a, _b, _c, _d, _e, _f;
5990
+ if (isPlainObject(options) && navigator.onLine) {
5991
+ options.name = formatAppName(options.name);
5992
+ options.url = formatAppURL(options.url, options.name);
5993
+ if (options.name && options.url && !appInstanceMap.has(options.name)) {
5994
+ const app = new CreateApp({
5995
+ name: options.name,
5996
+ url: options.url,
5997
+ isPrefetch: true,
5998
+ scopecss: !((_b = (_a = options['disable-scopecss']) !== null && _a !== void 0 ? _a : options.disableScopecss) !== null && _b !== void 0 ? _b : microApp.options['disable-scopecss']),
5999
+ useSandbox: !((_d = (_c = options['disable-sandbox']) !== null && _c !== void 0 ? _c : options.disableSandbox) !== null && _d !== void 0 ? _d : microApp.options['disable-sandbox']),
6000
+ inline: (_e = options.inline) !== null && _e !== void 0 ? _e : microApp.options.inline,
6001
+ esmodule: (_f = options.esmodule) !== null && _f !== void 0 ? _f : microApp.options.esmodule,
6002
+ prefetchLevel: options.level && PREFETCH_LEVEL.includes(options.level) ? options.level : microApp.options.prefetchLevel && PREFETCH_LEVEL.includes(microApp.options.prefetchLevel) ? microApp.options.prefetchLevel : 2,
6003
+ });
6004
+ const oldOnload = app.onLoad;
6005
+ const oldOnLoadError = app.onLoadError;
6006
+ app.onLoad = (html) => {
5085
6007
  resolve();
5086
- }
6008
+ oldOnload.call(app, html, options['default-page'], options['disable-patch-request']);
6009
+ };
6010
+ app.onLoadError = (...rests) => {
6011
+ resolve();
6012
+ oldOnLoadError.call(app, ...rests);
6013
+ };
5087
6014
  }
5088
6015
  else {
5089
6016
  resolve();
5090
6017
  }
5091
- });
6018
+ }
6019
+ else {
6020
+ resolve();
6021
+ }
5092
6022
  });
5093
6023
  }
5094
6024
  /**
@@ -5098,21 +6028,35 @@ function preFetchInSerial(prefetchApp) {
5098
6028
  function getGlobalAssets(assets) {
5099
6029
  if (isPlainObject(assets)) {
5100
6030
  requestIdleCallback(() => {
5101
- fetchGlobalResources(assets.js, 'js', globalScripts);
5102
- fetchGlobalResources(assets.css, 'css', globalLinks);
6031
+ fetchGlobalResources(assets.js, 'js', sourceCenter.script);
6032
+ fetchGlobalResources(assets.css, 'css', sourceCenter.link);
5103
6033
  });
5104
6034
  }
5105
6035
  }
5106
6036
  // TODO: requestIdleCallback for every file
5107
- function fetchGlobalResources(resources, suffix, cache) {
6037
+ function fetchGlobalResources(resources, suffix, sourceHandler) {
5108
6038
  if (isArray(resources)) {
5109
- const effectiveResource = resources.filter((path) => isString(path) && path.includes(`.${suffix}`) && !cache.has(path));
6039
+ const effectiveResource = resources.filter((path) => isString(path) && path.includes(`.${suffix}`) && !sourceHandler.hasInfo(path));
5110
6040
  const fetchResourcePromise = effectiveResource.map((path) => fetchSource(path));
5111
6041
  // fetch resource with stream
5112
6042
  promiseStream(fetchResourcePromise, (res) => {
5113
6043
  const path = effectiveResource[res.index];
5114
- if (!cache.has(path)) {
5115
- cache.set(path, res.data);
6044
+ if (suffix === 'js') {
6045
+ if (!sourceHandler.hasInfo(path)) {
6046
+ sourceHandler.setInfo(path, {
6047
+ code: res.data,
6048
+ isExternal: false,
6049
+ appSpace: {},
6050
+ });
6051
+ }
6052
+ }
6053
+ else {
6054
+ if (!sourceHandler.hasInfo(path)) {
6055
+ sourceHandler.setInfo(path, {
6056
+ code: res.data,
6057
+ appSpace: {}
6058
+ });
6059
+ }
5116
6060
  }
5117
6061
  }, (err) => {
5118
6062
  logError(err);
@@ -5123,13 +6067,14 @@ function fetchGlobalResources(resources, suffix, cache) {
5123
6067
  /**
5124
6068
  * if app not prefetch & not unmount, then app is active
5125
6069
  * @param excludeHiddenApp exclude hidden keep-alive app, default is false
6070
+ * @param excludePreRender exclude pre render app
5126
6071
  * @returns active apps
5127
6072
  */
5128
- function getActiveApps(excludeHiddenApp = false) {
6073
+ function getActiveApps({ excludeHiddenApp = false, excludePreRender = false, } = {}) {
5129
6074
  const activeApps = [];
5130
6075
  appInstanceMap.forEach((app, appName) => {
5131
6076
  if (appStates.UNMOUNT !== app.getAppState() &&
5132
- !app.isPrefetch &&
6077
+ (!app.isPrefetch || (app.isPrerender && !excludePreRender)) &&
5133
6078
  (!excludeHiddenApp ||
5134
6079
  keepAliveStates.KEEP_ALIVE_HIDDEN !== app.getKeepAliveState())) {
5135
6080
  activeApps.push(appName);
@@ -5155,33 +6100,43 @@ function unmountApp(appName, options) {
5155
6100
  if (options === null || options === void 0 ? void 0 : options.destroy) {
5156
6101
  app.actionsForCompletelyDestroy();
5157
6102
  }
5158
- resolve();
6103
+ resolve(true);
5159
6104
  }
5160
6105
  else if (app.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN) {
5161
6106
  if (options === null || options === void 0 ? void 0 : options.destroy) {
5162
- app.unmount(true, resolve);
6107
+ app.unmount({
6108
+ destroy: true,
6109
+ clearData: true,
6110
+ keepRouteState: true,
6111
+ unmountcb: resolve.bind(null, true)
6112
+ });
5163
6113
  }
5164
6114
  else if (options === null || options === void 0 ? void 0 : options.clearAliveState) {
5165
- app.unmount(false, resolve);
6115
+ app.unmount({
6116
+ destroy: false,
6117
+ clearData: !!options.clearData,
6118
+ keepRouteState: true,
6119
+ unmountcb: resolve.bind(null, true)
6120
+ });
5166
6121
  }
5167
6122
  else {
5168
- resolve();
6123
+ resolve(true);
5169
6124
  }
5170
6125
  }
5171
6126
  else {
5172
6127
  const container = getRootContainer(app.container);
5173
6128
  const unmountHandler = () => {
5174
- container.removeEventListener('unmount', unmountHandler);
5175
- container.removeEventListener('afterhidden', afterhiddenHandler);
5176
- resolve();
6129
+ container.removeEventListener(lifeCycles.UNMOUNT, unmountHandler);
6130
+ container.removeEventListener(lifeCycles.AFTERHIDDEN, afterhiddenHandler);
6131
+ resolve(true);
5177
6132
  };
5178
6133
  const afterhiddenHandler = () => {
5179
- container.removeEventListener('unmount', unmountHandler);
5180
- container.removeEventListener('afterhidden', afterhiddenHandler);
5181
- resolve();
6134
+ container.removeEventListener(lifeCycles.UNMOUNT, unmountHandler);
6135
+ container.removeEventListener(lifeCycles.AFTERHIDDEN, afterhiddenHandler);
6136
+ resolve(true);
5182
6137
  };
5183
- container.addEventListener('unmount', unmountHandler);
5184
- container.addEventListener('afterhidden', afterhiddenHandler);
6138
+ container.addEventListener(lifeCycles.UNMOUNT, unmountHandler);
6139
+ container.addEventListener(lifeCycles.AFTERHIDDEN, afterhiddenHandler);
5185
6140
  if (options === null || options === void 0 ? void 0 : options.destroy) {
5186
6141
  let destroyAttrValue, destoryAttrValue;
5187
6142
  container.hasAttribute('destroy') && (destroyAttrValue = container.getAttribute('destroy'));
@@ -5189,36 +6144,131 @@ function unmountApp(appName, options) {
5189
6144
  container.setAttribute('destroy', 'true');
5190
6145
  container.parentNode.removeChild(container);
5191
6146
  container.removeAttribute('destroy');
5192
- typeof destroyAttrValue === 'string' && container.setAttribute('destroy', destroyAttrValue);
5193
- typeof destoryAttrValue === 'string' && container.setAttribute('destory', destoryAttrValue);
6147
+ isString(destroyAttrValue) && container.setAttribute('destroy', destroyAttrValue);
6148
+ isString(destoryAttrValue) && container.setAttribute('destory', destoryAttrValue);
5194
6149
  }
5195
6150
  else if ((options === null || options === void 0 ? void 0 : options.clearAliveState) && container.hasAttribute('keep-alive')) {
5196
6151
  const keepAliveAttrValue = container.getAttribute('keep-alive');
5197
6152
  container.removeAttribute('keep-alive');
6153
+ let clearDataAttrValue;
6154
+ if (options.clearData) {
6155
+ clearDataAttrValue = container.getAttribute('clear-data');
6156
+ container.setAttribute('clear-data', 'true');
6157
+ }
5198
6158
  container.parentNode.removeChild(container);
5199
6159
  container.setAttribute('keep-alive', keepAliveAttrValue);
6160
+ isString(clearDataAttrValue) && container.setAttribute('clear-data', clearDataAttrValue);
5200
6161
  }
5201
6162
  else {
6163
+ let clearDataAttrValue;
6164
+ if (options === null || options === void 0 ? void 0 : options.clearData) {
6165
+ clearDataAttrValue = container.getAttribute('clear-data');
6166
+ container.setAttribute('clear-data', 'true');
6167
+ }
5202
6168
  container.parentNode.removeChild(container);
6169
+ isString(clearDataAttrValue) && container.setAttribute('clear-data', clearDataAttrValue);
5203
6170
  }
5204
6171
  }
5205
6172
  }
5206
6173
  else {
5207
6174
  logWarn(`app ${appName} does not exist`);
5208
- resolve();
6175
+ resolve(false);
5209
6176
  }
5210
6177
  });
5211
6178
  }
5212
6179
  // unmount all apps in turn
5213
6180
  function unmountAllApps(options) {
5214
- return Array.from(appInstanceMap.keys()).reduce((pre, next) => pre.then(() => unmountApp(next, options)), Promise.resolve());
6181
+ return Array.from(appInstanceMap.keys()).reduce((pre, next) => pre.then(() => unmountApp(next, options)), Promise.resolve(true));
6182
+ }
6183
+ /**
6184
+ * Re render app from the command line
6185
+ * microApp.reload(destroy)
6186
+ * @param appName app.name
6187
+ * @param destroy unmount app with destroy mode
6188
+ * @returns Promise<boolean>
6189
+ */
6190
+ function reload(appName, destroy) {
6191
+ return new Promise((resolve) => {
6192
+ const app = appInstanceMap.get(formatAppName(appName));
6193
+ if (app) {
6194
+ const rootContainer = app.container && getRootContainer(app.container);
6195
+ if (rootContainer) {
6196
+ resolve(rootContainer.reload(destroy));
6197
+ }
6198
+ else {
6199
+ logWarn(`app ${appName} is not rendered, cannot use reload`);
6200
+ resolve(false);
6201
+ }
6202
+ }
6203
+ else {
6204
+ logWarn(`app ${appName} does not exist`);
6205
+ resolve(false);
6206
+ }
6207
+ });
6208
+ }
6209
+ /**
6210
+ * Manually render app
6211
+ * @param options RenderAppOptions
6212
+ * @returns Promise<boolean>
6213
+ */
6214
+ function renderApp(options) {
6215
+ return new Promise((resolve) => {
6216
+ if (!isPlainObject(options))
6217
+ return logError('renderApp options must be an object');
6218
+ const container = isElement(options.container) ? options.container : isString(options.container) ? document.querySelector(options.container) : null;
6219
+ if (!isElement(container))
6220
+ return logError('Target container is not a DOM element.');
6221
+ const microAppElement = pureCreateElement(microApp.tagName);
6222
+ for (const attr in options) {
6223
+ if (attr === 'onDataChange') {
6224
+ if (isFunction(options[attr])) {
6225
+ microAppElement.addEventListener('datachange', options[attr]);
6226
+ }
6227
+ }
6228
+ else if (attr === 'lifeCycles') {
6229
+ const lifeCycleConfig = options[attr];
6230
+ if (isPlainObject(lifeCycleConfig)) {
6231
+ for (const lifeName in lifeCycleConfig) {
6232
+ if (lifeName.toUpperCase() in lifeCycles && isFunction(lifeCycleConfig[lifeName])) {
6233
+ microAppElement.addEventListener(lifeName.toLowerCase(), lifeCycleConfig[lifeName]);
6234
+ }
6235
+ }
6236
+ }
6237
+ }
6238
+ else if (attr !== 'container') {
6239
+ microAppElement.setAttribute(attr, options[attr]);
6240
+ }
6241
+ }
6242
+ const handleMount = () => {
6243
+ releaseListener();
6244
+ resolve(true);
6245
+ };
6246
+ const handleError = () => {
6247
+ releaseListener();
6248
+ resolve(false);
6249
+ };
6250
+ const releaseListener = () => {
6251
+ microAppElement.removeEventListener(lifeCycles.MOUNTED, handleMount);
6252
+ microAppElement.removeEventListener(lifeCycles.ERROR, handleError);
6253
+ };
6254
+ microAppElement.addEventListener(lifeCycles.MOUNTED, handleMount);
6255
+ microAppElement.addEventListener(lifeCycles.ERROR, handleError);
6256
+ container.appendChild(microAppElement);
6257
+ });
5215
6258
  }
5216
6259
  class MicroApp extends EventCenterForBaseApp {
5217
6260
  constructor() {
5218
6261
  super(...arguments);
5219
6262
  this.tagName = 'micro-app';
5220
- this.preFetch = preFetch;
6263
+ this.options = {};
5221
6264
  this.router = router;
6265
+ this.preFetch = preFetch;
6266
+ this.unmountApp = unmountApp;
6267
+ this.unmountAllApps = unmountAllApps;
6268
+ this.getActiveApps = getActiveApps;
6269
+ this.getAllApps = getAllApps;
6270
+ this.reload = reload;
6271
+ this.renderApp = renderApp;
5222
6272
  }
5223
6273
  start(options) {
5224
6274
  var _a, _b;
@@ -5237,26 +6287,10 @@ class MicroApp extends EventCenterForBaseApp {
5237
6287
  return logWarn(`element ${this.tagName} is already defined`);
5238
6288
  }
5239
6289
  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);
6290
+ if (isPlainObject(options)) {
6291
+ this.options = options;
6292
+ options['disable-scopecss'] = (_a = options['disable-scopecss']) !== null && _a !== void 0 ? _a : options.disableScopecss;
6293
+ options['disable-sandbox'] = (_b = options['disable-sandbox']) !== null && _b !== void 0 ? _b : options.disableSandbox;
5260
6294
  // load app assets when browser is idle
5261
6295
  options.preFetchApps && preFetch(options.preFetchApps);
5262
6296
  // load global assets when browser is idle
@@ -5272,15 +6306,14 @@ class MicroApp extends EventCenterForBaseApp {
5272
6306
  }
5273
6307
  }
5274
6308
  }
5275
- this.plugins = options.plugins;
5276
6309
  }
5277
6310
  }
5278
6311
  // define customElement after init
5279
6312
  defineElement(this.tagName);
5280
6313
  }
5281
6314
  }
5282
- var microApp = new MicroApp();
6315
+ const microApp = new MicroApp();
5283
6316
 
5284
6317
  export default microApp;
5285
- export { EventCenterForMicroApp, MicroApp, getActiveApps, getAllApps, preFetch, pureCreateElement, removeDomScope, unmountAllApps, unmountApp, version };
6318
+ export { EventCenterForMicroApp, MicroApp, getActiveApps, getAllApps, preFetch, pureCreateElement, reload, removeDomScope, renderApp, unmountAllApps, unmountApp, version };
5286
6319
  //# sourceMappingURL=index.esm.js.map