@micro-zoe/micro-app 1.0.0-alpha.4 → 1.0.0-alpha.7

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.4';
1
+ const version = '1.0.0-alpha.7';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -72,6 +72,12 @@ function isShadowRoot(target) {
72
72
  function isURL(target) {
73
73
  return target instanceof URL;
74
74
  }
75
+ function isElement(target) {
76
+ return target instanceof Element;
77
+ }
78
+ function isNode(target) {
79
+ return target instanceof Node;
80
+ }
75
81
  // is ProxyDocument
76
82
  function isProxyDocument(target) {
77
83
  return toString.call(target) === '[object ProxyDocument]';
@@ -258,8 +264,19 @@ const requestIdleCallback = globalThis.requestIdleCallback ||
258
264
  return Math.max(0, 50 - (Date.now() - lastTime));
259
265
  },
260
266
  });
261
- }, 50);
267
+ }, 1);
262
268
  };
269
+ /**
270
+ * Wrap requestIdleCallback with promise
271
+ * Exec callback when browser idle
272
+ */
273
+ function promiseRequestIdle(callback) {
274
+ return new Promise((resolve) => {
275
+ requestIdleCallback(() => {
276
+ callback(resolve);
277
+ });
278
+ });
279
+ }
263
280
  /**
264
281
  * Record the currently running app.name
265
282
  */
@@ -433,6 +450,59 @@ function useMapRecord() {
433
450
  }
434
451
  };
435
452
  }
453
+ function getAttributes(element) {
454
+ const attr = element.attributes;
455
+ const attrMap = new Map();
456
+ for (let i = 0; i < attr.length; i++) {
457
+ attrMap.set(attr[i].name, attr[i].value);
458
+ }
459
+ return attrMap;
460
+ }
461
+ /**
462
+ * if fiberTasks exist, wrap callback with promiseRequestIdle
463
+ * if not, execute callback
464
+ * @param fiberTasks fiber task list
465
+ * @param callback action callback
466
+ */
467
+ function injectFiberTask(fiberTasks, callback) {
468
+ if (fiberTasks) {
469
+ fiberTasks.push(() => promiseRequestIdle((resolve) => {
470
+ callback();
471
+ resolve();
472
+ }));
473
+ }
474
+ else {
475
+ callback();
476
+ }
477
+ }
478
+ /**
479
+ * serial exec fiber task of link, style, script
480
+ * @param tasks task array or null
481
+ */
482
+ function serialExecFiberTasks(tasks) {
483
+ return (tasks === null || tasks === void 0 ? void 0 : tasks.reduce((pre, next) => pre.then(next), Promise.resolve())) || null;
484
+ }
485
+ /**
486
+ * inline script start with inline-xxx
487
+ * @param address source address
488
+ */
489
+ function isInlineScript(address) {
490
+ return address.startsWith('inline-');
491
+ }
492
+ /**
493
+ * call function with try catch
494
+ * @param fn target function
495
+ * @param appName app.name
496
+ * @param args arguments
497
+ */
498
+ function callFnWithTryCatch(fn, appName, msgSuffix, ...args) {
499
+ try {
500
+ isFunction(fn) && fn(...args);
501
+ }
502
+ catch (e) {
503
+ logError(`an error occurred in app ${appName} ${msgSuffix} \n`, null, e);
504
+ }
505
+ }
436
506
 
437
507
  var ObservedAttrName;
438
508
  (function (ObservedAttrName) {
@@ -463,12 +533,38 @@ var lifeCycles;
463
533
  lifeCycles["AFTERSHOW"] = "aftershow";
464
534
  lifeCycles["AFTERHIDDEN"] = "afterhidden";
465
535
  })(lifeCycles || (lifeCycles = {}));
536
+ // global event of child app
537
+ var microGlobalEvent;
538
+ (function (microGlobalEvent) {
539
+ microGlobalEvent["ONMOUNT"] = "onmount";
540
+ microGlobalEvent["ONUNMOUNT"] = "onunmount";
541
+ })(microGlobalEvent || (microGlobalEvent = {}));
466
542
  // keep-alive status
467
543
  var keepAliveStates;
468
544
  (function (keepAliveStates) {
469
545
  keepAliveStates["KEEP_ALIVE_SHOW"] = "keep_alive_show";
470
546
  keepAliveStates["KEEP_ALIVE_HIDDEN"] = "keep_alive_hidden";
471
547
  })(keepAliveStates || (keepAliveStates = {}));
548
+ // micro-app config
549
+ var MicroAppConfig;
550
+ (function (MicroAppConfig) {
551
+ MicroAppConfig["DESTROY"] = "destroy";
552
+ MicroAppConfig["DESTORY"] = "destory";
553
+ MicroAppConfig["INLINE"] = "inline";
554
+ MicroAppConfig["DISABLESCOPECSS"] = "disableScopecss";
555
+ MicroAppConfig["DISABLESANDBOX"] = "disableSandbox";
556
+ MicroAppConfig["DISABLE_SCOPECSS"] = "disable-scopecss";
557
+ MicroAppConfig["DISABLE_SANDBOX"] = "disable-sandbox";
558
+ MicroAppConfig["DISABLE_MEMORY_ROUTER"] = "disable-memory-router";
559
+ MicroAppConfig["DISABLE_PATCH_REQUEST"] = "disable-patch-request";
560
+ MicroAppConfig["KEEP_ROUTER_STATE"] = "keep-router-state";
561
+ MicroAppConfig["HIDDEN_ROUTER"] = "hidden-router";
562
+ MicroAppConfig["KEEP_ALIVE"] = "keep-alive";
563
+ MicroAppConfig["CLEAR_DATA"] = "clear-data";
564
+ MicroAppConfig["ESMODULE"] = "esmodule";
565
+ MicroAppConfig["SSR"] = "ssr";
566
+ MicroAppConfig["FIBER"] = "fiber";
567
+ })(MicroAppConfig || (MicroAppConfig = {}));
472
568
  /**
473
569
  * global key must be static key, they can not rewrite
474
570
  * e.g.
@@ -493,8 +589,8 @@ function fetchSource(url, appName = null, options = {}) {
493
589
  * baseApp: <script crossorigin src="https://sgm-static.jd.com/sgm-2.8.0.js" name="SGMH5" sid="6f88a6e4ba4b4ae5acef2ec22c075085" appKey="jdb-adminb2b-pc"></script>
494
590
  */
495
591
  removeDomScope();
496
- if (isFunction(microApp.fetch)) {
497
- return microApp.fetch(url, options, appName);
592
+ if (isFunction(microApp.options.fetch)) {
593
+ return microApp.options.fetch(url, options, appName);
498
594
  }
499
595
  // Don’t use globalEnv.rawWindow.fetch, will cause sgm-2.8.0.js throw error in nest app
500
596
  return window.fetch(url, options).then((res) => {
@@ -531,7 +627,7 @@ class HTMLLoader {
531
627
  });
532
628
  }
533
629
  formatHTML(htmlUrl, htmlStr, appName) {
534
- return this.processHtml(htmlUrl, htmlStr, appName, microApp.plugins)
630
+ return this.processHtml(htmlUrl, htmlStr, appName, microApp.options.plugins)
535
631
  .replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
536
632
  return match
537
633
  .replace(/<head/i, '<micro-app-head')
@@ -553,7 +649,7 @@ class HTMLLoader {
553
649
  if (mergedPlugins.length > 0) {
554
650
  return mergedPlugins.reduce((preCode, plugin) => {
555
651
  if (isPlainObject(plugin) && isFunction(plugin.processHtml)) {
556
- return plugin.processHtml(preCode, url, plugin.options);
652
+ return plugin.processHtml(preCode, url);
557
653
  }
558
654
  return preCode;
559
655
  }, code);
@@ -591,11 +687,13 @@ class CSSParser {
591
687
  this.scopecssDisableSelectors = []; // disable or enable scopecss for specific selectors
592
688
  this.scopecssDisableNextLine = false; // use block comments /* scopecss-disable-next-line */ to disable scopecss on a specific line
593
689
  // https://developer.mozilla.org/en-US/docs/Web/API/CSSMediaRule
594
- this.mediaRule = this.createMatcherForAtRuleWithChildRule(/^@media *([^{]+)/, 'media');
690
+ this.mediaRule = this.createMatcherForRuleWithChildRule(/^@media *([^{]+)/, '@media');
595
691
  // 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');
692
+ this.supportsRule = this.createMatcherForRuleWithChildRule(/^@supports *([^{]+)/, '@supports');
693
+ this.documentRule = this.createMatcherForRuleWithChildRule(/^@([-\w]+)?document *([^{]+)/, '@document');
694
+ this.hostRule = this.createMatcherForRuleWithChildRule(/^@host\s*/, '@host');
695
+ // :global is CSS Modules rule, it will be converted to normal syntax
696
+ // private globalRule = this.createMatcherForRuleWithChildRule(/^:global([^{]*)/, ':global')
599
697
  // https://developer.mozilla.org/en-US/docs/Web/API/CSSImportRule
600
698
  this.importRule = this.createMatcherForNoneBraceAtRule('import');
601
699
  // Removed in most browsers
@@ -716,6 +814,13 @@ class CSSParser {
716
814
  this.hostRule() ||
717
815
  this.fontFaceRule();
718
816
  }
817
+ // :global is CSS Modules rule, it will be converted to normal syntax
818
+ // private matchGlobalRule (): boolean | void {
819
+ // if (this.cssText[0] !== ':') return false
820
+ // // reset scopecssDisableNextLine
821
+ // this.scopecssDisableNextLine = false
822
+ // return this.globalRule()
823
+ // }
719
824
  // https://developer.mozilla.org/en-US/docs/Web/API/CSSKeyframesRule
720
825
  keyframesRule() {
721
826
  if (!this.commonMatch(/^@([-\w]+)?keyframes\s*/))
@@ -769,17 +874,17 @@ class CSSParser {
769
874
  return false;
770
875
  return this.commonHandlerForAtRuleWithSelfRule('font-face');
771
876
  }
772
- // common matcher for @media, @supports, @document, @host
773
- createMatcherForAtRuleWithChildRule(reg, name) {
877
+ // common matcher for @media, @supports, @document, @host, :global
878
+ createMatcherForRuleWithChildRule(reg, name) {
774
879
  return () => {
775
880
  if (!this.commonMatch(reg))
776
881
  return false;
777
882
  if (!this.matchOpenBrace())
778
- return parseError(`@${name} missing '{'`, this.linkPath);
883
+ return parseError(`${name} missing '{'`, this.linkPath);
779
884
  this.matchComments();
780
885
  this.matchRules();
781
886
  if (!this.matchCloseBrace())
782
- return parseError(`@${name} missing '}'`, this.linkPath);
887
+ return parseError(`${name} missing '}'`, this.linkPath);
783
888
  this.matchLeadingSpaces();
784
889
  return true;
785
890
  };
@@ -906,20 +1011,20 @@ let parser;
906
1011
  * @param styleElement target style element
907
1012
  * @param appName app name
908
1013
  */
909
- function scopedCSS(styleElement, app) {
1014
+ function scopedCSS(styleElement, app, linkPath) {
910
1015
  if (app.scopecss) {
911
- const prefix = `${microApp.tagName}[name=${app.name}]`;
1016
+ const prefix = createPrefix(app.name);
912
1017
  if (!parser)
913
1018
  parser = new CSSParser();
914
1019
  if (styleElement.textContent) {
915
- commonAction(styleElement, app.name, prefix, app.url, styleElement.__MICRO_APP_LINK_PATH__);
1020
+ commonAction(styleElement, app.name, prefix, app.url, linkPath);
916
1021
  }
917
1022
  else {
918
1023
  const observer = new MutationObserver(function () {
919
1024
  observer.disconnect();
920
1025
  // styled-component will be ignore
921
1026
  if (styleElement.textContent && !styleElement.hasAttribute('data-styled')) {
922
- commonAction(styleElement, app.name, prefix, app.url, styleElement.__MICRO_APP_LINK_PATH__);
1027
+ commonAction(styleElement, app.name, prefix, app.url, linkPath);
923
1028
  }
924
1029
  });
925
1030
  observer.observe(styleElement, { childList: true });
@@ -927,6 +1032,10 @@ function scopedCSS(styleElement, app) {
927
1032
  }
928
1033
  return styleElement;
929
1034
  }
1035
+ function createPrefix(appName, reg = false) {
1036
+ const regCharacter = reg ? '\\' : '';
1037
+ return `${microApp.tagName}${regCharacter}[name=${appName}${regCharacter}]`;
1038
+ }
930
1039
 
931
1040
  function eventHandler(event, element) {
932
1041
  Object.defineProperties(event, {
@@ -968,8 +1077,76 @@ function dispatchOnErrorEvent(element) {
968
1077
  }
969
1078
  }
970
1079
 
971
- // Global links, reuse across apps
972
- const globalLinks = new Map();
1080
+ /**
1081
+ * SourceCenter is a resource management center
1082
+ * All html, js, css will be recorded and processed here
1083
+ * NOTE:
1084
+ * 1. All resources are global and shared between apps
1085
+ * 2. Pay attention to the case of html with parameters
1086
+ * 3. The resource is first processed by the plugin
1087
+ */
1088
+ function createSourceCenter() {
1089
+ const linkList = new Map();
1090
+ const scriptList = new Map();
1091
+ // setInterval(() => {
1092
+ // console.log(linkList, scriptList)
1093
+ // }, 10000);
1094
+ function createSourceHandler(targetList) {
1095
+ return {
1096
+ setInfo(address, info) {
1097
+ targetList.set(address, info);
1098
+ },
1099
+ getInfo(address) {
1100
+ var _a;
1101
+ return (_a = targetList.get(address)) !== null && _a !== void 0 ? _a : null;
1102
+ },
1103
+ hasInfo(address) {
1104
+ return targetList.has(address);
1105
+ },
1106
+ deleteInfo(address) {
1107
+ return targetList.delete(address);
1108
+ }
1109
+ };
1110
+ }
1111
+ return {
1112
+ link: createSourceHandler(linkList),
1113
+ script: Object.assign(Object.assign({}, createSourceHandler(scriptList)), { deleteInlineInfo(addressList) {
1114
+ addressList.forEach((address) => {
1115
+ if (isInlineScript(address)) {
1116
+ scriptList.delete(address);
1117
+ }
1118
+ });
1119
+ } }),
1120
+ };
1121
+ }
1122
+ var sourceCenter = createSourceCenter();
1123
+
1124
+ /**
1125
+ *
1126
+ * @param appName app.name
1127
+ * @param linkInfo linkInfo of current address
1128
+ */
1129
+ function getExistParseCode(appName, prefix, linkInfo) {
1130
+ const appSpace = linkInfo.appSpace;
1131
+ for (const item in appSpace) {
1132
+ if (item !== appName) {
1133
+ const appSpaceData = appSpace[item];
1134
+ if (appSpaceData.parsedCode) {
1135
+ return appSpaceData.parsedCode.replaceAll(new RegExp(createPrefix(item, true), 'g'), prefix);
1136
+ }
1137
+ }
1138
+ }
1139
+ }
1140
+ // transfer the attributes on the link to convertStyle
1141
+ function setConvertStyleAttr(convertStyle, attrs) {
1142
+ attrs.forEach((value, key) => {
1143
+ if (key === 'rel')
1144
+ return;
1145
+ if (key === 'href')
1146
+ key = 'data-origin-href';
1147
+ convertStyle.setAttribute(key, value);
1148
+ });
1149
+ }
973
1150
  /**
974
1151
  * Extract link elements
975
1152
  * @param link link element
@@ -984,22 +1161,29 @@ function extractLinkFromHtml(link, parent, app, isDynamic = false) {
984
1161
  let replaceComment = null;
985
1162
  if (rel === 'stylesheet' && href) {
986
1163
  href = CompletionPath(href, app.url);
1164
+ let linkInfo = sourceCenter.link.getInfo(href);
1165
+ const appSpaceData = {
1166
+ attrs: getAttributes(link),
1167
+ };
1168
+ if (!linkInfo) {
1169
+ linkInfo = {
1170
+ code: '',
1171
+ appSpace: {
1172
+ [app.name]: appSpaceData,
1173
+ }
1174
+ };
1175
+ }
1176
+ else {
1177
+ linkInfo.appSpace[app.name] = linkInfo.appSpace[app.name] || appSpaceData;
1178
+ }
1179
+ sourceCenter.link.setInfo(href, linkInfo);
987
1180
  if (!isDynamic) {
1181
+ app.source.links.add(href);
988
1182
  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
- });
1183
+ linkInfo.appSpace[app.name].placeholder = replaceComment;
994
1184
  }
995
1185
  else {
996
- return {
997
- url: href,
998
- info: {
999
- code: '',
1000
- isGlobal: link.hasAttribute('global'),
1001
- }
1002
- };
1186
+ return { address: href, linkInfo };
1003
1187
  }
1004
1188
  }
1005
1189
  else if (rel && ['prefetch', 'preload', 'prerender', 'icon', 'apple-touch-icon'].includes(rel)) {
@@ -1028,79 +1212,138 @@ function extractLinkFromHtml(link, parent, app, isDynamic = false) {
1028
1212
  * @param app app
1029
1213
  * @param microAppHead micro-app-head
1030
1214
  */
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);
1215
+ function fetchLinksFromHtml(wrapElement, app, microAppHead, fiberStyleResult) {
1216
+ const styleList = Array.from(app.source.links);
1217
+ const fetchLinkPromise = styleList.map((address) => {
1218
+ const linkInfo = sourceCenter.link.getInfo(address);
1219
+ return linkInfo.code ? linkInfo.code : fetchSource(address, app.name);
1035
1220
  });
1221
+ const fiberLinkTasks = app.isPrefetch || app.fiber ? [] : null;
1036
1222
  promiseStream(fetchLinkPromise, (res) => {
1037
- fetchLinkSuccess(linkEntries[res.index][0], linkEntries[res.index][1], res.data, microAppHead, app);
1223
+ injectFiberTask(fiberLinkTasks, () => fetchLinkSuccess(styleList[res.index], res.data, microAppHead, app));
1038
1224
  }, (err) => {
1039
1225
  logError(err, app.name);
1040
1226
  }, () => {
1041
- app.onLoad(wrapElement);
1227
+ if (fiberLinkTasks) {
1228
+ /**
1229
+ * 1. If fiberLinkTasks is not null, fiberStyleResult is not null
1230
+ * 2. Download link source while processing style
1231
+ * 3. Process style first, and then process link
1232
+ */
1233
+ fiberStyleResult.then(() => {
1234
+ fiberLinkTasks.push(() => Promise.resolve(app.onLoad(wrapElement)));
1235
+ serialExecFiberTasks(fiberLinkTasks);
1236
+ });
1237
+ }
1238
+ else {
1239
+ app.onLoad(wrapElement);
1240
+ }
1042
1241
  });
1043
1242
  }
1044
1243
  /**
1045
- * fetch link succeeded, replace placeholder with style tag
1046
- * @param url resource address
1047
- * @param info resource link info
1048
- * @param data code
1244
+ * Fetch link succeeded, replace placeholder with style tag
1245
+ * NOTE:
1246
+ * 1. Only exec when init, no longer exec when remount
1247
+ * 2. Only handler html link element, not dynamic link or style
1248
+ * 3. The same prefix can reuse parsedCode
1249
+ * 4. Async exec with requestIdleCallback in prefetch or fiber
1250
+ * 5. appSpace[app.name].placeholder/attrs must exist
1251
+ * @param address resource address
1252
+ * @param code link source code
1049
1253
  * @param microAppHead micro-app-head
1050
- * @param app app
1254
+ * @param app app instance
1051
1255
  */
1052
- function fetchLinkSuccess(url, info, data, microAppHead, app) {
1053
- if (info.isGlobal && !globalLinks.has(url)) {
1054
- globalLinks.set(url, data);
1256
+ function fetchLinkSuccess(address, code, microAppHead, app) {
1257
+ /**
1258
+ * linkInfo must exist, but linkInfo.code not
1259
+ * so we set code to linkInfo.code
1260
+ */
1261
+ const linkInfo = sourceCenter.link.getInfo(address);
1262
+ linkInfo.code = code;
1263
+ const appSpaceData = linkInfo.appSpace[app.name];
1264
+ const placeholder = appSpaceData.placeholder;
1265
+ /**
1266
+ * 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.
1267
+ * This causes placeholder to be possibly null
1268
+ * e.g.
1269
+ * 1. prefetch app.url different from <micro-app></micro-app>
1270
+ * 2. prefetch param different from <micro-app></micro-app>
1271
+ */
1272
+ if (placeholder) {
1273
+ const convertStyle = pureCreateElement('style');
1274
+ handleConvertStyle(app, address, convertStyle, linkInfo, appSpaceData.attrs);
1275
+ if (placeholder.parentNode) {
1276
+ placeholder.parentNode.replaceChild(convertStyle, placeholder);
1277
+ }
1278
+ else {
1279
+ microAppHead.appendChild(convertStyle);
1280
+ }
1281
+ // clear placeholder
1282
+ appSpaceData.placeholder = null;
1055
1283
  }
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);
1284
+ }
1285
+ /**
1286
+ * Get parsedCode, update convertStyle
1287
+ * Actions:
1288
+ * 1. get scope css (through scopedCSS or oldData)
1289
+ * 2. record parsedCode
1290
+ * 3. set parsedCode to convertStyle if need
1291
+ * @param app app instance
1292
+ * @param address resource address
1293
+ * @param convertStyle converted style
1294
+ * @param linkInfo linkInfo in sourceCenter
1295
+ * @param attrs attrs of link
1296
+ */
1297
+ function handleConvertStyle(app, address, convertStyle, linkInfo, attrs) {
1298
+ if (app.scopecss) {
1299
+ const appSpaceData = linkInfo.appSpace[app.name];
1300
+ appSpaceData.prefix = appSpaceData.prefix || createPrefix(app.name);
1301
+ if (!appSpaceData.parsedCode) {
1302
+ const existParsedCode = getExistParseCode(app.name, appSpaceData.prefix, linkInfo);
1303
+ if (!existParsedCode) {
1304
+ convertStyle.textContent = linkInfo.code;
1305
+ scopedCSS(convertStyle, app, address);
1306
+ }
1307
+ else {
1308
+ convertStyle.textContent = existParsedCode;
1309
+ }
1310
+ appSpaceData.parsedCode = convertStyle.textContent;
1311
+ }
1312
+ else {
1313
+ convertStyle.textContent = appSpaceData.parsedCode;
1314
+ }
1062
1315
  }
1063
1316
  else {
1064
- microAppHead.appendChild(scopedCSS(styleLink, app));
1317
+ convertStyle.textContent = linkInfo.code;
1065
1318
  }
1066
- info.placeholder = null;
1067
- info.code = data;
1319
+ setConvertStyleAttr(convertStyle, attrs);
1068
1320
  }
1069
1321
  /**
1070
- * get css from dynamic link
1071
- * @param url link address
1072
- * @param info info
1322
+ * Handle css of dynamic link
1323
+ * @param address link address
1073
1324
  * @param app app
1325
+ * @param linkInfo linkInfo
1074
1326
  * @param originLink origin link element
1075
- * @param replaceStyle style element which replaced origin link
1076
1327
  */
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;
1328
+ function formatDynamicLink(address, app, linkInfo, originLink) {
1329
+ const convertStyle = pureCreateElement('style');
1330
+ const handleDynamicLink = () => {
1331
+ handleConvertStyle(app, address, convertStyle, linkInfo, linkInfo.appSpace[app.name].attrs);
1332
+ dispatchOnLoadEvent(originLink);
1333
+ };
1334
+ if (linkInfo.code) {
1335
+ defer(handleDynamicLink);
1083
1336
  }
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;
1337
+ else {
1338
+ fetchSource(address, app.name).then((data) => {
1339
+ linkInfo.code = data;
1340
+ handleDynamicLink();
1341
+ }).catch((err) => {
1342
+ logError(err, app.name);
1343
+ dispatchOnErrorEvent(originLink);
1344
+ });
1092
1345
  }
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
- });
1346
+ return convertStyle;
1104
1347
  }
1105
1348
 
1106
1349
  class Adapter {
@@ -1169,17 +1412,25 @@ function fixReactHMRConflict(app) {
1169
1412
  function throttleDeferForParentNode(proxyDocument) {
1170
1413
  const html = globalEnv.rawDocument.firstElementChild;
1171
1414
  if (html && html.parentNode !== proxyDocument) {
1172
- setRootParentNode(html, proxyDocument);
1415
+ setParentNode(html, proxyDocument);
1173
1416
  defer(() => {
1174
- setRootParentNode(html, globalEnv.rawDocument);
1417
+ setParentNode(html, globalEnv.rawDocument);
1175
1418
  });
1176
1419
  }
1177
1420
  }
1178
- function setRootParentNode(root, value) {
1179
- Object.defineProperty(root, 'parentNode', {
1180
- value,
1181
- configurable: true,
1182
- });
1421
+ /**
1422
+ * Modify the point of parentNode
1423
+ * @param target target Node
1424
+ * @param value parentNode
1425
+ */
1426
+ function setParentNode(target, value) {
1427
+ const descriptor = Object.getOwnPropertyDescriptor(target, 'parentNode');
1428
+ if (!descriptor || descriptor.configurable) {
1429
+ rawDefineProperty(target, 'parentNode', {
1430
+ value,
1431
+ configurable: true,
1432
+ });
1433
+ }
1183
1434
  }
1184
1435
 
1185
1436
  // Record element and map element
@@ -1208,14 +1459,16 @@ function handleNewNode(parent, child, app) {
1208
1459
  dynamicElementInMicroAppMap.set(child, linkReplaceComment);
1209
1460
  return linkReplaceComment;
1210
1461
  }
1211
- else if (child.hasAttribute('ignore') || checkIgnoreUrl(child.getAttribute('href'), app.name)) {
1462
+ else if (child.hasAttribute('ignore') ||
1463
+ checkIgnoreUrl(child.getAttribute('href'), app.name) ||
1464
+ (child.href &&
1465
+ isFunction(microApp.options.excludeAssetFilter) &&
1466
+ microApp.options.excludeAssetFilter(child.href))) {
1212
1467
  return child;
1213
1468
  }
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);
1469
+ const { address, linkInfo, replaceComment } = extractLinkFromHtml(child, parent, app, true);
1470
+ if (address && linkInfo) {
1471
+ const replaceStyle = formatDynamicLink(address, app, linkInfo, child);
1219
1472
  dynamicElementInMicroAppMap.set(child, replaceStyle);
1220
1473
  return replaceStyle;
1221
1474
  }
@@ -1226,18 +1479,17 @@ function handleNewNode(parent, child, app) {
1226
1479
  return child;
1227
1480
  }
1228
1481
  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
- }
1482
+ if (child.src &&
1483
+ isFunction(microApp.options.excludeAssetFilter) &&
1484
+ microApp.options.excludeAssetFilter(child.src)) {
1485
+ return child;
1486
+ }
1487
+ const { replaceComment, address, scriptInfo } = extractScriptElement(child, parent, app, true) || {};
1488
+ if (address && scriptInfo) {
1489
+ // remote script or inline script
1490
+ const replaceElement = scriptInfo.isExternal ? runDynamicRemoteScript(address, app, scriptInfo, child) : runDynamicInlineScript(address, app, scriptInfo);
1491
+ dynamicElementInMicroAppMap.set(child, replaceElement);
1492
+ return replaceElement;
1241
1493
  }
1242
1494
  else if (replaceComment) {
1243
1495
  dynamicElementInMicroAppMap.set(child, replaceComment);
@@ -1256,20 +1508,40 @@ function handleNewNode(parent, child, app) {
1256
1508
  * @param passiveChild second param of insertBefore and replaceChild
1257
1509
  */
1258
1510
  function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
1259
- const hijackElement = getHijackElement(parent, app);
1511
+ const hijackParent = getHijackParent(parent, app);
1260
1512
  /**
1261
1513
  * If passiveChild is not the child node, insertBefore replaceChild will have a problem, at this time, it will be degraded to appendChild
1262
1514
  * E.g: document.head.insertBefore(targetChild, document.head.childNodes[0])
1263
1515
  */
1264
- if (hijackElement) {
1516
+ if (hijackParent) {
1517
+ /**
1518
+ * WARNING:
1519
+ * Verifying that the parentNode of the targetChild points to document.body will cause other problems ?
1520
+ */
1521
+ if (hijackParent.tagName === 'MICRO-APP-BODY' && rawMethod !== globalEnv.rawRemoveChild) {
1522
+ const descriptor = Object.getOwnPropertyDescriptor(targetChild, 'parentNode');
1523
+ if (!descriptor || descriptor.configurable) {
1524
+ rawDefineProperty(targetChild, 'parentNode', {
1525
+ configurable: true,
1526
+ get() {
1527
+ /**
1528
+ * When operate child from parentNode async, Element.prototype may reset
1529
+ * e.g.
1530
+ * target.parentNode.remove(target)
1531
+ */
1532
+ return Element.prototype.removeChild === globalEnv.rawRemoveChild ? hijackParent : document.body;
1533
+ },
1534
+ });
1535
+ }
1536
+ }
1265
1537
  /**
1266
1538
  * 1. If passiveChild exists, it must be insertBefore or replaceChild
1267
1539
  * 2. When removeChild, targetChild may not be in microAppHead or head
1268
1540
  */
1269
- if (passiveChild && !hijackElement.contains(passiveChild)) {
1270
- return globalEnv.rawAppendChild.call(hijackElement, targetChild);
1541
+ if (passiveChild && !hijackParent.contains(passiveChild)) {
1542
+ return globalEnv.rawAppendChild.call(hijackParent, targetChild);
1271
1543
  }
1272
- else if (rawMethod === globalEnv.rawRemoveChild && !hijackElement.contains(targetChild)) {
1544
+ else if (rawMethod === globalEnv.rawRemoveChild && !hijackParent.contains(targetChild)) {
1273
1545
  if (parent.contains(targetChild)) {
1274
1546
  return rawMethod.call(parent, targetChild);
1275
1547
  }
@@ -1281,12 +1553,12 @@ function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild
1281
1553
  rawMethod === globalEnv.rawAppendChild) {
1282
1554
  fixReactHMRConflict(app);
1283
1555
  }
1284
- return invokeRawMethod(rawMethod, hijackElement, targetChild, passiveChild);
1556
+ return invokeRawMethod(rawMethod, hijackParent, targetChild, passiveChild);
1285
1557
  }
1286
1558
  return invokeRawMethod(rawMethod, parent, targetChild, passiveChild);
1287
1559
  }
1288
1560
  // head/body map to micro-app-head/micro-app-body
1289
- function getHijackElement(node, app) {
1561
+ function getHijackParent(node, app) {
1290
1562
  var _a, _b;
1291
1563
  if (node === document.head) {
1292
1564
  return (_a = app === null || app === void 0 ? void 0 : app.container) === null || _a === void 0 ? void 0 : _a.querySelector('micro-app-head');
@@ -1319,13 +1591,13 @@ function getMappingNode(node) {
1319
1591
  */
1320
1592
  function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
1321
1593
  const currentAppName = getCurrentAppName();
1322
- if (newChild instanceof Node &&
1594
+ if (isNode(newChild) &&
1323
1595
  (newChild.__MICRO_APP_NAME__ ||
1324
1596
  (currentAppName && !newChild.__PURE_ELEMENT__))) {
1325
1597
  newChild.__MICRO_APP_NAME__ = newChild.__MICRO_APP_NAME__ || currentAppName;
1326
1598
  const app = appInstanceMap.get(newChild.__MICRO_APP_NAME__);
1327
1599
  if (app === null || app === void 0 ? void 0 : app.container) {
1328
- if (newChild instanceof Element) {
1600
+ if (isElement(newChild)) {
1329
1601
  if (/^(img|script)$/i.test(newChild.tagName)) {
1330
1602
  if (newChild.hasAttribute('src')) {
1331
1603
  globalEnv.rawSetAttribute.call(newChild, 'src', CompletionPath(newChild.getAttribute('src'), app.url));
@@ -1345,7 +1617,7 @@ function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
1345
1617
  }
1346
1618
  }
1347
1619
  else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
1348
- if (!(newChild instanceof Node) && currentAppName) {
1620
+ if (!isNode(newChild) && currentAppName) {
1349
1621
  const app = appInstanceMap.get(currentAppName);
1350
1622
  if (app === null || app === void 0 ? void 0 : app.container) {
1351
1623
  if (parent === document.head) {
@@ -1705,8 +1977,80 @@ function initGlobalEnv() {
1705
1977
  }
1706
1978
  }
1707
1979
 
1708
- // Global scripts, reuse across apps
1709
- const globalScripts = new Map();
1980
+ const scriptTypes = ['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module', 'systemjs-module', 'systemjs-importmap'];
1981
+ // whether use type='module' script
1982
+ function isTypeModule(app, scriptInfo) {
1983
+ return scriptInfo.appSpace[app.name].module && (!app.useSandbox || app.esmodule);
1984
+ }
1985
+ // special script element
1986
+ function isSpecialScript(app, scriptInfo) {
1987
+ const attrs = scriptInfo.appSpace[app.name].attrs;
1988
+ return attrs.has('id');
1989
+ }
1990
+ /**
1991
+ * whether to run js in inline mode
1992
+ * scene:
1993
+ * 1. inline config for app
1994
+ * 2. inline attr in script element
1995
+ * 3. module script
1996
+ * 4. script with special attr
1997
+ */
1998
+ function isInlineMode(app, scriptInfo) {
1999
+ return (app.inline ||
2000
+ scriptInfo.appSpace[app.name].inline ||
2001
+ isTypeModule(app, scriptInfo) ||
2002
+ isSpecialScript(app, scriptInfo));
2003
+ }
2004
+ // Convert string code to function
2005
+ function code2Function(code) {
2006
+ return new Function(code);
2007
+ }
2008
+ /**
2009
+ * If the appSpace of the current js address has other app, try to reuse parsedFunction of other app
2010
+ * @param appName app.name
2011
+ * @param scriptInfo scriptInfo of current address
2012
+ * @param currentCode pure code of current address
2013
+ */
2014
+ function getExistParseResult(appName, scriptInfo, currentCode) {
2015
+ const appSpace = scriptInfo.appSpace;
2016
+ for (const item in appSpace) {
2017
+ if (item !== appName) {
2018
+ const appSpaceData = appSpace[item];
2019
+ if (appSpaceData.parsedCode === currentCode && appSpaceData.parsedFunction) {
2020
+ return appSpaceData.parsedFunction;
2021
+ }
2022
+ }
2023
+ }
2024
+ }
2025
+ /**
2026
+ * get parsedFunction from exist data or parsedCode
2027
+ * @returns parsedFunction
2028
+ */
2029
+ function getParsedFunction(app, scriptInfo, parsedCode) {
2030
+ return getExistParseResult(app.name, scriptInfo, parsedCode) || code2Function(parsedCode);
2031
+ }
2032
+ // Prevent randomly created strings from repeating
2033
+ function getUniqueNonceSrc() {
2034
+ const nonceStr = createNonceSrc();
2035
+ if (sourceCenter.script.hasInfo(nonceStr)) {
2036
+ return getUniqueNonceSrc();
2037
+ }
2038
+ return nonceStr;
2039
+ }
2040
+ // transfer the attributes on the script to convertScript
2041
+ function setConvertScriptAttr(convertScript, attrs) {
2042
+ attrs.forEach((value, key) => {
2043
+ if ((key === 'type' && value === 'module') || key === 'defer' || key === 'async')
2044
+ return;
2045
+ if (key === 'src')
2046
+ key = 'data-origin-src';
2047
+ convertScript.setAttribute(key, value);
2048
+ });
2049
+ }
2050
+ // wrap code in sandbox
2051
+ function isWrapInSandBox(app, scriptInfo) {
2052
+ return app.useSandbox && !isTypeModule(app, scriptInfo);
2053
+ }
1710
2054
  /**
1711
2055
  * Extract script elements
1712
2056
  * @param script script element
@@ -1717,14 +2061,15 @@ const globalScripts = new Map();
1717
2061
  function extractScriptElement(script, parent, app, isDynamic = false) {
1718
2062
  let replaceComment = null;
1719
2063
  let src = script.getAttribute('src');
1720
- if (src) {
2064
+ if (src)
1721
2065
  src = CompletionPath(src, app.url);
1722
- }
1723
2066
  if (script.hasAttribute('exclude') || checkExcludeUrl(src, app.name)) {
1724
2067
  replaceComment = document.createComment('script element with exclude attribute removed by micro-app');
1725
2068
  }
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)) {
2069
+ else if ((script.type &&
2070
+ !scriptTypes.includes(script.type)) ||
2071
+ script.hasAttribute('ignore') ||
2072
+ checkIgnoreUrl(src, app.name)) {
1728
2073
  return null;
1729
2074
  }
1730
2075
  else if ((globalEnv.supportModuleScript && script.noModule) ||
@@ -1732,39 +2077,74 @@ function extractScriptElement(script, parent, app, isDynamic = false) {
1732
2077
  replaceComment = document.createComment(`${script.noModule ? 'noModule' : 'module'} script ignored by micro-app`);
1733
2078
  }
1734
2079
  else if (src) { // remote script
1735
- const info = {
1736
- code: '',
1737
- isExternal: true,
1738
- isDynamic: isDynamic,
2080
+ let scriptInfo = sourceCenter.script.getInfo(src);
2081
+ const appSpaceData = {
1739
2082
  async: script.hasAttribute('async'),
1740
2083
  defer: script.defer || script.type === 'module',
1741
2084
  module: script.type === 'module',
1742
- isGlobal: script.hasAttribute('global'),
2085
+ inline: script.hasAttribute('inline'),
2086
+ pure: script.hasAttribute('pure'),
2087
+ attrs: getAttributes(script),
1743
2088
  };
2089
+ if (!scriptInfo) {
2090
+ scriptInfo = {
2091
+ code: '',
2092
+ isExternal: true,
2093
+ appSpace: {
2094
+ [app.name]: appSpaceData,
2095
+ }
2096
+ };
2097
+ }
2098
+ else {
2099
+ /**
2100
+ * Reuse when appSpace exists
2101
+ * NOTE:
2102
+ * 1. The same static script, appSpace must be the same (in fact, it may be different when url change)
2103
+ * 2. The same dynamic script, appSpace may be the same, but we still reuse appSpace, which should pay attention
2104
+ */
2105
+ scriptInfo.appSpace[app.name] = scriptInfo.appSpace[app.name] || appSpaceData;
2106
+ }
2107
+ sourceCenter.script.setInfo(src, scriptInfo);
1744
2108
  if (!isDynamic) {
1745
- app.source.scripts.set(src, info);
2109
+ app.source.scripts.add(src);
1746
2110
  replaceComment = document.createComment(`script with src='${src}' extract by micro-app`);
1747
2111
  }
1748
2112
  else {
1749
- return { url: src, info };
2113
+ return { address: src, scriptInfo };
1750
2114
  }
1751
2115
  }
1752
2116
  else if (script.textContent) { // inline script
1753
- const nonceStr = createNonceSrc();
1754
- const info = {
2117
+ /**
2118
+ * NOTE:
2119
+ * 1. Each inline script is unique
2120
+ * 2. Every dynamic created inline script will be re-executed
2121
+ * ACTION:
2122
+ * 1. Delete dynamic inline script info after exec
2123
+ * 2. Delete static inline script info when destroy
2124
+ */
2125
+ const nonceStr = getUniqueNonceSrc();
2126
+ const scriptInfo = {
1755
2127
  code: script.textContent,
1756
2128
  isExternal: false,
1757
- isDynamic: isDynamic,
1758
- async: false,
1759
- defer: script.type === 'module',
1760
- module: script.type === 'module',
2129
+ appSpace: {
2130
+ [app.name]: {
2131
+ async: false,
2132
+ defer: script.type === 'module',
2133
+ module: script.type === 'module',
2134
+ inline: script.hasAttribute('inline'),
2135
+ pure: script.hasAttribute('pure'),
2136
+ attrs: getAttributes(script),
2137
+ }
2138
+ }
1761
2139
  };
1762
2140
  if (!isDynamic) {
1763
- app.source.scripts.set(nonceStr, info);
2141
+ app.source.scripts.add(nonceStr);
2142
+ sourceCenter.script.setInfo(nonceStr, scriptInfo);
1764
2143
  replaceComment = document.createComment('inline script extract by micro-app');
1765
2144
  }
1766
2145
  else {
1767
- return { url: nonceStr, info };
2146
+ // Because each dynamic script is unique, it is not put into sourceCenter
2147
+ return { address: nonceStr, scriptInfo };
1768
2148
  }
1769
2149
  }
1770
2150
  else if (!isDynamic) {
@@ -1787,38 +2167,38 @@ function extractScriptElement(script, parent, app, isDynamic = false) {
1787
2167
  */
1788
2168
  function getAssetsPlugins(appName) {
1789
2169
  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]) || [];
2170
+ const globalPlugins = ((_a = microApp.options.plugins) === null || _a === void 0 ? void 0 : _a.global) || [];
2171
+ const modulePlugins = ((_c = (_b = microApp.options.plugins) === null || _b === void 0 ? void 0 : _b.modules) === null || _c === void 0 ? void 0 : _c[appName]) || [];
1792
2172
  return [...globalPlugins, ...modulePlugins];
1793
2173
  }
1794
2174
  /**
1795
- * whether the url needs to be excluded
1796
- * @param url css or js link
2175
+ * whether the address needs to be excluded
2176
+ * @param address css or js link
1797
2177
  * @param plugins microApp plugins
1798
2178
  */
1799
- function checkExcludeUrl(url, appName) {
1800
- if (!url)
2179
+ function checkExcludeUrl(address, appName) {
2180
+ if (!address)
1801
2181
  return false;
1802
2182
  const plugins = getAssetsPlugins(appName) || [];
1803
2183
  return plugins.some(plugin => {
1804
2184
  if (!plugin.excludeChecker)
1805
2185
  return false;
1806
- return plugin.excludeChecker(url);
2186
+ return plugin.excludeChecker(address);
1807
2187
  });
1808
2188
  }
1809
2189
  /**
1810
- * whether the url needs to be ignore
1811
- * @param url css or js link
2190
+ * whether the address needs to be ignore
2191
+ * @param address css or js link
1812
2192
  * @param plugins microApp plugins
1813
2193
  */
1814
- function checkIgnoreUrl(url, appName) {
1815
- if (!url)
2194
+ function checkIgnoreUrl(address, appName) {
2195
+ if (!address)
1816
2196
  return false;
1817
2197
  const plugins = getAssetsPlugins(appName) || [];
1818
2198
  return plugins.some(plugin => {
1819
2199
  if (!plugin.ignoreChecker)
1820
2200
  return false;
1821
- return plugin.ignoreChecker(url);
2201
+ return plugin.ignoreChecker(address);
1822
2202
  });
1823
2203
  }
1824
2204
  /**
@@ -1827,28 +2207,31 @@ function checkIgnoreUrl(url, appName) {
1827
2207
  * @param app app
1828
2208
  */
1829
2209
  function fetchScriptsFromHtml(wrapElement, app) {
1830
- const scriptEntries = Array.from(app.source.scripts.entries());
2210
+ const scriptList = Array.from(app.source.scripts);
1831
2211
  const fetchScriptPromise = [];
1832
2212
  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
- }
2213
+ for (const address of scriptList) {
2214
+ const scriptInfo = sourceCenter.script.getInfo(address);
2215
+ const appSpaceData = scriptInfo.appSpace[app.name];
2216
+ if ((!appSpaceData.defer && !appSpaceData.async) || app.isPrefetch) {
2217
+ fetchScriptPromise.push(scriptInfo.code ? scriptInfo.code : fetchSource(address, app.name));
2218
+ fetchScriptPromiseInfo.push([address, scriptInfo]);
1843
2219
  }
1844
2220
  }
2221
+ const fiberScriptTasks = app.isPrefetch || app.fiber ? [] : null;
1845
2222
  if (fetchScriptPromise.length) {
1846
2223
  promiseStream(fetchScriptPromise, (res) => {
1847
- fetchScriptSuccess(fetchScriptPromiseInfo[res.index][0], fetchScriptPromiseInfo[res.index][1], res.data);
2224
+ injectFiberTask(fiberScriptTasks, () => fetchScriptSuccess(fetchScriptPromiseInfo[res.index][0], fetchScriptPromiseInfo[res.index][1], res.data, app));
1848
2225
  }, (err) => {
1849
2226
  logError(err, app.name);
1850
2227
  }, () => {
1851
- app.onLoad(wrapElement);
2228
+ if (fiberScriptTasks) {
2229
+ fiberScriptTasks.push(() => Promise.resolve(app.onLoad(wrapElement)));
2230
+ serialExecFiberTasks(fiberScriptTasks);
2231
+ }
2232
+ else {
2233
+ app.onLoad(wrapElement);
2234
+ }
1852
2235
  });
1853
2236
  }
1854
2237
  else {
@@ -1857,169 +2240,224 @@ function fetchScriptsFromHtml(wrapElement, app) {
1857
2240
  }
1858
2241
  /**
1859
2242
  * fetch js succeeded, record the code value
1860
- * @param url script address
1861
- * @param info resource script info
2243
+ * @param address script address
2244
+ * @param scriptInfo resource script info
1862
2245
  * @param data code
1863
2246
  */
1864
- function fetchScriptSuccess(url, info, data) {
1865
- if (info.isGlobal && !globalScripts.has(url)) {
1866
- globalScripts.set(url, data);
2247
+ function fetchScriptSuccess(address, scriptInfo, code, app) {
2248
+ // reset scriptInfo.code
2249
+ scriptInfo.code = code;
2250
+ /**
2251
+ * Pre parse script for prefetch, improve rendering performance
2252
+ * NOTE:
2253
+ * 1. if global parseResult exist, skip this step
2254
+ * 2. if app is inline or script is esmodule, skip this step
2255
+ * 3. if global parseResult not exist, the current script occupies the position, when js is reused, parseResult is reference
2256
+ */
2257
+ if (app.isPrefetch) {
2258
+ const appSpaceData = scriptInfo.appSpace[app.name];
2259
+ /**
2260
+ * 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.
2261
+ * This causes parsedCode to already exist when preloading ends
2262
+ * e.g.
2263
+ * 1. prefetch app.url different from <micro-app></micro-app>
2264
+ * 2. prefetch param different from <micro-app></micro-app>
2265
+ */
2266
+ if (!appSpaceData.parsedCode) {
2267
+ appSpaceData.parsedCode = bindScope(address, app, code, scriptInfo);
2268
+ appSpaceData.wrapInSandBox = isWrapInSandBox(app, scriptInfo);
2269
+ if (!isInlineMode(app, scriptInfo)) {
2270
+ try {
2271
+ appSpaceData.parsedFunction = getParsedFunction(app, scriptInfo, appSpaceData.parsedCode);
2272
+ }
2273
+ catch (err) {
2274
+ logWarn('Something went wrong while handling preloaded resources', app.name, '\n', err);
2275
+ }
2276
+ }
2277
+ }
1867
2278
  }
1868
- info.code = data;
1869
2279
  }
1870
2280
  /**
1871
2281
  * Execute js in the mount lifecycle
1872
- * @param scriptList script list
1873
2282
  * @param app app
1874
2283
  * @param initHook callback for umd mode
1875
2284
  */
1876
- function execScripts(scriptList, app, initHook) {
1877
- const scriptListEntries = Array.from(scriptList.entries());
2285
+ function execScripts(app, initHook) {
2286
+ const fiberScriptTasks = app.fiber ? [] : null;
2287
+ const scriptList = Array.from(app.source.scripts);
1878
2288
  const deferScriptPromise = [];
1879
2289
  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);
2290
+ for (const address of scriptList) {
2291
+ const scriptInfo = sourceCenter.script.getInfo(address);
2292
+ const appSpaceData = scriptInfo.appSpace[app.name];
2293
+ // Notice the second render
2294
+ if (appSpaceData.defer || appSpaceData.async) {
2295
+ if (scriptInfo.isExternal && !scriptInfo.code) {
2296
+ deferScriptPromise.push(fetchSource(address, app.name));
1892
2297
  }
1893
2298
  else {
1894
- runScript(url, app, info, false);
1895
- initHook(false);
2299
+ deferScriptPromise.push(scriptInfo.code);
1896
2300
  }
2301
+ deferScriptInfo.push([address, scriptInfo]);
2302
+ isTypeModule(app, scriptInfo) && (initHook.moduleCount = initHook.moduleCount ? ++initHook.moduleCount : 1);
2303
+ }
2304
+ else {
2305
+ injectFiberTask(fiberScriptTasks, () => {
2306
+ runScript(address, app, scriptInfo);
2307
+ initHook(false);
2308
+ });
1897
2309
  }
1898
2310
  }
1899
2311
  if (deferScriptPromise.length) {
1900
2312
  promiseStream(deferScriptPromise, (res) => {
1901
- const info = deferScriptInfo[res.index][1];
1902
- info.code = info.code || res.data;
2313
+ const scriptInfo = deferScriptInfo[res.index][1];
2314
+ scriptInfo.code = scriptInfo.code || res.data;
1903
2315
  }, (err) => {
1904
2316
  initHook.errorCount = initHook.errorCount ? ++initHook.errorCount : 1;
1905
2317
  logError(err, app.name);
1906
2318
  }, () => {
1907
- deferScriptInfo.forEach(([url, info]) => {
1908
- if (info.code) {
1909
- runScript(url, app, info, false, initHook);
1910
- !info.module && initHook(false);
2319
+ deferScriptInfo.forEach(([address, scriptInfo]) => {
2320
+ if (scriptInfo.code) {
2321
+ injectFiberTask(fiberScriptTasks, () => {
2322
+ runScript(address, app, scriptInfo, initHook);
2323
+ !isTypeModule(app, scriptInfo) && initHook(false);
2324
+ });
1911
2325
  }
1912
2326
  });
1913
- initHook(isUndefined(initHook.moduleCount) ||
1914
- initHook.errorCount === deferScriptPromise.length);
2327
+ /**
2328
+ * Fiber wraps js in requestIdleCallback and executes it in sequence
2329
+ * NOTE:
2330
+ * 1. In order to ensure the execution order, wait for all js loaded and then execute
2331
+ * 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
2332
+ *
2333
+ * BUG: NOTE.2 - execution order problem
2334
+ */
2335
+ if (fiberScriptTasks) {
2336
+ fiberScriptTasks.push(() => Promise.resolve(initHook(isUndefined(initHook.moduleCount) ||
2337
+ initHook.errorCount === deferScriptPromise.length)));
2338
+ serialExecFiberTasks(fiberScriptTasks);
2339
+ }
2340
+ else {
2341
+ initHook(isUndefined(initHook.moduleCount) ||
2342
+ initHook.errorCount === deferScriptPromise.length);
2343
+ }
1915
2344
  });
1916
2345
  }
1917
2346
  else {
1918
- initHook(true);
2347
+ if (fiberScriptTasks) {
2348
+ fiberScriptTasks.push(() => Promise.resolve(initHook(true)));
2349
+ serialExecFiberTasks(fiberScriptTasks);
2350
+ }
2351
+ else {
2352
+ initHook(true);
2353
+ }
1919
2354
  }
1920
2355
  }
1921
2356
  /**
1922
2357
  * run code
1923
- * @param url script address
2358
+ * @param address script address
1924
2359
  * @param app app
1925
- * @param info script info
1926
- * @param isDynamic dynamically created script
2360
+ * @param scriptInfo script info
1927
2361
  * @param callback callback of module script
1928
2362
  */
1929
- function runScript(url, app, info, isDynamic, callback) {
2363
+ function runScript(address, app, scriptInfo, callback, replaceElement) {
1930
2364
  var _a;
1931
2365
  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);
2366
+ actionsBeforeRunScript(app);
2367
+ const appSpaceData = scriptInfo.appSpace[app.name];
2368
+ const wrapInSandBox = isWrapInSandBox(app, scriptInfo);
2369
+ /**
2370
+ * NOTE:
2371
+ * 1. plugins and wrapCode will only be executed once
2372
+ * 2. if parsedCode not exist, parsedFunction is not exist
2373
+ * 3. if parsedCode exist, parsedFunction does not necessarily exist
2374
+ */
2375
+ if (!appSpaceData.parsedCode || appSpaceData.wrapInSandBox !== wrapInSandBox) {
2376
+ appSpaceData.parsedCode = bindScope(address, app, scriptInfo.code, scriptInfo);
2377
+ appSpaceData.wrapInSandBox = wrapInSandBox;
2378
+ appSpaceData.parsedFunction = null;
2379
+ }
2380
+ if (isInlineMode(app, scriptInfo)) {
2381
+ const scriptElement = replaceElement || pureCreateElement('script');
2382
+ runCode2InlineScript(address, appSpaceData.parsedCode, isTypeModule(app, scriptInfo), scriptElement, appSpaceData.attrs, callback);
2383
+ if (!replaceElement) {
2384
+ // TEST IGNORE
2385
+ (_a = app.container) === null || _a === void 0 ? void 0 : _a.querySelector('micro-app-body').appendChild(scriptElement);
2386
+ }
1940
2387
  }
1941
2388
  else {
1942
- runCode2Function(code, info);
1943
- if (isDynamic)
1944
- return document.createComment('dynamic script extract by micro-app');
2389
+ runParsedFunction(app, scriptInfo);
1945
2390
  }
1946
2391
  }
1947
2392
  catch (e) {
1948
- console.error(`[micro-app from runScript] app ${app.name}: `, e);
2393
+ console.error(`[micro-app from ${replaceElement ? 'runDynamicScript' : 'runScript'}] app ${app.name}: `, e, address);
1949
2394
  }
1950
2395
  }
1951
2396
  /**
1952
2397
  * Get dynamically created remote script
1953
- * @param url script address
1954
- * @param info info
1955
- * @param app app
2398
+ * @param address script address
2399
+ * @param app app instance
2400
+ * @param scriptInfo scriptInfo
1956
2401
  * @param originScript origin script element
1957
2402
  */
1958
- function runDynamicRemoteScript(url, info, app, originScript) {
2403
+ function runDynamicRemoteScript(address, app, scriptInfo, originScript) {
2404
+ const replaceElement = isInlineMode(app, scriptInfo) ? pureCreateElement('script') : document.createComment('dynamic script extract by micro-app');
1959
2405
  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');
2406
+ const runDynamicScript = () => {
2407
+ const descriptor = Object.getOwnPropertyDescriptor(globalEnv.rawDocument, 'currentScript');
2408
+ if (!descriptor || descriptor.configurable) {
2409
+ Object.defineProperty(globalEnv.rawDocument, 'currentScript', {
2410
+ value: originScript,
2411
+ configurable: true,
2412
+ });
2413
+ }
2414
+ runScript(address, app, scriptInfo, dispatchScriptOnLoadEvent, replaceElement);
2415
+ !isTypeModule(app, scriptInfo) && dispatchScriptOnLoadEvent();
2416
+ };
2417
+ if (scriptInfo.code) {
2418
+ defer(runDynamicScript);
1983
2419
  }
1984
2420
  else {
1985
- replaceElement = document.createComment(`dynamic script with src='${url}' extract by micro-app`);
2421
+ fetchSource(address, app.name).then((code) => {
2422
+ scriptInfo.code = code;
2423
+ runDynamicScript();
2424
+ }).catch((err) => {
2425
+ logError(err, app.name);
2426
+ dispatchOnErrorEvent(originScript);
2427
+ });
1986
2428
  }
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
- });
2429
+ return replaceElement;
2430
+ }
2431
+ /**
2432
+ * Get dynamically created inline script
2433
+ * @param address script address
2434
+ * @param app app instance
2435
+ * @param scriptInfo scriptInfo
2436
+ */
2437
+ function runDynamicInlineScript(address, app, scriptInfo) {
2438
+ const replaceElement = isInlineMode(app, scriptInfo) ? pureCreateElement('script') : document.createComment('dynamic script extract by micro-app');
2439
+ runScript(address, app, scriptInfo, void 0, replaceElement);
2008
2440
  return replaceElement;
2009
2441
  }
2010
2442
  /**
2011
2443
  * common handle for inline script
2012
- * @param url script address
2444
+ * @param address script address
2013
2445
  * @param code bound code
2014
2446
  * @param module type='module' of script
2015
2447
  * @param scriptElement target script element
2448
+ * @param attrs attributes of script element
2016
2449
  * @param callback callback of module script
2017
2450
  */
2018
- function runCode2InlineScript(url, code, module, scriptElement, callback) {
2451
+ function runCode2InlineScript(address, code, module, scriptElement, attrs, callback) {
2019
2452
  if (module) {
2020
2453
  // 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);
2454
+ if (isInlineScript(address)) {
2455
+ const blob = new Blob([code], { type: 'text/javascript' });
2456
+ scriptElement.src = URL.createObjectURL(blob);
2457
+ }
2458
+ else {
2459
+ scriptElement.src = address;
2460
+ }
2023
2461
  scriptElement.setAttribute('type', 'module');
2024
2462
  if (callback) {
2025
2463
  callback.moduleCount && callback.moduleCount--;
@@ -2029,55 +2467,65 @@ function runCode2InlineScript(url, code, module, scriptElement, callback) {
2029
2467
  else {
2030
2468
  scriptElement.textContent = code;
2031
2469
  }
2032
- if (!url.startsWith('inline-')) {
2033
- scriptElement.setAttribute('data-origin-src', url);
2034
- }
2470
+ setConvertScriptAttr(scriptElement, attrs);
2035
2471
  }
2036
2472
  // init & run code2Function
2037
- function runCode2Function(code, info) {
2038
- if (!info.code2Function) {
2039
- info.code2Function = new Function(code);
2473
+ function runParsedFunction(app, scriptInfo) {
2474
+ const appSpaceData = scriptInfo.appSpace[app.name];
2475
+ if (!appSpaceData.parsedFunction) {
2476
+ appSpaceData.parsedFunction = getParsedFunction(app, scriptInfo, appSpaceData.parsedCode);
2040
2477
  }
2041
- info.code2Function.call(window);
2478
+ appSpaceData.parsedFunction.call(window);
2042
2479
  }
2043
2480
  /**
2044
2481
  * bind js scope
2045
- * @param url script address
2046
2482
  * @param app app
2047
2483
  * @param code code
2048
- * @param info source script info
2484
+ * @param scriptInfo source script info
2049
2485
  */
2050
- function bindScope(url, app, code, info) {
2051
- // TODO: 增加缓存机制
2052
- if (isPlainObject(microApp.plugins)) {
2053
- code = usePlugins(url, code, app.name, microApp.plugins, info);
2486
+ function bindScope(address, app, code, scriptInfo) {
2487
+ // TODO: cache
2488
+ if (isPlainObject(microApp.options.plugins)) {
2489
+ code = usePlugins(address, code, app.name, microApp.options.plugins);
2054
2490
  }
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__);`;
2491
+ if (isWrapInSandBox(app, scriptInfo)) {
2492
+ 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
2493
  }
2059
2494
  return code;
2060
2495
  }
2496
+ /**
2497
+ * actions before run script
2498
+ */
2499
+ function actionsBeforeRunScript(app) {
2500
+ setActiveProxyWindow(app);
2501
+ }
2502
+ /**
2503
+ * set active sandBox.proxyWindow to window.__MICRO_APP_PROXY_WINDOW__
2504
+ */
2505
+ function setActiveProxyWindow(app) {
2506
+ if (app.sandBox) {
2507
+ globalEnv.rawWindow.__MICRO_APP_PROXY_WINDOW__ = app.sandBox.proxyWindow;
2508
+ }
2509
+ }
2061
2510
  /**
2062
2511
  * Call the plugin to process the file
2063
- * @param url script address
2512
+ * @param address script address
2064
2513
  * @param code code
2065
2514
  * @param appName app name
2066
2515
  * @param plugins plugin list
2067
- * @param info source script info
2068
2516
  */
2069
- function usePlugins(url, code, appName, plugins, info) {
2517
+ function usePlugins(address, code, appName, plugins) {
2070
2518
  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);
2519
+ const newCode = processCode(plugins.global, code, address);
2520
+ return processCode((_a = plugins.modules) === null || _a === void 0 ? void 0 : _a[appName], newCode, address);
2073
2521
  }
2074
- function processCode(configs, code, url, info) {
2522
+ function processCode(configs, code, address) {
2075
2523
  if (!isArray(configs)) {
2076
2524
  return code;
2077
2525
  }
2078
2526
  return configs.reduce((preCode, config) => {
2079
2527
  if (isPlainObject(config) && isFunction(config.loader)) {
2080
- return config.loader(preCode, url, config.options, info);
2528
+ return config.loader(preCode, address);
2081
2529
  }
2082
2530
  return preCode;
2083
2531
  }, code);
@@ -2098,10 +2546,10 @@ function getWrapElement(str) {
2098
2546
  * @param app app
2099
2547
  * @param microAppHead micro-app-head element
2100
2548
  */
2101
- function flatChildren(parent, app, microAppHead) {
2549
+ function flatChildren(parent, app, microAppHead, fiberStyleTasks) {
2102
2550
  const children = Array.from(parent.children);
2103
2551
  children.length && children.forEach((child) => {
2104
- flatChildren(child, app);
2552
+ flatChildren(child, app, microAppHead, fiberStyleTasks);
2105
2553
  });
2106
2554
  for (const dom of children) {
2107
2555
  if (dom instanceof HTMLLinkElement) {
@@ -2120,7 +2568,7 @@ function flatChildren(parent, app, microAppHead) {
2120
2568
  parent.replaceChild(document.createComment('style element with exclude attribute ignored by micro-app'), dom);
2121
2569
  }
2122
2570
  else if (app.scopecss && !dom.hasAttribute('ignore')) {
2123
- scopedCSS(dom, app);
2571
+ injectFiberTask(fiberStyleTasks, () => scopedCSS(dom, app));
2124
2572
  }
2125
2573
  }
2126
2574
  else if (dom instanceof HTMLScriptElement) {
@@ -2148,9 +2596,17 @@ function extractSourceDom(htmlStr, app) {
2148
2596
  app.onerror(new Error(msg));
2149
2597
  return logError(msg, app.name);
2150
2598
  }
2151
- flatChildren(wrapElement, app);
2599
+ const fiberStyleTasks = app.isPrefetch || app.fiber ? [] : null;
2600
+ flatChildren(wrapElement, app, microAppHead, fiberStyleTasks);
2601
+ /**
2602
+ * 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.
2603
+ */
2604
+ const fiberStyleResult = serialExecFiberTasks(fiberStyleTasks);
2152
2605
  if (app.source.links.size) {
2153
- fetchLinksFromHtml(wrapElement, app, microAppHead);
2606
+ fetchLinksFromHtml(wrapElement, app, microAppHead, fiberStyleResult);
2607
+ }
2608
+ else if (fiberStyleResult) {
2609
+ fiberStyleResult.then(() => app.onLoad(wrapElement));
2154
2610
  }
2155
2611
  else {
2156
2612
  app.onLoad(wrapElement);
@@ -2166,6 +2622,37 @@ function extractSourceDom(htmlStr, app) {
2166
2622
  class EventCenter {
2167
2623
  constructor() {
2168
2624
  this.eventList = new Map();
2625
+ this.queue = [];
2626
+ this.recordStep = {};
2627
+ // run task
2628
+ this.process = () => {
2629
+ var _a, _b;
2630
+ let name;
2631
+ const temRecordStep = this.recordStep;
2632
+ const queue = this.queue;
2633
+ this.recordStep = {};
2634
+ this.queue = [];
2635
+ while (name = queue.shift()) {
2636
+ const eventInfo = this.eventList.get(name);
2637
+ // clear tempData, force before exec nextStep
2638
+ const tempData = eventInfo.tempData;
2639
+ const force = eventInfo.force;
2640
+ eventInfo.tempData = null;
2641
+ eventInfo.force = false;
2642
+ if (force || !this.isEqual(eventInfo.data, tempData)) {
2643
+ eventInfo.data = tempData || eventInfo.data;
2644
+ for (const f of eventInfo.callbacks) {
2645
+ f(eventInfo.data);
2646
+ }
2647
+ (_b = (_a = temRecordStep[name]).dispatchDataEvent) === null || _b === void 0 ? void 0 : _b.call(_a);
2648
+ /**
2649
+ * WARING:
2650
+ * If data of other app is sent in nextStep, it may cause confusion of tempData and force
2651
+ */
2652
+ temRecordStep[name].nextStepList.forEach((nextStep) => nextStep());
2653
+ }
2654
+ }
2655
+ };
2169
2656
  }
2170
2657
  // whether the name is legal
2171
2658
  isLegalName(name) {
@@ -2175,6 +2662,39 @@ class EventCenter {
2175
2662
  }
2176
2663
  return true;
2177
2664
  }
2665
+ // add appName to queue
2666
+ enqueue(name, nextStep, dispatchDataEvent) {
2667
+ // this.nextStepList.push(nextStep)
2668
+ if (this.recordStep[name]) {
2669
+ this.recordStep[name].nextStepList.push(nextStep);
2670
+ dispatchDataEvent && (this.recordStep[name].dispatchDataEvent = dispatchDataEvent);
2671
+ }
2672
+ else {
2673
+ this.recordStep[name] = {
2674
+ nextStepList: [nextStep],
2675
+ dispatchDataEvent,
2676
+ };
2677
+ }
2678
+ /**
2679
+ * The micro task is executed async when the second render of child.
2680
+ * We should ensure that the data changes are executed before binding the listening function
2681
+ */
2682
+ (!this.queue.includes(name) && this.queue.push(name) === 1) && defer(this.process);
2683
+ }
2684
+ /**
2685
+ * In react, each setState will trigger setData, so we need a filter operation to avoid repeated trigger
2686
+ */
2687
+ isEqual(oldData, newData) {
2688
+ if (!newData || Object.keys(oldData).length !== Object.keys(newData).length)
2689
+ return false;
2690
+ for (const key in oldData) {
2691
+ if (Object.prototype.hasOwnProperty.call(oldData, key)) {
2692
+ if (oldData[key] !== newData[key])
2693
+ return false;
2694
+ }
2695
+ }
2696
+ return true;
2697
+ }
2178
2698
  /**
2179
2699
  * add listener
2180
2700
  * @param name event name
@@ -2194,7 +2714,10 @@ class EventCenter {
2194
2714
  };
2195
2715
  this.eventList.set(name, eventInfo);
2196
2716
  }
2197
- else if (autoTrigger && Object.getOwnPropertyNames(eventInfo.data).length) {
2717
+ else if (autoTrigger &&
2718
+ Object.keys(eventInfo.data).length &&
2719
+ (!this.queue.includes(name) ||
2720
+ this.isEqual(eventInfo.data, eventInfo.tempData))) {
2198
2721
  // auto trigger when data not null
2199
2722
  f(eventInfo.data);
2200
2723
  }
@@ -2215,21 +2738,27 @@ class EventCenter {
2215
2738
  }
2216
2739
  }
2217
2740
  }
2741
+ /**
2742
+ * clearData
2743
+ */
2744
+ clearData(name) {
2745
+ if (this.isLegalName(name)) {
2746
+ const eventInfo = this.eventList.get(name);
2747
+ if (eventInfo) {
2748
+ eventInfo.data = {};
2749
+ }
2750
+ }
2751
+ }
2218
2752
  // dispatch data
2219
- dispatch(name, data) {
2753
+ dispatch(name, data, nextStep, force, dispatchDataEvent) {
2220
2754
  if (this.isLegalName(name)) {
2221
2755
  if (!isPlainObject(data)) {
2222
2756
  return logError('event-center: data must be object');
2223
2757
  }
2224
2758
  let eventInfo = this.eventList.get(name);
2225
2759
  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
- }
2760
+ eventInfo.tempData = assign({}, eventInfo.tempData || eventInfo.data, data);
2761
+ !eventInfo.force && (eventInfo.force = !!force);
2233
2762
  }
2234
2763
  else {
2235
2764
  eventInfo = {
@@ -2237,7 +2766,13 @@ class EventCenter {
2237
2766
  callbacks: new Set(),
2238
2767
  };
2239
2768
  this.eventList.set(name, eventInfo);
2769
+ /**
2770
+ * When sent data to parent, eventInfo probably does not exist, because parent may listen to datachange
2771
+ */
2772
+ eventInfo.force = true;
2240
2773
  }
2774
+ // add to queue, event eventInfo is null
2775
+ this.enqueue(name, nextStep, dispatchDataEvent);
2241
2776
  }
2242
2777
  }
2243
2778
  // get data
@@ -2286,10 +2821,13 @@ class EventCenterForGlobal {
2286
2821
  * dispatch global data
2287
2822
  * @param data data
2288
2823
  */
2289
- setGlobalData(data) {
2824
+ setGlobalData(data, nextStep, force) {
2290
2825
  // clear dom scope before dispatch global data, apply to micro app
2291
2826
  removeDomScope();
2292
- eventCenter.dispatch('global', data);
2827
+ eventCenter.dispatch('global', data, () => isFunction(nextStep) && nextStep(), force);
2828
+ }
2829
+ forceSetGlobalData(data, nextStep) {
2830
+ this.setGlobalData(data, nextStep, true);
2293
2831
  }
2294
2832
  /**
2295
2833
  * get global data
@@ -2297,6 +2835,12 @@ class EventCenterForGlobal {
2297
2835
  getGlobalData() {
2298
2836
  return eventCenter.getData('global');
2299
2837
  }
2838
+ /**
2839
+ * clear global data
2840
+ */
2841
+ clearGlobalData() {
2842
+ eventCenter.clearData('global');
2843
+ }
2300
2844
  /**
2301
2845
  * clear all listener of global data
2302
2846
  * if appName exists, only the specified functions is cleared
@@ -2347,8 +2891,19 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
2347
2891
  * @param appName app.name
2348
2892
  * @param data data
2349
2893
  */
2350
- setData(appName, data) {
2351
- eventCenter.dispatch(formatEventName(formatAppName(appName), true), data);
2894
+ setData(appName, data, nextStep, force) {
2895
+ eventCenter.dispatch(formatEventName(formatAppName(appName), true), data, () => isFunction(nextStep) && nextStep(), force);
2896
+ }
2897
+ forceSetData(appName, data, nextStep) {
2898
+ this.setData(appName, data, nextStep, true);
2899
+ }
2900
+ /**
2901
+ * clear data from base app
2902
+ * @param appName app.name
2903
+ * @param fromBaseApp whether clear data from child app, default is true
2904
+ */
2905
+ clearData(appName, fromBaseApp = true) {
2906
+ eventCenter.clearData(formatEventName(formatAppName(appName), fromBaseApp));
2352
2907
  }
2353
2908
  /**
2354
2909
  * clear all listener for specified micro app
@@ -2384,25 +2939,36 @@ class EventCenterForMicroApp extends EventCenterForGlobal {
2384
2939
  /**
2385
2940
  * get data from base app
2386
2941
  */
2387
- getData() {
2388
- return eventCenter.getData(formatEventName(this.appName, true));
2942
+ getData(fromBaseApp = true) {
2943
+ return eventCenter.getData(formatEventName(this.appName, fromBaseApp));
2389
2944
  }
2390
2945
  /**
2391
2946
  * dispatch data to base app
2392
2947
  * @param data data
2393
2948
  */
2394
- dispatch(data) {
2949
+ dispatch(data, nextStep, force) {
2395
2950
  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
- }
2951
+ eventCenter.dispatch(formatEventName(this.appName, false), data, () => isFunction(nextStep) && nextStep(), force, () => {
2952
+ const app = appInstanceMap.get(this.appName);
2953
+ if ((app === null || app === void 0 ? void 0 : app.container) && isPlainObject(data)) {
2954
+ const event = new CustomEvent('datachange', {
2955
+ detail: {
2956
+ data: eventCenter.getData(formatEventName(this.appName, false))
2957
+ }
2958
+ });
2959
+ getRootContainer(app.container).dispatchEvent(event);
2960
+ }
2961
+ });
2962
+ }
2963
+ forceDispatch(data, nextStep) {
2964
+ this.dispatch(data, nextStep, true);
2965
+ }
2966
+ /**
2967
+ * clear data from child app
2968
+ * @param fromBaseApp whether clear data from base app, default is false
2969
+ */
2970
+ clearData(fromBaseApp = false) {
2971
+ eventCenter.clearData(formatEventName(this.appName, fromBaseApp));
2406
2972
  }
2407
2973
  /**
2408
2974
  * clear all listeners
@@ -2989,9 +3555,19 @@ function addHistoryListener(appName) {
2989
3555
  function dispatchPopStateEventToMicroApp(appName, proxyWindow) {
2990
3556
  // create PopStateEvent named popstate-appName with sub app state
2991
3557
  const newPopStateEvent = new PopStateEvent(formatEventName$1('popstate', appName), { state: getMicroState(appName) });
3558
+ /**
3559
+ * angular14 takes e.type as type judgment
3560
+ * when e.type is popstate-appName popstate event will be invalid
3561
+ */
3562
+ // Object.defineProperty(newPopStateEvent, 'type', {
3563
+ // value: 'popstate',
3564
+ // writable: true,
3565
+ // configurable: true,
3566
+ // enumerable: true,
3567
+ // })
2992
3568
  globalEnv.rawWindow.dispatchEvent(newPopStateEvent);
2993
3569
  // call function window.onpopstate if it exists
2994
- typeof proxyWindow.onpopstate === 'function' && proxyWindow.onpopstate(newPopStateEvent);
3570
+ isFunction(proxyWindow.onpopstate) && proxyWindow.onpopstate(newPopStateEvent);
2995
3571
  }
2996
3572
  /**
2997
3573
  * dispatch formatted hashchange event to microApp
@@ -3006,7 +3582,7 @@ function dispatchHashChangeEventToMicroApp(appName, proxyWindow, oldHref) {
3006
3582
  });
3007
3583
  globalEnv.rawWindow.dispatchEvent(newHashChangeEvent);
3008
3584
  // call function window.onhashchange if it exists
3009
- typeof proxyWindow.onhashchange === 'function' && proxyWindow.onhashchange(newHashChangeEvent);
3585
+ isFunction(proxyWindow.onhashchange) && proxyWindow.onhashchange(newHashChangeEvent);
3010
3586
  }
3011
3587
  /**
3012
3588
  * dispatch native PopStateEvent, simulate location behavior
@@ -3163,7 +3739,6 @@ function reWriteHistoryMethod(method) {
3163
3739
  * 1. Exec after apply pushState/replaceState
3164
3740
  * 2. Unable to catch when base app navigate with location
3165
3741
  * 3. When in nest app, rawPushState/rawReplaceState has been modified by parent
3166
- * 4.
3167
3742
  */
3168
3743
  getActiveApps(true).forEach(appName => {
3169
3744
  const app = appInstanceMap.get(appName);
@@ -3273,7 +3848,7 @@ function createRouterApi() {
3273
3848
  removeDomScope();
3274
3849
  for (const guard of guards) {
3275
3850
  if (isFunction(guard)) {
3276
- guard(appName, to, from);
3851
+ guard(to, from, appName);
3277
3852
  }
3278
3853
  else if (isPlainObject(guard) && isFunction(guard[appName])) {
3279
3854
  guard[appName](to, from);
@@ -3614,7 +4189,12 @@ function createMicroRouter(appName, url) {
3614
4189
  microHistory: createMicroHistory(appName, microLocation),
3615
4190
  };
3616
4191
  }
3617
- // 当沙箱执行start, 或者隐藏的keep-alive应用重新渲染时时才根据浏览器url更新location 或者 将参数更新到url上
4192
+ /**
4193
+ * 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
4194
+ * @param appName app.name
4195
+ * @param microLocation MicroLocation for sandbox
4196
+ * @param defaultPage default page
4197
+ */
3618
4198
  function initRouteStateWithURL(appName, microLocation, defaultPage) {
3619
4199
  const microPath = getMicroPathFromURL(appName);
3620
4200
  if (microPath) {
@@ -3766,7 +4346,7 @@ function useMicroEventSource() {
3766
4346
  const { createMicroEventSource, clearMicroEventSource } = useMicroEventSource();
3767
4347
  const globalPropertyList = ['window', 'self', 'globalThis'];
3768
4348
  class SandBox {
3769
- constructor(appName, url, useMemoryRouter = true) {
4349
+ constructor(appName, url) {
3770
4350
  /**
3771
4351
  * Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
3772
4352
  * Fix https://github.com/micro-zoe/micro-app/issues/234
@@ -3791,26 +4371,36 @@ class SandBox {
3791
4371
  // Rewrite global event listener & timeout
3792
4372
  assign(this, effect(appName, this.microAppWindow));
3793
4373
  // inject global properties
3794
- this.initStaticGlobalKeys(this.microAppWindow, appName, url, useMemoryRouter);
4374
+ this.initStaticGlobalKeys(this.microAppWindow, appName, url);
3795
4375
  }
3796
- // TODO: 重构
3797
- start(umdMode = false, baseRoute = '', useMemoryRouter = true, defaultPage = '', disablePatchRequest = false) {
4376
+ /**
4377
+ * open sandbox and perform some initial actions
4378
+ * @param umdMode is umd mode
4379
+ * @param baseroute base route for child
4380
+ * @param useMemoryRouter use virtual router
4381
+ * @param defaultPage default page when mount child base on virtual router
4382
+ * @param disablePatchRequest prevent patchRequestApi
4383
+ */
4384
+ start({ umdMode, baseroute, useMemoryRouter, defaultPage, disablePatchRequest, }) {
3798
4385
  if (!this.active) {
3799
4386
  this.active = true;
3800
4387
  if (useMemoryRouter) {
4388
+ if (isUndefined(this.microAppWindow.location)) {
4389
+ this.setMicroAppRouter(this.microAppWindow, this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__);
4390
+ }
3801
4391
  this.initRouteState(defaultPage);
3802
4392
  // unique listener of popstate event for sub app
3803
- this.removeHistoryListener = addHistoryListener(this.proxyWindow.__MICRO_APP_NAME__);
4393
+ this.removeHistoryListener = addHistoryListener(this.microAppWindow.__MICRO_APP_NAME__);
3804
4394
  }
3805
4395
  else {
3806
- this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseRoute;
4396
+ this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute;
3807
4397
  }
3808
4398
  /**
3809
4399
  * 1. prevent the key deleted during sandBox.stop after rewrite
3810
4400
  * 2. umd mode will not delete any keys during sandBox.stop
3811
4401
  */
3812
4402
  if (!umdMode) {
3813
- this.initGlobalKeysWhenStart(this.microAppWindow, this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.__MICRO_APP_URL__, disablePatchRequest);
4403
+ this.initGlobalKeysWhenStart(this.microAppWindow, this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__, disablePatchRequest);
3814
4404
  }
3815
4405
  if (++SandBox.activeCount === 1) {
3816
4406
  effectDocumentEvent();
@@ -3821,18 +4411,26 @@ class SandBox {
3821
4411
  fixBabelPolyfill6();
3822
4412
  }
3823
4413
  }
3824
- stop(umdMode, keepRouteState, clearEventSource) {
4414
+ /**
4415
+ * close sandbox and perform some clean up actions
4416
+ * @param umdMode is umd mode
4417
+ * @param keepRouteState prevent reset route
4418
+ * @param clearEventSource clear MicroEventSource when destroy
4419
+ * @param clearData clear data from base app
4420
+ */
4421
+ stop({ umdMode, keepRouteState, clearEventSource, clearData, }) {
3825
4422
  if (this.active) {
3826
4423
  this.releaseEffect();
3827
4424
  this.microAppWindow.microApp.clearDataListener();
3828
4425
  this.microAppWindow.microApp.clearGlobalDataListener();
4426
+ clearData && this.microAppWindow.microApp.clearData();
3829
4427
  if (this.removeHistoryListener) {
3830
4428
  this.clearRouteState(keepRouteState);
3831
4429
  // release listener of popstate
3832
4430
  this.removeHistoryListener();
3833
4431
  }
3834
4432
  if (clearEventSource) {
3835
- clearMicroEventSource(this.proxyWindow.__MICRO_APP_NAME__);
4433
+ clearMicroEventSource(this.microAppWindow.__MICRO_APP_NAME__);
3836
4434
  }
3837
4435
  /**
3838
4436
  * NOTE:
@@ -3883,9 +4481,9 @@ class SandBox {
3883
4481
  getSpecialProperties(appName) {
3884
4482
  var _a;
3885
4483
  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]);
4484
+ if (isPlainObject(microApp.options.plugins)) {
4485
+ this.commonActionForSpecialProperties(microApp.options.plugins.global);
4486
+ this.commonActionForSpecialProperties((_a = microApp.options.plugins.modules) === null || _a === void 0 ? void 0 : _a[appName]);
3889
4487
  }
3890
4488
  }
3891
4489
  // common action for global plugins and module plugins
@@ -4003,7 +4601,7 @@ class SandBox {
4003
4601
  * @param url app url
4004
4602
  * @param useMemoryRouter whether use memory router
4005
4603
  */
4006
- initStaticGlobalKeys(microAppWindow, appName, url, useMemoryRouter) {
4604
+ initStaticGlobalKeys(microAppWindow, appName, url) {
4007
4605
  microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
4008
4606
  microAppWindow.__MICRO_APP_NAME__ = appName;
4009
4607
  microAppWindow.__MICRO_APP_URL__ = url;
@@ -4018,8 +4616,6 @@ class SandBox {
4018
4616
  });
4019
4617
  this.setProxyDocument(microAppWindow, appName);
4020
4618
  this.setMappingPropertiesWithRawDescriptor(microAppWindow);
4021
- if (useMemoryRouter)
4022
- this.setMicroAppRouter(microAppWindow, appName, url);
4023
4619
  }
4024
4620
  setProxyDocument(microAppWindow, appName) {
4025
4621
  const { proxyDocument, MicroDocument } = this.createProxyDocument(appName);
@@ -4197,17 +4793,19 @@ class SandBox {
4197
4793
  return element;
4198
4794
  };
4199
4795
  const proxyDocument = new Proxy(rawDocument, {
4200
- get(target, key) {
4796
+ get: (target, key) => {
4201
4797
  throttleDeferForSetAppName(appName);
4202
4798
  throttleDeferForParentNode(proxyDocument);
4203
4799
  if (key === 'createElement')
4204
4800
  return createElement;
4205
4801
  if (key === Symbol.toStringTag)
4206
4802
  return 'ProxyDocument';
4803
+ if (key === 'defaultView')
4804
+ return this.proxyWindow;
4207
4805
  const rawValue = Reflect.get(target, key);
4208
4806
  return isFunction(rawValue) ? bindFunctionToRawObject(rawDocument, rawValue, 'DOCUMENT') : rawValue;
4209
4807
  },
4210
- set(target, key, value) {
4808
+ set: (target, key, value) => {
4211
4809
  // Fix TypeError: Illegal invocation when set document.title
4212
4810
  Reflect.set(target, key, value);
4213
4811
  /**
@@ -4299,10 +4897,8 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
4299
4897
  });
4300
4898
  formatEventInfo(event, element);
4301
4899
  // 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);
4900
+ if (isFunction((_a = microApp.options.lifeCycles) === null || _a === void 0 ? void 0 : _a[lifecycleName])) {
4901
+ microApp.options.lifeCycles[lifecycleName](event);
4306
4902
  }
4307
4903
  element.dispatchEvent(event);
4308
4904
  }
@@ -4322,7 +4918,7 @@ function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
4322
4918
  // micro app instances
4323
4919
  const appInstanceMap = new Map();
4324
4920
  class CreateApp {
4325
- constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, useMemoryRouter, baseroute, keepRouteState, hiddenRouter, defaultPage, disablePatchRequest, }) {
4921
+ constructor({ name, url, container, scopecss, useSandbox, inline, esmodule, ssrUrl, isPrefetch, }) {
4326
4922
  this.state = appStates.CREATED;
4327
4923
  this.keepAliveState = null;
4328
4924
  this.keepAliveContainer = null;
@@ -4331,30 +4927,26 @@ class CreateApp {
4331
4927
  this.umdHookUnmount = null;
4332
4928
  this.libraryName = null;
4333
4929
  this.umdMode = false;
4334
- this.isPrefetch = false;
4335
- this.prefetchResolve = null;
4336
- this.container = null;
4337
4930
  this.sandBox = null;
4931
+ this.keepRouteState = false;
4932
+ this.fiber = false;
4933
+ this.useMemoryRouter = true;
4338
4934
  this.name = name;
4339
4935
  this.url = url;
4340
4936
  this.useSandbox = useSandbox;
4341
4937
  this.scopecss = this.useSandbox && scopecss;
4342
- this.useMemoryRouter = this.useSandbox && useMemoryRouter;
4343
- // optional during init base on prefetch 👇
4938
+ this.inline = inline !== null && inline !== void 0 ? inline : false;
4939
+ this.esmodule = esmodule !== null && esmodule !== void 0 ? esmodule : false;
4940
+ // not exist when prefetch 👇
4344
4941
  this.container = container !== null && container !== void 0 ? container : null;
4345
4942
  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
- };
4943
+ // not exist when normal 👇
4944
+ this.isPrefetch = isPrefetch !== null && isPrefetch !== void 0 ? isPrefetch : false;
4945
+ // init actions
4946
+ appInstanceMap.set(this.name, this);
4947
+ this.source = { html: null, links: new Set(), scripts: new Set() };
4356
4948
  this.loadSourceCode();
4357
- this.useSandbox && (this.sandBox = new SandBox(name, url, this.useMemoryRouter));
4949
+ this.useSandbox && (this.sandBox = new SandBox(name, url));
4358
4950
  }
4359
4951
  // Load resources
4360
4952
  loadSourceCode() {
@@ -4365,16 +4957,12 @@ class CreateApp {
4365
4957
  * When resource is loaded, mount app if it is not prefetch or unmount
4366
4958
  */
4367
4959
  onLoad(html) {
4368
- var _a;
4369
4960
  if (++this.loadSourceLevel === 2) {
4370
4961
  this.source.html = html;
4371
- if (this.isPrefetch) {
4372
- (_a = this.prefetchResolve) === null || _a === void 0 ? void 0 : _a.call(this);
4373
- this.prefetchResolve = null;
4374
- }
4375
- else if (appStates.UNMOUNT !== this.state) {
4376
- this.state = appStates.LOADED;
4377
- this.mount();
4962
+ this.state = appStates.LOADED;
4963
+ if (!this.isPrefetch && appStates.UNMOUNT !== this.state) {
4964
+ // @ts-ignore
4965
+ getRootContainer(this.container).mount(this);
4378
4966
  }
4379
4967
  }
4380
4968
  }
@@ -4384,10 +4972,6 @@ class CreateApp {
4384
4972
  */
4385
4973
  onLoadError(e) {
4386
4974
  this.loadSourceLevel = -1;
4387
- if (this.prefetchResolve) {
4388
- this.prefetchResolve();
4389
- this.prefetchResolve = null;
4390
- }
4391
4975
  if (appStates.UNMOUNT !== this.state) {
4392
4976
  this.onerror(e);
4393
4977
  this.state = appStates.LOAD_FAILED;
@@ -4401,15 +4985,16 @@ class CreateApp {
4401
4985
  * @param keepRouteState keep route state when unmount, default is false
4402
4986
  * @param disablePatchRequest prevent rewrite request method of child app
4403
4987
  */
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;
4988
+ mount({ container, inline, esmodule, useMemoryRouter, baseroute, keepRouteState, defaultPage, disablePatchRequest, fiber, }) {
4989
+ var _a, _b;
4990
+ this.container = container;
4991
+ this.inline = inline;
4992
+ this.esmodule = esmodule;
4993
+ this.keepRouteState = keepRouteState;
4994
+ this.fiber = fiber;
4995
+ // use in sandbox/effect
4996
+ this.useMemoryRouter = useMemoryRouter;
4997
+ // this.hiddenRouter = hiddenRouter ?? this.hiddenRouter
4413
4998
  if (this.loadSourceLevel !== 2) {
4414
4999
  this.state = appStates.LOADING;
4415
5000
  return;
@@ -4417,24 +5002,34 @@ class CreateApp {
4417
5002
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
4418
5003
  this.state = appStates.MOUNTING;
4419
5004
  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);
5005
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.start({
5006
+ umdMode: this.umdMode,
5007
+ baseroute,
5008
+ useMemoryRouter,
5009
+ defaultPage,
5010
+ disablePatchRequest,
5011
+ });
4421
5012
  let umdHookMountResult; // result of mount function
4422
5013
  if (!this.umdMode) {
4423
5014
  let hasDispatchMountedEvent = false;
4424
5015
  // if all js are executed, param isFinished will be true
4425
- execScripts(this.source.scripts, this, (isFinished) => {
5016
+ execScripts(this, (isFinished) => {
4426
5017
  if (!this.umdMode) {
4427
5018
  const { mount, unmount } = this.getUmdLibraryHooks();
5019
+ /**
5020
+ * umdHookUnmount can works in non UMD mode
5021
+ * register with window.unmount
5022
+ */
5023
+ this.umdHookUnmount = unmount;
4428
5024
  // if mount & unmount is function, the sub app is umd mode
4429
5025
  if (isFunction(mount) && isFunction(unmount)) {
4430
5026
  this.umdHookMount = mount;
4431
- this.umdHookUnmount = unmount;
4432
5027
  this.umdMode = true;
4433
5028
  if (this.sandBox)
4434
5029
  this.sandBox.proxyWindow.__MICRO_APP_UMD_MODE__ = true;
4435
5030
  // this.sandBox?.recordUmdSnapshot()
4436
5031
  try {
4437
- umdHookMountResult = this.umdHookMount();
5032
+ umdHookMountResult = this.umdHookMount(microApp.getData(this.name, true));
4438
5033
  }
4439
5034
  catch (e) {
4440
5035
  logError('an error occurred in the mount function \n', this.name, e);
@@ -4448,7 +5043,7 @@ class CreateApp {
4448
5043
  });
4449
5044
  }
4450
5045
  else {
4451
- (_c = this.sandBox) === null || _c === void 0 ? void 0 : _c.rebuildUmdSnapshot();
5046
+ (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.rebuildUmdSnapshot();
4452
5047
  try {
4453
5048
  umdHookMountResult = this.umdHookMount();
4454
5049
  }
@@ -4478,6 +5073,9 @@ class CreateApp {
4478
5073
  dispatchMountedEvent() {
4479
5074
  if (appStates.UNMOUNT !== this.state) {
4480
5075
  this.state = appStates.MOUNTED;
5076
+ // call window.onmount of child app
5077
+ callFnWithTryCatch(this.getGlobalEventListener(microGlobalEvent.ONMOUNT), this.name, `window.${microGlobalEvent.ONMOUNT}`, microApp.getData(this.name, true));
5078
+ // dispatch event mounted to parent
4481
5079
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.MOUNTED);
4482
5080
  }
4483
5081
  }
@@ -4487,7 +5085,7 @@ class CreateApp {
4487
5085
  * @param destroy completely destroy, delete cache resources
4488
5086
  * @param unmountcb callback of unmount
4489
5087
  */
4490
- unmount(destroy, unmountcb) {
5088
+ unmount({ destroy, clearData, unmountcb, }) {
4491
5089
  if (this.state === appStates.LOAD_FAILED) {
4492
5090
  destroy = true;
4493
5091
  }
@@ -4500,17 +5098,19 @@ class CreateApp {
4500
5098
  * send an unmount event to the micro app or call umd unmount hook
4501
5099
  * before the sandbox is cleared
4502
5100
  */
4503
- if (this.umdHookUnmount) {
5101
+ if (isFunction(this.umdHookUnmount)) {
4504
5102
  try {
4505
- umdHookUnmountResult = this.umdHookUnmount();
5103
+ umdHookUnmountResult = this.umdHookUnmount(microApp.getData(this.name, true));
4506
5104
  }
4507
5105
  catch (e) {
4508
5106
  logError('an error occurred in the unmount function \n', this.name, e);
4509
5107
  }
4510
5108
  }
5109
+ // call window.onunmount of child app
5110
+ callFnWithTryCatch(this.getGlobalEventListener(microGlobalEvent.ONUNMOUNT), this.name, `window.${microGlobalEvent.ONUNMOUNT}`);
4511
5111
  // dispatch unmount event to micro app
4512
5112
  dispatchCustomEventToMicroApp('unmount', this.name);
4513
- this.handleUnmounted(destroy, umdHookUnmountResult, unmountcb);
5113
+ this.handleUnmounted(destroy, clearData, umdHookUnmountResult, unmountcb);
4514
5114
  }
4515
5115
  /**
4516
5116
  * handle for promise umdHookUnmount
@@ -4518,14 +5118,14 @@ class CreateApp {
4518
5118
  * @param umdHookUnmountResult result of umdHookUnmount
4519
5119
  * @param unmountcb callback of unmount
4520
5120
  */
4521
- handleUnmounted(destroy, umdHookUnmountResult, unmountcb) {
5121
+ handleUnmounted(destroy, clearData, umdHookUnmountResult, unmountcb) {
4522
5122
  if (isPromise(umdHookUnmountResult)) {
4523
5123
  umdHookUnmountResult
4524
- .then(() => this.actionsForUnmount(destroy, unmountcb))
4525
- .catch(() => this.actionsForUnmount(destroy, unmountcb));
5124
+ .then(() => this.actionsForUnmount(destroy, clearData, unmountcb))
5125
+ .catch(() => this.actionsForUnmount(destroy, clearData, unmountcb));
4526
5126
  }
4527
5127
  else {
4528
- this.actionsForUnmount(destroy, unmountcb);
5128
+ this.actionsForUnmount(destroy, clearData, unmountcb);
4529
5129
  }
4530
5130
  }
4531
5131
  /**
@@ -4533,7 +5133,7 @@ class CreateApp {
4533
5133
  * @param destroy completely destroy, delete cache resources
4534
5134
  * @param unmountcb callback of unmount
4535
5135
  */
4536
- actionsForUnmount(destroy, unmountcb) {
5136
+ actionsForUnmount(destroy, clearData, unmountcb) {
4537
5137
  var _a, _b;
4538
5138
  if (destroy) {
4539
5139
  this.actionsForCompletelyDestroy();
@@ -4544,13 +5144,21 @@ class CreateApp {
4544
5144
  if (this.umdMode) {
4545
5145
  (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordUmdSnapshot();
4546
5146
  }
5147
+ if (clearData || destroy) {
5148
+ microApp.clearData(this.name);
5149
+ }
4547
5150
  /**
4548
5151
  * this.container maybe contains micro-app element, stop sandbox should exec after cloneContainer
4549
5152
  * NOTE:
4550
5153
  * 1. if destroy is true, clear route state
4551
5154
  * 2. umd mode and keep-alive will not clear EventSource
4552
5155
  */
4553
- (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.stop(this.umdMode, this.keepRouteState && !destroy, !this.umdMode || destroy);
5156
+ (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.stop({
5157
+ umdMode: this.umdMode,
5158
+ keepRouteState: this.keepRouteState && !destroy,
5159
+ clearEventSource: !this.umdMode || destroy,
5160
+ clearData: clearData || destroy,
5161
+ });
4554
5162
  if (!getActiveApps().length) {
4555
5163
  releasePatchSetAttribute();
4556
5164
  }
@@ -4565,10 +5173,11 @@ class CreateApp {
4565
5173
  if (!this.useSandbox && this.umdMode) {
4566
5174
  delete window[this.libraryName];
4567
5175
  }
5176
+ sourceCenter.script.deleteInlineInfo(this.source.scripts);
4568
5177
  appInstanceMap.delete(this.name);
4569
5178
  }
4570
5179
  // hidden app when disconnectedCallback called with keep-alive
4571
- hiddenKeepAliveApp() {
5180
+ hiddenKeepAliveApp(callback) {
4572
5181
  var _a;
4573
5182
  const oldContainer = this.container;
4574
5183
  cloneContainer(this.container, this.keepAliveContainer ? this.keepAliveContainer : (this.keepAliveContainer = document.createElement('div')), false);
@@ -4583,6 +5192,7 @@ class CreateApp {
4583
5192
  dispatchLifecyclesEvent(oldContainer, this.name, lifeCycles.AFTERHIDDEN);
4584
5193
  // called after lifeCyclesEvent
4585
5194
  (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.removeRouteInfoForKeepAliveApp();
5195
+ callback && callback();
4586
5196
  }
4587
5197
  // show app when connectedCallback called with keep-alive
4588
5198
  showKeepAliveApp(container) {
@@ -4622,16 +5232,27 @@ class CreateApp {
4622
5232
  }
4623
5233
  // get umd library, if it not exist, return empty object
4624
5234
  getUmdLibraryHooks() {
4625
- var _a, _b;
5235
+ var _a, _b, _c, _d;
4626
5236
  // after execScripts, the app maybe unmounted
4627
5237
  if (appStates.UNMOUNT !== this.state) {
4628
5238
  const global = ((_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow) !== null && _b !== void 0 ? _b : globalEnv.rawWindow);
4629
5239
  this.libraryName = getRootContainer(this.container).getAttribute('library') || `micro-app-${this.name}`;
4630
- // do not use isObject
4631
- return typeof global[this.libraryName] === 'object' ? global[this.libraryName] : {};
5240
+ if (isObject(global[this.libraryName])) {
5241
+ return global[this.libraryName];
5242
+ }
5243
+ return {
5244
+ mount: (_c = this.sandBox) === null || _c === void 0 ? void 0 : _c.proxyWindow.mount,
5245
+ unmount: (_d = this.sandBox) === null || _d === void 0 ? void 0 : _d.proxyWindow.unmount,
5246
+ };
4632
5247
  }
4633
5248
  return {};
4634
5249
  }
5250
+ getGlobalEventListener(eventName) {
5251
+ var _a;
5252
+ // @ts-ignore
5253
+ const listener = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow[eventName];
5254
+ return isFunction(listener) ? listener : null;
5255
+ }
4635
5256
  }
4636
5257
 
4637
5258
  /**
@@ -4644,7 +5265,8 @@ function defineElement(tagName) {
4644
5265
  super();
4645
5266
  this.isWaiting = false;
4646
5267
  this.cacheData = null;
4647
- this.hasConnected = false;
5268
+ this.connectedCount = 0;
5269
+ this.connectStateMap = new Map();
4648
5270
  this.appName = ''; // app name
4649
5271
  this.appUrl = ''; // app url
4650
5272
  this.ssrUrl = ''; // html path in ssr mode
@@ -4654,6 +5276,8 @@ function defineElement(tagName) {
4654
5276
  */
4655
5277
  this.handleAttributeUpdate = () => {
4656
5278
  this.isWaiting = false;
5279
+ if (!this.connectStateMap.get(this.connectedCount))
5280
+ return;
4657
5281
  const formatAttrName = formatAppName(this.getAttribute('name'));
4658
5282
  const formatAttrUrl = formatAppURL(this.getAttribute('url'), this.appName);
4659
5283
  if (this.legalAttribute('name', formatAttrName) && this.legalAttribute('url', formatAttrUrl)) {
@@ -4664,7 +5288,7 @@ function defineElement(tagName) {
4664
5288
  keepAliveStates.KEEP_ALIVE_HIDDEN !== existApp.getKeepAliveState() &&
4665
5289
  !existApp.isPrefetch) {
4666
5290
  this.setAttribute('name', this.appName);
4667
- return logError(`app name conflict, an app named ${formatAttrName} is running`, this.appName);
5291
+ return logError(`app name conflict, an app named ${formatAttrName} is running`);
4668
5292
  }
4669
5293
  }
4670
5294
  if (formatAttrName !== this.appName || formatAttrUrl !== this.appUrl) {
@@ -4705,22 +5329,56 @@ function defineElement(tagName) {
4705
5329
  // baseRoute: route prefix, default is ''
4706
5330
  // keep-alive: open keep-alive mode
4707
5331
  connectedCallback() {
4708
- this.hasConnected = true;
4709
- defer(() => dispatchLifecyclesEvent(this, this.appName, lifeCycles.CREATED));
4710
- this.initialMount();
5332
+ const cacheCount = ++this.connectedCount;
5333
+ this.connectStateMap.set(cacheCount, true);
5334
+ /**
5335
+ * In some special scenes, such as vue's keep-alive, the micro-app will be inserted and deleted twice in an instant
5336
+ * So we execute the mount method async and record connectState to prevent repeated rendering
5337
+ */
5338
+ defer(() => {
5339
+ if (this.connectStateMap.get(cacheCount)) {
5340
+ dispatchLifecyclesEvent(this, this.appName, lifeCycles.CREATED);
5341
+ this.handleConnected();
5342
+ }
5343
+ });
4711
5344
  }
4712
5345
  disconnectedCallback() {
4713
- this.hasConnected = false;
5346
+ this.connectStateMap.set(this.connectedCount, false);
5347
+ this.handleDisconnected();
5348
+ }
5349
+ /**
5350
+ * Re render app from the command line
5351
+ * MicroAppElement.reload(destroy)
5352
+ */
5353
+ reload(destroy) {
5354
+ return new Promise((resolve) => {
5355
+ const handleAfterReload = () => {
5356
+ this.removeEventListener(lifeCycles.MOUNTED, handleAfterReload);
5357
+ this.removeEventListener(lifeCycles.AFTERSHOW, handleAfterReload);
5358
+ resolve(true);
5359
+ };
5360
+ this.addEventListener(lifeCycles.MOUNTED, handleAfterReload);
5361
+ this.addEventListener(lifeCycles.AFTERSHOW, handleAfterReload);
5362
+ this.handleDisconnected(destroy, () => {
5363
+ this.handleConnected();
5364
+ });
5365
+ });
5366
+ }
5367
+ /**
5368
+ * common action for unmount
5369
+ * @param destroy reload param
5370
+ */
5371
+ handleDisconnected(destroy = false, callback) {
4714
5372
  const app = appInstanceMap.get(this.appName);
4715
5373
  if (app &&
4716
5374
  app.getAppState() !== appStates.UNMOUNT &&
4717
5375
  app.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN) {
4718
5376
  // keep-alive
4719
- if (this.getKeepAliveModeResult()) {
4720
- this.handleHiddenKeepAliveApp();
5377
+ if (this.getKeepAliveModeResult() && !destroy) {
5378
+ this.handleHiddenKeepAliveApp(callback);
4721
5379
  }
4722
5380
  else {
4723
- this.handleUnmount(this.getDestroyCompatibleResult());
5381
+ this.handleUnmount(destroy || this.getDestroyCompatibleResult(), callback);
4724
5382
  }
4725
5383
  }
4726
5384
  }
@@ -4758,12 +5416,12 @@ function defineElement(tagName) {
4758
5416
  }
4759
5417
  // handle for connectedCallback run before attributeChangedCallback
4760
5418
  handleInitialNameAndUrl() {
4761
- this.hasConnected && this.initialMount();
5419
+ this.connectStateMap.get(this.connectedCount) && this.handleConnected();
4762
5420
  }
4763
5421
  /**
4764
5422
  * first mount of this app
4765
5423
  */
4766
- initialMount() {
5424
+ handleConnected() {
4767
5425
  if (!this.appName || !this.appUrl)
4768
5426
  return;
4769
5427
  if (this.getDisposeResult('shadowDOM') && !this.shadowRoot && isFunction(this.attachShadow)) {
@@ -4773,26 +5431,35 @@ function defineElement(tagName) {
4773
5431
  if (appInstanceMap.has(this.appName)) {
4774
5432
  const app = appInstanceMap.get(this.appName);
4775
5433
  const existAppUrl = app.ssrUrl || app.url;
4776
- const activeAppUrl = this.ssrUrl || this.appUrl;
4777
- // keep-alive don't care about ssrUrl
4778
- // 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
5434
+ const targetAppUrl = this.ssrUrl || this.appUrl;
5435
+ /**
5436
+ * NOTE:
5437
+ * 1. keep-alive don't care about ssrUrl
5438
+ * 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
5439
+ * 3. When scopecss, useSandbox of prefetch app different from target app, delete prefetch app and create new one
5440
+ */
4779
5441
  if (app.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN &&
4780
5442
  app.url === this.appUrl) {
4781
5443
  this.handleShowKeepAliveApp(app);
4782
5444
  }
4783
- else if (existAppUrl === activeAppUrl && (app.isPrefetch ||
4784
- app.getAppState() === appStates.UNMOUNT)) {
5445
+ else if (existAppUrl === targetAppUrl && (app.getAppState() === appStates.UNMOUNT ||
5446
+ (app.isPrefetch && (app.scopecss === this.isScopecss() &&
5447
+ app.useSandbox === this.isSandbox())))) {
4785
5448
  this.handleAppMount(app);
4786
5449
  }
4787
5450
  else if (app.isPrefetch || app.getAppState() === appStates.UNMOUNT) {
4788
- /**
4789
- * url is different & old app is unmounted or prefetch, create new app to replace old one
4790
- */
4791
- logWarn(`the ${app.isPrefetch ? 'prefetch' : 'unmounted'} app with url: ${existAppUrl} is replaced by a new app`, this.appName);
5451
+ if (process.env.NODE_ENV !== 'production' &&
5452
+ app.scopecss === this.isScopecss() &&
5453
+ app.useSandbox === this.isSandbox()) {
5454
+ /**
5455
+ * url is different & old app is unmounted or prefetch, create new app to replace old one
5456
+ */
5457
+ logWarn(`the ${app.isPrefetch ? 'prefetch' : 'unmounted'} app with url: ${existAppUrl} replaced by a new app with url: ${targetAppUrl}`, this.appName);
5458
+ }
4792
5459
  this.handleCreateApp();
4793
5460
  }
4794
5461
  else {
4795
- logError(`app name conflict, an app named ${this.appName} is running`, this.appName);
5462
+ logError(`app name conflict, an app named: ${this.appName} with url: ${existAppUrl} is running`);
4796
5463
  }
4797
5464
  }
4798
5465
  else {
@@ -4827,7 +5494,7 @@ function defineElement(tagName) {
4827
5494
  }
4828
5495
  else {
4829
5496
  // the hidden keep-alive app is still active
4830
- logError(`app name conflict, an app named ${this.appName} is running`, this.appName);
5497
+ logError(`app name conflict, an app named ${this.appName} is running`);
4831
5498
  }
4832
5499
  }
4833
5500
  else if (existApp.url === this.appUrl && existApp.ssrUrl === this.ssrUrl) {
@@ -4854,20 +5521,6 @@ function defineElement(tagName) {
4854
5521
  }
4855
5522
  return true;
4856
5523
  }
4857
- /**
4858
- * mount app
4859
- * some serious note before mount:
4860
- * 1. is prefetch ?
4861
- * 2. is remount in another container ?
4862
- * 3. is remount with change properties of the container ?
4863
- */
4864
- handleAppMount(app) {
4865
- app.isPrefetch = false;
4866
- defer(() => {
4867
- var _a;
4868
- 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'));
4869
- });
4870
- }
4871
5524
  // create app instance
4872
5525
  handleCreateApp() {
4873
5526
  var _a;
@@ -4878,41 +5531,69 @@ function defineElement(tagName) {
4878
5531
  if (appInstanceMap.has(this.appName)) {
4879
5532
  appInstanceMap.get(this.appName).actionsForCompletelyDestroy();
4880
5533
  }
4881
- const instance = new CreateApp({
5534
+ new CreateApp({
4882
5535
  name: this.appName,
4883
5536
  url: this.appUrl,
5537
+ scopecss: this.isScopecss(),
5538
+ useSandbox: this.isSandbox(),
5539
+ inline: this.getDisposeResult('inline'),
5540
+ esmodule: this.getDisposeResult('esmodule'),
5541
+ container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
4884
5542
  ssrUrl: this.ssrUrl,
5543
+ });
5544
+ }
5545
+ /**
5546
+ * mount app
5547
+ * some serious note before mount:
5548
+ * 1. is prefetch ?
5549
+ * 2. is remount in another container ?
5550
+ * 3. is remount with change properties of the container ?
5551
+ */
5552
+ handleAppMount(app) {
5553
+ app.isPrefetch = false;
5554
+ defer(() => this.mount(app));
5555
+ }
5556
+ /**
5557
+ * public mount action for micro_app_element & create_app
5558
+ */
5559
+ mount(app) {
5560
+ var _a;
5561
+ app.mount({
4885
5562
  container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
4886
5563
  inline: this.getDisposeResult('inline'),
4887
- scopecss: !(this.getDisposeResult('disable-scopecss') || this.getDisposeResult('shadowDOM')),
4888
- useSandbox: !this.getDisposeResult('disable-sandbox'),
5564
+ esmodule: this.getDisposeResult('esmodule'),
4889
5565
  useMemoryRouter: !this.getDisposeResult('disable-memory-router'),
4890
5566
  baseroute: this.getBaseRouteCompatible(),
4891
5567
  keepRouteState: this.getDisposeResult('keep-router-state'),
4892
5568
  defaultPage: this.getDefaultPageValue(),
4893
5569
  hiddenRouter: this.getDisposeResult('hidden-router'),
4894
5570
  disablePatchRequest: this.getDisposeResult('disable-patch-request'),
5571
+ fiber: this.getDisposeResult('fiber'),
4895
5572
  });
4896
- appInstanceMap.set(this.appName, instance);
4897
5573
  }
4898
5574
  /**
4899
5575
  * unmount app
4900
5576
  * @param destroy delete cache resources when unmount
4901
5577
  */
4902
- handleUnmount(destroy, unmountCb) {
5578
+ handleUnmount(destroy, unmountcb) {
4903
5579
  const app = appInstanceMap.get(this.appName);
4904
5580
  if (app &&
4905
5581
  app.getAppState() !== appStates.UNMOUNT) {
4906
- app.unmount(destroy, unmountCb);
5582
+ app.unmount({
5583
+ destroy,
5584
+ clearData: this.getDisposeResult('clear-data'),
5585
+ unmountcb,
5586
+ });
4907
5587
  }
4908
5588
  }
4909
5589
  // hidden app when disconnectedCallback called with keep-alive
4910
- handleHiddenKeepAliveApp() {
5590
+ handleHiddenKeepAliveApp(callback) {
4911
5591
  const app = appInstanceMap.get(this.appName);
4912
5592
  if (app &&
4913
5593
  app.getAppState() !== appStates.UNMOUNT &&
4914
- app.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN)
4915
- app.hiddenKeepAliveApp();
5594
+ app.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN) {
5595
+ app.hiddenKeepAliveApp(callback);
5596
+ }
4916
5597
  }
4917
5598
  // show app when connectedCallback called with keep-alive
4918
5599
  handleShowKeepAliveApp(app) {
@@ -4925,8 +5606,7 @@ function defineElement(tagName) {
4925
5606
  * @param name Configuration item name
4926
5607
  */
4927
5608
  getDisposeResult(name) {
4928
- // @ts-ignore
4929
- return (this.compatibleSpecialProperties(name) || !!microApp[name]) && this.compatibleDisableSpecialProperties(name);
5609
+ return (this.compatibleSpecialProperties(name) || !!microApp.options[name]) && this.compatibleDisableSpecialProperties(name);
4930
5610
  }
4931
5611
  // compatible of disableScopecss & disableSandbox
4932
5612
  compatibleSpecialProperties(name) {
@@ -4948,6 +5628,12 @@ function defineElement(tagName) {
4948
5628
  }
4949
5629
  return this.getAttribute(name) !== 'false';
4950
5630
  }
5631
+ isScopecss() {
5632
+ return !(this.getDisposeResult('disable-scopecss') || this.getDisposeResult('shadowDOM'));
5633
+ }
5634
+ isSandbox() {
5635
+ return !this.getDisposeResult('disable-sandbox');
5636
+ }
4951
5637
  /**
4952
5638
  * 2021-09-08
4953
5639
  * get baseRoute
@@ -4995,8 +5681,10 @@ function defineElement(tagName) {
4995
5681
  * get config of default page
4996
5682
  */
4997
5683
  getDefaultPageValue() {
4998
- var _a, _b, _c;
4999
- 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 : '';
5684
+ return (router.getDefaultPage(this.appName) ||
5685
+ this.getAttribute('default-page') ||
5686
+ this.getAttribute('defaultPage') ||
5687
+ '');
5000
5688
  }
5001
5689
  /**
5002
5690
  * Data from the base application
@@ -5022,7 +5710,7 @@ function defineElement(tagName) {
5022
5710
  return null;
5023
5711
  }
5024
5712
  }
5025
- window.customElements.define(tagName, MicroAppElement);
5713
+ globalEnv.rawWindow.customElements.define(tagName, MicroAppElement);
5026
5714
  }
5027
5715
 
5028
5716
  /**
@@ -5053,33 +5741,40 @@ function preFetch(apps) {
5053
5741
  });
5054
5742
  }
5055
5743
  // sequential preload app
5056
- function preFetchInSerial(prefetchApp) {
5057
- return new Promise((resolve) => {
5058
- requestIdleCallback(() => {
5059
- var _a, _b, _c, _d, _e;
5060
- if (isPlainObject(prefetchApp) && navigator.onLine) {
5061
- prefetchApp.name = formatAppName(prefetchApp.name);
5062
- prefetchApp.url = formatAppURL(prefetchApp.url, prefetchApp.name);
5063
- if (prefetchApp.name && prefetchApp.url && !appInstanceMap.has(prefetchApp.name)) {
5064
- const app = new CreateApp({
5065
- name: prefetchApp.name,
5066
- url: prefetchApp.url,
5067
- scopecss: !((_b = (_a = prefetchApp['disable-scopecss']) !== null && _a !== void 0 ? _a : prefetchApp.disableScopecss) !== null && _b !== void 0 ? _b : microApp['disable-scopecss']),
5068
- useSandbox: !((_d = (_c = prefetchApp['disable-sandbox']) !== null && _c !== void 0 ? _c : prefetchApp.disableSandbox) !== null && _d !== void 0 ? _d : microApp['disable-sandbox']),
5069
- useMemoryRouter: !((_e = prefetchApp['disable-memory-router']) !== null && _e !== void 0 ? _e : microApp['disable-memory-router']),
5070
- });
5071
- app.isPrefetch = true;
5072
- app.prefetchResolve = resolve;
5073
- appInstanceMap.set(prefetchApp.name, app);
5074
- }
5075
- else {
5744
+ function preFetchInSerial(options) {
5745
+ return promiseRequestIdle((resolve) => {
5746
+ var _a, _b, _c, _d, _e, _f;
5747
+ if (isPlainObject(options) && navigator.onLine) {
5748
+ options.name = formatAppName(options.name);
5749
+ options.url = formatAppURL(options.url, options.name);
5750
+ if (options.name && options.url && !appInstanceMap.has(options.name)) {
5751
+ const app = new CreateApp({
5752
+ name: options.name,
5753
+ url: options.url,
5754
+ scopecss: !((_b = (_a = options['disable-scopecss']) !== null && _a !== void 0 ? _a : options.disableScopecss) !== null && _b !== void 0 ? _b : microApp.options['disable-scopecss']),
5755
+ useSandbox: !((_d = (_c = options['disable-sandbox']) !== null && _c !== void 0 ? _c : options.disableSandbox) !== null && _d !== void 0 ? _d : microApp.options['disable-sandbox']),
5756
+ inline: (_e = options.inline) !== null && _e !== void 0 ? _e : microApp.options.inline,
5757
+ esmodule: (_f = options.esmodule) !== null && _f !== void 0 ? _f : microApp.options.esmodule,
5758
+ isPrefetch: true,
5759
+ });
5760
+ const oldOnload = app.onLoad;
5761
+ const oldOnLoadError = app.onLoadError;
5762
+ app.onLoad = (html) => {
5076
5763
  resolve();
5077
- }
5764
+ oldOnload.call(app, html);
5765
+ };
5766
+ app.onLoadError = (e) => {
5767
+ resolve();
5768
+ oldOnLoadError.call(app, e);
5769
+ };
5078
5770
  }
5079
5771
  else {
5080
5772
  resolve();
5081
5773
  }
5082
- });
5774
+ }
5775
+ else {
5776
+ resolve();
5777
+ }
5083
5778
  });
5084
5779
  }
5085
5780
  /**
@@ -5089,21 +5784,35 @@ function preFetchInSerial(prefetchApp) {
5089
5784
  function getGlobalAssets(assets) {
5090
5785
  if (isPlainObject(assets)) {
5091
5786
  requestIdleCallback(() => {
5092
- fetchGlobalResources(assets.js, 'js', globalScripts);
5093
- fetchGlobalResources(assets.css, 'css', globalLinks);
5787
+ fetchGlobalResources(assets.js, 'js', sourceCenter.script);
5788
+ fetchGlobalResources(assets.css, 'css', sourceCenter.link);
5094
5789
  });
5095
5790
  }
5096
5791
  }
5097
5792
  // TODO: requestIdleCallback for every file
5098
- function fetchGlobalResources(resources, suffix, cache) {
5793
+ function fetchGlobalResources(resources, suffix, sourceHandler) {
5099
5794
  if (isArray(resources)) {
5100
- const effectiveResource = resources.filter((path) => isString(path) && path.includes(`.${suffix}`) && !cache.has(path));
5795
+ const effectiveResource = resources.filter((path) => isString(path) && path.includes(`.${suffix}`) && !sourceHandler.hasInfo(path));
5101
5796
  const fetchResourcePromise = effectiveResource.map((path) => fetchSource(path));
5102
5797
  // fetch resource with stream
5103
5798
  promiseStream(fetchResourcePromise, (res) => {
5104
5799
  const path = effectiveResource[res.index];
5105
- if (!cache.has(path)) {
5106
- cache.set(path, res.data);
5800
+ if (suffix === 'js') {
5801
+ if (!sourceHandler.hasInfo(path)) {
5802
+ sourceHandler.setInfo(path, {
5803
+ code: res.data,
5804
+ isExternal: false,
5805
+ appSpace: {},
5806
+ });
5807
+ }
5808
+ }
5809
+ else {
5810
+ if (!sourceHandler.hasInfo(path)) {
5811
+ sourceHandler.setInfo(path, {
5812
+ code: res.data,
5813
+ appSpace: {}
5814
+ });
5815
+ }
5107
5816
  }
5108
5817
  }, (err) => {
5109
5818
  logError(err);
@@ -5146,33 +5855,41 @@ function unmountApp(appName, options) {
5146
5855
  if (options === null || options === void 0 ? void 0 : options.destroy) {
5147
5856
  app.actionsForCompletelyDestroy();
5148
5857
  }
5149
- resolve();
5858
+ resolve(true);
5150
5859
  }
5151
5860
  else if (app.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN) {
5152
5861
  if (options === null || options === void 0 ? void 0 : options.destroy) {
5153
- app.unmount(true, resolve);
5862
+ app.unmount({
5863
+ destroy: true,
5864
+ clearData: true,
5865
+ unmountcb: resolve.bind(null, true)
5866
+ });
5154
5867
  }
5155
5868
  else if (options === null || options === void 0 ? void 0 : options.clearAliveState) {
5156
- app.unmount(false, resolve);
5869
+ app.unmount({
5870
+ destroy: false,
5871
+ clearData: !!options.clearData,
5872
+ unmountcb: resolve.bind(null, true)
5873
+ });
5157
5874
  }
5158
5875
  else {
5159
- resolve();
5876
+ resolve(true);
5160
5877
  }
5161
5878
  }
5162
5879
  else {
5163
5880
  const container = getRootContainer(app.container);
5164
5881
  const unmountHandler = () => {
5165
- container.removeEventListener('unmount', unmountHandler);
5166
- container.removeEventListener('afterhidden', afterhiddenHandler);
5167
- resolve();
5882
+ container.removeEventListener(lifeCycles.UNMOUNT, unmountHandler);
5883
+ container.removeEventListener(lifeCycles.AFTERHIDDEN, afterhiddenHandler);
5884
+ resolve(true);
5168
5885
  };
5169
5886
  const afterhiddenHandler = () => {
5170
- container.removeEventListener('unmount', unmountHandler);
5171
- container.removeEventListener('afterhidden', afterhiddenHandler);
5172
- resolve();
5887
+ container.removeEventListener(lifeCycles.UNMOUNT, unmountHandler);
5888
+ container.removeEventListener(lifeCycles.AFTERHIDDEN, afterhiddenHandler);
5889
+ resolve(true);
5173
5890
  };
5174
- container.addEventListener('unmount', unmountHandler);
5175
- container.addEventListener('afterhidden', afterhiddenHandler);
5891
+ container.addEventListener(lifeCycles.UNMOUNT, unmountHandler);
5892
+ container.addEventListener(lifeCycles.AFTERHIDDEN, afterhiddenHandler);
5176
5893
  if (options === null || options === void 0 ? void 0 : options.destroy) {
5177
5894
  let destroyAttrValue, destoryAttrValue;
5178
5895
  container.hasAttribute('destroy') && (destroyAttrValue = container.getAttribute('destroy'));
@@ -5180,36 +5897,131 @@ function unmountApp(appName, options) {
5180
5897
  container.setAttribute('destroy', 'true');
5181
5898
  container.parentNode.removeChild(container);
5182
5899
  container.removeAttribute('destroy');
5183
- typeof destroyAttrValue === 'string' && container.setAttribute('destroy', destroyAttrValue);
5184
- typeof destoryAttrValue === 'string' && container.setAttribute('destory', destoryAttrValue);
5900
+ isString(destroyAttrValue) && container.setAttribute('destroy', destroyAttrValue);
5901
+ isString(destoryAttrValue) && container.setAttribute('destory', destoryAttrValue);
5185
5902
  }
5186
5903
  else if ((options === null || options === void 0 ? void 0 : options.clearAliveState) && container.hasAttribute('keep-alive')) {
5187
5904
  const keepAliveAttrValue = container.getAttribute('keep-alive');
5188
5905
  container.removeAttribute('keep-alive');
5906
+ let clearDataAttrValue;
5907
+ if (options.clearData) {
5908
+ clearDataAttrValue = container.getAttribute('clear-data');
5909
+ container.setAttribute('clear-data', 'true');
5910
+ }
5189
5911
  container.parentNode.removeChild(container);
5190
5912
  container.setAttribute('keep-alive', keepAliveAttrValue);
5913
+ isString(clearDataAttrValue) && container.setAttribute('clear-data', clearDataAttrValue);
5191
5914
  }
5192
5915
  else {
5916
+ let clearDataAttrValue;
5917
+ if (options === null || options === void 0 ? void 0 : options.clearData) {
5918
+ clearDataAttrValue = container.getAttribute('clear-data');
5919
+ container.setAttribute('clear-data', 'true');
5920
+ }
5193
5921
  container.parentNode.removeChild(container);
5922
+ isString(clearDataAttrValue) && container.setAttribute('clear-data', clearDataAttrValue);
5194
5923
  }
5195
5924
  }
5196
5925
  }
5197
5926
  else {
5198
5927
  logWarn(`app ${appName} does not exist`);
5199
- resolve();
5928
+ resolve(false);
5200
5929
  }
5201
5930
  });
5202
5931
  }
5203
5932
  // unmount all apps in turn
5204
5933
  function unmountAllApps(options) {
5205
- return Array.from(appInstanceMap.keys()).reduce((pre, next) => pre.then(() => unmountApp(next, options)), Promise.resolve());
5934
+ return Array.from(appInstanceMap.keys()).reduce((pre, next) => pre.then(() => unmountApp(next, options)), Promise.resolve(true));
5935
+ }
5936
+ /**
5937
+ * Re render app from the command line
5938
+ * microApp.reload(destroy)
5939
+ * @param appName app.name
5940
+ * @param destroy unmount app with destroy mode
5941
+ * @returns Promise<boolean>
5942
+ */
5943
+ function reload(appName, destroy) {
5944
+ return new Promise((resolve) => {
5945
+ const app = appInstanceMap.get(formatAppName(appName));
5946
+ if (app) {
5947
+ const rootContainer = app.container && getRootContainer(app.container);
5948
+ if (rootContainer) {
5949
+ resolve(rootContainer.reload(destroy));
5950
+ }
5951
+ else {
5952
+ logWarn(`app ${appName} is not rendered, cannot use reload`);
5953
+ resolve(false);
5954
+ }
5955
+ }
5956
+ else {
5957
+ logWarn(`app ${appName} does not exist`);
5958
+ resolve(false);
5959
+ }
5960
+ });
5961
+ }
5962
+ /**
5963
+ * Manually render app
5964
+ * @param options RenderAppOptions
5965
+ * @returns Promise<boolean>
5966
+ */
5967
+ function renderApp(options) {
5968
+ return new Promise((resolve) => {
5969
+ if (!isPlainObject(options))
5970
+ return logError('renderApp options must be an object');
5971
+ const container = isElement(options.container) ? options.container : isString(options.container) ? document.querySelector(options.container) : null;
5972
+ if (!isElement(container))
5973
+ return logError('Target container is not a DOM element.');
5974
+ const microAppElement = pureCreateElement(microApp.tagName);
5975
+ for (const attr in options) {
5976
+ if (attr === 'onDataChange') {
5977
+ if (isFunction(options[attr])) {
5978
+ microAppElement.addEventListener('datachange', options[attr]);
5979
+ }
5980
+ }
5981
+ else if (attr === 'lifeCycles') {
5982
+ const lifeCycleConfig = options[attr];
5983
+ if (isPlainObject(lifeCycleConfig)) {
5984
+ for (const lifeName in lifeCycleConfig) {
5985
+ if (lifeName.toUpperCase() in lifeCycles && isFunction(lifeCycleConfig[lifeName])) {
5986
+ microAppElement.addEventListener(lifeName.toLowerCase(), lifeCycleConfig[lifeName]);
5987
+ }
5988
+ }
5989
+ }
5990
+ }
5991
+ else if (attr !== 'container') {
5992
+ microAppElement.setAttribute(attr, options[attr]);
5993
+ }
5994
+ }
5995
+ const handleMount = () => {
5996
+ releaseListener();
5997
+ resolve(true);
5998
+ };
5999
+ const handleError = () => {
6000
+ releaseListener();
6001
+ resolve(false);
6002
+ };
6003
+ const releaseListener = () => {
6004
+ microAppElement.removeEventListener(lifeCycles.MOUNTED, handleMount);
6005
+ microAppElement.removeEventListener(lifeCycles.ERROR, handleError);
6006
+ };
6007
+ microAppElement.addEventListener(lifeCycles.MOUNTED, handleMount);
6008
+ microAppElement.addEventListener(lifeCycles.ERROR, handleError);
6009
+ container.appendChild(microAppElement);
6010
+ });
5206
6011
  }
5207
6012
  class MicroApp extends EventCenterForBaseApp {
5208
6013
  constructor() {
5209
6014
  super(...arguments);
5210
6015
  this.tagName = 'micro-app';
5211
- this.preFetch = preFetch;
6016
+ this.options = {};
5212
6017
  this.router = router;
6018
+ this.preFetch = preFetch;
6019
+ this.unmountApp = unmountApp;
6020
+ this.unmountAllApps = unmountAllApps;
6021
+ this.getActiveApps = getActiveApps;
6022
+ this.getAllApps = getAllApps;
6023
+ this.reload = reload;
6024
+ this.renderApp = renderApp;
5213
6025
  }
5214
6026
  start(options) {
5215
6027
  var _a, _b;
@@ -5228,26 +6040,10 @@ class MicroApp extends EventCenterForBaseApp {
5228
6040
  return logWarn(`element ${this.tagName} is already defined`);
5229
6041
  }
5230
6042
  initGlobalEnv();
5231
- if (options && isPlainObject(options)) {
5232
- this.shadowDOM = options.shadowDOM;
5233
- this.destroy = options.destroy;
5234
- /**
5235
- * compatible with versions below 0.4.2 of destroy
5236
- * do not merge with the previous line
5237
- */
5238
- // @ts-ignore
5239
- this.destory = options.destory;
5240
- this.inline = options.inline;
5241
- this['disable-scopecss'] = (_a = options['disable-scopecss']) !== null && _a !== void 0 ? _a : options.disableScopecss;
5242
- this['disable-sandbox'] = (_b = options['disable-sandbox']) !== null && _b !== void 0 ? _b : options.disableSandbox;
5243
- this['disable-memory-router'] = options['disable-memory-router'];
5244
- this['disable-patch-request'] = options['disable-patch-request'];
5245
- this['keep-router-state'] = options['keep-router-state'];
5246
- this['hidden-router'] = options['hidden-router'];
5247
- this.esmodule = options.esmodule;
5248
- this.ssr = options.ssr;
5249
- isFunction(options.fetch) && (this.fetch = options.fetch);
5250
- isPlainObject(options.lifeCycles) && (this.lifeCycles = options.lifeCycles);
6043
+ if (isPlainObject(options)) {
6044
+ this.options = options;
6045
+ options['disable-scopecss'] = (_a = options['disable-scopecss']) !== null && _a !== void 0 ? _a : options.disableScopecss;
6046
+ options['disable-sandbox'] = (_b = options['disable-sandbox']) !== null && _b !== void 0 ? _b : options.disableSandbox;
5251
6047
  // load app assets when browser is idle
5252
6048
  options.preFetchApps && preFetch(options.preFetchApps);
5253
6049
  // load global assets when browser is idle
@@ -5263,15 +6059,14 @@ class MicroApp extends EventCenterForBaseApp {
5263
6059
  }
5264
6060
  }
5265
6061
  }
5266
- this.plugins = options.plugins;
5267
6062
  }
5268
6063
  }
5269
6064
  // define customElement after init
5270
6065
  defineElement(this.tagName);
5271
6066
  }
5272
6067
  }
5273
- var microApp = new MicroApp();
6068
+ const microApp = new MicroApp();
5274
6069
 
5275
6070
  export default microApp;
5276
- export { EventCenterForMicroApp, MicroApp, getActiveApps, getAllApps, preFetch, pureCreateElement, removeDomScope, unmountAllApps, unmountApp, version };
6071
+ export { EventCenterForMicroApp, MicroApp, getActiveApps, getAllApps, preFetch, pureCreateElement, reload, removeDomScope, renderApp, unmountAllApps, unmountApp, version };
5277
6072
  //# sourceMappingURL=index.esm.js.map