@micro-zoe/micro-app 1.0.0-alpha.9 → 1.0.0-beta.1

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.9';
1
+ const version = '1.0.0-beta.1';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -7,7 +7,6 @@ const globalThis = (typeof global !== 'undefined')
7
7
  : ((typeof window !== 'undefined')
8
8
  ? window
9
9
  : ((typeof self !== 'undefined') ? self : Function('return this')()));
10
- const noop = () => { };
11
10
  const noopFalse = () => false;
12
11
  // Array.isArray
13
12
  const isArray = Array.isArray;
@@ -74,13 +73,50 @@ function isShadowRoot(target) {
74
73
  return typeof ShadowRoot !== 'undefined' && target instanceof ShadowRoot;
75
74
  }
76
75
  function isURL(target) {
77
- return target instanceof URL;
76
+ var _a;
77
+ return target instanceof URL || !!((_a = target) === null || _a === void 0 ? void 0 : _a.href);
78
78
  }
79
+ // iframe element not instanceof base app Element, use tagName instead
79
80
  function isElement(target) {
80
- return target instanceof Element;
81
+ var _a;
82
+ return target instanceof Element || isString((_a = target) === null || _a === void 0 ? void 0 : _a.tagName);
81
83
  }
84
+ // iframe node not instanceof base app Node, use nodeType instead
82
85
  function isNode(target) {
83
- return target instanceof Node;
86
+ var _a;
87
+ return target instanceof Node || isNumber((_a = target) === null || _a === void 0 ? void 0 : _a.nodeType);
88
+ }
89
+ function isLinkElement(target) {
90
+ var _a, _b;
91
+ return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'LINK';
92
+ }
93
+ function isStyleElement(target) {
94
+ var _a, _b;
95
+ return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'STYLE';
96
+ }
97
+ function isScriptElement(target) {
98
+ var _a, _b;
99
+ return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'SCRIPT';
100
+ }
101
+ function isIFrameElement(target) {
102
+ var _a, _b;
103
+ return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'IFRAME';
104
+ }
105
+ function isDivElement(target) {
106
+ var _a, _b;
107
+ return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'DIV';
108
+ }
109
+ function isImageElement(target) {
110
+ var _a, _b;
111
+ return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'IMG';
112
+ }
113
+ function isBaseElement(target) {
114
+ var _a, _b;
115
+ return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'BASE';
116
+ }
117
+ function isMicroAppBody(target) {
118
+ var _a, _b;
119
+ return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'MICRO-APP-BODY';
84
120
  }
85
121
  // is ProxyDocument
86
122
  function isProxyDocument(target) {
@@ -323,6 +359,7 @@ function pureCreateElement(tagName, options) {
323
359
  function cloneContainer(origin, target, deep) {
324
360
  target.innerHTML = '';
325
361
  if (deep) {
362
+ // TODO: ShadowRoot兼容,ShadowRoot不能直接使用cloneNode
326
363
  const clonedNode = origin.cloneNode(true);
327
364
  const fragment = document.createDocumentFragment();
328
365
  Array.from(clonedNode.childNodes).forEach((node) => {
@@ -335,6 +372,7 @@ function cloneContainer(origin, target, deep) {
335
372
  target.appendChild(node);
336
373
  });
337
374
  }
375
+ return target;
338
376
  }
339
377
  // is invalid key of querySelector
340
378
  function isInvalidQuerySelectorKey(key) {
@@ -500,12 +538,17 @@ function isInlineScript(address) {
500
538
  * @param appName app.name
501
539
  * @param args arguments
502
540
  */
503
- function callFnWithTryCatch(fn, appName, msgSuffix, ...args) {
541
+ function execMicroAppGlobalHook(fn, appName, hookName, ...args) {
504
542
  try {
505
543
  isFunction(fn) && fn(...args);
506
544
  }
507
545
  catch (e) {
508
- logError(`an error occurred in app ${appName} ${msgSuffix} \n`, null, e);
546
+ logError(`An error occurred in app ${appName} window.${hookName} \n`, null, e);
547
+ }
548
+ }
549
+ function clearDOM($dom) {
550
+ while ($dom === null || $dom === void 0 ? void 0 : $dom.firstChild) {
551
+ $dom.removeChild($dom.firstChild);
509
552
  }
510
553
  }
511
554
 
@@ -544,6 +587,11 @@ var microGlobalEvent;
544
587
  microGlobalEvent["ONMOUNT"] = "onmount";
545
588
  microGlobalEvent["ONUNMOUNT"] = "onunmount";
546
589
  })(microGlobalEvent || (microGlobalEvent = {}));
590
+ // custom event of child app
591
+ const microAppCustomEvent = [
592
+ 'unmount',
593
+ 'appstate-change',
594
+ ];
547
595
  // keep-alive status
548
596
  var keepAliveStates;
549
597
  (function (keepAliveStates) {
@@ -566,7 +614,6 @@ var MicroAppConfig;
566
614
  MicroAppConfig["HIDDEN_ROUTER"] = "hidden-router";
567
615
  MicroAppConfig["KEEP_ALIVE"] = "keep-alive";
568
616
  MicroAppConfig["CLEAR_DATA"] = "clear-data";
569
- MicroAppConfig["ESMODULE"] = "esmodule";
570
617
  MicroAppConfig["SSR"] = "ssr";
571
618
  MicroAppConfig["FIBER"] = "fiber";
572
619
  })(MicroAppConfig || (MicroAppConfig = {}));
@@ -1094,9 +1141,6 @@ function dispatchOnErrorEvent(element) {
1094
1141
  function createSourceCenter() {
1095
1142
  const linkList = new Map();
1096
1143
  const scriptList = new Map();
1097
- // setInterval(() => {
1098
- // console.log(linkList, scriptList)
1099
- // }, 10000);
1100
1144
  function createSourceHandler(targetList) {
1101
1145
  return {
1102
1146
  setInfo(address, info) {
@@ -1138,7 +1182,7 @@ function getExistParseCode(appName, prefix, linkInfo) {
1138
1182
  if (item !== appName) {
1139
1183
  const appSpaceData = appSpace[item];
1140
1184
  if (appSpaceData.parsedCode) {
1141
- return appSpaceData.parsedCode.replaceAll(new RegExp(createPrefix(item, true), 'g'), prefix);
1185
+ return appSpaceData.parsedCode.replace(new RegExp(createPrefix(item, true), 'g'), prefix);
1142
1186
  }
1143
1187
  }
1144
1188
  }
@@ -1224,18 +1268,18 @@ function fetchLinksFromHtml(wrapElement, app, microAppHead, fiberStyleResult) {
1224
1268
  const linkInfo = sourceCenter.link.getInfo(address);
1225
1269
  return linkInfo.code ? linkInfo.code : fetchSource(address, app.name);
1226
1270
  });
1227
- const fiberLinkTasks = app.isPrefetch || app.fiber ? [] : null;
1271
+ const fiberLinkTasks = fiberStyleResult ? [] : null;
1228
1272
  promiseStream(fetchLinkPromise, (res) => {
1229
1273
  injectFiberTask(fiberLinkTasks, () => fetchLinkSuccess(styleList[res.index], res.data, microAppHead, app));
1230
1274
  }, (err) => {
1231
1275
  logError(err, app.name);
1232
1276
  }, () => {
1233
- if (fiberLinkTasks) {
1234
- /**
1235
- * 1. If fiberLinkTasks is not null, fiberStyleResult is not null
1236
- * 2. Download link source while processing style
1237
- * 3. Process style first, and then process link
1238
- */
1277
+ /**
1278
+ * 1. If fiberStyleResult exist, fiberLinkTasks must exist
1279
+ * 2. Download link source while processing style
1280
+ * 3. Process style first, and then process link
1281
+ */
1282
+ if (fiberStyleResult) {
1239
1283
  fiberStyleResult.then(() => {
1240
1284
  fiberLinkTasks.push(() => Promise.resolve(app.onLoad(wrapElement)));
1241
1285
  serialExecFiberTasks(fiberLinkTasks);
@@ -1369,10 +1413,10 @@ class Adapter {
1369
1413
  'webpackHotUpdate',
1370
1414
  'Vue',
1371
1415
  ];
1372
- this.injectReactHRMProperty();
1416
+ this.injectReactHMRProperty();
1373
1417
  }
1374
1418
  // adapter for react
1375
- injectReactHRMProperty() {
1419
+ injectReactHMRProperty() {
1376
1420
  if ((process.env.NODE_ENV !== 'production')) {
1377
1421
  // react child in non-react env
1378
1422
  this.staticEscapeProperties.push('__REACT_ERROR_OVERLAY_GLOBAL_HOOK__');
@@ -1411,13 +1455,15 @@ function fixReactHMRConflict(app) {
1411
1455
  /**
1412
1456
  * reDefine parentNode of html
1413
1457
  * Scenes:
1414
- * 1. element-ui popover.js
1415
- * if (html.parentNode === document) ...
1458
+ * 1. element-ui@2/lib/utils/popper.js
1459
+ * var parent = element.parentNode;
1460
+ * // root is child app window
1461
+ * if (parent === root.document) ...
1416
1462
  */
1417
- function throttleDeferForParentNode(proxyDocument) {
1463
+ function throttleDeferForParentNode(microDocument) {
1418
1464
  const html = globalEnv.rawDocument.firstElementChild;
1419
- if (html && html.parentNode !== proxyDocument) {
1420
- setParentNode(html, proxyDocument);
1465
+ if ((html === null || html === void 0 ? void 0 : html.parentNode) === globalEnv.rawDocument) {
1466
+ setParentNode(html, microDocument);
1421
1467
  defer(() => {
1422
1468
  setParentNode(html, globalEnv.rawDocument);
1423
1469
  });
@@ -1437,6 +1483,72 @@ function setParentNode(target, value) {
1437
1483
  });
1438
1484
  }
1439
1485
  }
1486
+ // this events should be sent to the specified app
1487
+ const formatEventList = ['unmount', 'appstate-change'];
1488
+ /**
1489
+ * Format event name
1490
+ * @param eventName event name
1491
+ * @param appName app name
1492
+ */
1493
+ function formatEventName(eventName, appName) {
1494
+ var _a;
1495
+ if (!isIframeSandbox(appName) && (formatEventList.includes(eventName) ||
1496
+ ((eventName === 'popstate' || eventName === 'hashchange') && ((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.useMemoryRouter)))) {
1497
+ return `${eventName}-${appName}`;
1498
+ }
1499
+ return eventName;
1500
+ }
1501
+ /**
1502
+ * update dom tree of target dom
1503
+ * @param container target dom
1504
+ * @param appName app name
1505
+ */
1506
+ function patchElementTree(container, appName) {
1507
+ const children = Array.from(container.children);
1508
+ children.length && children.forEach((child) => {
1509
+ patchElementTree(child, appName);
1510
+ });
1511
+ for (const child of children) {
1512
+ updateElementInfo(child, appName);
1513
+ }
1514
+ }
1515
+ /**
1516
+ * rewrite baseURI, ownerDocument, __MICRO_APP_NAME__ of target node
1517
+ * @param node target node
1518
+ * @param appName app name
1519
+ * @returns target node
1520
+ */
1521
+ function updateElementInfo(node, appName) {
1522
+ var _a, _b;
1523
+ const proxyWindow = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.sandBox) === null || _b === void 0 ? void 0 : _b.proxyWindow;
1524
+ if (proxyWindow && isNode(node) && !node.__MICRO_APP_NAME__) {
1525
+ /**
1526
+ * TODO:
1527
+ * 1. 测试baseURI和ownerDocument在with沙箱中是否正确
1528
+ * 经过验证with沙箱不能重写ownerDocument,否则react点击事件会触发两次
1529
+ * 2. with沙箱所有node设置__MICRO_APP_NAME__都使用updateElementInfo
1530
+ * 3. 性能: defineProperty的性能肯定不如直接设置
1531
+ */
1532
+ rawDefineProperties(node, {
1533
+ baseURI: {
1534
+ configurable: true,
1535
+ get: () => proxyWindow.location.href,
1536
+ },
1537
+ __MICRO_APP_NAME__: {
1538
+ configurable: true,
1539
+ writable: true,
1540
+ value: appName,
1541
+ },
1542
+ });
1543
+ if (isIframeSandbox(appName)) {
1544
+ rawDefineProperty(node, 'ownerDocument', {
1545
+ configurable: true,
1546
+ get: () => proxyWindow.document,
1547
+ });
1548
+ }
1549
+ }
1550
+ return node;
1551
+ }
1440
1552
 
1441
1553
  // Record element and map element
1442
1554
  const dynamicElementInMicroAppMap = new WeakMap();
@@ -1447,7 +1559,7 @@ const dynamicElementInMicroAppMap = new WeakMap();
1447
1559
  * @param app app
1448
1560
  */
1449
1561
  function handleNewNode(parent, child, app) {
1450
- if (child instanceof HTMLStyleElement) {
1562
+ if (isStyleElement(child)) {
1451
1563
  if (child.hasAttribute('exclude')) {
1452
1564
  const replaceComment = document.createComment('style element with exclude attribute ignored by micro-app');
1453
1565
  dynamicElementInMicroAppMap.set(child, replaceComment);
@@ -1458,7 +1570,7 @@ function handleNewNode(parent, child, app) {
1458
1570
  }
1459
1571
  return child;
1460
1572
  }
1461
- else if (child instanceof HTMLLinkElement) {
1573
+ else if (isLinkElement(child)) {
1462
1574
  if (child.hasAttribute('exclude') || checkExcludeUrl(child.getAttribute('href'), app.name)) {
1463
1575
  const linkReplaceComment = document.createComment('link element with exclude attribute ignored by micro-app');
1464
1576
  dynamicElementInMicroAppMap.set(child, linkReplaceComment);
@@ -1483,7 +1595,7 @@ function handleNewNode(parent, child, app) {
1483
1595
  }
1484
1596
  return child;
1485
1597
  }
1486
- else if (child instanceof HTMLScriptElement) {
1598
+ else if (isScriptElement(child)) {
1487
1599
  if (child.src &&
1488
1600
  isFunction(microApp.options.excludeAssetFilter) &&
1489
1601
  microApp.options.excludeAssetFilter(child.src)) {
@@ -1513,29 +1625,46 @@ function handleNewNode(parent, child, app) {
1513
1625
  * @param passiveChild second param of insertBefore and replaceChild
1514
1626
  */
1515
1627
  function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
1516
- const hijackParent = getHijackParent(parent, app);
1628
+ const hijackParent = getHijackParent(parent, targetChild, app);
1517
1629
  /**
1518
1630
  * If passiveChild is not the child node, insertBefore replaceChild will have a problem, at this time, it will be degraded to appendChild
1519
1631
  * E.g: document.head.insertBefore(targetChild, document.head.childNodes[0])
1520
1632
  */
1521
1633
  if (hijackParent) {
1522
1634
  /**
1635
+ * If parentNode is <micro-app-body>, return rawDocument.body
1636
+ * Scenes:
1637
+ * 1. element-ui@2/lib/utils/vue-popper.js
1638
+ * if (this.popperElm.parentNode === document.body) ...
1523
1639
  * WARNING:
1524
- * Verifying that the parentNode of the targetChild points to document.body will cause other problems ?
1640
+ * 1. When operate child from parentNode async, may have been unmount
1641
+ * e.g. target.parentNode.remove(target)
1642
+ * ISSUE:
1643
+ * 1. https://github.com/micro-zoe/micro-app/issues/739
1644
+ * Solution: Return the true value when node not in document
1525
1645
  */
1526
- if (hijackParent.tagName === 'MICRO-APP-BODY' && rawMethod !== globalEnv.rawRemoveChild) {
1646
+ if (!isIframeSandbox(app.name) &&
1647
+ isMicroAppBody(hijackParent) &&
1648
+ rawMethod !== globalEnv.rawRemoveChild) {
1527
1649
  const descriptor = Object.getOwnPropertyDescriptor(targetChild, 'parentNode');
1528
- if (!descriptor || descriptor.configurable) {
1529
- rawDefineProperty(targetChild, 'parentNode', {
1530
- configurable: true,
1531
- get() {
1532
- /**
1533
- * When operate child from parentNode async, may have been unmount
1534
- * e.g.
1535
- * target.parentNode.remove(target)
1536
- */
1537
- return !app.container ? hijackParent : document.body;
1650
+ if ((!descriptor || descriptor.configurable) && !targetChild.__MICRO_APP_HAS_DPN__) {
1651
+ rawDefineProperties(targetChild, {
1652
+ parentNode: {
1653
+ configurable: true,
1654
+ get() {
1655
+ var _a, _b;
1656
+ const result = globalEnv.rawParentNodeDesc.get.call(this);
1657
+ if (isMicroAppBody(result) && app.container) {
1658
+ // TODO: remove getRootElementParentNode
1659
+ return ((_b = (_a = microApp.options).getRootElementParentNode) === null || _b === void 0 ? void 0 : _b.call(_a, this, app.name)) || document.body;
1660
+ }
1661
+ return result;
1662
+ },
1538
1663
  },
1664
+ __MICRO_APP_HAS_DPN__: {
1665
+ configurable: true,
1666
+ get: () => true,
1667
+ }
1539
1668
  });
1540
1669
  }
1541
1670
  }
@@ -1553,7 +1682,7 @@ function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild
1553
1682
  return targetChild;
1554
1683
  }
1555
1684
  if ((process.env.NODE_ENV !== 'production') &&
1556
- targetChild instanceof HTMLIFrameElement &&
1685
+ isIFrameElement(targetChild) &&
1557
1686
  rawMethod === globalEnv.rawAppendChild) {
1558
1687
  fixReactHMRConflict(app);
1559
1688
  }
@@ -1562,13 +1691,20 @@ function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild
1562
1691
  return invokeRawMethod(rawMethod, parent, targetChild, passiveChild);
1563
1692
  }
1564
1693
  // head/body map to micro-app-head/micro-app-body
1565
- function getHijackParent(node, app) {
1566
- var _a, _b;
1567
- if (node === document.head) {
1568
- return (_a = app === null || app === void 0 ? void 0 : app.container) === null || _a === void 0 ? void 0 : _a.querySelector('micro-app-head');
1569
- }
1570
- if (node === document.body) {
1571
- return (_b = app === null || app === void 0 ? void 0 : app.container) === null || _b === void 0 ? void 0 : _b.querySelector('micro-app-body');
1694
+ function getHijackParent(parent, targetChild, app) {
1695
+ if (app) {
1696
+ if (parent === document.head) {
1697
+ if (app.iframe && isScriptElement(targetChild)) {
1698
+ return app.sandBox.microHead;
1699
+ }
1700
+ return app.querySelector('micro-app-head');
1701
+ }
1702
+ if (parent === document.body || parent === document.body.parentNode) {
1703
+ if (app.iframe && isScriptElement(targetChild)) {
1704
+ return app.sandBox.microBody;
1705
+ }
1706
+ return app.querySelector('micro-app-body');
1707
+ }
1572
1708
  }
1573
1709
  return null;
1574
1710
  }
@@ -1596,8 +1732,9 @@ function getMappingNode(node) {
1596
1732
  function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
1597
1733
  const currentAppName = getCurrentAppName();
1598
1734
  if (isNode(newChild) &&
1735
+ !newChild.__PURE_ELEMENT__ &&
1599
1736
  (newChild.__MICRO_APP_NAME__ ||
1600
- (currentAppName && !newChild.__PURE_ELEMENT__))) {
1737
+ currentAppName)) {
1601
1738
  newChild.__MICRO_APP_NAME__ = newChild.__MICRO_APP_NAME__ || currentAppName;
1602
1739
  const app = appInstanceMap.get(newChild.__MICRO_APP_NAME__);
1603
1740
  if (app === null || app === void 0 ? void 0 : app.container) {
@@ -1625,10 +1762,10 @@ function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
1625
1762
  const app = appInstanceMap.get(currentAppName);
1626
1763
  if (app === null || app === void 0 ? void 0 : app.container) {
1627
1764
  if (parent === document.head) {
1628
- return rawMethod.call(app.container.querySelector('micro-app-head'), newChild);
1765
+ return rawMethod.call(app.querySelector('micro-app-head'), newChild);
1629
1766
  }
1630
1767
  else if (parent === document.body) {
1631
- return rawMethod.call(app.container.querySelector('micro-app-body'), newChild);
1768
+ return rawMethod.call(app.querySelector('micro-app-body'), newChild);
1632
1769
  }
1633
1770
  }
1634
1771
  }
@@ -1639,7 +1776,7 @@ function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
1639
1776
  /**
1640
1777
  * Rewrite element prototype method
1641
1778
  */
1642
- function patchElementPrototypeMethods() {
1779
+ function patchElementAndDocument() {
1643
1780
  patchDocument();
1644
1781
  // prototype methods of add element👇
1645
1782
  Element.prototype.appendChild = function appendChild(newChild) {
@@ -1688,6 +1825,71 @@ function patchElementPrototypeMethods() {
1688
1825
  this.__MICRO_APP_NAME__ && (clonedNode.__MICRO_APP_NAME__ = this.__MICRO_APP_NAME__);
1689
1826
  return clonedNode;
1690
1827
  };
1828
+ function getQueryTarget(node) {
1829
+ const currentAppName = getCurrentAppName();
1830
+ if ((node === document.body || node === document.head) && currentAppName) {
1831
+ const app = appInstanceMap.get(currentAppName);
1832
+ if (app === null || app === void 0 ? void 0 : app.container) {
1833
+ if (node === document.body) {
1834
+ return app.querySelector('micro-app-body');
1835
+ }
1836
+ else if (node === document.head) {
1837
+ return app.querySelector('micro-app-head');
1838
+ }
1839
+ }
1840
+ }
1841
+ return null;
1842
+ }
1843
+ Element.prototype.querySelector = function querySelector(selectors) {
1844
+ var _a;
1845
+ const target = (_a = getQueryTarget(this)) !== null && _a !== void 0 ? _a : this;
1846
+ return globalEnv.rawElementQuerySelector.call(target, selectors);
1847
+ };
1848
+ Element.prototype.querySelectorAll = function querySelectorAll(selectors) {
1849
+ var _a;
1850
+ const target = (_a = getQueryTarget(this)) !== null && _a !== void 0 ? _a : this;
1851
+ return globalEnv.rawElementQuerySelectorAll.call(target, selectors);
1852
+ };
1853
+ rawDefineProperty(Element.prototype, 'innerHTML', {
1854
+ configurable: true,
1855
+ enumerable: true,
1856
+ get() {
1857
+ return globalEnv.rawInnerHTMLDesc.get.call(this);
1858
+ },
1859
+ set(code) {
1860
+ globalEnv.rawInnerHTMLDesc.set.call(this, code);
1861
+ const currentAppName = getCurrentAppName();
1862
+ Array.from(this.children).forEach((child) => {
1863
+ if (isElement(child) && currentAppName) {
1864
+ child.__MICRO_APP_NAME__ = currentAppName;
1865
+ }
1866
+ });
1867
+ }
1868
+ });
1869
+ // Abandon this way at 2023.2.28 before v1.0.0-beta.0, it will cause vue2 throw error when render again
1870
+ // rawDefineProperty(Node.prototype, 'parentNode', {
1871
+ // configurable: true,
1872
+ // enumerable: true,
1873
+ // get () {
1874
+ // const result = globalEnv.rawParentNodeDesc.get.call(this)
1875
+ // /**
1876
+ // * If parentNode is <micro-app-body>, return rawDocument.body
1877
+ // * Scenes:
1878
+ // * 1. element-ui@2/lib/utils/vue-popper.js
1879
+ // * if (this.popperElm.parentNode === document.body) ...
1880
+ // * WARNING:
1881
+ // * Will it cause other problems ?
1882
+ // * e.g. target.parentNode.remove(target)
1883
+ // * BUG:
1884
+ // * 1. vue2 umdMode, throw error when render again (<div id='app'></div> will be deleted when render again )
1885
+ // */
1886
+ // if (isMicroAppBody(result) && appInstanceMap.get(this.__MICRO_APP_NAME__)?.container) {
1887
+ // return document.body
1888
+ // }
1889
+ // return result
1890
+ // },
1891
+ // set: undefined,
1892
+ // })
1691
1893
  }
1692
1894
  /**
1693
1895
  * Mark the newly created element in the micro application
@@ -1721,7 +1923,7 @@ function patchDocument() {
1721
1923
  };
1722
1924
  // query element👇
1723
1925
  function querySelector(selectors) {
1724
- var _a, _b, _c, _d;
1926
+ var _a, _b, _c;
1725
1927
  const _this = getBindTarget(this);
1726
1928
  const currentAppName = getCurrentAppName();
1727
1929
  if (!currentAppName ||
@@ -1732,10 +1934,10 @@ function patchDocument() {
1732
1934
  rawDocument !== _this) {
1733
1935
  return globalEnv.rawQuerySelector.call(_this, selectors);
1734
1936
  }
1735
- return (_d = (_c = (_b = appInstanceMap.get(currentAppName)) === null || _b === void 0 ? void 0 : _b.container) === null || _c === void 0 ? void 0 : _c.querySelector(selectors)) !== null && _d !== void 0 ? _d : null;
1937
+ return (_c = (_b = appInstanceMap.get(currentAppName)) === null || _b === void 0 ? void 0 : _b.querySelector(selectors)) !== null && _c !== void 0 ? _c : null;
1736
1938
  }
1737
1939
  function querySelectorAll(selectors) {
1738
- var _a, _b, _c, _d;
1940
+ var _a, _b, _c;
1739
1941
  const _this = getBindTarget(this);
1740
1942
  const currentAppName = getCurrentAppName();
1741
1943
  if (!currentAppName ||
@@ -1745,7 +1947,7 @@ function patchDocument() {
1745
1947
  rawDocument !== _this) {
1746
1948
  return globalEnv.rawQuerySelectorAll.call(_this, selectors);
1747
1949
  }
1748
- return (_d = (_c = (_b = appInstanceMap.get(currentAppName)) === null || _b === void 0 ? void 0 : _b.container) === null || _c === void 0 ? void 0 : _c.querySelectorAll(selectors)) !== null && _d !== void 0 ? _d : [];
1950
+ return (_c = (_b = appInstanceMap.get(currentAppName)) === null || _b === void 0 ? void 0 : _b.querySelectorAll(selectors)) !== null && _c !== void 0 ? _c : [];
1749
1951
  }
1750
1952
  rawRootDocument.prototype.querySelector = querySelector;
1751
1953
  rawRootDocument.prototype.querySelectorAll = querySelectorAll;
@@ -1833,7 +2035,7 @@ function patchSetAttribute() {
1833
2035
  const appName = this.__MICRO_APP_NAME__ || getCurrentAppName();
1834
2036
  if (appName &&
1835
2037
  appInstanceMap.has(appName) &&
1836
- (((key === 'src' || key === 'srcset') && /^(img|script)$/i.test(this.tagName)) ||
2038
+ (((key === 'src' || key === 'srcset') && /^(img|script|video|audio|source|embed)$/i.test(this.tagName)) ||
1837
2039
  (key === 'href' && /^link$/i.test(this.tagName)))) {
1838
2040
  const app = appInstanceMap.get(appName);
1839
2041
  value = CompletionPath(value, app.url);
@@ -1855,7 +2057,7 @@ function releasePatchDocument() {
1855
2057
  rawRootDocument.prototype.getElementsByName = globalEnv.rawGetElementsByName;
1856
2058
  }
1857
2059
  // release patch
1858
- function releasePatches() {
2060
+ function releasePatchElementAndDocument() {
1859
2061
  removeDomScope();
1860
2062
  releasePatchDocument();
1861
2063
  Element.prototype.appendChild = globalEnv.rawAppendChild;
@@ -1865,6 +2067,9 @@ function releasePatches() {
1865
2067
  Element.prototype.append = globalEnv.rawAppend;
1866
2068
  Element.prototype.prepend = globalEnv.rawPrepend;
1867
2069
  Element.prototype.cloneNode = globalEnv.rawCloneNode;
2070
+ Element.prototype.querySelector = globalEnv.rawElementQuerySelector;
2071
+ Element.prototype.querySelectorAll = globalEnv.rawElementQuerySelectorAll;
2072
+ rawDefineProperty(Element.prototype, 'innerHTML', globalEnv.rawInnerHTMLDesc);
1868
2073
  }
1869
2074
  // exec when last child unmount
1870
2075
  function releasePatchSetAttribute() {
@@ -1883,16 +2088,21 @@ function rejectMicroAppStyle() {
1883
2088
  }
1884
2089
  }
1885
2090
 
1886
- const globalEnv = {};
2091
+ const globalEnv = {
2092
+ // mark current application as base application
2093
+ __MICRO_APP_BASE_APPLICATION__: true,
2094
+ // active sandbox count
2095
+ activeSandbox: 0,
2096
+ };
1887
2097
  /**
1888
2098
  * Note loop nesting
1889
2099
  * Only prototype or unique values can be put here
1890
2100
  */
1891
2101
  function initGlobalEnv() {
1892
2102
  if (isBrowser) {
1893
- const rawWindow = Function('return window')();
1894
- const rawDocument = Function('return document')();
1895
- const rawRootDocument = Function('return Document')();
2103
+ const rawWindow = window.rawWindow || Function('return window')();
2104
+ const rawDocument = window.rawDocument || Function('return document')();
2105
+ const rawRootDocument = rawWindow.Document || Function('return Document')();
1896
2106
  const supportModuleScript = isSupportModuleScript();
1897
2107
  /**
1898
2108
  * save patch raw methods
@@ -1906,9 +2116,14 @@ function initGlobalEnv() {
1906
2116
  const rawAppend = Element.prototype.append;
1907
2117
  const rawPrepend = Element.prototype.prepend;
1908
2118
  const rawCloneNode = Element.prototype.cloneNode;
2119
+ const rawElementQuerySelector = Element.prototype.querySelector;
2120
+ const rawElementQuerySelectorAll = Element.prototype.querySelectorAll;
2121
+ const rawInnerHTMLDesc = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML');
2122
+ const rawParentNodeDesc = Object.getOwnPropertyDescriptor(Node.prototype, 'parentNode');
1909
2123
  const rawCreateElement = rawRootDocument.prototype.createElement;
1910
2124
  const rawCreateElementNS = rawRootDocument.prototype.createElementNS;
1911
2125
  const rawCreateDocumentFragment = rawRootDocument.prototype.createDocumentFragment;
2126
+ const rawCreateTextNode = rawRootDocument.prototype.createTextNode;
1912
2127
  const rawQuerySelector = rawRootDocument.prototype.querySelector;
1913
2128
  const rawQuerySelectorAll = rawRootDocument.prototype.querySelectorAll;
1914
2129
  const rawGetElementById = rawRootDocument.prototype.getElementById;
@@ -1926,18 +2141,19 @@ function initGlobalEnv() {
1926
2141
  * save effect raw methods
1927
2142
  * pay attention to this binding, especially setInterval, setTimeout, clearInterval, clearTimeout
1928
2143
  */
1929
- const rawWindowAddEventListener = rawWindow.addEventListener;
1930
- const rawWindowRemoveEventListener = rawWindow.removeEventListener;
1931
2144
  const rawSetInterval = rawWindow.setInterval;
1932
2145
  const rawSetTimeout = rawWindow.setTimeout;
1933
2146
  const rawClearInterval = rawWindow.clearInterval;
1934
2147
  const rawClearTimeout = rawWindow.clearTimeout;
1935
2148
  const rawPushState = rawWindow.history.pushState;
1936
2149
  const rawReplaceState = rawWindow.history.replaceState;
2150
+ const rawWindowAddEventListener = rawWindow.addEventListener;
2151
+ const rawWindowRemoveEventListener = rawWindow.removeEventListener;
1937
2152
  const rawDocumentAddEventListener = rawDocument.addEventListener;
1938
2153
  const rawDocumentRemoveEventListener = rawDocument.removeEventListener;
1939
- // mark current application as base application
1940
- window.__MICRO_APP_BASE_APPLICATION__ = true;
2154
+ // TODO: 统一使用 EventTarget 去掉上面四个
2155
+ const rawAddEventListener = EventTarget.prototype.addEventListener;
2156
+ const rawRemoveEventListener = EventTarget.prototype.removeEventListener;
1941
2157
  assign(globalEnv, {
1942
2158
  // common global vars
1943
2159
  rawWindow,
@@ -1953,9 +2169,14 @@ function initGlobalEnv() {
1953
2169
  rawAppend,
1954
2170
  rawPrepend,
1955
2171
  rawCloneNode,
2172
+ rawElementQuerySelector,
2173
+ rawElementQuerySelectorAll,
2174
+ rawInnerHTMLDesc,
2175
+ rawParentNodeDesc,
1956
2176
  rawCreateElement,
1957
2177
  rawCreateElementNS,
1958
2178
  rawCreateDocumentFragment,
2179
+ rawCreateTextNode,
1959
2180
  rawQuerySelector,
1960
2181
  rawQuerySelectorAll,
1961
2182
  rawGetElementById,
@@ -1974,6 +2195,8 @@ function initGlobalEnv() {
1974
2195
  rawDocumentRemoveEventListener,
1975
2196
  rawPushState,
1976
2197
  rawReplaceState,
2198
+ rawAddEventListener,
2199
+ rawRemoveEventListener,
1977
2200
  });
1978
2201
  // global effect
1979
2202
  rejectMicroAppStyle();
@@ -1983,7 +2206,7 @@ function initGlobalEnv() {
1983
2206
  const scriptTypes = ['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module', 'systemjs-module', 'systemjs-importmap'];
1984
2207
  // whether use type='module' script
1985
2208
  function isTypeModule(app, scriptInfo) {
1986
- return scriptInfo.appSpace[app.name].module && (!app.useSandbox || app.esmodule);
2209
+ return scriptInfo.appSpace[app.name].module && (!app.useSandbox || app.iframe);
1987
2210
  }
1988
2211
  // special script element
1989
2212
  function isSpecialScript(app, scriptInfo) {
@@ -2002,11 +2225,17 @@ function isInlineMode(app, scriptInfo) {
2002
2225
  return (app.inline ||
2003
2226
  scriptInfo.appSpace[app.name].inline ||
2004
2227
  isTypeModule(app, scriptInfo) ||
2005
- isSpecialScript(app, scriptInfo));
2228
+ isSpecialScript(app, scriptInfo) ||
2229
+ app.iframe);
2230
+ }
2231
+ // TODO: iframe重新插入window前后不一致,通过iframe Function创建的函数无法复用
2232
+ function getEffectWindow(app) {
2233
+ return app.iframe ? app.sandBox.microAppWindow : globalEnv.rawWindow;
2006
2234
  }
2007
2235
  // Convert string code to function
2008
- function code2Function(code) {
2009
- return new Function(code);
2236
+ function code2Function(app, code) {
2237
+ const targetWindow = getEffectWindow(app);
2238
+ return new targetWindow.Function(code);
2010
2239
  }
2011
2240
  /**
2012
2241
  * If the appSpace of the current js address has other app, try to reuse parsedFunction of other app
@@ -2014,10 +2243,10 @@ function code2Function(code) {
2014
2243
  * @param scriptInfo scriptInfo of current address
2015
2244
  * @param currentCode pure code of current address
2016
2245
  */
2017
- function getExistParseResult(appName, scriptInfo, currentCode) {
2246
+ function getExistParseResult(app, scriptInfo, currentCode) {
2018
2247
  const appSpace = scriptInfo.appSpace;
2019
2248
  for (const item in appSpace) {
2020
- if (item !== appName) {
2249
+ if (item !== app.name) {
2021
2250
  const appSpaceData = appSpace[item];
2022
2251
  if (appSpaceData.parsedCode === currentCode && appSpaceData.parsedFunction) {
2023
2252
  return appSpaceData.parsedFunction;
@@ -2030,7 +2259,7 @@ function getExistParseResult(appName, scriptInfo, currentCode) {
2030
2259
  * @returns parsedFunction
2031
2260
  */
2032
2261
  function getParsedFunction(app, scriptInfo, parsedCode) {
2033
- return getExistParseResult(app.name, scriptInfo, parsedCode) || code2Function(parsedCode);
2262
+ return getExistParseResult(app, scriptInfo, parsedCode) || code2Function(app, parsedCode);
2034
2263
  }
2035
2264
  // Prevent randomly created strings from repeating
2036
2265
  function getUniqueNonceSrc() {
@@ -2054,6 +2283,9 @@ function setConvertScriptAttr(convertScript, attrs) {
2054
2283
  function isWrapInSandBox(app, scriptInfo) {
2055
2284
  return app.useSandbox && !isTypeModule(app, scriptInfo);
2056
2285
  }
2286
+ function getSandboxType(app, scriptInfo) {
2287
+ return isWrapInSandBox(app, scriptInfo) ? app.iframe ? 'iframe' : 'with' : 'disable';
2288
+ }
2057
2289
  /**
2058
2290
  * Extract script elements
2059
2291
  * @param script script element
@@ -2062,6 +2294,7 @@ function isWrapInSandBox(app, scriptInfo) {
2062
2294
  * @param isDynamic dynamic insert
2063
2295
  */
2064
2296
  function extractScriptElement(script, parent, app, isDynamic = false) {
2297
+ var _a;
2065
2298
  let replaceComment = null;
2066
2299
  let src = script.getAttribute('src');
2067
2300
  if (src)
@@ -2073,6 +2306,10 @@ function extractScriptElement(script, parent, app, isDynamic = false) {
2073
2306
  !scriptTypes.includes(script.type)) ||
2074
2307
  script.hasAttribute('ignore') ||
2075
2308
  checkIgnoreUrl(src, app.name)) {
2309
+ // 配置为忽略的脚本,清空 rawDocument.currentScript,避免被忽略的脚本内获取 currentScript 出错
2310
+ if ((_a = globalEnv.rawDocument) === null || _a === void 0 ? void 0 : _a.currentScript) {
2311
+ delete globalEnv.rawDocument.currentScript;
2312
+ }
2076
2313
  return null;
2077
2314
  }
2078
2315
  else if ((globalEnv.supportModuleScript && script.noModule) ||
@@ -2268,13 +2505,13 @@ function fetchScriptSuccess(address, scriptInfo, code, app) {
2268
2505
  */
2269
2506
  if (!appSpaceData.parsedCode) {
2270
2507
  appSpaceData.parsedCode = bindScope(address, app, code, scriptInfo);
2271
- appSpaceData.wrapInSandBox = isWrapInSandBox(app, scriptInfo);
2508
+ appSpaceData.sandboxType = getSandboxType(app, scriptInfo);
2272
2509
  if (!isInlineMode(app, scriptInfo)) {
2273
2510
  try {
2274
2511
  appSpaceData.parsedFunction = getParsedFunction(app, scriptInfo, appSpaceData.parsedCode);
2275
2512
  }
2276
2513
  catch (err) {
2277
- logWarn('Something went wrong while handling preloaded resources', app.name, '\n', err);
2514
+ logError('Something went wrong while handling preloaded resources', app.name, '\n', err);
2278
2515
  }
2279
2516
  }
2280
2517
  }
@@ -2295,7 +2532,8 @@ function execScripts(app, initHook) {
2295
2532
  const appSpaceData = scriptInfo.appSpace[app.name];
2296
2533
  // Notice the second render
2297
2534
  if (appSpaceData.defer || appSpaceData.async) {
2298
- if (scriptInfo.isExternal && !scriptInfo.code) {
2535
+ // TODO: defer和module彻底分开,不要混在一起
2536
+ if (scriptInfo.isExternal && !scriptInfo.code && !(app.iframe && appSpaceData.module)) {
2299
2537
  deferScriptPromise.push(fetchSource(address, app.name));
2300
2538
  }
2301
2539
  else {
@@ -2320,7 +2558,7 @@ function execScripts(app, initHook) {
2320
2558
  logError(err, app.name);
2321
2559
  }, () => {
2322
2560
  deferScriptInfo.forEach(([address, scriptInfo]) => {
2323
- if (scriptInfo.code) {
2561
+ if (isString(scriptInfo.code)) {
2324
2562
  injectFiberTask(fiberScriptTasks, () => {
2325
2563
  runScript(address, app, scriptInfo, initHook);
2326
2564
  !isTypeModule(app, scriptInfo) && initHook(false);
@@ -2364,20 +2602,19 @@ function execScripts(app, initHook) {
2364
2602
  * @param callback callback of module script
2365
2603
  */
2366
2604
  function runScript(address, app, scriptInfo, callback, replaceElement) {
2367
- var _a;
2368
2605
  try {
2369
2606
  actionsBeforeRunScript(app);
2370
2607
  const appSpaceData = scriptInfo.appSpace[app.name];
2371
- const wrapInSandBox = isWrapInSandBox(app, scriptInfo);
2608
+ const sandboxType = getSandboxType(app, scriptInfo);
2372
2609
  /**
2373
2610
  * NOTE:
2374
2611
  * 1. plugins and wrapCode will only be executed once
2375
2612
  * 2. if parsedCode not exist, parsedFunction is not exist
2376
2613
  * 3. if parsedCode exist, parsedFunction does not necessarily exist
2377
2614
  */
2378
- if (!appSpaceData.parsedCode || appSpaceData.wrapInSandBox !== wrapInSandBox) {
2615
+ if (!appSpaceData.parsedCode || appSpaceData.sandboxType !== sandboxType) {
2379
2616
  appSpaceData.parsedCode = bindScope(address, app, scriptInfo.code, scriptInfo);
2380
- appSpaceData.wrapInSandBox = wrapInSandBox;
2617
+ appSpaceData.sandboxType = sandboxType;
2381
2618
  appSpaceData.parsedFunction = null;
2382
2619
  }
2383
2620
  if (isInlineMode(app, scriptInfo)) {
@@ -2385,7 +2622,8 @@ function runScript(address, app, scriptInfo, callback, replaceElement) {
2385
2622
  runCode2InlineScript(address, appSpaceData.parsedCode, isTypeModule(app, scriptInfo), scriptElement, appSpaceData.attrs, callback);
2386
2623
  if (!replaceElement) {
2387
2624
  // TEST IGNORE
2388
- (_a = app.container) === null || _a === void 0 ? void 0 : _a.querySelector('micro-app-body').appendChild(scriptElement);
2625
+ const parent = app.iframe ? app.sandBox.microBody : app.querySelector('micro-app-body');
2626
+ parent === null || parent === void 0 ? void 0 : parent.appendChild(scriptElement);
2389
2627
  }
2390
2628
  }
2391
2629
  else {
@@ -2417,7 +2655,7 @@ function runDynamicRemoteScript(address, app, scriptInfo, originScript) {
2417
2655
  runScript(address, app, scriptInfo, dispatchScriptOnLoadEvent, replaceElement);
2418
2656
  !isTypeModule(app, scriptInfo) && dispatchScriptOnLoadEvent();
2419
2657
  };
2420
- if (scriptInfo.code) {
2658
+ if (scriptInfo.code || (app.iframe && scriptInfo.appSpace[app.name].module)) {
2421
2659
  defer(runDynamicScript);
2422
2660
  }
2423
2661
  else {
@@ -2464,6 +2702,9 @@ function runCode2InlineScript(address, code, module, scriptElement, attrs, callb
2464
2702
  scriptElement.setAttribute('type', 'module');
2465
2703
  if (callback) {
2466
2704
  callback.moduleCount && callback.moduleCount--;
2705
+ /**
2706
+ * module script will execute onload method only after it insert to document/iframe
2707
+ */
2467
2708
  scriptElement.onload = callback.bind(scriptElement, callback.moduleCount === 0);
2468
2709
  }
2469
2710
  }
@@ -2478,7 +2719,7 @@ function runParsedFunction(app, scriptInfo) {
2478
2719
  if (!appSpaceData.parsedFunction) {
2479
2720
  appSpaceData.parsedFunction = getParsedFunction(app, scriptInfo, appSpaceData.parsedCode);
2480
2721
  }
2481
- appSpaceData.parsedFunction.call(window);
2722
+ appSpaceData.parsedFunction.call(getEffectWindow(app));
2482
2723
  }
2483
2724
  /**
2484
2725
  * bind js scope
@@ -2487,12 +2728,12 @@ function runParsedFunction(app, scriptInfo) {
2487
2728
  * @param scriptInfo source script info
2488
2729
  */
2489
2730
  function bindScope(address, app, code, scriptInfo) {
2490
- // TODO: cache
2731
+ // TODO: 1、cache 2、esm code is null
2491
2732
  if (isPlainObject(microApp.options.plugins)) {
2492
2733
  code = usePlugins(address, code, app.name, microApp.options.plugins);
2493
2734
  }
2494
2735
  if (isWrapInSandBox(app, scriptInfo)) {
2495
- 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__);`;
2736
+ return app.iframe ? `(function(window,self,global,location){;${code}\n${isInlineScript(address) ? '' : `//# sourceURL=${address}\n`}}).call(window.__MICRO_APP_SANDBOX__.proxyWindow,window.__MICRO_APP_SANDBOX__.proxyWindow,window.__MICRO_APP_SANDBOX__.proxyWindow,window.__MICRO_APP_SANDBOX__.proxyWindow,window.__MICRO_APP_SANDBOX__.proxyLocation);` : `;(function(proxyWindow){with(proxyWindow.__MICRO_APP_WINDOW__){(function(${globalKeyToBeCached}){;${code}\n${isInlineScript(address) ? '' : `//# sourceURL=${address}\n`}}).call(proxyWindow,${globalKeyToBeCached})}})(window.__MICRO_APP_PROXY_WINDOW__);`;
2496
2737
  }
2497
2738
  return code;
2498
2739
  }
@@ -2555,7 +2796,7 @@ function flatChildren(parent, app, microAppHead, fiberStyleTasks) {
2555
2796
  flatChildren(child, app, microAppHead, fiberStyleTasks);
2556
2797
  });
2557
2798
  for (const dom of children) {
2558
- if (dom instanceof HTMLLinkElement) {
2799
+ if (isLinkElement(dom)) {
2559
2800
  if (dom.hasAttribute('exclude') || checkExcludeUrl(dom.getAttribute('href'), app.name)) {
2560
2801
  parent.replaceChild(document.createComment('link element with exclude attribute ignored by micro-app'), dom);
2561
2802
  }
@@ -2566,7 +2807,7 @@ function flatChildren(parent, app, microAppHead, fiberStyleTasks) {
2566
2807
  dom.setAttribute('href', CompletionPath(dom.getAttribute('href'), app.url));
2567
2808
  }
2568
2809
  }
2569
- else if (dom instanceof HTMLStyleElement) {
2810
+ else if (isStyleElement(dom)) {
2570
2811
  if (dom.hasAttribute('exclude')) {
2571
2812
  parent.replaceChild(document.createComment('style element with exclude attribute ignored by micro-app'), dom);
2572
2813
  }
@@ -2574,10 +2815,10 @@ function flatChildren(parent, app, microAppHead, fiberStyleTasks) {
2574
2815
  injectFiberTask(fiberStyleTasks, () => scopedCSS(dom, app));
2575
2816
  }
2576
2817
  }
2577
- else if (dom instanceof HTMLScriptElement) {
2818
+ else if (isScriptElement(dom)) {
2578
2819
  extractScriptElement(dom, parent, app);
2579
2820
  }
2580
- else if (dom instanceof HTMLImageElement && dom.hasAttribute('src')) {
2821
+ else if (isImageElement(dom) && dom.hasAttribute('src')) {
2581
2822
  dom.setAttribute('src', CompletionPath(dom.getAttribute('src'), app.url));
2582
2823
  }
2583
2824
  /**
@@ -2600,8 +2841,8 @@ function flatChildren(parent, app, microAppHead, fiberStyleTasks) {
2600
2841
  */
2601
2842
  function extractSourceDom(htmlStr, app) {
2602
2843
  const wrapElement = getWrapElement(htmlStr);
2603
- const microAppHead = wrapElement.querySelector('micro-app-head');
2604
- const microAppBody = wrapElement.querySelector('micro-app-body');
2844
+ const microAppHead = globalEnv.rawElementQuerySelector.call(wrapElement, 'micro-app-head');
2845
+ const microAppBody = globalEnv.rawElementQuerySelector.call(wrapElement, 'micro-app-body');
2605
2846
  if (!microAppHead || !microAppBody) {
2606
2847
  const msg = `element ${microAppHead ? 'body' : 'head'} is missing`;
2607
2848
  app.onerror(new Error(msg));
@@ -2802,7 +3043,7 @@ const eventCenter = new EventCenter();
2802
3043
  * @param appName app.name
2803
3044
  * @param fromBaseApp is from base app
2804
3045
  */
2805
- function formatEventName(appName, fromBaseApp) {
3046
+ function createEventName(appName, fromBaseApp) {
2806
3047
  if (!isString(appName) || !appName)
2807
3048
  return '';
2808
3049
  return fromBaseApp ? `__from_base_app_${appName}__` : `__from_micro_app_${appName}__`;
@@ -2881,7 +3122,7 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
2881
3122
  * @param autoTrigger If there is cached data when first bind listener, whether it needs to trigger, default is false
2882
3123
  */
2883
3124
  addDataListener(appName, cb, autoTrigger) {
2884
- eventCenter.on(formatEventName(formatAppName(appName), false), cb, autoTrigger);
3125
+ eventCenter.on(createEventName(formatAppName(appName), false), cb, autoTrigger);
2885
3126
  }
2886
3127
  /**
2887
3128
  * remove listener
@@ -2889,7 +3130,7 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
2889
3130
  * @param cb listener
2890
3131
  */
2891
3132
  removeDataListener(appName, cb) {
2892
- isFunction(cb) && eventCenter.off(formatEventName(formatAppName(appName), false), cb);
3133
+ isFunction(cb) && eventCenter.off(createEventName(formatAppName(appName), false), cb);
2893
3134
  }
2894
3135
  /**
2895
3136
  * get data from micro app or base app
@@ -2897,7 +3138,7 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
2897
3138
  * @param fromBaseApp whether get data from base app, default is false
2898
3139
  */
2899
3140
  getData(appName, fromBaseApp = false) {
2900
- return eventCenter.getData(formatEventName(formatAppName(appName), fromBaseApp));
3141
+ return eventCenter.getData(createEventName(formatAppName(appName), fromBaseApp));
2901
3142
  }
2902
3143
  /**
2903
3144
  * Dispatch data to the specified micro app
@@ -2905,7 +3146,7 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
2905
3146
  * @param data data
2906
3147
  */
2907
3148
  setData(appName, data, nextStep, force) {
2908
- eventCenter.dispatch(formatEventName(formatAppName(appName), true), data, (resArr) => isFunction(nextStep) && nextStep(resArr), force);
3149
+ eventCenter.dispatch(createEventName(formatAppName(appName), true), data, (resArr) => isFunction(nextStep) && nextStep(resArr), force);
2909
3150
  }
2910
3151
  forceSetData(appName, data, nextStep) {
2911
3152
  this.setData(appName, data, nextStep, true);
@@ -2916,14 +3157,14 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
2916
3157
  * @param fromBaseApp whether clear data from child app, default is true
2917
3158
  */
2918
3159
  clearData(appName, fromBaseApp = true) {
2919
- eventCenter.clearData(formatEventName(formatAppName(appName), fromBaseApp));
3160
+ eventCenter.clearData(createEventName(formatAppName(appName), fromBaseApp));
2920
3161
  }
2921
3162
  /**
2922
3163
  * clear all listener for specified micro app
2923
3164
  * @param appName app.name
2924
3165
  */
2925
3166
  clearDataListener(appName) {
2926
- eventCenter.off(formatEventName(formatAppName(appName), false));
3167
+ eventCenter.off(createEventName(formatAppName(appName), false));
2927
3168
  }
2928
3169
  }
2929
3170
  // Event center for sub app
@@ -2940,20 +3181,20 @@ class EventCenterForMicroApp extends EventCenterForGlobal {
2940
3181
  */
2941
3182
  addDataListener(cb, autoTrigger) {
2942
3183
  cb.__AUTO_TRIGGER__ = autoTrigger;
2943
- eventCenter.on(formatEventName(this.appName, true), cb, autoTrigger);
3184
+ eventCenter.on(createEventName(this.appName, true), cb, autoTrigger);
2944
3185
  }
2945
3186
  /**
2946
3187
  * remove listener
2947
3188
  * @param cb listener
2948
3189
  */
2949
3190
  removeDataListener(cb) {
2950
- isFunction(cb) && eventCenter.off(formatEventName(this.appName, true), cb);
3191
+ isFunction(cb) && eventCenter.off(createEventName(this.appName, true), cb);
2951
3192
  }
2952
3193
  /**
2953
3194
  * get data from base app
2954
3195
  */
2955
3196
  getData(fromBaseApp = true) {
2956
- return eventCenter.getData(formatEventName(this.appName, fromBaseApp));
3197
+ return eventCenter.getData(createEventName(this.appName, fromBaseApp));
2957
3198
  }
2958
3199
  /**
2959
3200
  * dispatch data to base app
@@ -2961,12 +3202,12 @@ class EventCenterForMicroApp extends EventCenterForGlobal {
2961
3202
  */
2962
3203
  dispatch(data, nextStep, force) {
2963
3204
  removeDomScope();
2964
- eventCenter.dispatch(formatEventName(this.appName, false), data, (resArr) => isFunction(nextStep) && nextStep(resArr), force, () => {
3205
+ eventCenter.dispatch(createEventName(this.appName, false), data, (resArr) => isFunction(nextStep) && nextStep(resArr), force, () => {
2965
3206
  const app = appInstanceMap.get(this.appName);
2966
3207
  if ((app === null || app === void 0 ? void 0 : app.container) && isPlainObject(data)) {
2967
3208
  const event = new CustomEvent('datachange', {
2968
3209
  detail: {
2969
- data: eventCenter.getData(formatEventName(this.appName, false))
3210
+ data: eventCenter.getData(createEventName(this.appName, false))
2970
3211
  }
2971
3212
  });
2972
3213
  getRootContainer(app.container).dispatchEvent(event);
@@ -2981,33 +3222,35 @@ class EventCenterForMicroApp extends EventCenterForGlobal {
2981
3222
  * @param fromBaseApp whether clear data from base app, default is false
2982
3223
  */
2983
3224
  clearData(fromBaseApp = false) {
2984
- eventCenter.clearData(formatEventName(this.appName, fromBaseApp));
3225
+ eventCenter.clearData(createEventName(this.appName, fromBaseApp));
2985
3226
  }
2986
3227
  /**
2987
3228
  * clear all listeners
2988
3229
  */
2989
3230
  clearDataListener() {
2990
- eventCenter.off(formatEventName(this.appName, true));
3231
+ eventCenter.off(createEventName(this.appName, true));
2991
3232
  }
2992
3233
  }
2993
3234
  /**
2994
3235
  * Record UMD function before exec umdHookMount
2995
- * @param microAppEventCenter
3236
+ * NOTE: record maybe call twice when unmount prerender, keep-alive app manually with umd mode
3237
+ * @param microAppEventCenter instance of EventCenterForMicroApp
2996
3238
  */
2997
3239
  function recordDataCenterSnapshot(microAppEventCenter) {
2998
- const appName = microAppEventCenter.appName;
2999
- microAppEventCenter.umdDataListeners = { global: new Set(), normal: new Set() };
3000
- const globalEventInfo = eventCenter.eventList.get('global');
3001
- if (globalEventInfo) {
3002
- for (const cb of globalEventInfo.callbacks) {
3003
- if (appName === cb.__APP_NAME__) {
3004
- microAppEventCenter.umdDataListeners.global.add(cb);
3240
+ if (microAppEventCenter && !microAppEventCenter.umdDataListeners) {
3241
+ microAppEventCenter.umdDataListeners = { global: new Set(), normal: new Set() };
3242
+ const globalEventInfo = eventCenter.eventList.get('global');
3243
+ if (globalEventInfo) {
3244
+ for (const cb of globalEventInfo.callbacks) {
3245
+ if (microAppEventCenter.appName === cb.__APP_NAME__) {
3246
+ microAppEventCenter.umdDataListeners.global.add(cb);
3247
+ }
3005
3248
  }
3006
3249
  }
3007
- }
3008
- const subAppEventInfo = eventCenter.eventList.get(formatEventName(appName, true));
3009
- if (subAppEventInfo) {
3010
- microAppEventCenter.umdDataListeners.normal = new Set(subAppEventInfo.callbacks);
3250
+ const subAppEventInfo = eventCenter.eventList.get(createEventName(microAppEventCenter.appName, true));
3251
+ if (subAppEventInfo) {
3252
+ microAppEventCenter.umdDataListeners.normal = new Set(subAppEventInfo.callbacks);
3253
+ }
3011
3254
  }
3012
3255
  }
3013
3256
  /**
@@ -3015,13 +3258,24 @@ function recordDataCenterSnapshot(microAppEventCenter) {
3015
3258
  * @param microAppEventCenter instance of EventCenterForMicroApp
3016
3259
  */
3017
3260
  function rebuildDataCenterSnapshot(microAppEventCenter) {
3018
- for (const cb of microAppEventCenter.umdDataListeners.global) {
3019
- microAppEventCenter.addGlobalDataListener(cb, cb.__AUTO_TRIGGER__);
3020
- }
3021
- for (const cb of microAppEventCenter.umdDataListeners.normal) {
3022
- microAppEventCenter.addDataListener(cb, cb.__AUTO_TRIGGER__);
3261
+ // in withSandbox preRender mode with module script, umdDataListeners maybe undefined
3262
+ if (microAppEventCenter === null || microAppEventCenter === void 0 ? void 0 : microAppEventCenter.umdDataListeners) {
3263
+ for (const cb of microAppEventCenter.umdDataListeners.global) {
3264
+ microAppEventCenter.addGlobalDataListener(cb, cb.__AUTO_TRIGGER__);
3265
+ }
3266
+ for (const cb of microAppEventCenter.umdDataListeners.normal) {
3267
+ microAppEventCenter.addDataListener(cb, cb.__AUTO_TRIGGER__);
3268
+ }
3269
+ resetDataCenterSnapshot(microAppEventCenter);
3023
3270
  }
3024
3271
  }
3272
+ /**
3273
+ * delete umdDataListeners from microAppEventCenter
3274
+ * @param microAppEventCenter instance of EventCenterForMicroApp
3275
+ */
3276
+ function resetDataCenterSnapshot(microAppEventCenter) {
3277
+ delete microAppEventCenter.umdDataListeners;
3278
+ }
3025
3279
 
3026
3280
  // 管理 app 的单例
3027
3281
  class AppManager {
@@ -3084,12 +3338,12 @@ function isConstructorFunction(value) {
3084
3338
  return value.__MICRO_APP_IS_CONSTRUCTOR__ = isConstructor(value);
3085
3339
  }
3086
3340
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
3087
- function bindFunctionToRawObject(rawObject, value, key = 'WINDOW') {
3088
- const cacheKey = `__MICRO_APP_BOUND_${key}_FUNCTION__`;
3089
- if (value[cacheKey])
3090
- return value[cacheKey];
3091
- if (!isConstructorFunction(value) && !isBoundedFunction(value)) {
3092
- const bindRawObjectValue = value.bind(rawObject);
3341
+ function bindFunctionToRawTarget(value, rawTarget, key = 'WINDOW') {
3342
+ if (isFunction(value) && !isConstructorFunction(value) && !isBoundedFunction(value)) {
3343
+ const cacheKey = `__MICRO_APP_BOUND_${key}_FUNCTION__`;
3344
+ if (value[cacheKey])
3345
+ return value[cacheKey];
3346
+ const bindRawObjectValue = value.bind(rawTarget);
3093
3347
  for (const key in value) {
3094
3348
  bindRawObjectValue[key] = value[key];
3095
3349
  }
@@ -3106,21 +3360,6 @@ function bindFunctionToRawObject(rawObject, value, key = 'WINDOW') {
3106
3360
  return value;
3107
3361
  }
3108
3362
 
3109
- // this events should be sent to the specified app
3110
- const formatEventList = ['unmount', 'appstate-change'];
3111
- /**
3112
- * Format event name
3113
- * @param eventName event name
3114
- * @param appName app name
3115
- */
3116
- function formatEventName$1(eventName, appName) {
3117
- var _a;
3118
- if (formatEventList.includes(eventName) ||
3119
- ((eventName === 'popstate' || eventName === 'hashchange') && ((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.useMemoryRouter))) {
3120
- return `${eventName}-${appName}`;
3121
- }
3122
- return eventName;
3123
- }
3124
3363
  // document.onclick binding list, the binding function of each application is unique
3125
3364
  const documentClickListMap = new Map();
3126
3365
  let hasRewriteDocumentOnClick = false;
@@ -3231,7 +3470,7 @@ function effect(appName, microAppWindow) {
3231
3470
  const { rawWindow, rawDocument, rawWindowAddEventListener, rawWindowRemoveEventListener, rawSetInterval, rawSetTimeout, rawClearInterval, rawClearTimeout, rawDocumentRemoveEventListener, } = globalEnv;
3232
3471
  // listener may be null, e.g test-passive
3233
3472
  microAppWindow.addEventListener = function (type, listener, options) {
3234
- type = formatEventName$1(type, appName);
3473
+ type = formatEventName(type, appName);
3235
3474
  const listenerList = eventListenerMap.get(type);
3236
3475
  if (listenerList) {
3237
3476
  listenerList.add(listener);
@@ -3243,7 +3482,7 @@ function effect(appName, microAppWindow) {
3243
3482
  rawWindowAddEventListener.call(rawWindow, type, listener, options);
3244
3483
  };
3245
3484
  microAppWindow.removeEventListener = function (type, listener, options) {
3246
- type = formatEventName$1(type, appName);
3485
+ type = formatEventName(type, appName);
3247
3486
  const listenerList = eventListenerMap.get(type);
3248
3487
  if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
3249
3488
  listenerList.delete(listener);
@@ -3270,39 +3509,32 @@ function effect(appName, microAppWindow) {
3270
3509
  };
3271
3510
  const sstWindowListenerMap = new Map();
3272
3511
  const sstDocumentListenerMap = new Map();
3273
- let sstIntervalIdMap = new Map();
3274
- let sstTimeoutIdMap = new Map();
3275
3512
  let sstOnClickHandler;
3276
- const clearSnapshotData = () => {
3513
+ // reset snapshot data
3514
+ const reset = () => {
3277
3515
  sstWindowListenerMap.clear();
3278
- sstIntervalIdMap.clear();
3279
- sstTimeoutIdMap.clear();
3280
3516
  sstDocumentListenerMap.clear();
3281
3517
  sstOnClickHandler = null;
3282
3518
  };
3283
3519
  /**
3284
- * record event and timer
3285
- * Scenes:
3286
- * 1. exec umdMountHook in umd mode
3287
- * 2. hidden keep-alive app
3288
- * 3. after init prerender app
3520
+ * NOTE:
3521
+ * 1. about timer(events & properties should record & rebuild at all modes, exclude default mode)
3522
+ * 2. record maybe call twice when unmount prerender, keep-alive app manually with umd mode
3523
+ * 4 modes: default-mode、umd-mode、prerender、keep-alive
3524
+ * Solution:
3525
+ * 1. default-mode(normal): clear events & timers, not record & rebuild anything
3526
+ * 2. umd-mode(normal): not clear timers, record & rebuild events
3527
+ * 3. prerender/keep-alive(default, umd): not clear timers, record & rebuild events
3289
3528
  */
3290
- const recordEffect = () => {
3529
+ const record = () => {
3291
3530
  // record window event
3292
3531
  eventListenerMap.forEach((listenerList, type) => {
3293
3532
  if (listenerList.size) {
3294
3533
  sstWindowListenerMap.set(type, new Set(listenerList));
3295
3534
  }
3296
3535
  });
3297
- // record timers
3298
- if (intervalIdMap.size) {
3299
- sstIntervalIdMap = new Map(intervalIdMap);
3300
- }
3301
- if (timeoutIdMap.size) {
3302
- sstTimeoutIdMap = new Map(timeoutIdMap);
3303
- }
3304
3536
  // record onclick handler
3305
- sstOnClickHandler = documentClickListMap.get(appName);
3537
+ sstOnClickHandler = sstOnClickHandler || documentClickListMap.get(appName);
3306
3538
  // record document event
3307
3539
  const documentAppListenersMap = documentEventListenerMap.get(appName);
3308
3540
  if (documentAppListenersMap) {
@@ -3314,20 +3546,13 @@ function effect(appName, microAppWindow) {
3314
3546
  }
3315
3547
  };
3316
3548
  // rebuild event and timer before remount app
3317
- const rebuildEffect = () => {
3549
+ const rebuild = () => {
3318
3550
  // rebuild window event
3319
3551
  sstWindowListenerMap.forEach((listenerList, type) => {
3320
3552
  for (const listener of listenerList) {
3321
3553
  microAppWindow.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
3322
3554
  }
3323
3555
  });
3324
- // rebuild timer
3325
- sstIntervalIdMap.forEach((info) => {
3326
- microAppWindow.setInterval(info.handler, info.timeout, ...info.args);
3327
- });
3328
- sstTimeoutIdMap.forEach((info) => {
3329
- microAppWindow.setTimeout(info.handler, info.timeout, ...info.args);
3330
- });
3331
3556
  // rebuild onclick event
3332
3557
  sstOnClickHandler && documentClickListMap.set(appName, sstOnClickHandler);
3333
3558
  /**
@@ -3341,10 +3566,10 @@ function effect(appName, microAppWindow) {
3341
3566
  }
3342
3567
  });
3343
3568
  removeDomScope();
3344
- clearSnapshotData();
3569
+ reset();
3345
3570
  };
3346
3571
  // release all event listener & interval & timeout when unmount app
3347
- const releaseEffect = () => {
3572
+ const release = ({ umdMode, isPrerender, keepAlive, destroy }) => {
3348
3573
  // Clear window binding events
3349
3574
  if (eventListenerMap.size) {
3350
3575
  eventListenerMap.forEach((listenerList, type) => {
@@ -3354,17 +3579,15 @@ function effect(appName, microAppWindow) {
3354
3579
  });
3355
3580
  eventListenerMap.clear();
3356
3581
  }
3357
- // Clear timers
3358
- if (intervalIdMap.size) {
3582
+ // default mode(not keep-alive or isPrerender)
3583
+ if ((!umdMode && !keepAlive && !isPrerender) || destroy) {
3359
3584
  intervalIdMap.forEach((_, intervalId) => {
3360
3585
  rawClearInterval.call(rawWindow, intervalId);
3361
3586
  });
3362
- intervalIdMap.clear();
3363
- }
3364
- if (timeoutIdMap.size) {
3365
3587
  timeoutIdMap.forEach((_, timeoutId) => {
3366
3588
  rawClearTimeout.call(rawWindow, timeoutId);
3367
3589
  });
3590
+ intervalIdMap.clear();
3368
3591
  timeoutIdMap.clear();
3369
3592
  }
3370
3593
  // Clear the function bound by micro application through document.onclick
@@ -3381,9 +3604,10 @@ function effect(appName, microAppWindow) {
3381
3604
  }
3382
3605
  };
3383
3606
  return {
3384
- recordEffect,
3385
- rebuildEffect,
3386
- releaseEffect,
3607
+ reset,
3608
+ record,
3609
+ rebuild,
3610
+ release,
3387
3611
  };
3388
3612
  }
3389
3613
 
@@ -3578,25 +3802,29 @@ function addHistoryListener(appName) {
3578
3802
  * 1. unmount app & hidden keep-alive app will not receive popstate event
3579
3803
  * 2. filter out onlyForBrowser
3580
3804
  */
3581
- if (getActiveApps({ excludeHiddenApp: true, excludePreRender: true }).includes(appName) &&
3805
+ if (getActiveApps({
3806
+ excludeHiddenApp: true,
3807
+ excludePreRender: true,
3808
+ }).includes(appName) &&
3582
3809
  !e.onlyForBrowser) {
3583
3810
  const microPath = getMicroPathFromURL(appName);
3584
3811
  const app = appInstanceMap.get(appName);
3585
3812
  const proxyWindow = app.sandBox.proxyWindow;
3813
+ const microAppWindow = app.sandBox.microAppWindow;
3586
3814
  let isHashChange = false;
3587
3815
  // for hashChangeEvent
3588
3816
  const oldHref = proxyWindow.location.href;
3589
3817
  // Do not attach micro state to url when microPath is empty
3590
3818
  if (microPath) {
3591
3819
  const oldHash = proxyWindow.location.hash;
3592
- updateMicroLocation(appName, microPath, proxyWindow.location);
3820
+ updateMicroLocation(appName, microPath, microAppWindow.location);
3593
3821
  isHashChange = proxyWindow.location.hash !== oldHash;
3594
3822
  }
3595
3823
  // dispatch formatted popStateEvent to child
3596
- dispatchPopStateEventToMicroApp(appName, proxyWindow);
3824
+ dispatchPopStateEventToMicroApp(appName, proxyWindow, microAppWindow);
3597
3825
  // dispatch formatted hashChangeEvent to child when hash change
3598
3826
  if (isHashChange)
3599
- dispatchHashChangeEventToMicroApp(appName, proxyWindow, oldHref);
3827
+ dispatchHashChangeEventToMicroApp(appName, proxyWindow, microAppWindow, oldHref);
3600
3828
  // clear element scope before trigger event of next app
3601
3829
  removeDomScope();
3602
3830
  }
@@ -3612,10 +3840,9 @@ function addHistoryListener(appName) {
3612
3840
  * @param proxyWindow sandbox window
3613
3841
  * @param eventState history.state
3614
3842
  */
3615
- function dispatchPopStateEventToMicroApp(appName, proxyWindow) {
3616
- // create PopStateEvent named popstate-appName with sub app state
3617
- const newPopStateEvent = new PopStateEvent(formatEventName$1('popstate', appName), { state: getMicroState(appName) });
3843
+ function dispatchPopStateEventToMicroApp(appName, proxyWindow, microAppWindow) {
3618
3844
  /**
3845
+ * TODO: test
3619
3846
  * angular14 takes e.type as type judgment
3620
3847
  * when e.type is popstate-appName popstate event will be invalid
3621
3848
  */
@@ -3625,7 +3852,14 @@ function dispatchPopStateEventToMicroApp(appName, proxyWindow) {
3625
3852
  // configurable: true,
3626
3853
  // enumerable: true,
3627
3854
  // })
3628
- globalEnv.rawWindow.dispatchEvent(newPopStateEvent);
3855
+ // create PopStateEvent named popstate-appName with sub app state
3856
+ const newPopStateEvent = new PopStateEvent(formatEventName('popstate', appName), { state: getMicroState(appName) });
3857
+ if (isIframeSandbox(appName)) {
3858
+ microAppWindow.dispatchEvent(newPopStateEvent);
3859
+ }
3860
+ else {
3861
+ globalEnv.rawWindow.dispatchEvent(newPopStateEvent);
3862
+ }
3629
3863
  // call function window.onpopstate if it exists
3630
3864
  isFunction(proxyWindow.onpopstate) && proxyWindow.onpopstate(newPopStateEvent);
3631
3865
  }
@@ -3635,12 +3869,17 @@ function dispatchPopStateEventToMicroApp(appName, proxyWindow) {
3635
3869
  * @param proxyWindow sandbox window
3636
3870
  * @param oldHref old href
3637
3871
  */
3638
- function dispatchHashChangeEventToMicroApp(appName, proxyWindow, oldHref) {
3639
- const newHashChangeEvent = new HashChangeEvent(formatEventName$1('hashchange', appName), {
3872
+ function dispatchHashChangeEventToMicroApp(appName, proxyWindow, microAppWindow, oldHref) {
3873
+ const newHashChangeEvent = new HashChangeEvent(formatEventName('hashchange', appName), {
3640
3874
  newURL: proxyWindow.location.href,
3641
3875
  oldURL: oldHref,
3642
3876
  });
3643
- globalEnv.rawWindow.dispatchEvent(newHashChangeEvent);
3877
+ if (isIframeSandbox(appName)) {
3878
+ microAppWindow.dispatchEvent(newHashChangeEvent);
3879
+ }
3880
+ else {
3881
+ globalEnv.rawWindow.dispatchEvent(newHashChangeEvent);
3882
+ }
3644
3883
  // call function window.onhashchange if it exists
3645
3884
  isFunction(proxyWindow.onhashchange) && proxyWindow.onhashchange(newHashChangeEvent);
3646
3885
  }
@@ -3692,22 +3931,26 @@ function createMicroHistory(appName, microLocation) {
3692
3931
  const rawHistory = globalEnv.rawWindow.history;
3693
3932
  function getMicroHistoryMethod(methodName) {
3694
3933
  return function (...rests) {
3934
+ var _a, _b, _c;
3935
+ // TODO: 测试iframe的URL兼容isURL的情况
3695
3936
  if (isString(rests[2]) || isURL(rests[2])) {
3696
3937
  const targetLocation = createURL(rests[2], microLocation.href);
3697
- if (targetLocation.origin === microLocation.origin) {
3698
- navigateWithNativeEvent(appName, methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rests[0]), rests[1]);
3699
- const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3700
- if (targetFullPath !== microLocation.fullPath) {
3701
- updateMicroLocation(appName, targetFullPath, microLocation);
3702
- }
3703
- return void 0;
3938
+ const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3939
+ navigateWithNativeEvent(appName, methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rests[0]), rests[1]);
3940
+ if (targetFullPath !== microLocation.fullPath) {
3941
+ updateMicroLocation(appName, targetFullPath, microLocation);
3704
3942
  }
3943
+ (_c = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : (_b = _a.sandBox).updateIframeBase) === null || _c === void 0 ? void 0 : _c.call(_b);
3944
+ }
3945
+ else {
3946
+ nativeHistoryNavigate(appName, methodName, rests[2], rests[0], rests[1]);
3705
3947
  }
3706
- nativeHistoryNavigate(appName, methodName, rests[2], rests[0], rests[1]);
3707
3948
  };
3708
3949
  }
3709
3950
  const pushState = getMicroHistoryMethod('pushState');
3710
3951
  const replaceState = getMicroHistoryMethod('replaceState');
3952
+ if (isIframeSandbox(appName))
3953
+ return { pushState, replaceState };
3711
3954
  return new Proxy(rawHistory, {
3712
3955
  get(target, key) {
3713
3956
  if (key === 'state') {
@@ -3719,8 +3962,7 @@ function createMicroHistory(appName, microLocation) {
3719
3962
  else if (key === 'replaceState') {
3720
3963
  return replaceState;
3721
3964
  }
3722
- const rawValue = Reflect.get(target, key);
3723
- return isFunction(rawValue) ? bindFunctionToRawObject(target, rawValue, 'HISTORY') : rawValue;
3965
+ return bindFunctionToRawTarget(Reflect.get(target, key), target, 'HISTORY');
3724
3966
  },
3725
3967
  set(target, key, value) {
3726
3968
  Reflect.set(target, key, value);
@@ -3765,6 +4007,7 @@ function navigateWithNativeEvent(appName, methodName, result, onlyForBrowser, st
3765
4007
  if (isEffectiveApp(appName)) {
3766
4008
  const rawLocation = globalEnv.rawWindow.location;
3767
4009
  const oldFullPath = rawLocation.pathname + rawLocation.search + rawLocation.hash;
4010
+ // oldHref use for hashChangeEvent of base app
3768
4011
  const oldHref = result.isAttach2Hash && oldFullPath !== result.fullPath ? rawLocation.href : null;
3769
4012
  // navigate with native history method
3770
4013
  nativeHistoryNavigate(appName, methodName, result.fullPath, state, title);
@@ -3873,8 +4116,9 @@ function createRouterApi() {
3873
4116
  const microLocation = app.sandBox.proxyWindow.location;
3874
4117
  const targetLocation = createURL(to.path, microLocation.href);
3875
4118
  // Only get path data, even if the origin is different from microApp
4119
+ const currentFullPath = microLocation.pathname + microLocation.search + microLocation.hash;
3876
4120
  const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3877
- if (microLocation.fullPath !== targetFullPath || getMicroPathFromURL(appName) !== targetFullPath) {
4121
+ if (currentFullPath !== targetFullPath || getMicroPathFromURL(appName) !== targetFullPath) {
3878
4122
  const methodName = (replace && to.replace !== false) || to.replace === true ? 'replaceState' : 'pushState';
3879
4123
  navigateWithRawHistory(appName, methodName, targetLocation, to.state);
3880
4124
  }
@@ -4023,8 +4267,7 @@ function createRouterApi() {
4023
4267
  baseRouterProxy = new Proxy(baseRouter, {
4024
4268
  get(target, key) {
4025
4269
  removeDomScope();
4026
- const rawValue = Reflect.get(target, key);
4027
- return isFunction(rawValue) ? bindFunctionToRawObject(target, rawValue, 'BASEROUTER') : rawValue;
4270
+ return bindFunctionToRawTarget(Reflect.get(target, key), target, 'BASEROUTER');
4028
4271
  },
4029
4272
  set(target, key, value) {
4030
4273
  Reflect.set(target, key, value);
@@ -4052,9 +4295,118 @@ function createRouterApi() {
4052
4295
  }
4053
4296
  const { router, executeNavigationGuard, clearRouterWhenUnmount, } = createRouterApi();
4054
4297
 
4055
- const shadowLocationKeys = ['href', 'pathname', 'search', 'hash'];
4298
+ const escape2RawWindowKeys = [
4299
+ 'getComputedStyle',
4300
+ 'visualViewport',
4301
+ 'matchMedia',
4302
+ // 'DOMParser',
4303
+ 'ResizeObserver',
4304
+ 'IntersectionObserver',
4305
+ ];
4306
+ const escape2RawWindowRegExpKeys = [
4307
+ /animationFrame$/i,
4308
+ /mutationObserver$/i,
4309
+ /height$|width$/i,
4310
+ /offset$/i,
4311
+ // /event$/i,
4312
+ /^screen/i,
4313
+ /^scroll/i,
4314
+ /X$|Y$/,
4315
+ ];
4316
+ const scopeIframeWindowOnEvent = [
4317
+ 'onload',
4318
+ 'onbeforeunload',
4319
+ 'onunload',
4320
+ ];
4321
+ const scopeIframeWindowEvent = [
4322
+ 'hashchange',
4323
+ 'popstate',
4324
+ 'DOMContentLoaded',
4325
+ 'load',
4326
+ 'beforeunload',
4327
+ 'unload',
4328
+ ].concat(microAppCustomEvent);
4329
+ const scopeIframeDocumentEvent = [
4330
+ 'DOMContentLoaded',
4331
+ 'readystatechange',
4332
+ ];
4333
+ const scopeIframeDocumentOnEvent = [
4334
+ 'onreadystatechange',
4335
+ ];
4336
+ const uniqueDocumentElement = [
4337
+ 'body',
4338
+ 'head',
4339
+ 'html',
4340
+ 'title',
4341
+ ];
4342
+ const hijackMicroLocationKeys = [
4343
+ 'host',
4344
+ 'hostname',
4345
+ 'port',
4346
+ 'protocol',
4347
+ 'origin',
4348
+ ];
4349
+ // 有shadowRoot则代理到shadowRoot否则代理到原生document上 (属性)
4350
+ const proxy2RawDocOrShadowKeys = [
4351
+ 'childElementCount',
4352
+ 'children',
4353
+ 'firstElementChild',
4354
+ 'firstChild',
4355
+ 'lastElementChild',
4356
+ 'activeElement',
4357
+ 'fullscreenElement',
4358
+ 'pictureInPictureElement',
4359
+ 'pointerLockElement',
4360
+ 'styleSheets',
4361
+ ];
4362
+ // 有shadowRoot则代理到shadowRoot否则代理到原生document上 (方法)
4363
+ const proxy2RawDocOrShadowMethods = [
4364
+ 'append',
4365
+ 'contains',
4366
+ 'replaceChildren',
4367
+ 'getSelection',
4368
+ 'elementFromPoint',
4369
+ 'elementsFromPoint',
4370
+ 'getAnimations',
4371
+ ];
4372
+ // 直接代理到原生document上 (属性)
4373
+ const proxy2RawDocumentKeys = [
4374
+ 'characterSet',
4375
+ 'compatMode',
4376
+ 'contentType',
4377
+ 'designMode',
4378
+ 'dir',
4379
+ 'doctype',
4380
+ 'embeds',
4381
+ 'fullscreenEnabled',
4382
+ 'hidden',
4383
+ 'implementation',
4384
+ 'lastModified',
4385
+ 'pictureInPictureEnabled',
4386
+ 'plugins',
4387
+ 'readyState',
4388
+ 'referrer',
4389
+ 'visibilityState',
4390
+ 'fonts',
4391
+ ];
4392
+ // 直接代理到原生document上 (方法)
4393
+ const proxy2RawDocumentMethods = [
4394
+ 'execCommand',
4395
+ 'createRange',
4396
+ 'exitFullscreen',
4397
+ 'exitPictureInPicture',
4398
+ 'getElementsByTagNameNS',
4399
+ 'hasFocus',
4400
+ 'prepend',
4401
+ ];
4402
+ const globalPropertyList = [
4403
+ 'window',
4404
+ 'self',
4405
+ 'globalThis'
4406
+ ];
4407
+
4056
4408
  // origin is readonly, so we ignore when updateMicroLocation
4057
- const locationKeys = [...shadowLocationKeys, 'host', 'hostname', 'port', 'protocol', 'search'];
4409
+ const locationKeys = ['href', 'pathname', 'search', 'hash', 'host', 'hostname', 'port', 'protocol', 'search'];
4058
4410
  // origin, fullPath is necessary for guardLocation
4059
4411
  const guardLocationKeys = [...locationKeys, 'origin', 'fullPath'];
4060
4412
  /**
@@ -4062,19 +4414,27 @@ const guardLocationKeys = [...locationKeys, 'origin', 'fullPath'];
4062
4414
  * MDN https://developer.mozilla.org/en-US/docs/Web/API/Location
4063
4415
  * @param appName app name
4064
4416
  * @param url app url
4417
+ * @param microAppWindow iframeWindow, iframe only
4418
+ * @param childStaticLocation real child location info, iframe only
4419
+ * @param browserHost host of browser, iframe only
4420
+ * @param childHost host of child app, iframe only
4065
4421
  */
4066
- function createMicroLocation(appName, url) {
4422
+ function createMicroLocation(appName, url, microAppWindow, childStaticLocation, browserHost, childHost) {
4067
4423
  const rawWindow = globalEnv.rawWindow;
4068
4424
  const rawLocation = rawWindow.location;
4069
- // microLocation is the location of child app, it is globally unique
4070
- const microLocation = createURL(url);
4071
- // shadowLocation is the current location information (href, pathname, search, hash)
4072
- const shadowLocation = {
4073
- href: microLocation.href,
4074
- pathname: microLocation.pathname,
4075
- search: microLocation.search,
4076
- hash: microLocation.hash,
4077
- };
4425
+ const isIframe = !!microAppWindow;
4426
+ /**
4427
+ * withLocation is microLocation for with sandbox
4428
+ * it is globally unique for child app
4429
+ */
4430
+ const withLocation = createURL(url);
4431
+ /**
4432
+ * In iframe, jump through raw iframeLocation will cause microAppWindow.location reset
4433
+ * So we get location dynamically
4434
+ */
4435
+ function getTarget() {
4436
+ return isIframe ? microAppWindow.location : withLocation;
4437
+ }
4078
4438
  /**
4079
4439
  * Common handler for href, assign, replace
4080
4440
  * It is mainly used to deal with special scenes about hash
@@ -4082,10 +4442,10 @@ function createMicroLocation(appName, url) {
4082
4442
  * @param methodName pushState/replaceState
4083
4443
  * @returns origin value or formatted value
4084
4444
  */
4085
- const commonHandler = (value, methodName) => {
4086
- const targetLocation = createURL(value, microLocation.href);
4445
+ function commonHandler(value, methodName) {
4446
+ const targetLocation = createURL(value, proxyLocation.href);
4087
4447
  // Even if the origin is the same, developers still have the possibility of want to jump to a new page
4088
- if (targetLocation.origin === microLocation.origin) {
4448
+ if (targetLocation.origin === proxyLocation.origin) {
4089
4449
  const setMicroPathResult = setMicroPathToURL(appName, targetLocation);
4090
4450
  /**
4091
4451
  * change hash with location.href will not trigger the browser reload
@@ -4094,10 +4454,10 @@ function createMicroLocation(appName, url) {
4094
4454
  * 1. if child app only change hash, it should not trigger browser reload
4095
4455
  * 2. if address is same and has hash, it should not add route stack
4096
4456
  */
4097
- if (targetLocation.pathname === shadowLocation.pathname &&
4098
- targetLocation.search === shadowLocation.search) {
4457
+ if (targetLocation.pathname === proxyLocation.pathname &&
4458
+ targetLocation.search === proxyLocation.search) {
4099
4459
  let oldHref = null;
4100
- if (targetLocation.hash !== shadowLocation.hash) {
4460
+ if (targetLocation.hash !== proxyLocation.hash) {
4101
4461
  if (setMicroPathResult.isAttach2Hash)
4102
4462
  oldHref = rawLocation.href;
4103
4463
  nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath);
@@ -4106,35 +4466,22 @@ function createMicroLocation(appName, url) {
4106
4466
  dispatchNativeEvent(appName, false, oldHref);
4107
4467
  }
4108
4468
  else {
4109
- rawReload();
4469
+ reload();
4110
4470
  }
4111
4471
  return void 0;
4112
4472
  /**
4113
4473
  * when baseApp is hash router, address change of child can not reload browser
4114
- * so we imitate behavior of browser (reload)
4474
+ * so we imitate behavior of browser (reload) manually
4115
4475
  */
4116
4476
  }
4117
4477
  else if (setMicroPathResult.isAttach2Hash) {
4118
4478
  nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath);
4119
- rawReload();
4479
+ reload();
4120
4480
  return void 0;
4121
4481
  }
4122
- value = setMicroPathResult.fullPath;
4482
+ return setMicroPathResult.fullPath;
4123
4483
  }
4124
4484
  return value;
4125
- };
4126
- /**
4127
- * create location PropertyDescriptor (href, pathname, search, hash)
4128
- * @param key property name
4129
- * @param setter setter of location property
4130
- */
4131
- function createPropertyDescriptor(getter, setter) {
4132
- return {
4133
- enumerable: true,
4134
- configurable: true,
4135
- get: getter,
4136
- set: setter,
4137
- };
4138
4485
  }
4139
4486
  /**
4140
4487
  * common handler for location.pathname & location.search
@@ -4144,7 +4491,7 @@ function createMicroLocation(appName, url) {
4144
4491
  function handleForPathNameAndSearch(targetPath, key) {
4145
4492
  const targetLocation = createURL(targetPath, url);
4146
4493
  // When the browser url has a hash value, the same pathname/search will not refresh browser
4147
- if (targetLocation[key] === shadowLocation[key] && shadowLocation.hash) {
4494
+ if (targetLocation[key] === proxyLocation[key] && proxyLocation.hash) {
4148
4495
  // The href has not changed, not need to dispatch hashchange event
4149
4496
  dispatchNativeEvent(appName, false);
4150
4497
  }
@@ -4155,58 +4502,96 @@ function createMicroLocation(appName, url) {
4155
4502
  * pathname: /path ==> /path#hash, /path ==> /path?query
4156
4503
  * search: ?query ==> ?query#hash
4157
4504
  */
4158
- nativeHistoryNavigate(appName, targetLocation[key] === shadowLocation[key] ? 'replaceState' : 'pushState', setMicroPathToURL(appName, targetLocation).fullPath);
4159
- rawReload();
4505
+ nativeHistoryNavigate(appName, targetLocation[key] === proxyLocation[key] ? 'replaceState' : 'pushState', setMicroPathToURL(appName, targetLocation).fullPath);
4506
+ reload();
4160
4507
  }
4161
4508
  }
4162
- function rawReload() {
4163
- isEffectiveApp(appName) && rawLocation.reload();
4164
- }
4165
- /**
4166
- * Special processing for four keys: href, pathname, search and hash
4167
- * They take values from shadowLocation, and require special operations when assigning values
4168
- */
4169
- rawDefineProperties(microLocation, {
4170
- href: createPropertyDescriptor(() => shadowLocation.href, (value) => {
4171
- if (isEffectiveApp(appName)) {
4172
- const targetPath = commonHandler(value, 'pushState');
4173
- if (targetPath)
4174
- rawLocation.href = targetPath;
4175
- }
4176
- }),
4177
- pathname: createPropertyDescriptor(() => shadowLocation.pathname, (value) => {
4178
- const targetPath = ('/' + value).replace(/^\/+/, '/') + shadowLocation.search + shadowLocation.hash;
4179
- handleForPathNameAndSearch(targetPath, 'pathname');
4180
- }),
4181
- search: createPropertyDescriptor(() => shadowLocation.search, (value) => {
4182
- const targetPath = shadowLocation.pathname + ('?' + value).replace(/^\?+/, '?') + shadowLocation.hash;
4183
- handleForPathNameAndSearch(targetPath, 'search');
4184
- }),
4185
- hash: createPropertyDescriptor(() => shadowLocation.hash, (value) => {
4186
- const targetPath = shadowLocation.pathname + shadowLocation.search + ('#' + value).replace(/^#+/, '#');
4187
- const targetLocation = createURL(targetPath, url);
4188
- // The same hash will not trigger popStateEvent
4189
- if (targetLocation.hash !== shadowLocation.hash) {
4190
- navigateWithNativeEvent(appName, 'pushState', setMicroPathToURL(appName, targetLocation), false);
4191
- }
4192
- }),
4193
- fullPath: createPropertyDescriptor(() => shadowLocation.pathname + shadowLocation.search + shadowLocation.hash, noop),
4194
- });
4195
4509
  const createLocationMethod = (locationMethodName) => {
4196
4510
  return function (value) {
4197
4511
  if (isEffectiveApp(appName)) {
4198
4512
  const targetPath = commonHandler(value, locationMethodName === 'assign' ? 'pushState' : 'replaceState');
4199
- if (targetPath)
4200
- rawLocation[locationMethodName](targetPath);
4513
+ if (targetPath) {
4514
+ // Same as href, complete targetPath with browser origin in vite env
4515
+ rawLocation[locationMethodName](createURL(targetPath, rawLocation.origin).href);
4516
+ }
4201
4517
  }
4202
4518
  };
4203
4519
  };
4204
- return assign(microLocation, {
4205
- assign: createLocationMethod('assign'),
4206
- replace: createLocationMethod('replace'),
4207
- reload: (forcedReload) => rawLocation.reload(forcedReload),
4208
- shadowLocation,
4520
+ const assign = createLocationMethod('assign');
4521
+ const replace = createLocationMethod('replace');
4522
+ const reload = (forcedReload) => rawLocation.reload(forcedReload);
4523
+ rawDefineProperty(getTarget(), 'fullPath', {
4524
+ enumerable: true,
4525
+ configurable: true,
4526
+ get: () => proxyLocation.pathname + proxyLocation.search + proxyLocation.hash,
4527
+ });
4528
+ /**
4529
+ * location.assign/replace is readonly, cannot be proxy, so we use empty object as proxy target
4530
+ */
4531
+ const proxyLocation = new Proxy({}, {
4532
+ get: (_, key) => {
4533
+ const target = getTarget();
4534
+ if (isIframe) {
4535
+ // host hostname port protocol
4536
+ if (hijackMicroLocationKeys.includes(key)) {
4537
+ return childStaticLocation[key];
4538
+ }
4539
+ if (key === 'href') {
4540
+ // do not use target, because target may be deleted
4541
+ return target[key].replace(browserHost, childHost);
4542
+ }
4543
+ }
4544
+ if (key === 'assign')
4545
+ return assign;
4546
+ if (key === 'replace')
4547
+ return replace;
4548
+ if (key === 'reload')
4549
+ return reload;
4550
+ if (key === 'self')
4551
+ return target;
4552
+ return bindFunctionToRawTarget(Reflect.get(target, key), target, 'LOCATION');
4553
+ },
4554
+ set: (_, key, value) => {
4555
+ if (isEffectiveApp(appName)) {
4556
+ const target = getTarget();
4557
+ if (key === 'href') {
4558
+ const targetPath = commonHandler(value, 'pushState');
4559
+ /**
4560
+ * In vite, targetPath without origin will be completed with child origin
4561
+ * So we use browser origin to complete targetPath to avoid this problem
4562
+ * But, why child app can affect browser jump?
4563
+ * Guess(need check):
4564
+ * 1. vite records the origin when init
4565
+ * 2. listen for browser jump and automatically complete the address
4566
+ */
4567
+ if (targetPath) {
4568
+ rawLocation.href = createURL(targetPath, rawLocation.origin).href;
4569
+ }
4570
+ }
4571
+ else if (key === 'pathname') {
4572
+ const targetPath = ('/' + value).replace(/^\/+/, '/') + proxyLocation.search + proxyLocation.hash;
4573
+ handleForPathNameAndSearch(targetPath, 'pathname');
4574
+ }
4575
+ else if (key === 'search') {
4576
+ const targetPath = proxyLocation.pathname + ('?' + value).replace(/^\?+/, '?') + proxyLocation.hash;
4577
+ handleForPathNameAndSearch(targetPath, 'search');
4578
+ }
4579
+ else if (key === 'hash') {
4580
+ const targetPath = proxyLocation.pathname + proxyLocation.search + ('#' + value).replace(/^#+/, '#');
4581
+ const targetLocation = createURL(targetPath, url);
4582
+ // The same hash will not trigger popStateEvent
4583
+ if (targetLocation.hash !== proxyLocation.hash) {
4584
+ navigateWithNativeEvent(appName, 'pushState', setMicroPathToURL(appName, targetLocation), false);
4585
+ }
4586
+ }
4587
+ else {
4588
+ Reflect.set(target, key, value);
4589
+ }
4590
+ }
4591
+ return true;
4592
+ },
4209
4593
  });
4594
+ return proxyLocation;
4210
4595
  }
4211
4596
  /**
4212
4597
  * create guardLocation by microLocation, used for router guard
@@ -4238,17 +4623,17 @@ function autoTriggerNavigationGuard(appName, microLocation) {
4238
4623
  * @param type auto prevent
4239
4624
  */
4240
4625
  function updateMicroLocation(appName, path, microLocation, type) {
4241
- const newLocation = createURL(path, microLocation.href);
4626
+ var _a;
4242
4627
  // record old values of microLocation to `from`
4243
4628
  const from = createGuardLocation(appName, microLocation);
4244
- for (const key of locationKeys) {
4245
- if (shadowLocationKeys.includes(key)) {
4246
- // reference of shadowLocation
4247
- microLocation.shadowLocation[key] = newLocation[key];
4248
- }
4249
- else {
4250
- // @ts-ignore reference of microLocation
4251
- microLocation[key] = newLocation[key];
4629
+ const newLocation = createURL(path, microLocation.href);
4630
+ if (isIframeSandbox(appName)) {
4631
+ const microAppWindow = appInstanceMap.get(appName).sandBox.microAppWindow;
4632
+ (_a = microAppWindow.rawReplaceState) === null || _a === void 0 ? void 0 : _a.call(microAppWindow.history, getMicroState(appName), '', newLocation.href);
4633
+ }
4634
+ else {
4635
+ for (const key of locationKeys) {
4636
+ microLocation.self[key] = newLocation[key];
4252
4637
  }
4253
4638
  }
4254
4639
  // update latest values of microLocation to `to`
@@ -4426,8 +4811,8 @@ function useMicroEventSource() {
4426
4811
  }
4427
4812
 
4428
4813
  const { createMicroEventSource, clearMicroEventSource } = useMicroEventSource();
4429
- const globalPropertyList = ['window', 'self', 'globalThis'];
4430
- class SandBox {
4814
+ const globalPropertyList$1 = ['window', 'self', 'globalThis'];
4815
+ class WithSandBox {
4431
4816
  constructor(appName, url) {
4432
4817
  /**
4433
4818
  * Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
@@ -4478,18 +4863,21 @@ class SandBox {
4478
4863
  this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute;
4479
4864
  }
4480
4865
  /**
4481
- * 1. Prevent the key deleted during sandBox.stop after rewrite
4482
- * 2. Umd mode will not delete any keys during sandBox.stop
4483
- * 3. It must not be umd mode when call sandbox.start at the first time
4866
+ * Target: Ensure default mode action exactly same to first time when render again
4867
+ * 1. The following globalKey maybe modified when render, reset them when render again in default mode
4868
+ * 2. Umd mode will not delete any keys during sandBox.stop, ignore umd mode
4869
+ * 3. When sandbox.start called for the first time, it must be the default mode
4484
4870
  */
4485
4871
  if (!umdMode) {
4486
4872
  this.initGlobalKeysWhenStart(this.microAppWindow, this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__, disablePatchRequest);
4487
4873
  }
4488
- if (++SandBox.activeCount === 1) {
4874
+ if (++globalEnv.activeSandbox === 1) {
4875
+ patchElementAndDocument();
4876
+ patchHistory();
4877
+ }
4878
+ if (++WithSandBox.activeCount === 1) {
4489
4879
  effectDocumentEvent();
4490
- patchElementPrototypeMethods();
4491
4880
  initEnvOfNestedApp();
4492
- patchHistory();
4493
4881
  }
4494
4882
  fixBabelPolyfill6();
4495
4883
  }
@@ -4498,28 +4886,25 @@ class SandBox {
4498
4886
  * close sandbox and perform some clean up actions
4499
4887
  * @param umdMode is umd mode
4500
4888
  * @param keepRouteState prevent reset route
4501
- * @param clearEventSource clear MicroEventSource when destroy
4889
+ * @param destroy completely destroy, delete cache resources
4502
4890
  * @param clearData clear data from base app
4503
4891
  */
4504
- stop({ umdMode, keepRouteState, clearEventSource, clearData, }) {
4892
+ stop({ umdMode, keepRouteState, destroy, clearData, }) {
4505
4893
  if (this.active) {
4506
- // clear global event, timeout, data listener
4507
- this.releaseGlobalEffect(clearData);
4894
+ this.recordAndReleaseEffect({ clearData, destroy }, !umdMode || destroy);
4508
4895
  if (this.removeHistoryListener) {
4509
4896
  this.clearRouteState(keepRouteState);
4510
4897
  // release listener of popstate
4511
4898
  this.removeHistoryListener();
4512
4899
  }
4513
- if (clearEventSource) {
4514
- clearMicroEventSource(this.microAppWindow.__MICRO_APP_NAME__);
4515
- }
4516
4900
  /**
4517
4901
  * NOTE:
4518
4902
  * 1. injectedKeys and escapeKeys must be placed at the back
4519
4903
  * 2. if key in initial microAppWindow, and then rewrite, this key will be delete from microAppWindow when stop, and lost when restart
4520
4904
  * 3. umd mode will not delete global keys
4521
4905
  */
4522
- if (!umdMode) {
4906
+ if (!umdMode || destroy) {
4907
+ clearMicroEventSource(this.microAppWindow.__MICRO_APP_NAME__);
4523
4908
  this.injectedKeys.forEach((key) => {
4524
4909
  Reflect.deleteProperty(this.microAppWindow, key);
4525
4910
  });
@@ -4529,30 +4914,47 @@ class SandBox {
4529
4914
  });
4530
4915
  this.escapeKeys.clear();
4531
4916
  }
4532
- if (--SandBox.activeCount === 0) {
4533
- releaseEffectDocumentEvent();
4534
- releasePatches();
4917
+ if (--globalEnv.activeSandbox === 0) {
4918
+ releasePatchElementAndDocument();
4535
4919
  releasePatchHistory();
4536
4920
  }
4921
+ if (--WithSandBox.activeCount === 0) {
4922
+ releaseEffectDocumentEvent();
4923
+ }
4537
4924
  this.active = false;
4538
4925
  }
4539
4926
  }
4540
4927
  /**
4541
- * clear global event, timeout, data listener
4928
+ * Record global effect and then release (effect: global event, timeout, data listener)
4542
4929
  * Scenes:
4543
- * 1. unmount of normal/umd app
4930
+ * 1. unmount of default/umd app
4544
4931
  * 2. hidden keep-alive app
4545
4932
  * 3. after init prerender app
4546
- * @param clearData clear data from base app
4933
+ * @param options {
4934
+ * @param clearData clear data from base app
4935
+ * @param isPrerender is prerender app
4936
+ * @param keepAlive is keep-alive app
4937
+ * }
4938
+ * @param preventRecord prevent record effect events
4547
4939
  */
4548
- releaseGlobalEffect(clearData = false) {
4549
- this.effectController.releaseEffect();
4550
- this.microAppWindow.microApp.clearDataListener();
4551
- this.microAppWindow.microApp.clearGlobalDataListener();
4552
- if (clearData) {
4553
- microApp.clearData(this.microAppWindow.__MICRO_APP_NAME__);
4554
- this.microAppWindow.microApp.clearData();
4940
+ recordAndReleaseEffect(options, preventRecord = false) {
4941
+ if (preventRecord) {
4942
+ this.resetEffectSnapshot();
4943
+ }
4944
+ else {
4945
+ this.recordEffectSnapshot();
4555
4946
  }
4947
+ this.releaseGlobalEffect(options);
4948
+ }
4949
+ /**
4950
+ * reset effect snapshot data in default mode or destroy
4951
+ * Scenes:
4952
+ * 1. unmount hidden keep-alive app manually
4953
+ * 2. unmount prerender app manually
4954
+ */
4955
+ resetEffectSnapshot() {
4956
+ this.effectController.reset();
4957
+ resetDataCenterSnapshot(this.microAppWindow.microApp);
4556
4958
  }
4557
4959
  /**
4558
4960
  * record umd snapshot before the first execution of umdHookMount
@@ -4563,7 +4965,7 @@ class SandBox {
4563
4965
  */
4564
4966
  recordEffectSnapshot() {
4565
4967
  // this.microAppWindow.__MICRO_APP_UMD_MODE__ = true
4566
- this.effectController.recordEffect();
4968
+ this.effectController.record();
4567
4969
  recordDataCenterSnapshot(this.microAppWindow.microApp);
4568
4970
  // this.recordUmdInjectedValues = new Map<PropertyKey, unknown>()
4569
4971
  // this.injectedKeys.forEach((key: PropertyKey) => {
@@ -4575,12 +4977,34 @@ class SandBox {
4575
4977
  // this.recordUmdInjectedValues!.forEach((value: unknown, key: PropertyKey) => {
4576
4978
  // Reflect.set(this.proxyWindow, key, value)
4577
4979
  // })
4578
- this.effectController.rebuildEffect();
4980
+ this.effectController.rebuild();
4579
4981
  rebuildDataCenterSnapshot(this.microAppWindow.microApp);
4580
4982
  }
4581
- // set __MICRO_APP_PRE_RENDER__ state
4582
- setPreRenderState(state) {
4583
- this.microAppWindow.__MICRO_APP_PRE_RENDER__ = state;
4983
+ /**
4984
+ * clear global event, timeout, data listener
4985
+ * Scenes:
4986
+ * 1. unmount of default/umd app
4987
+ * 2. hidden keep-alive app
4988
+ * 3. after init prerender app
4989
+ * @param clearData clear data from base app
4990
+ * @param isPrerender is prerender app
4991
+ * @param keepAlive is keep-alive app
4992
+ * @param destroy completely destroy
4993
+ */
4994
+ releaseGlobalEffect({ clearData = false, isPrerender = false, keepAlive = false, destroy = false, }) {
4995
+ var _a, _b, _c;
4996
+ this.effectController.release({
4997
+ umdMode: this.proxyWindow.__MICRO_APP_UMD_MODE__,
4998
+ isPrerender,
4999
+ keepAlive,
5000
+ destroy,
5001
+ });
5002
+ (_a = this.microAppWindow.microApp) === null || _a === void 0 ? void 0 : _a.clearDataListener();
5003
+ (_b = this.microAppWindow.microApp) === null || _b === void 0 ? void 0 : _b.clearGlobalDataListener();
5004
+ if (clearData) {
5005
+ microApp.clearData(this.microAppWindow.__MICRO_APP_NAME__);
5006
+ (_c = this.microAppWindow.microApp) === null || _c === void 0 ? void 0 : _c.clearData();
5007
+ }
4584
5008
  }
4585
5009
  /**
4586
5010
  * get scopeProperties and escapeProperties from plugins & adapter
@@ -4613,7 +5037,6 @@ class SandBox {
4613
5037
  createProxyWindow(appName) {
4614
5038
  const rawWindow = globalEnv.rawWindow;
4615
5039
  const descriptorTargetMap = new Map();
4616
- // window.xxx will trigger proxy
4617
5040
  return new Proxy(this.microAppWindow, {
4618
5041
  get: (target, key) => {
4619
5042
  throttleDeferForSetAppName(appName);
@@ -4621,11 +5044,14 @@ class SandBox {
4621
5044
  (isString(key) && /^__MICRO_APP_/.test(key)) ||
4622
5045
  this.scopeProperties.includes(key))
4623
5046
  return Reflect.get(target, key);
4624
- const rawValue = Reflect.get(rawWindow, key);
4625
- return isFunction(rawValue) ? bindFunctionToRawObject(rawWindow, rawValue) : rawValue;
5047
+ return bindFunctionToRawTarget(Reflect.get(rawWindow, key), rawWindow);
4626
5048
  },
4627
5049
  set: (target, key, value) => {
4628
5050
  if (this.active) {
5051
+ /**
5052
+ * TODO:
5053
+ * 1、location域名相同,子应用内部跳转时的处理
5054
+ */
4629
5055
  if (this.adapter.escapeSetterKeyList.includes(key)) {
4630
5056
  Reflect.set(rawWindow, key, value);
4631
5057
  }
@@ -4646,15 +5072,15 @@ class SandBox {
4646
5072
  this.injectedKeys.add(key);
4647
5073
  }
4648
5074
  else {
5075
+ !Reflect.has(target, key) && this.injectedKeys.add(key);
4649
5076
  Reflect.set(target, key, value);
4650
- this.injectedKeys.add(key);
4651
5077
  }
4652
5078
  if ((this.escapeProperties.includes(key) ||
4653
5079
  (this.adapter.staticEscapeProperties.includes(key) &&
4654
5080
  !Reflect.has(rawWindow, key))) &&
4655
5081
  !this.scopeProperties.includes(key)) {
5082
+ !Reflect.has(rawWindow, key) && this.escapeKeys.add(key);
4656
5083
  Reflect.set(rawWindow, key, value);
4657
- this.escapeKeys.add(key);
4658
5084
  }
4659
5085
  }
4660
5086
  return true;
@@ -4702,6 +5128,13 @@ class SandBox {
4702
5128
  },
4703
5129
  });
4704
5130
  }
5131
+ // set __MICRO_APP_PRE_RENDER__ state
5132
+ setPreRenderState(state) {
5133
+ this.microAppWindow.__MICRO_APP_PRE_RENDER__ = state;
5134
+ }
5135
+ markUmdMode(state) {
5136
+ this.microAppWindow.__MICRO_APP_UMD_MODE__ = state;
5137
+ }
4705
5138
  /**
4706
5139
  * inject global properties to microAppWindow
4707
5140
  * @param microAppWindow micro window
@@ -4714,8 +5147,10 @@ class SandBox {
4714
5147
  microAppWindow.__MICRO_APP_NAME__ = appName;
4715
5148
  microAppWindow.__MICRO_APP_URL__ = url;
4716
5149
  microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
5150
+ microAppWindow.__MICRO_APP_BASE_ROUTE__ = '';
4717
5151
  microAppWindow.__MICRO_APP_WINDOW__ = microAppWindow;
4718
5152
  microAppWindow.__MICRO_APP_PRE_RENDER__ = false;
5153
+ microAppWindow.__MICRO_APP_UMD_MODE__ = false;
4719
5154
  microAppWindow.rawWindow = globalEnv.rawWindow;
4720
5155
  microAppWindow.rawDocument = globalEnv.rawDocument;
4721
5156
  microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
@@ -4760,7 +5195,7 @@ class SandBox {
4760
5195
  }
4761
5196
  rawDefineProperty(microAppWindow, 'top', this.createDescriptorForMicroAppWindow('top', topValue));
4762
5197
  rawDefineProperty(microAppWindow, 'parent', this.createDescriptorForMicroAppWindow('parent', parentValue));
4763
- globalPropertyList.forEach((key) => {
5198
+ globalPropertyList$1.forEach((key) => {
4764
5199
  rawDefineProperty(microAppWindow, key, this.createDescriptorForMicroAppWindow(key, this.proxyWindow));
4765
5200
  });
4766
5201
  }
@@ -4890,23 +5325,39 @@ class SandBox {
4890
5325
  });
4891
5326
  }
4892
5327
  initRouteState(defaultPage) {
4893
- initRouteStateWithURL(this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.location, defaultPage);
5328
+ initRouteStateWithURL(this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.location, defaultPage);
4894
5329
  }
4895
5330
  clearRouteState(keepRouteState) {
4896
- clearRouteStateFromURL(this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.__MICRO_APP_URL__, this.proxyWindow.location, keepRouteState);
5331
+ clearRouteStateFromURL(this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__, this.microAppWindow.location, keepRouteState);
4897
5332
  }
4898
5333
  setRouteInfoForKeepAliveApp() {
4899
- updateBrowserURLWithLocation(this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.location);
5334
+ updateBrowserURLWithLocation(this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.location);
4900
5335
  }
4901
5336
  removeRouteInfoForKeepAliveApp() {
4902
- removeStateAndPathFromBrowser(this.proxyWindow.__MICRO_APP_NAME__);
5337
+ removeStateAndPathFromBrowser(this.microAppWindow.__MICRO_APP_NAME__);
4903
5338
  }
4904
5339
  /**
4905
- * Create new document and Document
5340
+ * Format all html elements when init
5341
+ * @param container micro app container
4906
5342
  */
4907
- createProxyDocument(appName) {
4908
- const rawDocument = globalEnv.rawDocument;
4909
- const rawRootDocument = globalEnv.rawRootDocument;
5343
+ patchStaticElement(container) {
5344
+ patchElementTree(container, this.microAppWindow.__MICRO_APP_NAME__);
5345
+ }
5346
+ /**
5347
+ * action before exec scripts when mount
5348
+ * Actions:
5349
+ * 1. patch static elements from html
5350
+ * @param container micro app container
5351
+ */
5352
+ actionBeforeExecScripts(container) {
5353
+ this.patchStaticElement(container);
5354
+ }
5355
+ /**
5356
+ * Create new document and Document
5357
+ */
5358
+ createProxyDocument(appName) {
5359
+ const rawDocument = globalEnv.rawDocument;
5360
+ const rawRootDocument = globalEnv.rawRootDocument;
4910
5361
  const createElement = function (tagName, options) {
4911
5362
  const element = globalEnv.rawCreateElement.call(rawDocument, tagName, options);
4912
5363
  element.__MICRO_APP_NAME__ = appName;
@@ -4914,67 +5365,1055 @@ class SandBox {
4914
5365
  };
4915
5366
  const proxyDocument = new Proxy(rawDocument, {
4916
5367
  get: (target, key) => {
4917
- throttleDeferForSetAppName(appName);
4918
- throttleDeferForParentNode(proxyDocument);
4919
- if (key === 'createElement')
4920
- return createElement;
4921
- if (key === Symbol.toStringTag)
4922
- return 'ProxyDocument';
4923
- if (key === 'defaultView')
5368
+ throttleDeferForSetAppName(appName);
5369
+ throttleDeferForParentNode(proxyDocument);
5370
+ if (key === 'createElement')
5371
+ return createElement;
5372
+ if (key === Symbol.toStringTag)
5373
+ return 'ProxyDocument';
5374
+ if (key === 'defaultView')
5375
+ return this.proxyWindow;
5376
+ return bindFunctionToRawTarget(Reflect.get(target, key), rawDocument, 'DOCUMENT');
5377
+ },
5378
+ set: (target, key, value) => {
5379
+ /**
5380
+ * 1. Fix TypeError: Illegal invocation when set document.title
5381
+ * 2. If the set method returns false, and the assignment happened in strict-mode code, a TypeError will be thrown.
5382
+ */
5383
+ Reflect.set(target, key, value);
5384
+ return true;
5385
+ }
5386
+ });
5387
+ class MicroDocument {
5388
+ static [Symbol.hasInstance](target) {
5389
+ let proto = target;
5390
+ while (proto = Object.getPrototypeOf(proto)) {
5391
+ if (proto === MicroDocument.prototype) {
5392
+ return true;
5393
+ }
5394
+ }
5395
+ return (target === proxyDocument ||
5396
+ target instanceof rawRootDocument);
5397
+ }
5398
+ }
5399
+ /**
5400
+ * TIP:
5401
+ * 1. child class __proto__, which represents the inherit of the constructor, always points to the parent class
5402
+ * 2. child class prototype.__proto__, which represents the inherit of methods, always points to parent class prototype
5403
+ * e.g.
5404
+ * class B extends A {}
5405
+ * B.__proto__ === A // true
5406
+ * B.prototype.__proto__ === A.prototype // true
5407
+ */
5408
+ Object.setPrototypeOf(MicroDocument, rawRootDocument);
5409
+ // Object.create(rawRootDocument.prototype) will cause MicroDocument and proxyDocument methods not same when exec Document.prototype.xxx = xxx in child app
5410
+ Object.setPrototypeOf(MicroDocument.prototype, new Proxy(rawRootDocument.prototype, {
5411
+ get(target, key) {
5412
+ throttleDeferForSetAppName(appName);
5413
+ return bindFunctionToRawTarget(Reflect.get(target, key), rawDocument, 'DOCUMENT');
5414
+ },
5415
+ set(target, key, value) {
5416
+ Reflect.set(target, key, value);
5417
+ return true;
5418
+ }
5419
+ }));
5420
+ return {
5421
+ proxyDocument,
5422
+ MicroDocument,
5423
+ };
5424
+ }
5425
+ }
5426
+ WithSandBox.activeCount = 0; // number of active sandbox
5427
+
5428
+ function patchIframeRoute(appName, microAppWindow, childFullPath) {
5429
+ const microHistory = microAppWindow.history;
5430
+ microAppWindow.rawReplaceState = microHistory.replaceState;
5431
+ assign(microHistory, createMicroHistory(appName, microAppWindow.location));
5432
+ // exec updateMicroLocation after patch microHistory
5433
+ updateMicroLocation(appName, childFullPath, microAppWindow.location, 'prevent');
5434
+ }
5435
+
5436
+ function patchIframeWindow(appName, microAppWindow) {
5437
+ const rawWindow = globalEnv.rawWindow;
5438
+ escape2RawWindowKeys.forEach((key) => {
5439
+ microAppWindow[key] = bindFunctionToRawTarget(rawWindow[key], rawWindow);
5440
+ });
5441
+ Object.getOwnPropertyNames(microAppWindow)
5442
+ .filter((key) => {
5443
+ escape2RawWindowRegExpKeys.some((reg) => {
5444
+ if (reg.test(key) && key in microAppWindow.parent) {
5445
+ if (isFunction(rawWindow[key])) {
5446
+ microAppWindow[key] = bindFunctionToRawTarget(rawWindow[key], rawWindow);
5447
+ }
5448
+ else {
5449
+ const { configurable, enumerable } = Object.getOwnPropertyDescriptor(microAppWindow, key) || {
5450
+ configurable: true,
5451
+ enumerable: true,
5452
+ };
5453
+ if (configurable) {
5454
+ rawDefineProperty(microAppWindow, key, {
5455
+ configurable,
5456
+ enumerable,
5457
+ get: () => rawWindow[key],
5458
+ set: (value) => { rawWindow[key] = value; },
5459
+ });
5460
+ }
5461
+ }
5462
+ return true;
5463
+ }
5464
+ return false;
5465
+ });
5466
+ return /^on/.test(key) && !scopeIframeWindowOnEvent.includes(key);
5467
+ })
5468
+ .forEach((eventName) => {
5469
+ const { enumerable, writable, set } = Object.getOwnPropertyDescriptor(microAppWindow, eventName) || {
5470
+ enumerable: true,
5471
+ writable: true,
5472
+ };
5473
+ try {
5474
+ /**
5475
+ * 如果设置了iframeWindow上的这些on事件,处理函数会设置到原生window上,但this会绑定到iframeWindow
5476
+ * 获取这些值,则直接从原生window上取
5477
+ * 总结:这些on事件全部都代理到原生window上
5478
+ *
5479
+ * 问题:
5480
+ * 1、如果子应用没有设置,基座设置了on事件,子应用触发事件是会不会执行基座的函数?
5481
+ * 比如 基座定义了 window.onpopstate,子应用执行跳转会不会触发基座的onpopstate函数?
5482
+ *
5483
+ * 2、如果基座已经定义了 window.onpopstate,子应用定义会不会覆盖基座的?
5484
+ * 现在的逻辑看来,是会覆盖的,那么问题1就是 肯定的
5485
+ * TODO: 一些特殊事件onpopstate、onhashchange不代理,放在scopeIframeWindowOnEvent中
5486
+ */
5487
+ rawDefineProperty(microAppWindow, eventName, {
5488
+ enumerable,
5489
+ configurable: true,
5490
+ get: () => rawWindow[eventName],
5491
+ set: (writable !== null && writable !== void 0 ? writable : !!set) ? (value) => { rawWindow[eventName] = isFunction(value) ? value.bind(microAppWindow) : value; }
5492
+ : undefined,
5493
+ });
5494
+ }
5495
+ catch (e) {
5496
+ logWarn(e, appName);
5497
+ }
5498
+ });
5499
+ return windowEffect(microAppWindow);
5500
+ }
5501
+ function windowEffect(microAppWindow) {
5502
+ const { rawWindow, rawAddEventListener, rawRemoveEventListener, } = globalEnv;
5503
+ const eventListenerMap = new Map();
5504
+ const sstWindowListenerMap = new Map();
5505
+ function getEventTarget(type) {
5506
+ return scopeIframeWindowEvent.includes(type) ? microAppWindow : rawWindow;
5507
+ }
5508
+ microAppWindow.addEventListener = function (type, listener, options) {
5509
+ const listenerList = eventListenerMap.get(type);
5510
+ if (listenerList) {
5511
+ listenerList.add(listener);
5512
+ }
5513
+ else {
5514
+ eventListenerMap.set(type, new Set([listener]));
5515
+ }
5516
+ listener && (listener.__MICRO_APP_MARK_OPTIONS__ = options);
5517
+ rawAddEventListener.call(getEventTarget(type), type, listener, options);
5518
+ };
5519
+ microAppWindow.removeEventListener = function (type, listener, options) {
5520
+ const listenerList = eventListenerMap.get(type);
5521
+ if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
5522
+ listenerList.delete(listener);
5523
+ }
5524
+ rawRemoveEventListener.call(getEventTarget(type), type, listener, options);
5525
+ };
5526
+ const reset = () => {
5527
+ sstWindowListenerMap.clear();
5528
+ };
5529
+ /**
5530
+ * NOTE:
5531
+ * 1. about timer(events & properties should record & rebuild at all modes, exclude default mode)
5532
+ * 2. record maybe call twice when unmount prerender, keep-alive app manually with umd mode
5533
+ * 4 modes: default-mode、umd-mode、prerender、keep-alive
5534
+ * Solution:
5535
+ * 1. default-mode(normal): clear events & timers, not record & rebuild anything
5536
+ * 2. umd-mode(normal): not clear timers, record & rebuild events
5537
+ * 3. prerender/keep-alive(default, umd): not clear timers, record & rebuild events
5538
+ *
5539
+ * TODO: 现在的 清除、记录和恢复操作分散的太零散,sandbox、create_app中都有分散,将代码再优化一下,集中处理
5540
+ */
5541
+ const record = () => {
5542
+ // record window event
5543
+ eventListenerMap.forEach((listenerList, type) => {
5544
+ if (listenerList.size) {
5545
+ sstWindowListenerMap.set(type, new Set(listenerList));
5546
+ }
5547
+ });
5548
+ };
5549
+ // rebuild event and timer before remount app
5550
+ const rebuild = () => {
5551
+ // rebuild window event
5552
+ sstWindowListenerMap.forEach((listenerList, type) => {
5553
+ for (const listener of listenerList) {
5554
+ microAppWindow.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
5555
+ }
5556
+ });
5557
+ reset();
5558
+ };
5559
+ const release = () => {
5560
+ // Clear window binding events
5561
+ if (eventListenerMap.size) {
5562
+ eventListenerMap.forEach((listenerList, type) => {
5563
+ for (const listener of listenerList) {
5564
+ rawRemoveEventListener.call(getEventTarget(type), type, listener);
5565
+ }
5566
+ });
5567
+ eventListenerMap.clear();
5568
+ }
5569
+ };
5570
+ return {
5571
+ reset,
5572
+ record,
5573
+ rebuild,
5574
+ release,
5575
+ };
5576
+ }
5577
+
5578
+ /**
5579
+ * TODO:
5580
+ * 1、shadowDOM
5581
+ * 2、重构
5582
+ */
5583
+ function patchIframeDocument(appName, microAppWindow, proxyLocation) {
5584
+ patchDocumentPrototype(appName, microAppWindow);
5585
+ patchDocumentProperties(appName, microAppWindow, proxyLocation);
5586
+ return documentEffect(appName, microAppWindow);
5587
+ }
5588
+ function patchDocumentPrototype(appName, microAppWindow) {
5589
+ const rawDocument = globalEnv.rawDocument;
5590
+ const microRootDocument = microAppWindow.Document;
5591
+ const microDocument = microAppWindow.document;
5592
+ microRootDocument.prototype.createElement = function createElement(tagName, options) {
5593
+ const element = globalEnv.rawCreateElement.call(this, tagName, options);
5594
+ return updateElementInfo(element, appName);
5595
+ };
5596
+ microRootDocument.prototype.createTextNode = function createTextNode(data) {
5597
+ const element = globalEnv.rawCreateTextNode.call(this, data);
5598
+ return updateElementInfo(element, appName);
5599
+ };
5600
+ function getDefaultRawTarget(target) {
5601
+ return microDocument !== target ? target : rawDocument;
5602
+ }
5603
+ // query element👇
5604
+ function querySelector(selectors) {
5605
+ var _a, _b;
5606
+ if (isUniqueElement(selectors) ||
5607
+ microDocument !== this) {
5608
+ const _this = getDefaultRawTarget(this);
5609
+ return globalEnv.rawQuerySelector.call(_this, selectors);
5610
+ }
5611
+ return (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.querySelector(selectors)) !== null && _b !== void 0 ? _b : null;
5612
+ }
5613
+ function querySelectorAll(selectors) {
5614
+ var _a, _b;
5615
+ if (isUniqueElement(selectors) ||
5616
+ microDocument !== this) {
5617
+ const _this = getDefaultRawTarget(this);
5618
+ return globalEnv.rawQuerySelectorAll.call(_this, selectors);
5619
+ }
5620
+ return (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.querySelectorAll(selectors)) !== null && _b !== void 0 ? _b : [];
5621
+ }
5622
+ microRootDocument.prototype.querySelector = querySelector;
5623
+ microRootDocument.prototype.querySelectorAll = querySelectorAll;
5624
+ microRootDocument.prototype.getElementById = function getElementById(key) {
5625
+ const _this = getDefaultRawTarget(this);
5626
+ if (isInvalidQuerySelectorKey(key)) {
5627
+ return globalEnv.rawGetElementById.call(_this, key);
5628
+ }
5629
+ try {
5630
+ return querySelector.call(this, `#${key}`);
5631
+ }
5632
+ catch (_a) {
5633
+ return globalEnv.rawGetElementById.call(_this, key);
5634
+ }
5635
+ };
5636
+ microRootDocument.prototype.getElementsByClassName = function getElementsByClassName(key) {
5637
+ const _this = getDefaultRawTarget(this);
5638
+ if (isInvalidQuerySelectorKey(key)) {
5639
+ return globalEnv.rawGetElementsByClassName.call(_this, key);
5640
+ }
5641
+ try {
5642
+ return querySelectorAll.call(this, `.${key}`);
5643
+ }
5644
+ catch (_a) {
5645
+ return globalEnv.rawGetElementsByClassName.call(_this, key);
5646
+ }
5647
+ };
5648
+ microRootDocument.prototype.getElementsByTagName = function getElementsByTagName(key) {
5649
+ var _a;
5650
+ const _this = getDefaultRawTarget(this);
5651
+ if (isUniqueElement(key) ||
5652
+ isInvalidQuerySelectorKey(key) ||
5653
+ (!((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.inline) && /^script$/i.test(key))) {
5654
+ return globalEnv.rawGetElementsByTagName.call(_this, key);
5655
+ }
5656
+ try {
5657
+ return querySelectorAll.call(this, key);
5658
+ }
5659
+ catch (_b) {
5660
+ return globalEnv.rawGetElementsByTagName.call(_this, key);
5661
+ }
5662
+ };
5663
+ microRootDocument.prototype.getElementsByName = function getElementsByName(key) {
5664
+ const _this = getDefaultRawTarget(this);
5665
+ if (isInvalidQuerySelectorKey(key)) {
5666
+ return globalEnv.rawGetElementsByName.call(_this, key);
5667
+ }
5668
+ try {
5669
+ return querySelectorAll.call(this, `[name=${key}]`);
5670
+ }
5671
+ catch (_a) {
5672
+ return globalEnv.rawGetElementsByName.call(_this, key);
5673
+ }
5674
+ };
5675
+ }
5676
+ function patchDocumentProperties(appName, microAppWindow, proxyLocation) {
5677
+ const rawDocument = globalEnv.rawDocument;
5678
+ const microRootDocument = microAppWindow.Document;
5679
+ const microDocument = microAppWindow.document;
5680
+ const getCommonDescriptor = (key, getter) => {
5681
+ const { enumerable } = Object.getOwnPropertyDescriptor(microRootDocument.prototype, key) || {
5682
+ enumerable: true,
5683
+ writable: true,
5684
+ };
5685
+ return {
5686
+ configurable: true,
5687
+ enumerable,
5688
+ get: getter,
5689
+ };
5690
+ };
5691
+ const createDescriptors = () => {
5692
+ const result = {};
5693
+ const descList = [
5694
+ ['documentURI', () => proxyLocation.href],
5695
+ ['URL', () => proxyLocation.href],
5696
+ ['documentElement', () => rawDocument.documentElement],
5697
+ ['scrollingElement', () => rawDocument.scrollingElement],
5698
+ ['forms', () => microRootDocument.prototype.querySelectorAll.call(microDocument, 'form')],
5699
+ ['images', () => microRootDocument.prototype.querySelectorAll.call(microDocument, 'img')],
5700
+ ['links', () => microRootDocument.prototype.querySelectorAll.call(microDocument, 'a')],
5701
+ ];
5702
+ descList.forEach((desc) => {
5703
+ result[desc[0]] = getCommonDescriptor(desc[0], desc[1]);
5704
+ });
5705
+ // TODO: shadowDOM
5706
+ proxy2RawDocOrShadowKeys.forEach((key) => {
5707
+ result[key] = getCommonDescriptor(key, () => rawDocument[key]);
5708
+ });
5709
+ // TODO: shadowDOM
5710
+ proxy2RawDocOrShadowMethods.forEach((key) => {
5711
+ result[key] = getCommonDescriptor(key, () => bindFunctionToRawTarget(rawDocument[key], rawDocument, 'DOCUMENT'));
5712
+ });
5713
+ proxy2RawDocumentKeys.forEach((key) => {
5714
+ result[key] = getCommonDescriptor(key, () => rawDocument[key]);
5715
+ });
5716
+ proxy2RawDocumentMethods.forEach((key) => {
5717
+ result[key] = getCommonDescriptor(key, () => bindFunctionToRawTarget(rawDocument[key], rawDocument, 'DOCUMENT'));
5718
+ });
5719
+ return result;
5720
+ };
5721
+ rawDefineProperties(microRootDocument.prototype, createDescriptors());
5722
+ // head, body, html, title
5723
+ uniqueDocumentElement.forEach((tagName) => {
5724
+ rawDefineProperty(microDocument, tagName, {
5725
+ enumerable: true,
5726
+ configurable: true,
5727
+ get: () => rawDocument[tagName],
5728
+ set: undefined,
5729
+ });
5730
+ });
5731
+ }
5732
+ function documentEffect(appName, microAppWindow) {
5733
+ const documentEventListenerMap = new Map();
5734
+ const sstDocumentListenerMap = new Map();
5735
+ let onClickHandler = null;
5736
+ let sstOnClickHandler = null;
5737
+ const microRootDocument = microAppWindow.Document;
5738
+ const microDocument = microAppWindow.document;
5739
+ const { rawDocument, rawAddEventListener, rawRemoveEventListener, } = globalEnv;
5740
+ function getEventTarget(type, bindTarget) {
5741
+ return scopeIframeDocumentEvent.includes(type) ? bindTarget : rawDocument;
5742
+ }
5743
+ microRootDocument.prototype.addEventListener = function (type, listener, options) {
5744
+ const handler = isFunction(listener) ? (listener.__MICRO_APP_BOUND_FUNCTION__ = listener.__MICRO_APP_BOUND_FUNCTION__ || listener.bind(this)) : listener;
5745
+ const appListenersMap = documentEventListenerMap.get(appName);
5746
+ if (appListenersMap) {
5747
+ const appListenerList = appListenersMap.get(type);
5748
+ if (appListenerList) {
5749
+ appListenerList.add(listener);
5750
+ }
5751
+ else {
5752
+ appListenersMap.set(type, new Set([listener]));
5753
+ }
5754
+ }
5755
+ else {
5756
+ documentEventListenerMap.set(appName, new Map([[type, new Set([listener])]]));
5757
+ }
5758
+ listener && (listener.__MICRO_APP_MARK_OPTIONS__ = options);
5759
+ rawAddEventListener.call(getEventTarget(type, this), type, handler, options);
5760
+ };
5761
+ microRootDocument.prototype.removeEventListener = function (type, listener, options) {
5762
+ const appListenersMap = documentEventListenerMap.get(appName);
5763
+ if (appListenersMap) {
5764
+ const appListenerList = appListenersMap.get(type);
5765
+ if ((appListenerList === null || appListenerList === void 0 ? void 0 : appListenerList.size) && appListenerList.has(listener)) {
5766
+ appListenerList.delete(listener);
5767
+ }
5768
+ }
5769
+ const handler = (listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_BOUND_FUNCTION__) || listener;
5770
+ rawRemoveEventListener.call(getEventTarget(type, this), type, handler, options);
5771
+ };
5772
+ // 重新定义microRootDocument.prototype 上的on开头方法
5773
+ function createSetterHandler(eventName) {
5774
+ if (eventName === 'onclick') {
5775
+ return (value) => {
5776
+ if (isFunction(onClickHandler)) {
5777
+ rawRemoveEventListener.call(rawDocument, 'click', onClickHandler, false);
5778
+ }
5779
+ if (isFunction(value)) {
5780
+ onClickHandler = value.bind(microDocument);
5781
+ rawAddEventListener.call(rawDocument, 'click', onClickHandler, false);
5782
+ }
5783
+ else {
5784
+ onClickHandler = value;
5785
+ }
5786
+ };
5787
+ }
5788
+ return (value) => { rawDocument[eventName] = isFunction(value) ? value.bind(microDocument) : value; };
5789
+ }
5790
+ /**
5791
+ * TODO:
5792
+ * 1、直接代理到原生document是否正确
5793
+ * 2、shadowDOM
5794
+ */
5795
+ Object.getOwnPropertyNames(microRootDocument.prototype)
5796
+ .filter((key) => /^on/.test(key) && !scopeIframeDocumentOnEvent.includes(key))
5797
+ .forEach((eventName) => {
5798
+ const { enumerable, writable, set } = Object.getOwnPropertyDescriptor(microRootDocument.prototype, eventName) || {
5799
+ enumerable: true,
5800
+ writable: true,
5801
+ };
5802
+ try {
5803
+ rawDefineProperty(microRootDocument.prototype, eventName, {
5804
+ enumerable,
5805
+ configurable: true,
5806
+ get: () => {
5807
+ if (eventName === 'onclick')
5808
+ return onClickHandler;
5809
+ return rawDocument[eventName];
5810
+ },
5811
+ set: (writable !== null && writable !== void 0 ? writable : !!set) ? createSetterHandler(eventName) : undefined,
5812
+ });
5813
+ }
5814
+ catch (e) {
5815
+ logWarn(e, appName);
5816
+ }
5817
+ });
5818
+ const reset = () => {
5819
+ sstDocumentListenerMap.clear();
5820
+ sstOnClickHandler = null;
5821
+ };
5822
+ /**
5823
+ * record event
5824
+ * NOTE:
5825
+ * 1.record maybe call twice when unmount prerender, keep-alive app manually with umd mode
5826
+ * Scenes:
5827
+ * 1. exec umdMountHook in umd mode
5828
+ * 2. hidden keep-alive app
5829
+ * 3. after init prerender app
5830
+ */
5831
+ const record = () => {
5832
+ // record onclick handler
5833
+ sstOnClickHandler = sstOnClickHandler || onClickHandler;
5834
+ // record document event
5835
+ const documentAppListenersMap = documentEventListenerMap.get(appName);
5836
+ if (documentAppListenersMap) {
5837
+ documentAppListenersMap.forEach((listenerList, type) => {
5838
+ if (listenerList.size) {
5839
+ sstDocumentListenerMap.set(type, new Set(listenerList));
5840
+ }
5841
+ });
5842
+ }
5843
+ };
5844
+ // rebuild event and timer before remount app
5845
+ const rebuild = () => {
5846
+ // rebuild onclick event
5847
+ if (sstOnClickHandler)
5848
+ microDocument.onclick = sstOnClickHandler;
5849
+ sstDocumentListenerMap.forEach((listenerList, type) => {
5850
+ for (const listener of listenerList) {
5851
+ microDocument.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
5852
+ }
5853
+ });
5854
+ reset();
5855
+ };
5856
+ const release = () => {
5857
+ // Clear the function bound by micro application through document.onclick
5858
+ if (isFunction(onClickHandler)) {
5859
+ rawRemoveEventListener.call(rawDocument, 'click', onClickHandler);
5860
+ onClickHandler = null;
5861
+ }
5862
+ // Clear document binding event
5863
+ const documentAppListenersMap = documentEventListenerMap.get(appName);
5864
+ if (documentAppListenersMap) {
5865
+ documentAppListenersMap.forEach((listenerList, type) => {
5866
+ for (const listener of listenerList) {
5867
+ rawRemoveEventListener.call(getEventTarget(type, microDocument), type, (listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_BOUND_FUNCTION__) || listener);
5868
+ }
5869
+ });
5870
+ documentAppListenersMap.clear();
5871
+ }
5872
+ };
5873
+ return {
5874
+ reset,
5875
+ record,
5876
+ rebuild,
5877
+ release,
5878
+ };
5879
+ }
5880
+
5881
+ function patchIframeElement(appName, url, microAppWindow, iframeSandbox) {
5882
+ patchIframeNode(appName, microAppWindow, iframeSandbox);
5883
+ patchIframeAttribute(appName, url, microAppWindow);
5884
+ }
5885
+ function patchIframeNode(appName, microAppWindow, iframeSandbox) {
5886
+ const microDocument = microAppWindow.document;
5887
+ const rawDocument = globalEnv.rawDocument;
5888
+ const microRootNode = microAppWindow.Node;
5889
+ const microRootElement = microAppWindow.Element;
5890
+ // const rawMicroGetRootNode = microRootNode.prototype.getRootNode
5891
+ const rawMicroAppendChild = microRootNode.prototype.appendChild;
5892
+ const rawMicroInsertBefore = microRootNode.prototype.insertBefore;
5893
+ const rawMicroReplaceChild = microRootNode.prototype.replaceChild;
5894
+ const rawMicroCloneNode = microRootNode.prototype.cloneNode;
5895
+ const rawInnerHTMLDesc = Object.getOwnPropertyDescriptor(microRootElement.prototype, 'innerHTML');
5896
+ const rawParentNodeLDesc = Object.getOwnPropertyDescriptor(microRootNode.prototype, 'parentNode');
5897
+ const isPureNode = (target) => {
5898
+ return (isScriptElement(target) || isBaseElement(target)) && target.__PURE_ELEMENT__;
5899
+ };
5900
+ const getRawTarget = (target) => {
5901
+ if (target === iframeSandbox.microHead) {
5902
+ return rawDocument.head;
5903
+ }
5904
+ else if (target === iframeSandbox.microBody) {
5905
+ return rawDocument.body;
5906
+ }
5907
+ return target;
5908
+ };
5909
+ microRootNode.prototype.getRootNode = function getRootNode() {
5910
+ return microDocument;
5911
+ // TODO: 什么情况下返回原生document?
5912
+ // const rootNode = rawMicroGetRootNode.call(this, options)
5913
+ // if (rootNode === appInstanceMap.get(appName)?.container) return microDocument
5914
+ // return rootNode
5915
+ };
5916
+ microRootNode.prototype.appendChild = function appendChild(node) {
5917
+ updateElementInfo(node, appName);
5918
+ // TODO:只有script才可以这样拦截,link、style不应该拦截
5919
+ if (isPureNode(node)) {
5920
+ return rawMicroAppendChild.call(this, node);
5921
+ }
5922
+ const _this = getRawTarget(this);
5923
+ if (_this !== this) {
5924
+ return _this.appendChild(node);
5925
+ }
5926
+ return rawMicroAppendChild.call(_this, node);
5927
+ };
5928
+ // TODO: 更多场景适配
5929
+ microRootNode.prototype.insertBefore = function insertBefore(node, child) {
5930
+ updateElementInfo(node, appName);
5931
+ if (isPureNode(node)) {
5932
+ return rawMicroInsertBefore.call(this, node, child);
5933
+ }
5934
+ const _this = getRawTarget(this);
5935
+ if (_this !== this) {
5936
+ if (child && !_this.contains(child)) {
5937
+ return _this.appendChild(node);
5938
+ }
5939
+ return _this.insertBefore(node, child);
5940
+ }
5941
+ return rawMicroInsertBefore.call(_this, node, child);
5942
+ };
5943
+ // TODO: 更多场景适配
5944
+ microRootNode.prototype.replaceChild = function replaceChild(node, child) {
5945
+ updateElementInfo(node, appName);
5946
+ if (isPureNode(node)) {
5947
+ return rawMicroReplaceChild.call(this, node, child);
5948
+ }
5949
+ const _this = getRawTarget(this);
5950
+ if (_this !== this) {
5951
+ if (child && !_this.contains(child)) {
5952
+ _this.appendChild(node);
5953
+ return child;
5954
+ }
5955
+ return _this.replaceChild(node, child);
5956
+ }
5957
+ return rawMicroReplaceChild.call(_this, node, child);
5958
+ };
5959
+ // patch cloneNode
5960
+ microRootNode.prototype.cloneNode = function cloneNode(deep) {
5961
+ const clonedNode = rawMicroCloneNode.call(this, deep);
5962
+ return updateElementInfo(clonedNode, appName);
5963
+ };
5964
+ rawDefineProperty(microRootElement.prototype, 'innerHTML', {
5965
+ configurable: true,
5966
+ enumerable: true,
5967
+ get() {
5968
+ return rawInnerHTMLDesc.get.call(this);
5969
+ },
5970
+ set(code) {
5971
+ rawInnerHTMLDesc.set.call(this, code);
5972
+ Array.from(this.children).forEach((child) => {
5973
+ if (isElement(child)) {
5974
+ updateElementInfo(child, appName);
5975
+ }
5976
+ });
5977
+ }
5978
+ });
5979
+ // patch parentNode
5980
+ rawDefineProperty(microRootNode.prototype, 'parentNode', {
5981
+ configurable: true,
5982
+ enumerable: true,
5983
+ get() {
5984
+ var _a, _b, _c;
5985
+ // set html.parentNode to microDocument
5986
+ throttleDeferForParentNode(microDocument);
5987
+ const result = rawParentNodeLDesc.get.call(this);
5988
+ /**
5989
+ * If parentNode is <micro-app-body>, return rawDocument.body
5990
+ * Scenes:
5991
+ * 1. element-ui@2/lib/utils/vue-popper.js
5992
+ * if (this.popperElm.parentNode === document.body) ...
5993
+ * WARNING:
5994
+ * Will it cause other problems ?
5995
+ * e.g. target.parentNode.remove(target)
5996
+ */
5997
+ if (isMicroAppBody(result) && ((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container)) {
5998
+ return ((_c = (_b = microApp.options).getRootElementParentNode) === null || _c === void 0 ? void 0 : _c.call(_b, this, appName)) || rawDocument.body;
5999
+ }
6000
+ return result;
6001
+ },
6002
+ set: undefined,
6003
+ });
6004
+ // Adapt to new image(...) scene
6005
+ const ImageProxy = new Proxy(microAppWindow.Image, {
6006
+ construct(Target, args) {
6007
+ const elementImage = new Target(...args);
6008
+ updateElementInfo(elementImage, appName);
6009
+ return elementImage;
6010
+ },
6011
+ });
6012
+ rawDefineProperty(microAppWindow, 'Image', {
6013
+ configurable: true,
6014
+ writable: true,
6015
+ value: ImageProxy,
6016
+ });
6017
+ /**
6018
+ * TODO:
6019
+ * 1、append prepend
6020
+ * 2、cloneNode -- 完成
6021
+ * 3、innerHTML
6022
+ * 4、querySelector、querySelectorAll (head, body)
6023
+ * 5、Image -- 完成
6024
+ * 都是Element原型链上的方法
6025
+ */
6026
+ }
6027
+ function patchIframeAttribute(appName, url, microAppWindow) {
6028
+ const microRootElement = microAppWindow.Element;
6029
+ const rawMicroSetAttribute = microRootElement.prototype.setAttribute;
6030
+ microRootElement.prototype.setAttribute = function setAttribute(key, value) {
6031
+ if (((key === 'src' || key === 'srcset') && /^(img|script)$/i.test(this.tagName)) ||
6032
+ (key === 'href' && /^link$/i.test(this.tagName))) {
6033
+ value = CompletionPath(value, url);
6034
+ }
6035
+ rawMicroSetAttribute.call(this, key, value);
6036
+ };
6037
+ const protoAttrList = [
6038
+ [microAppWindow.HTMLImageElement.prototype, 'src'],
6039
+ [microAppWindow.HTMLScriptElement.prototype, 'src'],
6040
+ [microAppWindow.HTMLLinkElement.prototype, 'href'],
6041
+ ];
6042
+ protoAttrList.forEach(([target, attr]) => {
6043
+ const { enumerable, configurable, get, set } = Object.getOwnPropertyDescriptor(target, attr) || {
6044
+ enumerable: true,
6045
+ configurable: true,
6046
+ };
6047
+ rawDefineProperty(target, attr, {
6048
+ enumerable,
6049
+ configurable,
6050
+ get: function () {
6051
+ return get === null || get === void 0 ? void 0 : get.call(this);
6052
+ },
6053
+ set: function (value) {
6054
+ set === null || set === void 0 ? void 0 : set.call(this, CompletionPath(value, url));
6055
+ },
6056
+ });
6057
+ });
6058
+ }
6059
+
6060
+ class IframeSandbox {
6061
+ constructor(appName, url) {
6062
+ this.active = false;
6063
+ // Properties that can be escape to rawWindow
6064
+ this.escapeProperties = [];
6065
+ // Properties escape to rawWindow, cleared when unmount
6066
+ this.escapeKeys = new Set();
6067
+ // TODO: 初始化和每次跳转时都要更新base的href
6068
+ this.updateIframeBase = () => {
6069
+ var _a;
6070
+ (_a = this.baseElement) === null || _a === void 0 ? void 0 : _a.setAttribute('href', this.proxyLocation.protocol + '//' + this.proxyLocation.host + this.proxyLocation.pathname);
6071
+ };
6072
+ const rawLocation = globalEnv.rawWindow.location;
6073
+ const browserHost = rawLocation.protocol + '//' + rawLocation.host;
6074
+ const childStaticLocation = new URL(url);
6075
+ const childHost = childStaticLocation.protocol + '//' + childStaticLocation.host;
6076
+ const childFullPath = childStaticLocation.pathname + childStaticLocation.search + childStaticLocation.hash;
6077
+ this.deleteIframeElement = this.createIframeElement(appName, browserHost);
6078
+ this.microAppWindow = this.iframe.contentWindow;
6079
+ this.patchIframe(this.microAppWindow, (resolve) => {
6080
+ // TODO: 优化代码
6081
+ // exec before initStaticGlobalKeys
6082
+ this.createProxyLocation(appName, url, this.microAppWindow, childStaticLocation, browserHost, childHost);
6083
+ this.createProxyWindow(this.microAppWindow);
6084
+ this.initStaticGlobalKeys(appName, url);
6085
+ // get escapeProperties from plugins
6086
+ this.getSpecialProperties(appName);
6087
+ this.createIframeTemplate(this.microAppWindow);
6088
+ patchIframeRoute(appName, this.microAppWindow, childFullPath);
6089
+ this.windowEffect = patchIframeWindow(appName, this.microAppWindow);
6090
+ this.documentEffect = patchIframeDocument(appName, this.microAppWindow, this.proxyLocation);
6091
+ patchIframeElement(appName, url, this.microAppWindow, this);
6092
+ resolve();
6093
+ });
6094
+ }
6095
+ /**
6096
+ * create iframe for sandbox
6097
+ * @param appName app name
6098
+ * @param browserHost browser origin
6099
+ * @returns release callback
6100
+ */
6101
+ createIframeElement(appName, browserHost) {
6102
+ this.iframe = pureCreateElement('iframe');
6103
+ const iframeAttrs = {
6104
+ src: browserHost,
6105
+ style: 'display: none',
6106
+ id: appName,
6107
+ };
6108
+ Object.keys(iframeAttrs).forEach((key) => this.iframe.setAttribute(key, iframeAttrs[key]));
6109
+ // effect action during construct
6110
+ globalEnv.rawDocument.body.appendChild(this.iframe);
6111
+ /**
6112
+ * If dom operated async when unmount, premature deletion of iframe will cause unexpected problems
6113
+ * e.g.
6114
+ * 1. antd: notification.destroy()
6115
+ * WARNING:
6116
+ * If async operation time is too long, defer cannot avoid the problem
6117
+ * TODO: more test
6118
+ */
6119
+ return () => defer(() => {
6120
+ var _a, _b;
6121
+ // default mode or destroy, iframe will be deleted when unmount
6122
+ (_b = (_a = this.iframe) === null || _a === void 0 ? void 0 : _a.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(this.iframe);
6123
+ this.iframe = null;
6124
+ });
6125
+ }
6126
+ start({ baseroute, useMemoryRouter, defaultPage, disablePatchRequest, }) {
6127
+ if (!this.active) {
6128
+ this.active = true;
6129
+ // TODO: 虚拟路由升级
6130
+ if (useMemoryRouter) {
6131
+ this.initRouteState(defaultPage);
6132
+ // unique listener of popstate event for sub app
6133
+ this.removeHistoryListener = addHistoryListener(this.microAppWindow.__MICRO_APP_NAME__);
6134
+ }
6135
+ else {
6136
+ this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute;
6137
+ }
6138
+ /**
6139
+ * create base element to iframe
6140
+ * WARNING: This will also affect a, image, link and script
6141
+ */
6142
+ if (!disablePatchRequest) {
6143
+ this.createIframeBase();
6144
+ }
6145
+ if (++globalEnv.activeSandbox === 1) {
6146
+ patchElementAndDocument();
6147
+ patchHistory();
6148
+ }
6149
+ if (++IframeSandbox.activeCount === 1) ;
6150
+ }
6151
+ }
6152
+ stop({ umdMode, keepRouteState, destroy, clearData, }) {
6153
+ if (this.active) {
6154
+ this.recordAndReleaseEffect({ clearData }, !umdMode || destroy);
6155
+ if (this.removeHistoryListener) {
6156
+ this.clearRouteState(keepRouteState);
6157
+ // release listener of popstate
6158
+ this.removeHistoryListener();
6159
+ }
6160
+ if (!umdMode || destroy) {
6161
+ this.deleteIframeElement();
6162
+ this.escapeKeys.forEach((key) => {
6163
+ Reflect.deleteProperty(globalEnv.rawWindow, key);
6164
+ });
6165
+ this.escapeKeys.clear();
6166
+ }
6167
+ if (--globalEnv.activeSandbox === 0) {
6168
+ releasePatchElementAndDocument();
6169
+ releasePatchHistory();
6170
+ }
6171
+ if (--IframeSandbox.activeCount === 0) ;
6172
+ this.active = false;
6173
+ }
6174
+ }
6175
+ /**
6176
+ * Record global effect and then release (effect: global event, timeout, data listener)
6177
+ * Scenes:
6178
+ * 1. unmount of default/umd app
6179
+ * 2. hidden keep-alive app
6180
+ * 3. after init prerender app
6181
+ * @param options {
6182
+ * @param clearData clear data from base app
6183
+ * @param isPrerender is prerender app
6184
+ * @param keepAlive is keep-alive app
6185
+ * }
6186
+ * @param preventRecord prevent record effect events (default or destroy)
6187
+ */
6188
+ recordAndReleaseEffect(options, preventRecord = false) {
6189
+ if (preventRecord) {
6190
+ this.resetEffectSnapshot();
6191
+ }
6192
+ else {
6193
+ this.recordEffectSnapshot();
6194
+ }
6195
+ this.releaseGlobalEffect(options);
6196
+ }
6197
+ /**
6198
+ * reset effect snapshot data in default mode or destroy
6199
+ * Scenes:
6200
+ * 1. unmount hidden keep-alive app manually
6201
+ * 2. unmount prerender app manually
6202
+ */
6203
+ resetEffectSnapshot() {
6204
+ this.windowEffect.reset();
6205
+ this.documentEffect.reset();
6206
+ resetDataCenterSnapshot(this.microAppWindow.microApp);
6207
+ }
6208
+ /**
6209
+ * record umd snapshot before the first execution of umdHookMount
6210
+ * Scenes:
6211
+ * 1. exec umdMountHook in umd mode
6212
+ * 2. hidden keep-alive app
6213
+ * 3. after init prerender app
6214
+ */
6215
+ recordEffectSnapshot() {
6216
+ this.windowEffect.record();
6217
+ this.documentEffect.record();
6218
+ recordDataCenterSnapshot(this.microAppWindow.microApp);
6219
+ }
6220
+ // rebuild umd snapshot before remount umd app
6221
+ rebuildEffectSnapshot() {
6222
+ this.windowEffect.rebuild();
6223
+ this.documentEffect.rebuild();
6224
+ rebuildDataCenterSnapshot(this.microAppWindow.microApp);
6225
+ }
6226
+ /**
6227
+ * clear global event, timeout, data listener
6228
+ * Scenes:
6229
+ * 1. unmount of normal/umd app
6230
+ * 2. hidden keep-alive app
6231
+ * 3. after init prerender app
6232
+ * @param clearData clear data from base app
6233
+ * @param isPrerender is prerender app
6234
+ * @param keepAlive is keep-alive app
6235
+ */
6236
+ releaseGlobalEffect({ clearData = false }) {
6237
+ var _a, _b, _c;
6238
+ this.windowEffect.release();
6239
+ this.documentEffect.release();
6240
+ (_a = this.microAppWindow.microApp) === null || _a === void 0 ? void 0 : _a.clearDataListener();
6241
+ (_b = this.microAppWindow.microApp) === null || _b === void 0 ? void 0 : _b.clearGlobalDataListener();
6242
+ if (clearData) {
6243
+ microApp.clearData(this.microAppWindow.__MICRO_APP_NAME__);
6244
+ (_c = this.microAppWindow.microApp) === null || _c === void 0 ? void 0 : _c.clearData();
6245
+ }
6246
+ }
6247
+ // set __MICRO_APP_PRE_RENDER__ state
6248
+ setPreRenderState(state) {
6249
+ this.microAppWindow.__MICRO_APP_PRE_RENDER__ = state;
6250
+ }
6251
+ markUmdMode(state) {
6252
+ this.microAppWindow.__MICRO_APP_UMD_MODE__ = state;
6253
+ }
6254
+ initStaticGlobalKeys(appName, url) {
6255
+ this.microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
6256
+ this.microAppWindow.__MICRO_APP_NAME__ = appName;
6257
+ this.microAppWindow.__MICRO_APP_URL__ = url;
6258
+ this.microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
6259
+ this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = '';
6260
+ this.microAppWindow.__MICRO_APP_WINDOW__ = this.microAppWindow;
6261
+ this.microAppWindow.__MICRO_APP_PRE_RENDER__ = false;
6262
+ this.microAppWindow.__MICRO_APP_UMD_MODE__ = false;
6263
+ this.microAppWindow.__MICRO_APP_SANDBOX__ = this;
6264
+ this.microAppWindow.__MICRO_APP_PROXY_WINDOW__ = this.proxyWindow;
6265
+ this.microAppWindow.rawWindow = globalEnv.rawWindow;
6266
+ this.microAppWindow.rawDocument = globalEnv.rawDocument;
6267
+ this.microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
6268
+ removeDomScope,
6269
+ pureCreateElement,
6270
+ location: this.proxyLocation,
6271
+ router,
6272
+ });
6273
+ }
6274
+ // TODO: RESTRUCTURE
6275
+ patchIframe(microAppWindow, cb) {
6276
+ const oldMicroDocument = microAppWindow.document;
6277
+ this.sandboxReady = new Promise((resolve) => {
6278
+ (function iframeLocationReady() {
6279
+ setTimeout(() => {
6280
+ try {
6281
+ if (microAppWindow.document === oldMicroDocument) {
6282
+ iframeLocationReady();
6283
+ }
6284
+ else {
6285
+ /**
6286
+ * NOTE:
6287
+ * 1. microAppWindow will not be recreated
6288
+ * 2. the properties of microAppWindow may be recreated, such as document
6289
+ * 3. the variables added to microAppWindow may be cleared
6290
+ */
6291
+ microAppWindow.stop();
6292
+ cb(resolve);
6293
+ }
6294
+ }
6295
+ catch (e) {
6296
+ iframeLocationReady();
6297
+ }
6298
+ }, 0);
6299
+ })();
6300
+ });
6301
+ }
6302
+ // TODO: RESTRUCTURE
6303
+ createIframeTemplate(microAppWindow) {
6304
+ const microDocument = microAppWindow.document;
6305
+ clearDOM(microDocument);
6306
+ const html = microDocument.createElement('html');
6307
+ html.innerHTML = '<head></head><body></body>';
6308
+ microDocument.appendChild(html);
6309
+ // 记录iframe原生body
6310
+ this.microBody = microDocument.body;
6311
+ this.microHead = microDocument.head;
6312
+ }
6313
+ /**
6314
+ * baseElement will complete the relative address of element according to the URL
6315
+ * e.g: a image link script fetch ajax EventSource
6316
+ */
6317
+ createIframeBase() {
6318
+ this.baseElement = pureCreateElement('base');
6319
+ this.updateIframeBase();
6320
+ this.microHead.appendChild(this.baseElement);
6321
+ }
6322
+ createProxyLocation(appName, url, microAppWindow, childStaticLocation, browserHost, childHost) {
6323
+ this.proxyLocation = createMicroLocation(appName, url, microAppWindow, childStaticLocation, browserHost, childHost);
6324
+ }
6325
+ createProxyWindow(microAppWindow) {
6326
+ const rawWindow = globalEnv.rawWindow;
6327
+ this.proxyWindow = new Proxy(microAppWindow, {
6328
+ get: (target, key) => {
6329
+ if (key === 'location') {
6330
+ return this.proxyLocation;
6331
+ }
6332
+ if (globalPropertyList.includes(key.toString())) {
4924
6333
  return this.proxyWindow;
4925
- const rawValue = Reflect.get(target, key);
4926
- return isFunction(rawValue) ? bindFunctionToRawObject(rawDocument, rawValue, 'DOCUMENT') : rawValue;
6334
+ }
6335
+ return bindFunctionToRawTarget(Reflect.get(target, key), target);
4927
6336
  },
4928
6337
  set: (target, key, value) => {
4929
- /**
4930
- * 1. Fix TypeError: Illegal invocation when set document.title
4931
- * 2. If the set method returns false, and the assignment happened in strict-mode code, a TypeError will be thrown.
4932
- */
4933
- Reflect.set(target, key, value);
6338
+ if (this.active) {
6339
+ /**
6340
+ * TODO:
6341
+ * 1、location域名相同,子应用内部跳转时的处理
6342
+ * 2、和with沙箱的变量相同,提取成公共数组
6343
+ */
6344
+ if (key === 'location') {
6345
+ return Reflect.set(rawWindow, key, value);
6346
+ }
6347
+ Reflect.set(target, key, value);
6348
+ if (this.escapeProperties.includes(key)) {
6349
+ !Reflect.has(rawWindow, key) && this.escapeKeys.add(key);
6350
+ Reflect.set(rawWindow, key, value);
6351
+ }
6352
+ }
4934
6353
  return true;
4935
- }
6354
+ },
6355
+ has: (target, key) => key in target,
6356
+ deleteProperty: (target, key) => {
6357
+ if (Reflect.has(target, key)) {
6358
+ this.escapeKeys.has(key) && Reflect.deleteProperty(rawWindow, key);
6359
+ return Reflect.deleteProperty(target, key);
6360
+ }
6361
+ return true;
6362
+ },
4936
6363
  });
4937
- class MicroDocument {
4938
- static [Symbol.hasInstance](target) {
4939
- let proto = target;
4940
- while (proto = Object.getPrototypeOf(proto)) {
4941
- if (proto === MicroDocument.prototype) {
4942
- return true;
6364
+ }
6365
+ /**
6366
+ * get escapeProperties from plugins & adapter
6367
+ * @param appName app name
6368
+ */
6369
+ getSpecialProperties(appName) {
6370
+ var _a;
6371
+ if (isPlainObject(microApp.options.plugins)) {
6372
+ this.commonActionForSpecialProperties(microApp.options.plugins.global);
6373
+ this.commonActionForSpecialProperties((_a = microApp.options.plugins.modules) === null || _a === void 0 ? void 0 : _a[appName]);
6374
+ }
6375
+ }
6376
+ // common action for global plugins and module plugins
6377
+ commonActionForSpecialProperties(plugins) {
6378
+ if (isArray(plugins)) {
6379
+ for (const plugin of plugins) {
6380
+ if (isPlainObject(plugin)) {
6381
+ if (isArray(plugin.escapeProperties)) {
6382
+ this.escapeProperties = this.escapeProperties.concat(plugin.escapeProperties);
4943
6383
  }
4944
6384
  }
4945
- return (target === proxyDocument ||
4946
- target instanceof rawRootDocument);
4947
6385
  }
4948
6386
  }
4949
- /**
4950
- * TIP:
4951
- * 1. child class __proto__, which represents the inherit of the constructor, always points to the parent class
4952
- * 2. child class prototype.__proto__, which represents the inherit of methods, always points to parent class prototype
4953
- * e.g.
4954
- * class B extends A {}
4955
- * B.__proto__ === A // true
4956
- * B.prototype.__proto__ === A.prototype // true
4957
- */
4958
- Object.setPrototypeOf(MicroDocument, rawRootDocument);
4959
- // Object.create(rawRootDocument.prototype) will cause MicroDocument and proxyDocument methods not same when exec Document.prototype.xxx = xxx in child app
4960
- Object.setPrototypeOf(MicroDocument.prototype, new Proxy(rawRootDocument.prototype, {
4961
- get(target, key) {
4962
- throttleDeferForSetAppName(appName);
4963
- const rawValue = Reflect.get(target, key);
4964
- return isFunction(rawValue) ? bindFunctionToRawObject(rawDocument, rawValue, 'DOCUMENT') : rawValue;
4965
- },
4966
- set(target, key, value) {
4967
- Reflect.set(target, key, value);
4968
- return true;
4969
- }
4970
- }));
4971
- return {
4972
- proxyDocument,
4973
- MicroDocument,
4974
- };
6387
+ }
6388
+ initRouteState(defaultPage) {
6389
+ initRouteStateWithURL(this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.location, defaultPage);
6390
+ }
6391
+ clearRouteState(keepRouteState) {
6392
+ clearRouteStateFromURL(this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__, this.microAppWindow.location, keepRouteState);
6393
+ }
6394
+ setRouteInfoForKeepAliveApp() {
6395
+ updateBrowserURLWithLocation(this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.location);
6396
+ }
6397
+ removeRouteInfoForKeepAliveApp() {
6398
+ removeStateAndPathFromBrowser(this.microAppWindow.__MICRO_APP_NAME__);
6399
+ }
6400
+ /**
6401
+ * Format all html elements when init
6402
+ * @param container micro app container
6403
+ */
6404
+ patchStaticElement(container) {
6405
+ patchElementTree(container, this.microAppWindow.__MICRO_APP_NAME__);
6406
+ }
6407
+ /**
6408
+ * Actions:
6409
+ * 1. patch static elements from html
6410
+ * @param container micro app container
6411
+ */
6412
+ actionBeforeExecScripts(container) {
6413
+ this.patchStaticElement(container);
4975
6414
  }
4976
6415
  }
4977
- SandBox.activeCount = 0; // number of active sandbox
6416
+ IframeSandbox.activeCount = 0; // number of active sandbox
4978
6417
 
4979
6418
  function formatEventInfo(event, element) {
4980
6419
  Object.defineProperties(event, {
@@ -5024,38 +6463,40 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
5024
6463
  }
5025
6464
  /**
5026
6465
  * Dispatch custom event to micro app
6466
+ * @param app app
5027
6467
  * @param eventName event name
5028
- * @param appName app name
5029
6468
  * @param detail event detail
5030
6469
  */
5031
- function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
5032
- const event = new CustomEvent(formatEventName$1(eventName, appName), {
6470
+ function dispatchCustomEventToMicroApp(app, eventName, detail = {}) {
6471
+ const event = new CustomEvent(formatEventName(eventName, app.name), {
5033
6472
  detail,
5034
6473
  });
5035
- window.dispatchEvent(event);
6474
+ const target = app.iframe ? app.sandBox.microAppWindow : window;
6475
+ target.dispatchEvent(event);
5036
6476
  }
5037
6477
 
5038
6478
  // micro app instances
5039
6479
  const appInstanceMap = new Map();
5040
6480
  class CreateApp {
5041
- constructor({ name, url, container, scopecss, useSandbox, inline, esmodule, ssrUrl, isPrefetch, prefetchLevel, }) {
6481
+ constructor({ name, url, container, scopecss, useSandbox, inline, iframe, ssrUrl, isPrefetch, prefetchLevel, }) {
5042
6482
  this.state = appStates.CREATED;
5043
6483
  this.keepAliveState = null;
5044
- this.keepAliveContainer = null;
5045
6484
  this.loadSourceLevel = 0;
5046
6485
  this.umdHookMount = null;
5047
6486
  this.umdHookUnmount = null;
5048
- this.libraryName = null;
5049
6487
  this.umdMode = false;
6488
+ // TODO: 类型优化,加上iframe沙箱
5050
6489
  this.sandBox = null;
5051
6490
  this.fiber = false;
5052
6491
  this.useMemoryRouter = true;
6492
+ appInstanceMap.set(name, this);
6493
+ // init actions
5053
6494
  this.name = name;
5054
6495
  this.url = url;
5055
6496
  this.useSandbox = useSandbox;
5056
6497
  this.scopecss = this.useSandbox && scopecss;
5057
6498
  this.inline = inline !== null && inline !== void 0 ? inline : false;
5058
- this.esmodule = esmodule !== null && esmodule !== void 0 ? esmodule : false;
6499
+ this.iframe = iframe !== null && iframe !== void 0 ? iframe : false;
5059
6500
  // not exist when prefetch 👇
5060
6501
  this.container = container !== null && container !== void 0 ? container : null;
5061
6502
  this.ssrUrl = ssrUrl !== null && ssrUrl !== void 0 ? ssrUrl : '';
@@ -5063,15 +6504,13 @@ class CreateApp {
5063
6504
  this.isPrefetch = isPrefetch !== null && isPrefetch !== void 0 ? isPrefetch : false;
5064
6505
  this.isPrerender = prefetchLevel === 3;
5065
6506
  this.prefetchLevel = prefetchLevel;
5066
- // init actions
5067
- appInstanceMap.set(this.name, this);
5068
6507
  this.source = { html: null, links: new Set(), scripts: new Set() };
5069
6508
  this.loadSourceCode();
5070
- this.useSandbox && (this.sandBox = new SandBox(name, url));
6509
+ this.createSandbox();
5071
6510
  }
5072
6511
  // Load resources
5073
6512
  loadSourceCode() {
5074
- this.state = appStates.LOADING;
6513
+ this.setAppState(appStates.LOADING);
5075
6514
  HTMLLoader.getInstance().run(this, extractSourceDom);
5076
6515
  }
5077
6516
  /**
@@ -5081,7 +6520,7 @@ class CreateApp {
5081
6520
  var _a;
5082
6521
  if (++this.loadSourceLevel === 2) {
5083
6522
  this.source.html = html;
5084
- this.state = appStates.LOADED;
6523
+ this.setAppState(appStates.LOADED);
5085
6524
  if (!this.isPrefetch && appStates.UNMOUNT !== this.state) {
5086
6525
  getRootContainer(this.container).mount(this);
5087
6526
  }
@@ -5092,7 +6531,7 @@ class CreateApp {
5092
6531
  * 1. fiber forced on
5093
6532
  * 2. only virtual router support
5094
6533
  *
5095
- * NOTE: (4P: not - update browser url, dispatch popstateEvent, reload window, dispatch lifecycle event)
6534
+ * NOTE: (Don't update browser url, dispatch popstateEvent, reload window, dispatch lifecycle event)
5096
6535
  * 1. pushState/replaceState in child can update microLocation, but will not attach router info to browser url
5097
6536
  * 2. prevent dispatch popstate/hashchange event to browser
5098
6537
  * 3. all navigation actions of location are invalid (In the future, we can consider update microLocation without trigger browser reload)
@@ -5112,7 +6551,6 @@ class CreateApp {
5112
6551
  useMemoryRouter: true,
5113
6552
  baseroute: '',
5114
6553
  fiber: true,
5115
- esmodule: this.esmodule,
5116
6554
  defaultPage: defaultPage !== null && defaultPage !== void 0 ? defaultPage : '',
5117
6555
  disablePatchRequest: disablePatchRequest !== null && disablePatchRequest !== void 0 ? disablePatchRequest : false,
5118
6556
  });
@@ -5127,7 +6565,7 @@ class CreateApp {
5127
6565
  this.loadSourceLevel = -1;
5128
6566
  if (appStates.UNMOUNT !== this.state) {
5129
6567
  this.onerror(e);
5130
- this.state = appStates.LOAD_FAILED;
6568
+ this.setAppState(appStates.LOAD_FAILED);
5131
6569
  }
5132
6570
  }
5133
6571
  /**
@@ -5139,150 +6577,151 @@ class CreateApp {
5139
6577
  * @param baseroute route prefix, default is ''
5140
6578
  * @param disablePatchRequest prevent rewrite request method of child app
5141
6579
  * @param fiber run js in fiber mode
5142
- * @param esmodule support type='module' script
5143
6580
  */
5144
- mount({ container, inline, useMemoryRouter, defaultPage, baseroute, disablePatchRequest, fiber, esmodule, }) {
5145
- var _a, _b, _c, _d, _e, _f;
6581
+ mount({ container, inline, useMemoryRouter, defaultPage, baseroute, disablePatchRequest, fiber, }) {
5146
6582
  if (this.loadSourceLevel !== 2) {
5147
6583
  /**
5148
- * unmount prefetch app when loading source, when mount again before loading end,
5149
- * isPrefetch & isPrerender will be reset, and this.container sill be null
5150
- * so we should set this.container
6584
+ * container cannot be null when load end
6585
+ * NOTE:
6586
+ * 1. render prefetch app before load end
6587
+ * 2. unmount prefetch app and mount again before load end
5151
6588
  */
5152
6589
  this.container = container;
5153
6590
  // mount before prerender exec mount (loading source), set isPrerender to false
5154
6591
  this.isPrerender = false;
5155
6592
  // reset app state to LOADING
5156
- this.state = appStates.LOADING;
6593
+ this.setAppState(appStates.LOADING);
5157
6594
  return;
5158
6595
  }
5159
- /**
5160
- * Mount app with prerender, this.container is empty
5161
- * When rendering again, identify prerender by this.container
5162
- * Transfer the contents of div to the <micro-app> tag
5163
- *
5164
- * Special scenes:
5165
- * 1. mount before prerender exec mount (loading source)
5166
- * 2. mount when prerender js executing
5167
- * 3. mount after prerender js exec end
5168
- *
5169
- * TODO: test shadowDOM
5170
- */
5171
- if (this.container instanceof HTMLDivElement &&
5172
- this.container.hasAttribute('prerender')) {
5173
- /**
5174
- * rebuild effect event of window, document, data center
5175
- * explain:
5176
- * 1. rebuild before exec mount, do nothing
5177
- * 2. rebuild when js executing, recovery recorded effect event, because prerender fiber mode
5178
- * 3. rebuild after js exec end, normal recovery effect event
5179
- */
5180
- (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.rebuildEffectSnapshot();
5181
- // current this.container is <div prerender='true'></div>
5182
- cloneContainer(this.container, container, false);
6596
+ this.createSandbox();
6597
+ const nextAction = () => {
6598
+ var _a, _b, _c, _d, _e, _f, _g;
5183
6599
  /**
5184
- * set this.container to <micro-app></micro-app>
5185
- * NOTE:
5186
- * must before exec this.preRenderEvent?.forEach((cb) => cb())
6600
+ * Special scenes:
6601
+ * 1. mount before prerender exec mount (loading source)
6602
+ * 2. mount when prerender js executing
6603
+ * 3. mount after prerender js exec end
6604
+ * 4. mount after prerender unmounted
6605
+ *
6606
+ * TODO: test shadowDOM
5187
6607
  */
5188
- this.container = container;
5189
- (_b = this.preRenderEvent) === null || _b === void 0 ? void 0 : _b.forEach((cb) => cb());
5190
- // reset isPrerender config
5191
- this.isPrerender = false;
5192
- this.preRenderEvent = undefined;
5193
- // attach router info to browser url
5194
- router.attachToURL(this.name);
5195
- return (_c = this.sandBox) === null || _c === void 0 ? void 0 : _c.setPreRenderState(false);
5196
- }
5197
- this.container = container;
5198
- this.inline = inline;
5199
- this.esmodule = esmodule;
5200
- this.fiber = fiber;
5201
- // use in sandbox/effect
5202
- this.useMemoryRouter = useMemoryRouter;
5203
- // this.hiddenRouter = hiddenRouter ?? this.hiddenRouter
5204
- const dispatchBeforeMount = () => {
5205
- dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
5206
- };
5207
- if (this.isPrerender) {
5208
- ((_d = this.preRenderEvent) !== null && _d !== void 0 ? _d : (this.preRenderEvent = [])).push(dispatchBeforeMount);
5209
- }
5210
- else {
5211
- dispatchBeforeMount();
5212
- }
5213
- this.state = appStates.MOUNTING;
5214
- cloneContainer(this.source.html, this.container, !this.umdMode);
5215
- (_e = this.sandBox) === null || _e === void 0 ? void 0 : _e.start({
5216
- umdMode: this.umdMode,
5217
- baseroute,
5218
- useMemoryRouter,
5219
- defaultPage,
5220
- disablePatchRequest,
5221
- });
5222
- let umdHookMountResult; // result of mount function
5223
- if (!this.umdMode) {
5224
- let hasDispatchMountedEvent = false;
5225
- // if all js are executed, param isFinished will be true
5226
- execScripts(this, (isFinished) => {
5227
- var _a;
6608
+ if (this.isPrerender &&
6609
+ isDivElement(this.container) &&
6610
+ this.container.hasAttribute('prerender')) {
6611
+ /**
6612
+ * rebuild effect event of window, document, data center
6613
+ * explain:
6614
+ * 1. rebuild before exec mount, do nothing
6615
+ * 2. rebuild when js executing, recovery recorded effect event, because prerender fiber mode
6616
+ * 3. rebuild after js exec end, normal recovery effect event
6617
+ */
6618
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.rebuildEffectSnapshot();
6619
+ // current this.container is <div prerender='true'></div>
6620
+ cloneContainer(this.container, container, false);
6621
+ /**
6622
+ * set this.container to <micro-app></micro-app>
6623
+ * NOTE:
6624
+ * must exec before this.preRenderEvents?.forEach((cb) => cb())
6625
+ */
6626
+ this.container = container;
6627
+ (_b = this.preRenderEvents) === null || _b === void 0 ? void 0 : _b.forEach((cb) => cb());
6628
+ // reset isPrerender config
6629
+ this.isPrerender = false;
6630
+ this.preRenderEvents = null;
6631
+ // attach router info to browser url
6632
+ router.attachToURL(this.name);
6633
+ (_c = this.sandBox) === null || _c === void 0 ? void 0 : _c.setPreRenderState(false);
6634
+ }
6635
+ else {
6636
+ this.container = container;
6637
+ this.inline = inline;
6638
+ this.fiber = fiber;
6639
+ // use in sandbox/effect
6640
+ this.useMemoryRouter = useMemoryRouter;
6641
+ // this.hiddenRouter = hiddenRouter ?? this.hiddenRouter
6642
+ const dispatchBeforeMount = () => dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
6643
+ if (this.isPrerender) {
6644
+ ((_d = this.preRenderEvents) !== null && _d !== void 0 ? _d : (this.preRenderEvents = [])).push(dispatchBeforeMount);
6645
+ }
6646
+ else {
6647
+ dispatchBeforeMount();
6648
+ }
6649
+ this.setAppState(appStates.MOUNTING);
6650
+ // TODO: 将所有cloneContainer中的'as Element'去掉,兼容shadowRoot的场景
6651
+ cloneContainer(this.source.html, this.container, !this.umdMode);
6652
+ (_e = this.sandBox) === null || _e === void 0 ? void 0 : _e.start({
6653
+ umdMode: this.umdMode,
6654
+ baseroute,
6655
+ useMemoryRouter,
6656
+ defaultPage,
6657
+ disablePatchRequest,
6658
+ });
5228
6659
  if (!this.umdMode) {
5229
- const { mount, unmount } = this.getUmdLibraryHooks();
5230
- /**
5231
- * umdHookUnmount can works in non UMD mode
5232
- * register with window.unmount
5233
- */
5234
- this.umdHookUnmount = unmount;
5235
- // if mount & unmount is function, the sub app is umd mode
5236
- if (isFunction(mount) && isFunction(unmount)) {
5237
- this.umdHookMount = mount;
5238
- this.umdMode = true;
5239
- if (this.sandBox)
5240
- this.sandBox.proxyWindow.__MICRO_APP_UMD_MODE__ = true;
5241
- // this.sandBox?.recordEffectSnapshot()
5242
- try {
5243
- umdHookMountResult = this.umdHookMount(microApp.getData(this.name, true));
6660
+ // update element info of html
6661
+ (_f = this.sandBox) === null || _f === void 0 ? void 0 : _f.actionBeforeExecScripts(this.container);
6662
+ // if all js are executed, param isFinished will be true
6663
+ execScripts(this, (isFinished) => {
6664
+ if (!this.umdMode) {
6665
+ const { mount, unmount } = this.getUmdLibraryHooks();
6666
+ /**
6667
+ * umdHookUnmount can works in default mode
6668
+ * register through window.unmount
6669
+ */
6670
+ this.umdHookUnmount = unmount;
6671
+ // if mount & unmount is function, the sub app is umd mode
6672
+ if (isFunction(mount) && isFunction(unmount)) {
6673
+ this.umdHookMount = mount;
6674
+ // sandbox must exist
6675
+ this.sandBox.markUmdMode(this.umdMode = true);
6676
+ try {
6677
+ this.handleMounted(this.umdHookMount(microApp.getData(this.name, true)));
6678
+ }
6679
+ catch (e) {
6680
+ logError('An error occurred in function mount \n', this.name, e);
6681
+ }
6682
+ }
6683
+ else if (isFinished === true) {
6684
+ this.handleMounted();
6685
+ }
5244
6686
  }
5245
- catch (e) {
5246
- logError('an error occurred in the mount function \n', this.name, e);
5247
- }
5248
- }
6687
+ });
5249
6688
  }
5250
- if (!hasDispatchMountedEvent && (isFinished === true || this.umdMode)) {
5251
- hasDispatchMountedEvent = true;
5252
- const dispatchMounted = () => this.handleMounted(umdHookMountResult);
5253
- if (this.isPrerender) {
5254
- ((_a = this.preRenderEvent) !== null && _a !== void 0 ? _a : (this.preRenderEvent = [])).push(dispatchMounted);
5255
- this.recordAndReleaseEffect();
6689
+ else {
6690
+ (_g = this.sandBox) === null || _g === void 0 ? void 0 : _g.rebuildEffectSnapshot();
6691
+ try {
6692
+ this.handleMounted(this.umdHookMount(microApp.getData(this.name, true)));
5256
6693
  }
5257
- else {
5258
- dispatchMounted();
6694
+ catch (e) {
6695
+ logError('An error occurred in function mount \n', this.name, e);
5259
6696
  }
5260
6697
  }
5261
- });
5262
- }
5263
- else {
5264
- (_f = this.sandBox) === null || _f === void 0 ? void 0 : _f.rebuildEffectSnapshot();
5265
- try {
5266
- umdHookMountResult = this.umdHookMount();
5267
- }
5268
- catch (e) {
5269
- logError('an error occurred in the mount function \n', this.name, e);
5270
6698
  }
5271
- this.handleMounted(umdHookMountResult);
5272
- }
6699
+ };
6700
+ // TODO: any替换为iframe沙箱类型
6701
+ this.iframe ? this.sandBox.sandboxReady.then(nextAction) : nextAction();
5273
6702
  }
5274
6703
  /**
5275
6704
  * handle for promise umdHookMount
5276
6705
  * @param umdHookMountResult result of umdHookMount
5277
6706
  */
5278
6707
  handleMounted(umdHookMountResult) {
5279
- if (isPromise(umdHookMountResult)) {
5280
- umdHookMountResult
5281
- .then(() => this.dispatchMountedEvent())
5282
- .catch((e) => this.onerror(e));
6708
+ var _a, _b;
6709
+ const dispatchAction = () => {
6710
+ if (isPromise(umdHookMountResult)) {
6711
+ umdHookMountResult
6712
+ .then(() => this.dispatchMountedEvent())
6713
+ .catch((e) => this.onerror(e));
6714
+ }
6715
+ else {
6716
+ this.dispatchMountedEvent();
6717
+ }
6718
+ };
6719
+ if (this.isPrerender) {
6720
+ (_a = this.preRenderEvents) === null || _a === void 0 ? void 0 : _a.push(dispatchAction);
6721
+ (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.recordAndReleaseEffect({ isPrerender: true });
5283
6722
  }
5284
6723
  else {
5285
- this.dispatchMountedEvent();
6724
+ dispatchAction();
5286
6725
  }
5287
6726
  }
5288
6727
  /**
@@ -5290,9 +6729,9 @@ class CreateApp {
5290
6729
  */
5291
6730
  dispatchMountedEvent() {
5292
6731
  if (appStates.UNMOUNT !== this.state) {
5293
- this.state = appStates.MOUNTED;
6732
+ this.setAppState(appStates.MOUNTED);
5294
6733
  // call window.onmount of child app
5295
- callFnWithTryCatch(this.getGlobalEventListener(microGlobalEvent.ONMOUNT), this.name, `window.${microGlobalEvent.ONMOUNT}`, microApp.getData(this.name, true));
6734
+ execMicroAppGlobalHook(this.getMicroAppGlobalHook(microGlobalEvent.ONMOUNT), this.name, microGlobalEvent.ONMOUNT, microApp.getData(this.name, true));
5296
6735
  // dispatch event mounted to parent
5297
6736
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.MOUNTED);
5298
6737
  }
@@ -5306,30 +6745,25 @@ class CreateApp {
5306
6745
  * @param unmountcb callback of unmount
5307
6746
  */
5308
6747
  unmount({ destroy, clearData, keepRouteState, unmountcb, }) {
5309
- if (this.state === appStates.LOAD_FAILED) {
5310
- destroy = true;
5311
- }
5312
- this.state = appStates.UNMOUNT;
5313
- this.keepAliveState = null;
5314
- this.keepAliveContainer = null;
6748
+ var _a;
6749
+ destroy = destroy || this.state === appStates.LOAD_FAILED;
6750
+ this.setAppState(appStates.UNMOUNT);
5315
6751
  // result of unmount function
5316
- let umdHookUnmountResult;
6752
+ let umdHookUnmountResult = null;
5317
6753
  /**
5318
6754
  * send an unmount event to the micro app or call umd unmount hook
5319
6755
  * before the sandbox is cleared
5320
6756
  */
5321
- if (isFunction(this.umdHookUnmount)) {
5322
- try {
5323
- umdHookUnmountResult = this.umdHookUnmount(microApp.getData(this.name, true));
5324
- }
5325
- catch (e) {
5326
- logError('an error occurred in the unmount function \n', this.name, e);
5327
- }
6757
+ try {
6758
+ umdHookUnmountResult = (_a = this.umdHookUnmount) === null || _a === void 0 ? void 0 : _a.call(this, microApp.getData(this.name, true));
6759
+ }
6760
+ catch (e) {
6761
+ logError('An error occurred in function unmount \n', this.name, e);
5328
6762
  }
5329
- // call window.onunmount of child app
5330
- callFnWithTryCatch(this.getGlobalEventListener(microGlobalEvent.ONUNMOUNT), this.name, `window.${microGlobalEvent.ONUNMOUNT}`);
5331
6763
  // dispatch unmount event to micro app
5332
- dispatchCustomEventToMicroApp('unmount', this.name);
6764
+ dispatchCustomEventToMicroApp(this, 'unmount');
6765
+ // call window.onunmount of child app
6766
+ execMicroAppGlobalHook(this.getMicroAppGlobalHook(microGlobalEvent.ONUNMOUNT), this.name, microGlobalEvent.ONUNMOUNT);
5333
6767
  this.handleUnmounted(destroy, clearData, keepRouteState, umdHookUnmountResult, unmountcb);
5334
6768
  }
5335
6769
  /**
@@ -5341,19 +6775,17 @@ class CreateApp {
5341
6775
  * @param unmountcb callback of unmount
5342
6776
  */
5343
6777
  handleUnmounted(destroy, clearData, keepRouteState, umdHookUnmountResult, unmountcb) {
5344
- const unmountParam = {
6778
+ const nextAction = () => this.actionsForUnmount({
5345
6779
  destroy,
5346
6780
  clearData,
5347
6781
  keepRouteState,
5348
6782
  unmountcb,
5349
- };
6783
+ });
5350
6784
  if (isPromise(umdHookUnmountResult)) {
5351
- umdHookUnmountResult
5352
- .then(() => this.actionsForUnmount(unmountParam))
5353
- .catch(() => this.actionsForUnmount(unmountParam));
6785
+ umdHookUnmountResult.then(nextAction).catch(nextAction);
5354
6786
  }
5355
6787
  else {
5356
- this.actionsForUnmount(unmountParam);
6788
+ nextAction();
5357
6789
  }
5358
6790
  }
5359
6791
  /**
@@ -5364,26 +6796,20 @@ class CreateApp {
5364
6796
  * @param unmountcb callback of unmount
5365
6797
  */
5366
6798
  actionsForUnmount({ destroy, clearData, keepRouteState, unmountcb }) {
5367
- var _a, _b;
5368
- if (destroy) {
5369
- this.actionsForCompletelyDestroy();
5370
- }
5371
- else if (this.umdMode && this.container.childElementCount) {
6799
+ var _a;
6800
+ if (this.umdMode && this.container && !destroy) {
5372
6801
  cloneContainer(this.container, this.source.html, false);
5373
6802
  }
5374
- if (this.umdMode) {
5375
- (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordEffectSnapshot();
5376
- }
5377
6803
  /**
5378
6804
  * this.container maybe contains micro-app element, stop sandbox should exec after cloneContainer
5379
6805
  * NOTE:
5380
6806
  * 1. if destroy is true, clear route state
5381
6807
  * 2. umd mode and keep-alive will not clear EventSource
5382
6808
  */
5383
- (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.stop({
6809
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.stop({
5384
6810
  umdMode: this.umdMode,
5385
6811
  keepRouteState: keepRouteState && !destroy,
5386
- clearEventSource: !this.umdMode || destroy,
6812
+ destroy,
5387
6813
  clearData: clearData || destroy,
5388
6814
  });
5389
6815
  if (!getActiveApps().length) {
@@ -5391,63 +6817,67 @@ class CreateApp {
5391
6817
  }
5392
6818
  // dispatch unmount event to base app
5393
6819
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.UNMOUNT);
5394
- this.resetConfig();
5395
- unmountcb && unmountcb();
6820
+ this.clearOptions(destroy);
6821
+ unmountcb === null || unmountcb === void 0 ? void 0 : unmountcb();
5396
6822
  }
5397
- resetConfig() {
6823
+ clearOptions(destroy) {
5398
6824
  this.container.innerHTML = '';
5399
6825
  this.container = null;
5400
6826
  this.isPrerender = false;
5401
- this.preRenderEvent = undefined;
6827
+ this.preRenderEvents = null;
6828
+ this.setKeepAliveState(null);
6829
+ // in iframe sandbox & default mode, delete the sandbox & iframeElement
6830
+ if (this.iframe && !this.umdMode)
6831
+ this.sandBox = null;
6832
+ if (destroy)
6833
+ this.actionsForCompletelyDestroy();
5402
6834
  }
5403
6835
  // actions for completely destroy
5404
6836
  actionsForCompletelyDestroy() {
5405
- if (!this.useSandbox && this.umdMode) {
5406
- delete window[this.libraryName];
5407
- }
6837
+ var _a, _b;
6838
+ (_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.deleteIframeElement) === null || _b === void 0 ? void 0 : _b.call(_a);
5408
6839
  sourceCenter.script.deleteInlineInfo(this.source.scripts);
5409
6840
  appInstanceMap.delete(this.name);
5410
6841
  }
5411
6842
  // hidden app when disconnectedCallback called with keep-alive
5412
6843
  hiddenKeepAliveApp(callback) {
5413
- var _a;
5414
- const oldContainer = this.container;
5415
- cloneContainer(this.container, this.keepAliveContainer ? this.keepAliveContainer : (this.keepAliveContainer = document.createElement('div')), false);
5416
- this.container = this.keepAliveContainer;
5417
- this.keepAliveState = keepAliveStates.KEEP_ALIVE_HIDDEN;
5418
- // event should dispatch before clone node
5419
- // dispatch afterHidden event to micro-app
5420
- dispatchCustomEventToMicroApp('appstate-change', this.name, {
6844
+ var _a, _b;
6845
+ this.setKeepAliveState(keepAliveStates.KEEP_ALIVE_HIDDEN);
6846
+ /**
6847
+ * event should dispatch before clone node
6848
+ * dispatch afterHidden event to micro-app
6849
+ */
6850
+ dispatchCustomEventToMicroApp(this, 'appstate-change', {
5421
6851
  appState: 'afterhidden',
5422
6852
  });
5423
6853
  // dispatch afterHidden event to base app
5424
- dispatchLifecyclesEvent(oldContainer, this.name, lifeCycles.AFTERHIDDEN);
6854
+ dispatchLifecyclesEvent(this.container, this.name, lifeCycles.AFTERHIDDEN);
5425
6855
  if (this.useMemoryRouter) {
5426
6856
  // called after lifeCyclesEvent
5427
6857
  (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.removeRouteInfoForKeepAliveApp();
5428
6858
  }
5429
- this.recordAndReleaseEffect();
5430
- callback && callback();
6859
+ this.container = cloneContainer(this.container, pureCreateElement('div'), false);
6860
+ (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.recordAndReleaseEffect({ keepAlive: true });
6861
+ callback === null || callback === void 0 ? void 0 : callback();
5431
6862
  }
5432
6863
  // show app when connectedCallback called with keep-alive
5433
6864
  showKeepAliveApp(container) {
5434
6865
  var _a, _b;
5435
6866
  (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.rebuildEffectSnapshot();
5436
6867
  // dispatch beforeShow event to micro-app
5437
- dispatchCustomEventToMicroApp('appstate-change', this.name, {
6868
+ dispatchCustomEventToMicroApp(this, 'appstate-change', {
5438
6869
  appState: 'beforeshow',
5439
6870
  });
5440
6871
  // dispatch beforeShow event to base app
5441
6872
  dispatchLifecyclesEvent(container, this.name, lifeCycles.BEFORESHOW);
5442
- cloneContainer(this.container, container, false);
5443
- this.container = container;
5444
- this.keepAliveState = keepAliveStates.KEEP_ALIVE_SHOW;
6873
+ this.setKeepAliveState(keepAliveStates.KEEP_ALIVE_SHOW);
6874
+ this.container = cloneContainer(this.container, container, false);
5445
6875
  if (this.useMemoryRouter) {
5446
6876
  // called before lifeCyclesEvent
5447
6877
  (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.setRouteInfoForKeepAliveApp();
5448
6878
  }
5449
6879
  // dispatch afterShow event to micro-app
5450
- dispatchCustomEventToMicroApp('appstate-change', this.name, {
6880
+ dispatchCustomEventToMicroApp(this, 'appstate-change', {
5451
6881
  appState: 'aftershow',
5452
6882
  });
5453
6883
  // dispatch afterShow event to base app
@@ -5460,48 +6890,71 @@ class CreateApp {
5460
6890
  onerror(e) {
5461
6891
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.ERROR, e);
5462
6892
  }
6893
+ /**
6894
+ * Scene:
6895
+ * 1. create app
6896
+ * 2. remount of default mode with iframe sandbox
6897
+ * In default mode with iframe sandbox, unmount app will delete iframeElement & sandBox, and create sandBox when mount again, used to solve the problem that module script cannot be execute when append it again
6898
+ */
6899
+ createSandbox() {
6900
+ if (this.useSandbox && !this.sandBox) {
6901
+ if (this.iframe) {
6902
+ this.sandBox = new IframeSandbox(this.name, this.url);
6903
+ }
6904
+ else {
6905
+ this.sandBox = new WithSandBox(this.name, this.url);
6906
+ }
6907
+ }
6908
+ }
6909
+ // set app state
6910
+ setAppState(state) {
6911
+ this.state = state;
6912
+ }
5463
6913
  // get app state
5464
6914
  getAppState() {
5465
6915
  return this.state;
5466
6916
  }
6917
+ // set keep-alive state
6918
+ setKeepAliveState(state) {
6919
+ this.keepAliveState = state;
6920
+ }
5467
6921
  // get keep-alive state
5468
6922
  getKeepAliveState() {
5469
6923
  return this.keepAliveState;
5470
6924
  }
5471
6925
  // get umd library, if it not exist, return empty object
5472
6926
  getUmdLibraryHooks() {
5473
- var _a, _b, _c, _d;
5474
6927
  // after execScripts, the app maybe unmounted
5475
- if (appStates.UNMOUNT !== this.state) {
5476
- const global = ((_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow) !== null && _b !== void 0 ? _b : globalEnv.rawWindow);
5477
- this.libraryName = getRootContainer(this.container).getAttribute('library') || `micro-app-${this.name}`;
5478
- if (isObject(global[this.libraryName])) {
5479
- return global[this.libraryName];
6928
+ if (appStates.UNMOUNT !== this.state && this.sandBox) {
6929
+ const libraryName = getRootContainer(this.container).getAttribute('library') || `micro-app-${this.name}`;
6930
+ const proxyWindow = this.sandBox.proxyWindow;
6931
+ // compatible with pre versions
6932
+ if (isObject(proxyWindow[libraryName])) {
6933
+ return proxyWindow[libraryName];
5480
6934
  }
5481
6935
  return {
5482
- mount: (_c = this.sandBox) === null || _c === void 0 ? void 0 : _c.proxyWindow.mount,
5483
- unmount: (_d = this.sandBox) === null || _d === void 0 ? void 0 : _d.proxyWindow.unmount,
6936
+ mount: proxyWindow.mount,
6937
+ unmount: proxyWindow.unmount,
5484
6938
  };
5485
6939
  }
5486
6940
  return {};
5487
6941
  }
5488
- getGlobalEventListener(eventName) {
5489
- var _a;
5490
- // @ts-ignore
5491
- const listener = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow[eventName];
6942
+ getMicroAppGlobalHook(eventName) {
6943
+ var _a, _b;
6944
+ const listener = (_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow) === null || _b === void 0 ? void 0 : _b[eventName];
5492
6945
  return isFunction(listener) ? listener : null;
5493
6946
  }
5494
- /**
5495
- * Record global effect and then release (effect: global event, timeout, data listener)
5496
- * Scenes:
5497
- * 1. hidden keep-alive app
5498
- * 2. after init prerender app
5499
- */
5500
- recordAndReleaseEffect() {
5501
- var _a, _b;
5502
- (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordEffectSnapshot();
5503
- (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.releaseGlobalEffect();
6947
+ querySelector(selectors) {
6948
+ return this.container ? globalEnv.rawElementQuerySelector.call(this.container, selectors) : null;
5504
6949
  }
6950
+ querySelectorAll(selectors) {
6951
+ return this.container ? globalEnv.rawElementQuerySelectorAll.call(this.container, selectors) : [];
6952
+ }
6953
+ }
6954
+ // iframe route mode
6955
+ function isIframeSandbox(appName) {
6956
+ var _a, _b;
6957
+ return (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.iframe) !== null && _b !== void 0 ? _b : false;
5505
6958
  }
5506
6959
 
5507
6960
  /**
@@ -5530,12 +6983,14 @@ function defineElement(tagName) {
5530
6983
  const formatAttrName = formatAppName(this.getAttribute('name'));
5531
6984
  const formatAttrUrl = formatAppURL(this.getAttribute('url'), this.appName);
5532
6985
  if (this.legalAttribute('name', formatAttrName) && this.legalAttribute('url', formatAttrUrl)) {
5533
- const existApp = appInstanceMap.get(formatAttrName);
5534
- if (formatAttrName !== this.appName && existApp) {
5535
- // handling of cached and non-prefetch apps
5536
- if (appStates.UNMOUNT !== existApp.getAppState() &&
5537
- keepAliveStates.KEEP_ALIVE_HIDDEN !== existApp.getKeepAliveState() &&
5538
- !existApp.isPrefetch) {
6986
+ const oldApp = appInstanceMap.get(formatAttrName);
6987
+ /**
6988
+ * If oldApp exist & appName is different, determine whether oldApp is running
6989
+ */
6990
+ if (formatAttrName !== this.appName && oldApp) {
6991
+ if (oldApp.getAppState() !== appStates.UNMOUNT &&
6992
+ oldApp.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN &&
6993
+ !oldApp.isPrefetch) {
5539
6994
  this.setAttribute('name', this.appName);
5540
6995
  return logError(`app name conflict, an app named ${formatAttrName} is running`);
5541
6996
  }
@@ -5543,16 +6998,16 @@ function defineElement(tagName) {
5543
6998
  if (formatAttrName !== this.appName || formatAttrUrl !== this.appUrl) {
5544
6999
  if (formatAttrName === this.appName) {
5545
7000
  this.handleUnmount(true, () => {
5546
- this.actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp);
7001
+ this.actionsForAttributeChange(formatAttrName, formatAttrUrl, oldApp);
5547
7002
  });
5548
7003
  }
5549
7004
  else if (this.getKeepAliveModeResult()) {
5550
7005
  this.handleHiddenKeepAliveApp();
5551
- this.actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp);
7006
+ this.actionsForAttributeChange(formatAttrName, formatAttrUrl, oldApp);
5552
7007
  }
5553
7008
  else {
5554
7009
  this.handleUnmount(this.getDestroyCompatibleResult(), () => {
5555
- this.actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp);
7010
+ this.actionsForAttributeChange(formatAttrName, formatAttrUrl, oldApp);
5556
7011
  });
5557
7012
  }
5558
7013
  }
@@ -5684,37 +7139,35 @@ function defineElement(tagName) {
5684
7139
  }
5685
7140
  this.updateSsrUrl(this.appUrl);
5686
7141
  if (appInstanceMap.has(this.appName)) {
5687
- const app = appInstanceMap.get(this.appName);
5688
- const existAppUrl = app.ssrUrl || app.url;
5689
- const targetAppUrl = this.ssrUrl || this.appUrl;
7142
+ const oldApp = appInstanceMap.get(this.appName);
7143
+ const oldAppUrl = oldApp.ssrUrl || oldApp.url;
7144
+ const targetUrl = this.ssrUrl || this.appUrl;
5690
7145
  /**
5691
7146
  * NOTE:
5692
7147
  * 1. keep-alive don't care about ssrUrl
5693
7148
  * 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
5694
7149
  * 3. When scopecss, useSandbox of prefetch app different from target app, delete prefetch app and create new one
5695
7150
  */
5696
- if (app.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN &&
5697
- app.url === this.appUrl) {
5698
- this.handleShowKeepAliveApp(app);
7151
+ if (oldApp.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN &&
7152
+ oldApp.url === this.appUrl) {
7153
+ this.handleShowKeepAliveApp(oldApp);
5699
7154
  }
5700
- else if (existAppUrl === targetAppUrl && (app.getAppState() === appStates.UNMOUNT ||
5701
- (app.isPrefetch && (app.scopecss === this.isScopecss() &&
5702
- app.useSandbox === this.isSandbox())))) {
5703
- this.handleAppMount(app);
7155
+ else if (oldAppUrl === targetUrl && (oldApp.getAppState() === appStates.UNMOUNT ||
7156
+ (oldApp.isPrefetch &&
7157
+ this.sameCoreOptions(oldApp)))) {
7158
+ this.handleAppMount(oldApp);
5704
7159
  }
5705
- else if (app.isPrefetch || app.getAppState() === appStates.UNMOUNT) {
5706
- if ((process.env.NODE_ENV !== 'production') &&
5707
- app.scopecss === this.isScopecss() &&
5708
- app.useSandbox === this.isSandbox()) {
7160
+ else if (oldApp.isPrefetch || oldApp.getAppState() === appStates.UNMOUNT) {
7161
+ if ((process.env.NODE_ENV !== 'production') && this.sameCoreOptions(oldApp)) {
5709
7162
  /**
5710
7163
  * url is different & old app is unmounted or prefetch, create new app to replace old one
5711
7164
  */
5712
- logWarn(`the ${app.isPrefetch ? 'prefetch' : 'unmounted'} app with url: ${existAppUrl} replaced by a new app with url: ${targetAppUrl}`, this.appName);
7165
+ logWarn(`the ${oldApp.isPrefetch ? 'prefetch' : 'unmounted'} app with url: ${oldAppUrl} replaced by a new app with url: ${targetUrl}`, this.appName);
5713
7166
  }
5714
7167
  this.handleCreateApp();
5715
7168
  }
5716
7169
  else {
5717
- logError(`app name conflict, an app named: ${this.appName} with url: ${existAppUrl} is running`);
7170
+ logError(`app name conflict, an app named: ${this.appName} with url: ${oldAppUrl} is running`);
5718
7171
  }
5719
7172
  }
5720
7173
  else {
@@ -5722,7 +7175,7 @@ function defineElement(tagName) {
5722
7175
  }
5723
7176
  }
5724
7177
  // remount app or create app if attribute url or name change
5725
- actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp) {
7178
+ actionsForAttributeChange(formatAttrName, formatAttrUrl, oldApp) {
5726
7179
  var _a;
5727
7180
  /**
5728
7181
  * do not add judgment of formatAttrUrl === this.appUrl
@@ -5735,26 +7188,38 @@ function defineElement(tagName) {
5735
7188
  this.setAttribute('name', this.appName);
5736
7189
  }
5737
7190
  /**
5738
- * when existApp not null: this.appName === existApp.name
5739
- * scene1: if formatAttrName and this.appName are equal: exitApp is the current app, the url must be different, existApp has been unmounted
5740
- * scene2: if formatAttrName and this.appName are different: existApp must be prefetch or unmounted, if url is equal, then just mount, if url is different, then create new app to replace existApp
7191
+ * when oldApp not null: this.appName === oldApp.name
7192
+ * scene1: if formatAttrName and this.appName are equal: exitApp is the current app, the url must be different, oldApp has been unmounted
7193
+ * scene2: if formatAttrName and this.appName are different: oldApp must be prefetch or unmounted, if url is equal, then just mount, if url is different, then create new app to replace oldApp
5741
7194
  * scene3: url is different but ssrUrl is equal
5742
7195
  * scene4: url is equal but ssrUrl is different, if url is equal, name must different
5743
- * scene5: if existApp is KEEP_ALIVE_HIDDEN, name must different
7196
+ * scene5: if oldApp is KEEP_ALIVE_HIDDEN, name must different
5744
7197
  */
5745
- if (existApp) {
5746
- if (existApp.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN) {
5747
- if (existApp.url === this.appUrl) {
5748
- this.handleShowKeepAliveApp(existApp);
7198
+ if (oldApp) {
7199
+ if (oldApp.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN) {
7200
+ if (oldApp.url === this.appUrl) {
7201
+ this.handleShowKeepAliveApp(oldApp);
5749
7202
  }
5750
7203
  else {
5751
7204
  // the hidden keep-alive app is still active
5752
7205
  logError(`app name conflict, an app named ${this.appName} is running`);
5753
7206
  }
7207
+ /**
7208
+ * TODO:
7209
+ * 1. oldApp必是unmountApp或preFetchApp,这里还应该考虑沙箱、iframe、样式隔离不一致的情况
7210
+ * 2. unmountApp要不要判断样式隔离、沙箱、iframe,然后彻底删除并再次渲染?(包括handleConnected里的处理,先不改?)
7211
+ * 推荐:if (
7212
+ * oldApp.url === this.appUrl &&
7213
+ * oldApp.ssrUrl === this.ssrUrl && (
7214
+ * oldApp.getAppState() === appStates.UNMOUNT ||
7215
+ * (oldApp.isPrefetch && this.sameCoreOptions(oldApp))
7216
+ * )
7217
+ * )
7218
+ */
5754
7219
  }
5755
- else if (existApp.url === this.appUrl && existApp.ssrUrl === this.ssrUrl) {
7220
+ else if (oldApp.url === this.appUrl && oldApp.ssrUrl === this.ssrUrl) {
5756
7221
  // mount app
5757
- this.handleAppMount(existApp);
7222
+ this.handleAppMount(oldApp);
5758
7223
  }
5759
7224
  else {
5760
7225
  this.handleCreateApp();
@@ -5778,24 +7243,39 @@ function defineElement(tagName) {
5778
7243
  }
5779
7244
  // create app instance
5780
7245
  handleCreateApp() {
5781
- var _a;
7246
+ const createAppInstance = () => {
7247
+ var _a;
7248
+ return new CreateApp({
7249
+ name: this.appName,
7250
+ url: this.appUrl,
7251
+ scopecss: this.useScopecss(),
7252
+ useSandbox: this.useSandbox(),
7253
+ inline: this.getDisposeResult('inline'),
7254
+ iframe: this.getDisposeResult('iframe'),
7255
+ container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
7256
+ ssrUrl: this.ssrUrl,
7257
+ });
7258
+ };
5782
7259
  /**
5783
- * actions for destroy old app
5784
- * fix of unmounted umd app with disableSandbox
7260
+ * Actions for destroy old app
7261
+ * If oldApp exist, it must be 3 scenes:
7262
+ * 1. oldApp is unmounted app (url is is different)
7263
+ * 2. oldApp is prefetch, not prerender (url, scopecss, useSandbox, iframe is different)
7264
+ * 3. oldApp is prerender (url, scopecss, useSandbox, iframe is different)
5785
7265
  */
5786
- if (appInstanceMap.has(this.appName)) {
5787
- appInstanceMap.get(this.appName).actionsForCompletelyDestroy();
7266
+ const oldApp = appInstanceMap.get(this.appName);
7267
+ if (oldApp) {
7268
+ if (oldApp.isPrerender) {
7269
+ this.handleUnmount(true, createAppInstance);
7270
+ }
7271
+ else {
7272
+ oldApp.actionsForCompletelyDestroy();
7273
+ createAppInstance();
7274
+ }
7275
+ }
7276
+ else {
7277
+ createAppInstance();
5788
7278
  }
5789
- new CreateApp({
5790
- name: this.appName,
5791
- url: this.appUrl,
5792
- scopecss: this.isScopecss(),
5793
- useSandbox: this.isSandbox(),
5794
- inline: this.getDisposeResult('inline'),
5795
- esmodule: this.getDisposeResult('esmodule'),
5796
- container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
5797
- ssrUrl: this.ssrUrl,
5798
- });
5799
7279
  }
5800
7280
  /**
5801
7281
  * mount app
@@ -5821,7 +7301,6 @@ function defineElement(tagName) {
5821
7301
  baseroute: this.getBaseRouteCompatible(),
5822
7302
  disablePatchRequest: this.getDisposeResult('disable-patch-request'),
5823
7303
  fiber: this.getDisposeResult('fiber'),
5824
- esmodule: this.getDisposeResult('esmodule'),
5825
7304
  });
5826
7305
  }
5827
7306
  /**
@@ -5860,10 +7339,10 @@ function defineElement(tagName) {
5860
7339
  * @param name Configuration item name
5861
7340
  */
5862
7341
  getDisposeResult(name) {
5863
- return (this.compatibleSpecialProperties(name) || !!microApp.options[name]) && this.compatibleDisableSpecialProperties(name);
7342
+ return (this.compatibleProperties(name) || !!microApp.options[name]) && this.compatibleDisableProperties(name);
5864
7343
  }
5865
7344
  // compatible of disableScopecss & disableSandbox
5866
- compatibleSpecialProperties(name) {
7345
+ compatibleProperties(name) {
5867
7346
  if (name === 'disable-scopecss') {
5868
7347
  return this.hasAttribute('disable-scopecss') || this.hasAttribute('disableScopecss');
5869
7348
  }
@@ -5873,7 +7352,7 @@ function defineElement(tagName) {
5873
7352
  return this.hasAttribute(name);
5874
7353
  }
5875
7354
  // compatible of disableScopecss & disableSandbox
5876
- compatibleDisableSpecialProperties(name) {
7355
+ compatibleDisableProperties(name) {
5877
7356
  if (name === 'disable-scopecss') {
5878
7357
  return this.getAttribute('disable-scopecss') !== 'false' && this.getAttribute('disableScopecss') !== 'false';
5879
7358
  }
@@ -5882,12 +7361,20 @@ function defineElement(tagName) {
5882
7361
  }
5883
7362
  return this.getAttribute(name) !== 'false';
5884
7363
  }
5885
- isScopecss() {
7364
+ useScopecss() {
5886
7365
  return !(this.getDisposeResult('disable-scopecss') || this.getDisposeResult('shadowDOM'));
5887
7366
  }
5888
- isSandbox() {
7367
+ useSandbox() {
5889
7368
  return !this.getDisposeResult('disable-sandbox');
5890
7369
  }
7370
+ /**
7371
+ * Determine whether the core options of the existApp is consistent with the new one
7372
+ */
7373
+ sameCoreOptions(app) {
7374
+ return (app.scopecss === this.useScopecss() &&
7375
+ app.useSandbox === this.useSandbox() &&
7376
+ app.iframe === this.getDisposeResult('iframe'));
7377
+ }
5891
7378
  /**
5892
7379
  * 2021-09-08
5893
7380
  * get baseRoute
@@ -5972,7 +7459,7 @@ function defineElement(tagName) {
5972
7459
  * {
5973
7460
  * name: string,
5974
7461
  * url: string,
5975
- * esmodule: boolean,
7462
+ * iframe: boolean,
5976
7463
  * inline: boolean,
5977
7464
  * 'disable-scopecss': boolean,
5978
7465
  * 'disable-sandbox': boolean,
@@ -6036,7 +7523,7 @@ function preFetchAction(options) {
6036
7523
  scopecss: !((_b = (_a = options['disable-scopecss']) !== null && _a !== void 0 ? _a : options.disableScopecss) !== null && _b !== void 0 ? _b : microApp.options['disable-scopecss']),
6037
7524
  useSandbox: !((_d = (_c = options['disable-sandbox']) !== null && _c !== void 0 ? _c : options.disableSandbox) !== null && _d !== void 0 ? _d : microApp.options['disable-sandbox']),
6038
7525
  inline: (_e = options.inline) !== null && _e !== void 0 ? _e : microApp.options.inline,
6039
- esmodule: (_f = options.esmodule) !== null && _f !== void 0 ? _f : microApp.options.esmodule,
7526
+ iframe: (_f = options.iframe) !== null && _f !== void 0 ? _f : microApp.options.iframe,
6040
7527
  prefetchLevel: options.level && PREFETCH_LEVEL.includes(options.level) ? options.level : microApp.options.prefetchLevel && PREFETCH_LEVEL.includes(microApp.options.prefetchLevel) ? microApp.options.prefetchLevel : 2,
6041
7528
  });
6042
7529
  const oldOnload = app.onLoad;
@@ -6135,10 +7622,19 @@ function unmountApp(appName, options) {
6135
7622
  return new Promise((resolve) => {
6136
7623
  if (app) {
6137
7624
  if (app.getAppState() === appStates.UNMOUNT || app.isPrefetch) {
6138
- if (options === null || options === void 0 ? void 0 : options.destroy) {
6139
- app.actionsForCompletelyDestroy();
7625
+ if (app.isPrerender) {
7626
+ app.unmount({
7627
+ destroy: !!(options === null || options === void 0 ? void 0 : options.destroy),
7628
+ clearData: !!(options === null || options === void 0 ? void 0 : options.clearData),
7629
+ keepRouteState: false,
7630
+ unmountcb: resolve.bind(null, true)
7631
+ });
7632
+ }
7633
+ else {
7634
+ if (options === null || options === void 0 ? void 0 : options.destroy)
7635
+ app.actionsForCompletelyDestroy();
7636
+ resolve(true);
6140
7637
  }
6141
- resolve(true);
6142
7638
  }
6143
7639
  else if (app.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN) {
6144
7640
  if (options === null || options === void 0 ? void 0 : options.destroy) {
@@ -6188,7 +7684,7 @@ function unmountApp(appName, options) {
6188
7684
  else if ((options === null || options === void 0 ? void 0 : options.clearAliveState) && container.hasAttribute('keep-alive')) {
6189
7685
  const keepAliveAttrValue = container.getAttribute('keep-alive');
6190
7686
  container.removeAttribute('keep-alive');
6191
- let clearDataAttrValue;
7687
+ let clearDataAttrValue = null;
6192
7688
  if (options.clearData) {
6193
7689
  clearDataAttrValue = container.getAttribute('clear-data');
6194
7690
  container.setAttribute('clear-data', 'true');
@@ -6198,7 +7694,7 @@ function unmountApp(appName, options) {
6198
7694
  isString(clearDataAttrValue) && container.setAttribute('clear-data', clearDataAttrValue);
6199
7695
  }
6200
7696
  else {
6201
- let clearDataAttrValue;
7697
+ let clearDataAttrValue = null;
6202
7698
  if (options === null || options === void 0 ? void 0 : options.clearData) {
6203
7699
  clearDataAttrValue = container.getAttribute('clear-data');
6204
7700
  container.setAttribute('clear-data', 'true');
@@ -6321,10 +7817,10 @@ class MicroApp extends EventCenterForBaseApp {
6321
7817
  return logError(`${options.tagName} is invalid tagName`);
6322
7818
  }
6323
7819
  }
6324
- if (window.customElements.get(this.tagName)) {
7820
+ initGlobalEnv();
7821
+ if (globalEnv.rawWindow.customElements.get(this.tagName)) {
6325
7822
  return logWarn(`element ${this.tagName} is already defined`);
6326
7823
  }
6327
- initGlobalEnv();
6328
7824
  if (isPlainObject(options)) {
6329
7825
  this.options = options;
6330
7826
  options['disable-scopecss'] = (_a = options['disable-scopecss']) !== null && _a !== void 0 ? _a : options.disableScopecss;