@micro-zoe/micro-app 1.0.0-alpha.6 → 1.0.0-alpha.9

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.6';
1
+ const version = '1.0.0-alpha.9';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -33,6 +33,10 @@ function isString(target) {
33
33
  function isBoolean(target) {
34
34
  return typeof target === 'boolean';
35
35
  }
36
+ // is Number
37
+ function isNumber(target) {
38
+ return typeof target === 'number';
39
+ }
36
40
  // is function
37
41
  function isFunction(target) {
38
42
  return typeof target === 'function';
@@ -72,6 +76,12 @@ function isShadowRoot(target) {
72
76
  function isURL(target) {
73
77
  return target instanceof URL;
74
78
  }
79
+ function isElement(target) {
80
+ return target instanceof Element;
81
+ }
82
+ function isNode(target) {
83
+ return target instanceof Node;
84
+ }
75
85
  // is ProxyDocument
76
86
  function isProxyDocument(target) {
77
87
  return toString.call(target) === '[object ProxyDocument]';
@@ -334,7 +344,8 @@ function isInvalidQuerySelectorKey(key) {
334
344
  function isUniqueElement(key) {
335
345
  return (/^body$/i.test(key) ||
336
346
  /^head$/i.test(key) ||
337
- /^html$/i.test(key));
347
+ /^html$/i.test(key) ||
348
+ /^title$/i.test(key));
338
349
  }
339
350
  /**
340
351
  * get micro-app element
@@ -483,6 +494,20 @@ function serialExecFiberTasks(tasks) {
483
494
  function isInlineScript(address) {
484
495
  return address.startsWith('inline-');
485
496
  }
497
+ /**
498
+ * call function with try catch
499
+ * @param fn target function
500
+ * @param appName app.name
501
+ * @param args arguments
502
+ */
503
+ function callFnWithTryCatch(fn, appName, msgSuffix, ...args) {
504
+ try {
505
+ isFunction(fn) && fn(...args);
506
+ }
507
+ catch (e) {
508
+ logError(`an error occurred in app ${appName} ${msgSuffix} \n`, null, e);
509
+ }
510
+ }
486
511
 
487
512
  var ObservedAttrName;
488
513
  (function (ObservedAttrName) {
@@ -513,12 +538,39 @@ var lifeCycles;
513
538
  lifeCycles["AFTERSHOW"] = "aftershow";
514
539
  lifeCycles["AFTERHIDDEN"] = "afterhidden";
515
540
  })(lifeCycles || (lifeCycles = {}));
541
+ // global event of child app
542
+ var microGlobalEvent;
543
+ (function (microGlobalEvent) {
544
+ microGlobalEvent["ONMOUNT"] = "onmount";
545
+ microGlobalEvent["ONUNMOUNT"] = "onunmount";
546
+ })(microGlobalEvent || (microGlobalEvent = {}));
516
547
  // keep-alive status
517
548
  var keepAliveStates;
518
549
  (function (keepAliveStates) {
519
550
  keepAliveStates["KEEP_ALIVE_SHOW"] = "keep_alive_show";
520
551
  keepAliveStates["KEEP_ALIVE_HIDDEN"] = "keep_alive_hidden";
521
552
  })(keepAliveStates || (keepAliveStates = {}));
553
+ // micro-app config
554
+ var MicroAppConfig;
555
+ (function (MicroAppConfig) {
556
+ MicroAppConfig["DESTROY"] = "destroy";
557
+ MicroAppConfig["DESTORY"] = "destory";
558
+ MicroAppConfig["INLINE"] = "inline";
559
+ MicroAppConfig["DISABLESCOPECSS"] = "disableScopecss";
560
+ MicroAppConfig["DISABLESANDBOX"] = "disableSandbox";
561
+ MicroAppConfig["DISABLE_SCOPECSS"] = "disable-scopecss";
562
+ MicroAppConfig["DISABLE_SANDBOX"] = "disable-sandbox";
563
+ MicroAppConfig["DISABLE_MEMORY_ROUTER"] = "disable-memory-router";
564
+ MicroAppConfig["DISABLE_PATCH_REQUEST"] = "disable-patch-request";
565
+ MicroAppConfig["KEEP_ROUTER_STATE"] = "keep-router-state";
566
+ MicroAppConfig["HIDDEN_ROUTER"] = "hidden-router";
567
+ MicroAppConfig["KEEP_ALIVE"] = "keep-alive";
568
+ MicroAppConfig["CLEAR_DATA"] = "clear-data";
569
+ MicroAppConfig["ESMODULE"] = "esmodule";
570
+ MicroAppConfig["SSR"] = "ssr";
571
+ MicroAppConfig["FIBER"] = "fiber";
572
+ })(MicroAppConfig || (MicroAppConfig = {}));
573
+ const PREFETCH_LEVEL = [1, 2, 3];
522
574
  /**
523
575
  * global key must be static key, they can not rewrite
524
576
  * e.g.
@@ -527,7 +579,7 @@ var keepAliveStates;
527
579
  * NOTE:
528
580
  * 1. Do not add fetch, XMLHttpRequest, EventSource
529
581
  */
530
- const globalKeyToBeCached = 'window,self,globalThis,Array,Object,String,Boolean,Math,Number,Symbol,Date,Function,Proxy,WeakMap,WeakSet,Set,Map,Reflect,Element,Node,Document,RegExp,Error,TypeError,JSON,isNaN,parseFloat,parseInt,performance,console,decodeURI,encodeURI,decodeURIComponent,encodeURIComponent,navigator,undefined,location,history';
582
+ const globalKeyToBeCached = 'window,self,globalThis,document,Document,Array,Object,String,Boolean,Math,Number,Symbol,Date,Function,Proxy,WeakMap,WeakSet,Set,Map,Reflect,Element,Node,RegExp,Error,TypeError,JSON,isNaN,parseFloat,parseInt,performance,console,decodeURI,encodeURI,decodeURIComponent,encodeURIComponent,navigator,undefined,location,history';
531
583
 
532
584
  /**
533
585
  * fetch source of html, js, css
@@ -1319,10 +1371,9 @@ class Adapter {
1319
1371
  ];
1320
1372
  this.injectReactHRMProperty();
1321
1373
  }
1322
- // TODO: __DEV__ process.env.NODE_ENV !== 'production'
1323
1374
  // adapter for react
1324
1375
  injectReactHRMProperty() {
1325
- if (process.env.NODE_ENV !== 'production') {
1376
+ if ((process.env.NODE_ENV !== 'production')) {
1326
1377
  // react child in non-react env
1327
1378
  this.staticEscapeProperties.push('__REACT_ERROR_OVERLAY_GLOBAL_HOOK__');
1328
1379
  // in react parent
@@ -1346,7 +1397,7 @@ function fixBabelPolyfill6() {
1346
1397
  */
1347
1398
  function fixReactHMRConflict(app) {
1348
1399
  var _a;
1349
- if (process.env.NODE_ENV !== 'production') {
1400
+ if ((process.env.NODE_ENV !== 'production')) {
1350
1401
  const rawReactErrorHook = globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__;
1351
1402
  const childReactErrorHook = (_a = app.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__;
1352
1403
  if (rawReactErrorHook && childReactErrorHook) {
@@ -1366,16 +1417,21 @@ function fixReactHMRConflict(app) {
1366
1417
  function throttleDeferForParentNode(proxyDocument) {
1367
1418
  const html = globalEnv.rawDocument.firstElementChild;
1368
1419
  if (html && html.parentNode !== proxyDocument) {
1369
- setRootParentNode(html, proxyDocument);
1420
+ setParentNode(html, proxyDocument);
1370
1421
  defer(() => {
1371
- setRootParentNode(html, globalEnv.rawDocument);
1422
+ setParentNode(html, globalEnv.rawDocument);
1372
1423
  });
1373
1424
  }
1374
1425
  }
1375
- function setRootParentNode(root, value) {
1376
- const descriptor = Object.getOwnPropertyDescriptor(root, 'parentNode');
1426
+ /**
1427
+ * Modify the point of parentNode
1428
+ * @param target target Node
1429
+ * @param value parentNode
1430
+ */
1431
+ function setParentNode(target, value) {
1432
+ const descriptor = Object.getOwnPropertyDescriptor(target, 'parentNode');
1377
1433
  if (!descriptor || descriptor.configurable) {
1378
- Object.defineProperty(root, 'parentNode', {
1434
+ rawDefineProperty(target, 'parentNode', {
1379
1435
  value,
1380
1436
  configurable: true,
1381
1437
  });
@@ -1457,37 +1513,56 @@ function handleNewNode(parent, child, app) {
1457
1513
  * @param passiveChild second param of insertBefore and replaceChild
1458
1514
  */
1459
1515
  function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
1460
- const hijackElement = getHijackElement(parent, app);
1516
+ const hijackParent = getHijackParent(parent, app);
1461
1517
  /**
1462
1518
  * If passiveChild is not the child node, insertBefore replaceChild will have a problem, at this time, it will be degraded to appendChild
1463
1519
  * E.g: document.head.insertBefore(targetChild, document.head.childNodes[0])
1464
1520
  */
1465
- if (hijackElement) {
1521
+ if (hijackParent) {
1522
+ /**
1523
+ * WARNING:
1524
+ * Verifying that the parentNode of the targetChild points to document.body will cause other problems ?
1525
+ */
1526
+ if (hijackParent.tagName === 'MICRO-APP-BODY' && rawMethod !== globalEnv.rawRemoveChild) {
1527
+ 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;
1538
+ },
1539
+ });
1540
+ }
1541
+ }
1466
1542
  /**
1467
1543
  * 1. If passiveChild exists, it must be insertBefore or replaceChild
1468
1544
  * 2. When removeChild, targetChild may not be in microAppHead or head
1469
1545
  */
1470
- if (passiveChild && !hijackElement.contains(passiveChild)) {
1471
- return globalEnv.rawAppendChild.call(hijackElement, targetChild);
1546
+ if (passiveChild && !hijackParent.contains(passiveChild)) {
1547
+ return globalEnv.rawAppendChild.call(hijackParent, targetChild);
1472
1548
  }
1473
- else if (rawMethod === globalEnv.rawRemoveChild && !hijackElement.contains(targetChild)) {
1549
+ else if (rawMethod === globalEnv.rawRemoveChild && !hijackParent.contains(targetChild)) {
1474
1550
  if (parent.contains(targetChild)) {
1475
1551
  return rawMethod.call(parent, targetChild);
1476
1552
  }
1477
1553
  return targetChild;
1478
1554
  }
1479
- // TODO: __DEV__
1480
- if (process.env.NODE_ENV !== 'production' &&
1555
+ if ((process.env.NODE_ENV !== 'production') &&
1481
1556
  targetChild instanceof HTMLIFrameElement &&
1482
1557
  rawMethod === globalEnv.rawAppendChild) {
1483
1558
  fixReactHMRConflict(app);
1484
1559
  }
1485
- return invokeRawMethod(rawMethod, hijackElement, targetChild, passiveChild);
1560
+ return invokeRawMethod(rawMethod, hijackParent, targetChild, passiveChild);
1486
1561
  }
1487
1562
  return invokeRawMethod(rawMethod, parent, targetChild, passiveChild);
1488
1563
  }
1489
1564
  // head/body map to micro-app-head/micro-app-body
1490
- function getHijackElement(node, app) {
1565
+ function getHijackParent(node, app) {
1491
1566
  var _a, _b;
1492
1567
  if (node === document.head) {
1493
1568
  return (_a = app === null || app === void 0 ? void 0 : app.container) === null || _a === void 0 ? void 0 : _a.querySelector('micro-app-head');
@@ -1520,13 +1595,13 @@ function getMappingNode(node) {
1520
1595
  */
1521
1596
  function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
1522
1597
  const currentAppName = getCurrentAppName();
1523
- if (newChild instanceof Node &&
1598
+ if (isNode(newChild) &&
1524
1599
  (newChild.__MICRO_APP_NAME__ ||
1525
1600
  (currentAppName && !newChild.__PURE_ELEMENT__))) {
1526
1601
  newChild.__MICRO_APP_NAME__ = newChild.__MICRO_APP_NAME__ || currentAppName;
1527
1602
  const app = appInstanceMap.get(newChild.__MICRO_APP_NAME__);
1528
1603
  if (app === null || app === void 0 ? void 0 : app.container) {
1529
- if (newChild instanceof Element) {
1604
+ if (isElement(newChild)) {
1530
1605
  if (/^(img|script)$/i.test(newChild.tagName)) {
1531
1606
  if (newChild.hasAttribute('src')) {
1532
1607
  globalEnv.rawSetAttribute.call(newChild, 'src', CompletionPath(newChild.getAttribute('src'), app.url));
@@ -1546,7 +1621,7 @@ function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
1546
1621
  }
1547
1622
  }
1548
1623
  else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
1549
- if (!(newChild instanceof Node) && currentAppName) {
1624
+ if (!isNode(newChild) && currentAppName) {
1550
1625
  const app = appInstanceMap.get(currentAppName);
1551
1626
  if (app === null || app === void 0 ? void 0 : app.container) {
1552
1627
  if (parent === document.head) {
@@ -1593,7 +1668,6 @@ function patchElementPrototypeMethods() {
1593
1668
  };
1594
1669
  // prototype methods of delete element👇
1595
1670
  Element.prototype.removeChild = function removeChild(oldChild) {
1596
- var _a;
1597
1671
  if (oldChild === null || oldChild === void 0 ? void 0 : oldChild.__MICRO_APP_NAME__) {
1598
1672
  const app = appInstanceMap.get(oldChild.__MICRO_APP_NAME__);
1599
1673
  if (app === null || app === void 0 ? void 0 : app.container) {
@@ -1602,8 +1676,8 @@ function patchElementPrototypeMethods() {
1602
1676
  try {
1603
1677
  return globalEnv.rawRemoveChild.call(this, oldChild);
1604
1678
  }
1605
- catch (_b) {
1606
- return (_a = oldChild === null || oldChild === void 0 ? void 0 : oldChild.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(oldChild);
1679
+ catch (_a) {
1680
+ return ((oldChild === null || oldChild === void 0 ? void 0 : oldChild.parentNode) && globalEnv.rawRemoveChild.call(oldChild.parentNode, oldChild));
1607
1681
  }
1608
1682
  }
1609
1683
  return globalEnv.rawRemoveChild.call(this, oldChild);
@@ -2142,7 +2216,7 @@ function fetchScriptsFromHtml(wrapElement, app) {
2142
2216
  for (const address of scriptList) {
2143
2217
  const scriptInfo = sourceCenter.script.getInfo(address);
2144
2218
  const appSpaceData = scriptInfo.appSpace[app.name];
2145
- if ((!appSpaceData.defer && !appSpaceData.async) || app.isPrefetch) {
2219
+ if ((!appSpaceData.defer && !appSpaceData.async) || (app.isPrefetch && !app.isPrerender)) {
2146
2220
  fetchScriptPromise.push(scriptInfo.code ? scriptInfo.code : fetchSource(address, app.name));
2147
2221
  fetchScriptPromiseInfo.push([address, scriptInfo]);
2148
2222
  }
@@ -2183,7 +2257,7 @@ function fetchScriptSuccess(address, scriptInfo, code, app) {
2183
2257
  * 2. if app is inline or script is esmodule, skip this step
2184
2258
  * 3. if global parseResult not exist, the current script occupies the position, when js is reused, parseResult is reference
2185
2259
  */
2186
- if (app.isPrefetch) {
2260
+ if (app.isPrefetch && app.prefetchLevel === 2) {
2187
2261
  const appSpaceData = scriptInfo.appSpace[app.name];
2188
2262
  /**
2189
2263
  * When prefetch app is replaced by a new app in the processing phase, since the scriptInfo is common, when the scriptInfo of the prefetch app is processed, it may have already been processed.
@@ -2503,12 +2577,20 @@ function flatChildren(parent, app, microAppHead, fiberStyleTasks) {
2503
2577
  else if (dom instanceof HTMLScriptElement) {
2504
2578
  extractScriptElement(dom, parent, app);
2505
2579
  }
2506
- else if (dom instanceof HTMLMetaElement || dom instanceof HTMLTitleElement) {
2507
- parent.removeChild(dom);
2508
- }
2509
2580
  else if (dom instanceof HTMLImageElement && dom.hasAttribute('src')) {
2510
2581
  dom.setAttribute('src', CompletionPath(dom.getAttribute('src'), app.url));
2511
2582
  }
2583
+ /**
2584
+ * Don't remove meta and title, they have some special scenes
2585
+ * e.g.
2586
+ * document.querySelector('meta[name="viewport"]') // for flexible
2587
+ * document.querySelector('meta[name="baseurl"]').baseurl // for api request
2588
+ *
2589
+ * Title point to main app title, child app title used to be compatible with some special scenes
2590
+ */
2591
+ // else if (dom instanceof HTMLMetaElement || dom instanceof HTMLTitleElement) {
2592
+ // parent.removeChild(dom)
2593
+ // }
2512
2594
  }
2513
2595
  }
2514
2596
  /**
@@ -2551,6 +2633,39 @@ function extractSourceDom(htmlStr, app) {
2551
2633
  class EventCenter {
2552
2634
  constructor() {
2553
2635
  this.eventList = new Map();
2636
+ this.queue = [];
2637
+ this.recordStep = {};
2638
+ // run task
2639
+ this.process = () => {
2640
+ var _a, _b;
2641
+ let name;
2642
+ const temRecordStep = this.recordStep;
2643
+ const queue = this.queue;
2644
+ this.recordStep = {};
2645
+ this.queue = [];
2646
+ while (name = queue.shift()) {
2647
+ const eventInfo = this.eventList.get(name);
2648
+ // clear tempData, force before exec nextStep
2649
+ const tempData = eventInfo.tempData;
2650
+ const force = eventInfo.force;
2651
+ eventInfo.tempData = null;
2652
+ eventInfo.force = false;
2653
+ let resArr;
2654
+ if (force || !this.isEqual(eventInfo.data, tempData)) {
2655
+ eventInfo.data = tempData || eventInfo.data;
2656
+ for (const f of eventInfo.callbacks) {
2657
+ const res = f(eventInfo.data);
2658
+ res && (resArr !== null && resArr !== void 0 ? resArr : (resArr = [])).push(res);
2659
+ }
2660
+ (_b = (_a = temRecordStep[name]).dispatchDataEvent) === null || _b === void 0 ? void 0 : _b.call(_a);
2661
+ /**
2662
+ * WARING:
2663
+ * If data of other app is sent in nextStep, it may cause confusion of tempData and force
2664
+ */
2665
+ temRecordStep[name].nextStepList.forEach((nextStep) => nextStep(resArr));
2666
+ }
2667
+ }
2668
+ };
2554
2669
  }
2555
2670
  // whether the name is legal
2556
2671
  isLegalName(name) {
@@ -2560,6 +2675,39 @@ class EventCenter {
2560
2675
  }
2561
2676
  return true;
2562
2677
  }
2678
+ // add appName to queue
2679
+ enqueue(name, nextStep, dispatchDataEvent) {
2680
+ // this.nextStepList.push(nextStep)
2681
+ if (this.recordStep[name]) {
2682
+ this.recordStep[name].nextStepList.push(nextStep);
2683
+ dispatchDataEvent && (this.recordStep[name].dispatchDataEvent = dispatchDataEvent);
2684
+ }
2685
+ else {
2686
+ this.recordStep[name] = {
2687
+ nextStepList: [nextStep],
2688
+ dispatchDataEvent,
2689
+ };
2690
+ }
2691
+ /**
2692
+ * The micro task is executed async when the second render of child.
2693
+ * We should ensure that the data changes are executed before binding the listening function
2694
+ */
2695
+ (!this.queue.includes(name) && this.queue.push(name) === 1) && defer(this.process);
2696
+ }
2697
+ /**
2698
+ * In react, each setState will trigger setData, so we need a filter operation to avoid repeated trigger
2699
+ */
2700
+ isEqual(oldData, newData) {
2701
+ if (!newData || Object.keys(oldData).length !== Object.keys(newData).length)
2702
+ return false;
2703
+ for (const key in oldData) {
2704
+ if (Object.prototype.hasOwnProperty.call(oldData, key)) {
2705
+ if (oldData[key] !== newData[key])
2706
+ return false;
2707
+ }
2708
+ }
2709
+ return true;
2710
+ }
2563
2711
  /**
2564
2712
  * add listener
2565
2713
  * @param name event name
@@ -2579,7 +2727,10 @@ class EventCenter {
2579
2727
  };
2580
2728
  this.eventList.set(name, eventInfo);
2581
2729
  }
2582
- else if (autoTrigger && Object.getOwnPropertyNames(eventInfo.data).length) {
2730
+ else if (autoTrigger &&
2731
+ Object.keys(eventInfo.data).length &&
2732
+ (!this.queue.includes(name) ||
2733
+ this.isEqual(eventInfo.data, eventInfo.tempData))) {
2583
2734
  // auto trigger when data not null
2584
2735
  f(eventInfo.data);
2585
2736
  }
@@ -2600,21 +2751,27 @@ class EventCenter {
2600
2751
  }
2601
2752
  }
2602
2753
  }
2754
+ /**
2755
+ * clearData
2756
+ */
2757
+ clearData(name) {
2758
+ if (this.isLegalName(name)) {
2759
+ const eventInfo = this.eventList.get(name);
2760
+ if (eventInfo) {
2761
+ eventInfo.data = {};
2762
+ }
2763
+ }
2764
+ }
2603
2765
  // dispatch data
2604
- dispatch(name, data) {
2766
+ dispatch(name, data, nextStep, force, dispatchDataEvent) {
2605
2767
  if (this.isLegalName(name)) {
2606
2768
  if (!isPlainObject(data)) {
2607
2769
  return logError('event-center: data must be object');
2608
2770
  }
2609
2771
  let eventInfo = this.eventList.get(name);
2610
2772
  if (eventInfo) {
2611
- // Update when the data is not equal
2612
- if (eventInfo.data !== data) {
2613
- eventInfo.data = data;
2614
- for (const f of eventInfo.callbacks) {
2615
- f(data);
2616
- }
2617
- }
2773
+ eventInfo.tempData = assign({}, eventInfo.tempData || eventInfo.data, data);
2774
+ !eventInfo.force && (eventInfo.force = !!force);
2618
2775
  }
2619
2776
  else {
2620
2777
  eventInfo = {
@@ -2622,7 +2779,13 @@ class EventCenter {
2622
2779
  callbacks: new Set(),
2623
2780
  };
2624
2781
  this.eventList.set(name, eventInfo);
2782
+ /**
2783
+ * When sent data to parent, eventInfo probably does not exist, because parent may listen to datachange
2784
+ */
2785
+ eventInfo.force = true;
2625
2786
  }
2787
+ // add to queue, event eventInfo is null
2788
+ this.enqueue(name, nextStep, dispatchDataEvent);
2626
2789
  }
2627
2790
  }
2628
2791
  // get data
@@ -2671,10 +2834,13 @@ class EventCenterForGlobal {
2671
2834
  * dispatch global data
2672
2835
  * @param data data
2673
2836
  */
2674
- setGlobalData(data) {
2837
+ setGlobalData(data, nextStep, force) {
2675
2838
  // clear dom scope before dispatch global data, apply to micro app
2676
2839
  removeDomScope();
2677
- eventCenter.dispatch('global', data);
2840
+ eventCenter.dispatch('global', data, (resArr) => isFunction(nextStep) && nextStep(resArr), force);
2841
+ }
2842
+ forceSetGlobalData(data, nextStep) {
2843
+ this.setGlobalData(data, nextStep, true);
2678
2844
  }
2679
2845
  /**
2680
2846
  * get global data
@@ -2682,6 +2848,12 @@ class EventCenterForGlobal {
2682
2848
  getGlobalData() {
2683
2849
  return eventCenter.getData('global');
2684
2850
  }
2851
+ /**
2852
+ * clear global data
2853
+ */
2854
+ clearGlobalData() {
2855
+ eventCenter.clearData('global');
2856
+ }
2685
2857
  /**
2686
2858
  * clear all listener of global data
2687
2859
  * if appName exists, only the specified functions is cleared
@@ -2732,8 +2904,19 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
2732
2904
  * @param appName app.name
2733
2905
  * @param data data
2734
2906
  */
2735
- setData(appName, data) {
2736
- eventCenter.dispatch(formatEventName(formatAppName(appName), true), data);
2907
+ setData(appName, data, nextStep, force) {
2908
+ eventCenter.dispatch(formatEventName(formatAppName(appName), true), data, (resArr) => isFunction(nextStep) && nextStep(resArr), force);
2909
+ }
2910
+ forceSetData(appName, data, nextStep) {
2911
+ this.setData(appName, data, nextStep, true);
2912
+ }
2913
+ /**
2914
+ * clear data from base app
2915
+ * @param appName app.name
2916
+ * @param fromBaseApp whether clear data from child app, default is true
2917
+ */
2918
+ clearData(appName, fromBaseApp = true) {
2919
+ eventCenter.clearData(formatEventName(formatAppName(appName), fromBaseApp));
2737
2920
  }
2738
2921
  /**
2739
2922
  * clear all listener for specified micro app
@@ -2769,25 +2952,36 @@ class EventCenterForMicroApp extends EventCenterForGlobal {
2769
2952
  /**
2770
2953
  * get data from base app
2771
2954
  */
2772
- getData() {
2773
- return eventCenter.getData(formatEventName(this.appName, true));
2955
+ getData(fromBaseApp = true) {
2956
+ return eventCenter.getData(formatEventName(this.appName, fromBaseApp));
2774
2957
  }
2775
2958
  /**
2776
2959
  * dispatch data to base app
2777
2960
  * @param data data
2778
2961
  */
2779
- dispatch(data) {
2962
+ dispatch(data, nextStep, force) {
2780
2963
  removeDomScope();
2781
- eventCenter.dispatch(formatEventName(this.appName, false), data);
2782
- const app = appInstanceMap.get(this.appName);
2783
- if ((app === null || app === void 0 ? void 0 : app.container) && isPlainObject(data)) {
2784
- const event = new CustomEvent('datachange', {
2785
- detail: {
2786
- data,
2787
- }
2788
- });
2789
- getRootContainer(app.container).dispatchEvent(event);
2790
- }
2964
+ eventCenter.dispatch(formatEventName(this.appName, false), data, (resArr) => isFunction(nextStep) && nextStep(resArr), force, () => {
2965
+ const app = appInstanceMap.get(this.appName);
2966
+ if ((app === null || app === void 0 ? void 0 : app.container) && isPlainObject(data)) {
2967
+ const event = new CustomEvent('datachange', {
2968
+ detail: {
2969
+ data: eventCenter.getData(formatEventName(this.appName, false))
2970
+ }
2971
+ });
2972
+ getRootContainer(app.container).dispatchEvent(event);
2973
+ }
2974
+ });
2975
+ }
2976
+ forceDispatch(data, nextStep) {
2977
+ this.dispatch(data, nextStep, true);
2978
+ }
2979
+ /**
2980
+ * clear data from child app
2981
+ * @param fromBaseApp whether clear data from base app, default is false
2982
+ */
2983
+ clearData(fromBaseApp = false) {
2984
+ eventCenter.clearData(formatEventName(this.appName, fromBaseApp));
2791
2985
  }
2792
2986
  /**
2793
2987
  * clear all listeners
@@ -2977,12 +3171,14 @@ function effectDocumentEvent() {
2977
3171
  const { rawDocument, rawDocumentAddEventListener, rawDocumentRemoveEventListener, } = globalEnv;
2978
3172
  !hasRewriteDocumentOnClick && overwriteDocumentOnClick();
2979
3173
  document.addEventListener = function (type, listener, options) {
2980
- var _a;
2981
3174
  const appName = getCurrentAppName();
2982
3175
  /**
2983
3176
  * ignore bound function of document event in umd mode, used to solve problem of react global events
3177
+ * update in 2022-09-02:
3178
+ * boundFunction is no longer exclude, because events in UMD mode will not cleared from v1.0.0-alpha.4
3179
+ * if (appName && !(appInstanceMap.get(appName)?.umdMode && isBoundFunction(listener))) {
2984
3180
  */
2985
- if (appName && !(((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.umdMode) && isBoundFunction(listener))) {
3181
+ if (appName) {
2986
3182
  const appListenersMap = documentEventListenerMap.get(appName);
2987
3183
  if (appListenersMap) {
2988
3184
  const appListenerList = appListenersMap.get(type);
@@ -3001,9 +3197,13 @@ function effectDocumentEvent() {
3001
3197
  rawDocumentAddEventListener.call(rawDocument, type, listener, options);
3002
3198
  };
3003
3199
  document.removeEventListener = function (type, listener, options) {
3004
- var _a;
3005
3200
  const appName = getCurrentAppName();
3006
- if (appName && !(((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.umdMode) && isBoundFunction(listener))) {
3201
+ /**
3202
+ * update in 2022-09-02:
3203
+ * boundFunction is no longer exclude, because events in UMD mode will not cleared from v1.0.0-alpha.4
3204
+ * if (appName && !(appInstanceMap.get(appName)?.umdMode && isBoundFunction(listener))) {
3205
+ */
3206
+ if (appName) {
3007
3207
  const appListenersMap = documentEventListenerMap.get(appName);
3008
3208
  if (appListenersMap) {
3009
3209
  const appListenerList = appListenersMap.get(type);
@@ -3068,69 +3268,80 @@ function effect(appName, microAppWindow) {
3068
3268
  timeoutIdMap.delete(timeoutId);
3069
3269
  rawClearTimeout.call(rawWindow, timeoutId);
3070
3270
  };
3071
- const umdWindowListenerMap = new Map();
3072
- const umdDocumentListenerMap = new Map();
3073
- let umdIntervalIdMap = new Map();
3074
- let umdTimeoutIdMap = new Map();
3075
- let umdOnClickHandler;
3076
- const clearUmdSnapshotData = () => {
3077
- umdWindowListenerMap.clear();
3078
- umdIntervalIdMap.clear();
3079
- umdTimeoutIdMap.clear();
3080
- umdDocumentListenerMap.clear();
3081
- umdOnClickHandler = null;
3271
+ const sstWindowListenerMap = new Map();
3272
+ const sstDocumentListenerMap = new Map();
3273
+ let sstIntervalIdMap = new Map();
3274
+ let sstTimeoutIdMap = new Map();
3275
+ let sstOnClickHandler;
3276
+ const clearSnapshotData = () => {
3277
+ sstWindowListenerMap.clear();
3278
+ sstIntervalIdMap.clear();
3279
+ sstTimeoutIdMap.clear();
3280
+ sstDocumentListenerMap.clear();
3281
+ sstOnClickHandler = null;
3082
3282
  };
3083
- // record event and timer before exec umdMountHook
3084
- const recordUmdEffect = () => {
3283
+ /**
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
3289
+ */
3290
+ const recordEffect = () => {
3085
3291
  // record window event
3086
3292
  eventListenerMap.forEach((listenerList, type) => {
3087
3293
  if (listenerList.size) {
3088
- umdWindowListenerMap.set(type, new Set(listenerList));
3294
+ sstWindowListenerMap.set(type, new Set(listenerList));
3089
3295
  }
3090
3296
  });
3091
3297
  // record timers
3092
3298
  if (intervalIdMap.size) {
3093
- umdIntervalIdMap = new Map(intervalIdMap);
3299
+ sstIntervalIdMap = new Map(intervalIdMap);
3094
3300
  }
3095
3301
  if (timeoutIdMap.size) {
3096
- umdTimeoutIdMap = new Map(timeoutIdMap);
3302
+ sstTimeoutIdMap = new Map(timeoutIdMap);
3097
3303
  }
3098
3304
  // record onclick handler
3099
- umdOnClickHandler = documentClickListMap.get(appName);
3305
+ sstOnClickHandler = documentClickListMap.get(appName);
3100
3306
  // record document event
3101
3307
  const documentAppListenersMap = documentEventListenerMap.get(appName);
3102
3308
  if (documentAppListenersMap) {
3103
3309
  documentAppListenersMap.forEach((listenerList, type) => {
3104
3310
  if (listenerList.size) {
3105
- umdDocumentListenerMap.set(type, new Set(listenerList));
3311
+ sstDocumentListenerMap.set(type, new Set(listenerList));
3106
3312
  }
3107
3313
  });
3108
3314
  }
3109
3315
  };
3110
- // rebuild event and timer before remount umd app
3111
- const rebuildUmdEffect = () => {
3316
+ // rebuild event and timer before remount app
3317
+ const rebuildEffect = () => {
3112
3318
  // rebuild window event
3113
- umdWindowListenerMap.forEach((listenerList, type) => {
3319
+ sstWindowListenerMap.forEach((listenerList, type) => {
3114
3320
  for (const listener of listenerList) {
3115
3321
  microAppWindow.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
3116
3322
  }
3117
3323
  });
3118
3324
  // rebuild timer
3119
- umdIntervalIdMap.forEach((info) => {
3325
+ sstIntervalIdMap.forEach((info) => {
3120
3326
  microAppWindow.setInterval(info.handler, info.timeout, ...info.args);
3121
3327
  });
3122
- umdTimeoutIdMap.forEach((info) => {
3328
+ sstTimeoutIdMap.forEach((info) => {
3123
3329
  microAppWindow.setTimeout(info.handler, info.timeout, ...info.args);
3124
3330
  });
3125
3331
  // rebuild onclick event
3126
- umdOnClickHandler && documentClickListMap.set(appName, umdOnClickHandler);
3127
- // rebuild document event
3128
- umdDocumentListenerMap.forEach((listenerList, type) => {
3332
+ sstOnClickHandler && documentClickListMap.set(appName, sstOnClickHandler);
3333
+ /**
3334
+ * rebuild document event
3335
+ * WARNING!!: do not delete setCurrentAppName & removeDomScope
3336
+ */
3337
+ setCurrentAppName(appName);
3338
+ sstDocumentListenerMap.forEach((listenerList, type) => {
3129
3339
  for (const listener of listenerList) {
3130
3340
  document.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
3131
3341
  }
3132
3342
  });
3133
- clearUmdSnapshotData();
3343
+ removeDomScope();
3344
+ clearSnapshotData();
3134
3345
  };
3135
3346
  // release all event listener & interval & timeout when unmount app
3136
3347
  const releaseEffect = () => {
@@ -3170,8 +3381,8 @@ function effect(appName, microAppWindow) {
3170
3381
  }
3171
3382
  };
3172
3383
  return {
3173
- recordUmdEffect,
3174
- rebuildUmdEffect,
3384
+ recordEffect,
3385
+ rebuildEffect,
3175
3386
  releaseEffect,
3176
3387
  };
3177
3388
  }
@@ -3197,7 +3408,6 @@ function removeMicroState(appName, rawState) {
3197
3408
  delete rawState.microAppState;
3198
3409
  }
3199
3410
  }
3200
- // 生成新的state对象
3201
3411
  return assign({}, rawState);
3202
3412
  }
3203
3413
  // get micro app state form origin state
@@ -3209,12 +3419,15 @@ const ENC_AD_RE = /&/g; // %M1
3209
3419
  const ENC_EQ_RE = /=/g; // %M2
3210
3420
  const DEC_AD_RE = /%M1/g; // &
3211
3421
  const DEC_EQ_RE = /%M2/g; // =
3422
+ // encode path with special symbol
3212
3423
  function encodeMicroPath(path) {
3213
3424
  return encodeURIComponent(commonDecode(path).replace(ENC_AD_RE, '%M1').replace(ENC_EQ_RE, '%M2'));
3214
3425
  }
3426
+ // decode path
3215
3427
  function decodeMicroPath(path) {
3216
3428
  return commonDecode(path).replace(DEC_AD_RE, '&').replace(DEC_EQ_RE, '=');
3217
3429
  }
3430
+ // Recursively resolve address
3218
3431
  function commonDecode(path) {
3219
3432
  try {
3220
3433
  const decPath = decodeURIComponent(path);
@@ -3226,12 +3439,15 @@ function commonDecode(path) {
3226
3439
  return path;
3227
3440
  }
3228
3441
  }
3229
- // 格式化query参数key,防止与原有参数的冲突
3442
+ // Format the query parameter key to prevent conflicts with the original parameters
3230
3443
  function formatQueryAppName(appName) {
3231
3444
  // return `app-${appName}`
3232
3445
  return appName;
3233
3446
  }
3234
- // 根据浏览器url参数,获取当前子应用的path
3447
+ /**
3448
+ * Get app fullPath from browser url
3449
+ * @param appName app.name
3450
+ */
3235
3451
  function getMicroPathFromURL(appName) {
3236
3452
  var _a, _b;
3237
3453
  const rawLocation = globalEnv.rawWindow.location;
@@ -3239,15 +3455,23 @@ function getMicroPathFromURL(appName) {
3239
3455
  const microPath = ((_a = queryObject.hashQuery) === null || _a === void 0 ? void 0 : _a[formatQueryAppName(appName)]) || ((_b = queryObject.searchQuery) === null || _b === void 0 ? void 0 : _b[formatQueryAppName(appName)]);
3240
3456
  return isString(microPath) ? decodeMicroPath(microPath) : null;
3241
3457
  }
3242
- // 将name=encodeUrl地址插入到浏览器url上
3458
+ /**
3459
+ * Attach child app fullPath to browser url
3460
+ * @param appName app.name
3461
+ * @param microLocation location of child app
3462
+ */
3243
3463
  function setMicroPathToURL(appName, microLocation) {
3244
3464
  let { pathname, search, hash } = globalEnv.rawWindow.location;
3245
3465
  const queryObject = getQueryObjectFromURL(search, hash);
3246
3466
  const encodedMicroPath = encodeMicroPath(microLocation.pathname +
3247
3467
  microLocation.search +
3248
3468
  microLocation.hash);
3249
- let isAttach2Hash = false; // 基座是否是hash模式,这个其实也不准,只是表示参数加到了hash上
3250
- // hash存在且search不存在,则认为是hash路由
3469
+ /**
3470
+ * Is parent is hash router
3471
+ * In fact, this is not true. It just means that the parameter is added to the hash
3472
+ */
3473
+ let isAttach2Hash = false;
3474
+ // If hash exists and search does not exist, it is considered as a hash route
3251
3475
  if (hash && !search) {
3252
3476
  isAttach2Hash = true;
3253
3477
  if (queryObject.hashQuery) {
@@ -3277,7 +3501,11 @@ function setMicroPathToURL(appName, microLocation) {
3277
3501
  isAttach2Hash,
3278
3502
  };
3279
3503
  }
3280
- // 将name=encodeUrl的参数从浏览器url上删除
3504
+ /**
3505
+ * Delete child app fullPath from browser url
3506
+ * @param appName app.name
3507
+ * @param targetLocation target Location, default is rawLocation
3508
+ */
3281
3509
  function removeMicroPathFromURL(appName, targetLocation) {
3282
3510
  var _a, _b, _c, _d;
3283
3511
  let { pathname, search, hash } = targetLocation || globalEnv.rawWindow.location;
@@ -3300,7 +3528,7 @@ function removeMicroPathFromURL(appName, targetLocation) {
3300
3528
  };
3301
3529
  }
3302
3530
  /**
3303
- * 根据location获取query对象
3531
+ * Format search, hash to object
3304
3532
  */
3305
3533
  function getQueryObjectFromURL(search, hash) {
3306
3534
  const queryObject = {};
@@ -3322,6 +3550,18 @@ function getNoHashMicroPathFromURL(appName, baseUrl) {
3322
3550
  const formatLocation = createURL(microPath, baseUrl);
3323
3551
  return formatLocation.origin + formatLocation.pathname + formatLocation.search;
3324
3552
  }
3553
+ /**
3554
+ * Effect app is an app that can perform route navigation
3555
+ * NOTE: Invalid app action
3556
+ * 1. prevent update browser url, dispatch popStateEvent, reload browser
3557
+ * 2. It can update path with pushState/replaceState
3558
+ * 3. Can not update path outside (with router api)
3559
+ * 3. Can not update path by location
3560
+ */
3561
+ function isEffectiveApp(appName) {
3562
+ const app = appInstanceMap.get(appName);
3563
+ return !!(app && !app.isPrefetch);
3564
+ }
3325
3565
 
3326
3566
  /**
3327
3567
  * dispatch PopStateEvent & HashChangeEvent to child app
@@ -3338,7 +3578,8 @@ function addHistoryListener(appName) {
3338
3578
  * 1. unmount app & hidden keep-alive app will not receive popstate event
3339
3579
  * 2. filter out onlyForBrowser
3340
3580
  */
3341
- if (getActiveApps(true).includes(appName) && !e.onlyForBrowser) {
3581
+ if (getActiveApps({ excludeHiddenApp: true, excludePreRender: true }).includes(appName) &&
3582
+ !e.onlyForBrowser) {
3342
3583
  const microPath = getMicroPathFromURL(appName);
3343
3584
  const app = appInstanceMap.get(appName);
3344
3585
  const proxyWindow = app.sandBox.proxyWindow;
@@ -3426,15 +3667,18 @@ function dispatchNativeHashChangeEvent(oldHref) {
3426
3667
  }
3427
3668
  /**
3428
3669
  * dispatch popstate & hashchange event to browser
3670
+ * @param appName app.name
3429
3671
  * @param onlyForBrowser only dispatch event to browser
3430
3672
  * @param oldHref old href of rawWindow.location
3431
3673
  */
3432
- function dispatchNativeEvent(onlyForBrowser, oldHref) {
3674
+ function dispatchNativeEvent(appName, onlyForBrowser, oldHref) {
3433
3675
  // clear element scope before dispatch global event
3434
3676
  removeDomScope();
3435
- dispatchNativePopStateEvent(onlyForBrowser);
3436
- if (oldHref) {
3437
- dispatchNativeHashChangeEvent(oldHref);
3677
+ if (isEffectiveApp(appName)) {
3678
+ dispatchNativePopStateEvent(onlyForBrowser);
3679
+ if (oldHref) {
3680
+ dispatchNativeHashChangeEvent(oldHref);
3681
+ }
3438
3682
  }
3439
3683
  }
3440
3684
 
@@ -3451,7 +3695,7 @@ function createMicroHistory(appName, microLocation) {
3451
3695
  if (isString(rests[2]) || isURL(rests[2])) {
3452
3696
  const targetLocation = createURL(rests[2], microLocation.href);
3453
3697
  if (targetLocation.origin === microLocation.origin) {
3454
- navigateWithNativeEvent(methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rests[0]), rests[1]);
3698
+ navigateWithNativeEvent(appName, methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rests[0]), rests[1]);
3455
3699
  const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3456
3700
  if (targetFullPath !== microLocation.fullPath) {
3457
3701
  updateMicroLocation(appName, targetFullPath, microLocation);
@@ -3459,7 +3703,7 @@ function createMicroHistory(appName, microLocation) {
3459
3703
  return void 0;
3460
3704
  }
3461
3705
  }
3462
- nativeHistoryNavigate(methodName, rests[2], rests[0], rests[1]);
3706
+ nativeHistoryNavigate(appName, methodName, rests[2], rests[0], rests[1]);
3463
3707
  };
3464
3708
  }
3465
3709
  const pushState = getMicroHistoryMethod('pushState');
@@ -3491,14 +3735,17 @@ function createMicroHistory(appName, microLocation) {
3491
3735
  }
3492
3736
  /**
3493
3737
  * navigate to new path base on native method of history
3738
+ * @param appName app.name
3494
3739
  * @param methodName pushState/replaceState
3495
3740
  * @param fullPath full path
3496
3741
  * @param state history.state, default is null
3497
3742
  * @param title history.title, default is ''
3498
3743
  */
3499
- function nativeHistoryNavigate(methodName, fullPath, state = null, title = '') {
3500
- const method = methodName === 'pushState' ? globalEnv.rawPushState : globalEnv.rawReplaceState;
3501
- method.call(globalEnv.rawWindow.history, state, title, fullPath);
3744
+ function nativeHistoryNavigate(appName, methodName, fullPath, state = null, title = '') {
3745
+ if (isEffectiveApp(appName)) {
3746
+ const method = methodName === 'pushState' ? globalEnv.rawPushState : globalEnv.rawReplaceState;
3747
+ method.call(globalEnv.rawWindow.history, state, title, fullPath);
3748
+ }
3502
3749
  }
3503
3750
  /**
3504
3751
  * Navigate to new path, and dispatch native popStateEvent/hashChangeEvent to browser
@@ -3507,29 +3754,33 @@ function nativeHistoryNavigate(methodName, fullPath, state = null, title = '') {
3507
3754
  * 2. proxyHistory.pushState/replaceState with limited popstateEvent
3508
3755
  * 3. api microApp.router.push/replace
3509
3756
  * 4. proxyLocation.hash = xxx
3757
+ * @param appName app.name
3510
3758
  * @param methodName pushState/replaceState
3511
3759
  * @param result result of add/remove microApp path on browser url
3512
3760
  * @param onlyForBrowser only dispatch event to browser
3513
3761
  * @param state history.state, not required
3514
3762
  * @param title history.title, not required
3515
3763
  */
3516
- function navigateWithNativeEvent(methodName, result, onlyForBrowser, state, title) {
3517
- const rawLocation = globalEnv.rawWindow.location;
3518
- const oldFullPath = rawLocation.pathname + rawLocation.search + rawLocation.hash;
3519
- const oldHref = result.isAttach2Hash && oldFullPath !== result.fullPath ? rawLocation.href : null;
3520
- // navigate with native history method
3521
- nativeHistoryNavigate(methodName, result.fullPath, state, title);
3522
- if (oldFullPath !== result.fullPath)
3523
- dispatchNativeEvent(onlyForBrowser, oldHref);
3764
+ function navigateWithNativeEvent(appName, methodName, result, onlyForBrowser, state, title) {
3765
+ if (isEffectiveApp(appName)) {
3766
+ const rawLocation = globalEnv.rawWindow.location;
3767
+ const oldFullPath = rawLocation.pathname + rawLocation.search + rawLocation.hash;
3768
+ const oldHref = result.isAttach2Hash && oldFullPath !== result.fullPath ? rawLocation.href : null;
3769
+ // navigate with native history method
3770
+ nativeHistoryNavigate(appName, methodName, result.fullPath, state, title);
3771
+ if (oldFullPath !== result.fullPath)
3772
+ dispatchNativeEvent(appName, onlyForBrowser, oldHref);
3773
+ }
3524
3774
  }
3525
3775
  /**
3526
3776
  * update browser url when mount/unmount/hidden/show/attachToURL/attachAllToURL
3527
3777
  * just attach microRoute info to browser, dispatch event to base app(exclude child)
3778
+ * @param appName app.name
3528
3779
  * @param result result of add/remove microApp path on browser url
3529
3780
  * @param state history.state
3530
3781
  */
3531
- function attachRouteToBrowserURL(result, state) {
3532
- navigateWithNativeEvent('replaceState', result, true, state);
3782
+ function attachRouteToBrowserURL(appName, result, state) {
3783
+ navigateWithNativeEvent(appName, 'replaceState', result, true, state);
3533
3784
  }
3534
3785
  /**
3535
3786
  * When path is same, keep the microAppState in history.state
@@ -3559,10 +3810,13 @@ function reWriteHistoryMethod(method) {
3559
3810
  * 2. Unable to catch when base app navigate with location
3560
3811
  * 3. When in nest app, rawPushState/rawReplaceState has been modified by parent
3561
3812
  */
3562
- getActiveApps(true).forEach(appName => {
3813
+ getActiveApps({
3814
+ excludeHiddenApp: true,
3815
+ excludePreRender: true,
3816
+ }).forEach(appName => {
3563
3817
  const app = appInstanceMap.get(appName);
3564
3818
  if (app.sandBox && app.useMemoryRouter && !getMicroPathFromURL(appName)) {
3565
- attachRouteToBrowserURL(setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName)));
3819
+ attachRouteToBrowserURL(appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName)));
3566
3820
  }
3567
3821
  });
3568
3822
  // fix bug for nest app
@@ -3594,7 +3848,7 @@ function createRouterApi() {
3594
3848
  * @param state to.state
3595
3849
  */
3596
3850
  function navigateWithRawHistory(appName, methodName, targetLocation, state) {
3597
- navigateWithNativeEvent(methodName, setMicroPathToURL(appName, targetLocation), false, setMicroState(appName, state !== null && state !== void 0 ? state : null));
3851
+ navigateWithNativeEvent(appName, methodName, setMicroPathToURL(appName, targetLocation), false, setMicroState(appName, state !== null && state !== void 0 ? state : null));
3598
3852
  // clear element scope after navigate
3599
3853
  removeDomScope();
3600
3854
  }
@@ -3615,7 +3869,7 @@ function createRouterApi() {
3615
3869
  return logError(`navigation failed, memory router of app ${appName} is closed`);
3616
3870
  }
3617
3871
  // active apps, include hidden keep-alive app
3618
- if (getActiveApps().includes(appName)) {
3872
+ if (getActiveApps({ excludePreRender: true }).includes(appName)) {
3619
3873
  const microLocation = app.sandBox.proxyWindow.location;
3620
3874
  const targetLocation = createURL(to.path, microLocation.href);
3621
3875
  // Only get path data, even if the origin is different from microApp
@@ -3698,7 +3952,7 @@ function createRouterApi() {
3698
3952
  function commonHandlerForAttachToURL(appName) {
3699
3953
  const app = appInstanceMap.get(appName);
3700
3954
  if (app.sandBox && app.useMemoryRouter) {
3701
- attachRouteToBrowserURL(setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName)));
3955
+ attachRouteToBrowserURL(appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName)));
3702
3956
  }
3703
3957
  }
3704
3958
  /**
@@ -3713,10 +3967,14 @@ function createRouterApi() {
3713
3967
  }
3714
3968
  /**
3715
3969
  * Attach all active app router info to browser url
3716
- * exclude hidden keep-alive app
3970
+ * @param includeHiddenApp include hidden keep-alive app
3971
+ * @param includePreRender include preRender app
3717
3972
  */
3718
- function attachAllToURL(includeHiddenApp = false) {
3719
- getActiveApps(!includeHiddenApp).forEach(appName => commonHandlerForAttachToURL(appName));
3973
+ function attachAllToURL({ includeHiddenApp = false, includePreRender = false, }) {
3974
+ getActiveApps({
3975
+ excludeHiddenApp: !includeHiddenApp,
3976
+ excludePreRender: !includePreRender,
3977
+ }).forEach(appName => commonHandlerForAttachToURL(appName));
3720
3978
  }
3721
3979
  function createDefaultPageApi() {
3722
3980
  // defaultPage data
@@ -3731,7 +3989,7 @@ function createRouterApi() {
3731
3989
  function setDefaultPage(options) {
3732
3990
  const appName = formatAppName(options.name);
3733
3991
  if (!appName || !options.path) {
3734
- if (process.env.NODE_ENV !== 'production') {
3992
+ if ((process.env.NODE_ENV !== 'production')) {
3735
3993
  if (!appName) {
3736
3994
  logWarn(`setDefaultPage: invalid appName "${appName}"`);
3737
3995
  }
@@ -3774,7 +4032,7 @@ function createRouterApi() {
3774
4032
  }
3775
4033
  });
3776
4034
  }
3777
- else if (process.env.NODE_ENV !== 'production') {
4035
+ else if ((process.env.NODE_ENV !== 'production')) {
3778
4036
  logWarn('setBaseAppRouter: Invalid base router');
3779
4037
  }
3780
4038
  }
@@ -3842,13 +4100,13 @@ function createMicroLocation(appName, url) {
3842
4100
  if (targetLocation.hash !== shadowLocation.hash) {
3843
4101
  if (setMicroPathResult.isAttach2Hash)
3844
4102
  oldHref = rawLocation.href;
3845
- nativeHistoryNavigate(methodName, setMicroPathResult.fullPath);
4103
+ nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath);
3846
4104
  }
3847
4105
  if (targetLocation.hash) {
3848
- dispatchNativeEvent(false, oldHref);
4106
+ dispatchNativeEvent(appName, false, oldHref);
3849
4107
  }
3850
4108
  else {
3851
- rawLocation.reload();
4109
+ rawReload();
3852
4110
  }
3853
4111
  return void 0;
3854
4112
  /**
@@ -3857,8 +4115,8 @@ function createMicroLocation(appName, url) {
3857
4115
  */
3858
4116
  }
3859
4117
  else if (setMicroPathResult.isAttach2Hash) {
3860
- nativeHistoryNavigate(methodName, setMicroPathResult.fullPath);
3861
- rawLocation.reload();
4118
+ nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath);
4119
+ rawReload();
3862
4120
  return void 0;
3863
4121
  }
3864
4122
  value = setMicroPathResult.fullPath;
@@ -3888,7 +4146,7 @@ function createMicroLocation(appName, url) {
3888
4146
  // When the browser url has a hash value, the same pathname/search will not refresh browser
3889
4147
  if (targetLocation[key] === shadowLocation[key] && shadowLocation.hash) {
3890
4148
  // The href has not changed, not need to dispatch hashchange event
3891
- dispatchNativeEvent(false);
4149
+ dispatchNativeEvent(appName, false);
3892
4150
  }
3893
4151
  else {
3894
4152
  /**
@@ -3897,19 +4155,24 @@ function createMicroLocation(appName, url) {
3897
4155
  * pathname: /path ==> /path#hash, /path ==> /path?query
3898
4156
  * search: ?query ==> ?query#hash
3899
4157
  */
3900
- nativeHistoryNavigate(targetLocation[key] === shadowLocation[key] ? 'replaceState' : 'pushState', setMicroPathToURL(appName, targetLocation).fullPath);
3901
- rawLocation.reload();
4158
+ nativeHistoryNavigate(appName, targetLocation[key] === shadowLocation[key] ? 'replaceState' : 'pushState', setMicroPathToURL(appName, targetLocation).fullPath);
4159
+ rawReload();
3902
4160
  }
3903
4161
  }
4162
+ function rawReload() {
4163
+ isEffectiveApp(appName) && rawLocation.reload();
4164
+ }
3904
4165
  /**
3905
4166
  * Special processing for four keys: href, pathname, search and hash
3906
4167
  * They take values from shadowLocation, and require special operations when assigning values
3907
4168
  */
3908
4169
  rawDefineProperties(microLocation, {
3909
4170
  href: createPropertyDescriptor(() => shadowLocation.href, (value) => {
3910
- const targetPath = commonHandler(value, 'pushState');
3911
- if (targetPath)
3912
- rawLocation.href = targetPath;
4171
+ if (isEffectiveApp(appName)) {
4172
+ const targetPath = commonHandler(value, 'pushState');
4173
+ if (targetPath)
4174
+ rawLocation.href = targetPath;
4175
+ }
3913
4176
  }),
3914
4177
  pathname: createPropertyDescriptor(() => shadowLocation.pathname, (value) => {
3915
4178
  const targetPath = ('/' + value).replace(/^\/+/, '/') + shadowLocation.search + shadowLocation.hash;
@@ -3924,16 +4187,18 @@ function createMicroLocation(appName, url) {
3924
4187
  const targetLocation = createURL(targetPath, url);
3925
4188
  // The same hash will not trigger popStateEvent
3926
4189
  if (targetLocation.hash !== shadowLocation.hash) {
3927
- navigateWithNativeEvent('pushState', setMicroPathToURL(appName, targetLocation), false);
4190
+ navigateWithNativeEvent(appName, 'pushState', setMicroPathToURL(appName, targetLocation), false);
3928
4191
  }
3929
4192
  }),
3930
4193
  fullPath: createPropertyDescriptor(() => shadowLocation.pathname + shadowLocation.search + shadowLocation.hash, noop),
3931
4194
  });
3932
4195
  const createLocationMethod = (locationMethodName) => {
3933
4196
  return function (value) {
3934
- const targetPath = commonHandler(value, locationMethodName === 'assign' ? 'pushState' : 'replaceState');
3935
- if (targetPath)
3936
- rawLocation[locationMethodName](targetPath);
4197
+ if (isEffectiveApp(appName)) {
4198
+ const targetPath = commonHandler(value, locationMethodName === 'assign' ? 'pushState' : 'replaceState');
4199
+ if (targetPath)
4200
+ rawLocation[locationMethodName](targetPath);
4201
+ }
3937
4202
  };
3938
4203
  };
3939
4204
  return assign(microLocation, {
@@ -4032,7 +4297,7 @@ function updateBrowserURLWithLocation(appName, microLocation, defaultPage) {
4032
4297
  if (defaultPage)
4033
4298
  updateMicroLocation(appName, defaultPage, microLocation, 'prevent');
4034
4299
  // attach microApp route info to browser URL
4035
- attachRouteToBrowserURL(setMicroPathToURL(appName, microLocation), setMicroState(appName, null));
4300
+ attachRouteToBrowserURL(appName, setMicroPathToURL(appName, microLocation), setMicroState(appName, null));
4036
4301
  // trigger guards after change browser URL
4037
4302
  autoTriggerNavigationGuard(appName, microLocation);
4038
4303
  }
@@ -4056,7 +4321,7 @@ function clearRouteStateFromURL(appName, url, microLocation, keepRouteState) {
4056
4321
  * called on sandbox.stop or hidden of keep-alive app
4057
4322
  */
4058
4323
  function removeStateAndPathFromBrowser(appName) {
4059
- attachRouteToBrowserURL(removeMicroPathFromURL(appName), removeMicroState(appName, globalEnv.rawWindow.history.state));
4324
+ attachRouteToBrowserURL(appName, removeMicroPathFromURL(appName), removeMicroState(appName, globalEnv.rawWindow.history.state));
4060
4325
  }
4061
4326
 
4062
4327
  /**
@@ -4079,8 +4344,6 @@ function createMicroFetch(url, target) {
4079
4344
  * When fetch rewrite by baseApp, domScope still active when exec rawWindow.fetch
4080
4345
  * If baseApp operate dom in fetch, it will cause error
4081
4346
  * The same for XMLHttpRequest, EventSource
4082
- * e.g.
4083
- * baseApp: <script crossorigin src="https://sgm-static.jd.com/sgm-2.8.0.js" name="SGMH5" sid="6f88a6e4ba4b4ae5acef2ec22c075085" appKey="jdb-adminb2b-pc"></script>
4084
4347
  */
4085
4348
  removeDomScope();
4086
4349
  return rawFetch.call(globalEnv.rawWindow, input, init, ...rests);
@@ -4188,7 +4451,7 @@ class SandBox {
4188
4451
  // create proxyWindow with Proxy(microAppWindow)
4189
4452
  this.proxyWindow = this.createProxyWindow(appName);
4190
4453
  // Rewrite global event listener & timeout
4191
- assign(this, effect(appName, this.microAppWindow));
4454
+ this.effectController = effect(appName, this.microAppWindow);
4192
4455
  // inject global properties
4193
4456
  this.initStaticGlobalKeys(this.microAppWindow, appName, url);
4194
4457
  }
@@ -4215,8 +4478,9 @@ class SandBox {
4215
4478
  this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute;
4216
4479
  }
4217
4480
  /**
4218
- * 1. prevent the key deleted during sandBox.stop after rewrite
4219
- * 2. umd mode will not delete any keys during sandBox.stop
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
4220
4484
  */
4221
4485
  if (!umdMode) {
4222
4486
  this.initGlobalKeysWhenStart(this.microAppWindow, this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__, disablePatchRequest);
@@ -4235,12 +4499,12 @@ class SandBox {
4235
4499
  * @param umdMode is umd mode
4236
4500
  * @param keepRouteState prevent reset route
4237
4501
  * @param clearEventSource clear MicroEventSource when destroy
4502
+ * @param clearData clear data from base app
4238
4503
  */
4239
- stop({ umdMode, keepRouteState, clearEventSource, }) {
4504
+ stop({ umdMode, keepRouteState, clearEventSource, clearData, }) {
4240
4505
  if (this.active) {
4241
- this.releaseEffect();
4242
- this.microAppWindow.microApp.clearDataListener();
4243
- this.microAppWindow.microApp.clearGlobalDataListener();
4506
+ // clear global event, timeout, data listener
4507
+ this.releaseGlobalEffect(clearData);
4244
4508
  if (this.removeHistoryListener) {
4245
4509
  this.clearRouteState(keepRouteState);
4246
4510
  // release listener of popstate
@@ -4273,10 +4537,33 @@ class SandBox {
4273
4537
  this.active = false;
4274
4538
  }
4275
4539
  }
4276
- // record umd snapshot before the first execution of umdHookMount
4277
- recordUmdSnapshot() {
4540
+ /**
4541
+ * clear global event, timeout, data listener
4542
+ * Scenes:
4543
+ * 1. unmount of normal/umd app
4544
+ * 2. hidden keep-alive app
4545
+ * 3. after init prerender app
4546
+ * @param clearData clear data from base app
4547
+ */
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();
4555
+ }
4556
+ }
4557
+ /**
4558
+ * record umd snapshot before the first execution of umdHookMount
4559
+ * Scenes:
4560
+ * 1. exec umdMountHook in umd mode
4561
+ * 2. hidden keep-alive app
4562
+ * 3. after init prerender app
4563
+ */
4564
+ recordEffectSnapshot() {
4278
4565
  // this.microAppWindow.__MICRO_APP_UMD_MODE__ = true
4279
- this.recordUmdEffect();
4566
+ this.effectController.recordEffect();
4280
4567
  recordDataCenterSnapshot(this.microAppWindow.microApp);
4281
4568
  // this.recordUmdInjectedValues = new Map<PropertyKey, unknown>()
4282
4569
  // this.injectedKeys.forEach((key: PropertyKey) => {
@@ -4284,13 +4571,17 @@ class SandBox {
4284
4571
  // })
4285
4572
  }
4286
4573
  // rebuild umd snapshot before remount umd app
4287
- rebuildUmdSnapshot() {
4574
+ rebuildEffectSnapshot() {
4288
4575
  // this.recordUmdInjectedValues!.forEach((value: unknown, key: PropertyKey) => {
4289
4576
  // Reflect.set(this.proxyWindow, key, value)
4290
4577
  // })
4291
- this.rebuildUmdEffect();
4578
+ this.effectController.rebuildEffect();
4292
4579
  rebuildDataCenterSnapshot(this.microAppWindow.microApp);
4293
4580
  }
4581
+ // set __MICRO_APP_PRE_RENDER__ state
4582
+ setPreRenderState(state) {
4583
+ this.microAppWindow.__MICRO_APP_PRE_RENDER__ = state;
4584
+ }
4294
4585
  /**
4295
4586
  * get scopeProperties and escapeProperties from plugins & adapter
4296
4587
  * @param appName app name
@@ -4424,6 +4715,7 @@ class SandBox {
4424
4715
  microAppWindow.__MICRO_APP_URL__ = url;
4425
4716
  microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
4426
4717
  microAppWindow.__MICRO_APP_WINDOW__ = microAppWindow;
4718
+ microAppWindow.__MICRO_APP_PRE_RENDER__ = false;
4427
4719
  microAppWindow.rawWindow = globalEnv.rawWindow;
4428
4720
  microAppWindow.rawDocument = globalEnv.rawDocument;
4429
4721
  microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
@@ -4441,7 +4733,6 @@ class SandBox {
4441
4733
  configurable: false,
4442
4734
  enumerable: true,
4443
4735
  get() {
4444
- throttleDeferForSetAppName(appName);
4445
4736
  // return globalEnv.rawDocument
4446
4737
  return proxyDocument;
4447
4738
  },
@@ -4450,7 +4741,6 @@ class SandBox {
4450
4741
  configurable: false,
4451
4742
  enumerable: false,
4452
4743
  get() {
4453
- throttleDeferForSetAppName(appName);
4454
4744
  // return globalEnv.rawRootDocument
4455
4745
  return MicroDocument;
4456
4746
  },
@@ -4496,6 +4786,7 @@ class SandBox {
4496
4786
  this.setHijackProperty(microAppWindow, appName);
4497
4787
  if (!disablePatchRequest)
4498
4788
  this.patchRequestApi(microAppWindow, appName, url);
4789
+ this.setScopeProperties(microAppWindow);
4499
4790
  }
4500
4791
  // set hijack Properties to microAppWindow
4501
4792
  setHijackProperty(microAppWindow, appName) {
@@ -4563,6 +4854,18 @@ class SandBox {
4563
4854
  },
4564
4855
  });
4565
4856
  }
4857
+ /**
4858
+ * Init scope keys to microAppWindow, prevent fall to rawWindow from with(microAppWindow)
4859
+ * like: if (!xxx) {}
4860
+ * NOTE:
4861
+ * 1. Symbol.unscopables cannot affect undefined keys
4862
+ * 2. Doesn't use for window.xxx because it fall to proxyWindow
4863
+ */
4864
+ setScopeProperties(microAppWindow) {
4865
+ this.scopeProperties.forEach((key) => {
4866
+ Reflect.set(microAppWindow, key, microAppWindow[key]);
4867
+ });
4868
+ }
4566
4869
  // set location & history for memory router
4567
4870
  setMicroAppRouter(microAppWindow, appName, url) {
4568
4871
  const { microLocation, microHistory } = createMicroRouter(appName, url);
@@ -4623,11 +4926,11 @@ class SandBox {
4623
4926
  return isFunction(rawValue) ? bindFunctionToRawObject(rawDocument, rawValue, 'DOCUMENT') : rawValue;
4624
4927
  },
4625
4928
  set: (target, key, value) => {
4626
- // Fix TypeError: Illegal invocation when set document.title
4627
- Reflect.set(target, key, value);
4628
4929
  /**
4629
- * If the set method returns false, and the assignment happened in strict-mode code, a TypeError will be thrown.
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.
4630
4932
  */
4933
+ Reflect.set(target, key, value);
4631
4934
  return true;
4632
4935
  }
4633
4936
  });
@@ -4735,7 +5038,7 @@ function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
4735
5038
  // micro app instances
4736
5039
  const appInstanceMap = new Map();
4737
5040
  class CreateApp {
4738
- constructor({ name, url, container, scopecss, useSandbox, inline, esmodule, ssrUrl, isPrefetch, }) {
5041
+ constructor({ name, url, container, scopecss, useSandbox, inline, esmodule, ssrUrl, isPrefetch, prefetchLevel, }) {
4739
5042
  this.state = appStates.CREATED;
4740
5043
  this.keepAliveState = null;
4741
5044
  this.keepAliveContainer = null;
@@ -4745,7 +5048,6 @@ class CreateApp {
4745
5048
  this.libraryName = null;
4746
5049
  this.umdMode = false;
4747
5050
  this.sandBox = null;
4748
- this.keepRouteState = false;
4749
5051
  this.fiber = false;
4750
5052
  this.useMemoryRouter = true;
4751
5053
  this.name = name;
@@ -4757,8 +5059,10 @@ class CreateApp {
4757
5059
  // not exist when prefetch 👇
4758
5060
  this.container = container !== null && container !== void 0 ? container : null;
4759
5061
  this.ssrUrl = ssrUrl !== null && ssrUrl !== void 0 ? ssrUrl : '';
4760
- // not exist when normal 👇
5062
+ // exist only prefetch 👇
4761
5063
  this.isPrefetch = isPrefetch !== null && isPrefetch !== void 0 ? isPrefetch : false;
5064
+ this.isPrerender = prefetchLevel === 3;
5065
+ this.prefetchLevel = prefetchLevel;
4762
5066
  // init actions
4763
5067
  appInstanceMap.set(this.name, this);
4764
5068
  this.source = { html: null, links: new Set(), scripts: new Set() };
@@ -4773,14 +5077,46 @@ class CreateApp {
4773
5077
  /**
4774
5078
  * When resource is loaded, mount app if it is not prefetch or unmount
4775
5079
  */
4776
- onLoad(html) {
5080
+ onLoad(html, defaultPage, disablePatchRequest) {
5081
+ var _a;
4777
5082
  if (++this.loadSourceLevel === 2) {
4778
5083
  this.source.html = html;
4779
5084
  this.state = appStates.LOADED;
4780
5085
  if (!this.isPrefetch && appStates.UNMOUNT !== this.state) {
4781
- // @ts-ignore
4782
5086
  getRootContainer(this.container).mount(this);
4783
5087
  }
5088
+ else if (this.isPrerender) {
5089
+ /**
5090
+ * PreRender is an option of prefetch, it will render app during prefetch
5091
+ * Limit:
5092
+ * 1. fiber forced on
5093
+ * 2. only virtual router support
5094
+ *
5095
+ * NOTE: (4P: not - update browser url, dispatch popstateEvent, reload window, dispatch lifecycle event)
5096
+ * 1. pushState/replaceState in child can update microLocation, but will not attach router info to browser url
5097
+ * 2. prevent dispatch popstate/hashchange event to browser
5098
+ * 3. all navigation actions of location are invalid (In the future, we can consider update microLocation without trigger browser reload)
5099
+ * 4. lifecycle event will not trigger when prerender
5100
+ *
5101
+ * Special scenes
5102
+ * 1. unmount prerender app when loading
5103
+ * 2. unmount prerender app when exec js
5104
+ * 2. unmount prerender app after exec js
5105
+ */
5106
+ const container = pureCreateElement('div');
5107
+ container.setAttribute('prerender', 'true');
5108
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.setPreRenderState(true);
5109
+ this.mount({
5110
+ container,
5111
+ inline: this.inline,
5112
+ useMemoryRouter: true,
5113
+ baseroute: '',
5114
+ fiber: true,
5115
+ esmodule: this.esmodule,
5116
+ defaultPage: defaultPage !== null && defaultPage !== void 0 ? defaultPage : '',
5117
+ disablePatchRequest: disablePatchRequest !== null && disablePatchRequest !== void 0 ? disablePatchRequest : false,
5118
+ });
5119
+ }
4784
5120
  }
4785
5121
  }
4786
5122
  /**
@@ -4797,29 +5133,86 @@ class CreateApp {
4797
5133
  /**
4798
5134
  * mount app
4799
5135
  * @param container app container
4800
- * @param inline js runs in inline mode
5136
+ * @param inline run js in inline mode
5137
+ * @param useMemoryRouter use virtual router
5138
+ * @param defaultPage default page of virtual router
4801
5139
  * @param baseroute route prefix, default is ''
4802
- * @param keepRouteState keep route state when unmount, default is false
4803
5140
  * @param disablePatchRequest prevent rewrite request method of child app
5141
+ * @param fiber run js in fiber mode
5142
+ * @param esmodule support type='module' script
4804
5143
  */
4805
- mount({ container, inline, esmodule, useMemoryRouter, baseroute, keepRouteState, defaultPage, disablePatchRequest, fiber, }) {
4806
- var _a, _b;
5144
+ mount({ container, inline, useMemoryRouter, defaultPage, baseroute, disablePatchRequest, fiber, esmodule, }) {
5145
+ var _a, _b, _c, _d, _e, _f;
5146
+ if (this.loadSourceLevel !== 2) {
5147
+ /**
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
5151
+ */
5152
+ this.container = container;
5153
+ // mount before prerender exec mount (loading source), set isPrerender to false
5154
+ this.isPrerender = false;
5155
+ // reset app state to LOADING
5156
+ this.state = appStates.LOADING;
5157
+ return;
5158
+ }
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);
5183
+ /**
5184
+ * set this.container to <micro-app></micro-app>
5185
+ * NOTE:
5186
+ * must before exec this.preRenderEvent?.forEach((cb) => cb())
5187
+ */
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
+ }
4807
5197
  this.container = container;
4808
5198
  this.inline = inline;
4809
5199
  this.esmodule = esmodule;
4810
- this.keepRouteState = keepRouteState;
4811
5200
  this.fiber = fiber;
4812
5201
  // use in sandbox/effect
4813
5202
  this.useMemoryRouter = useMemoryRouter;
4814
5203
  // this.hiddenRouter = hiddenRouter ?? this.hiddenRouter
4815
- if (this.loadSourceLevel !== 2) {
4816
- this.state = appStates.LOADING;
4817
- return;
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();
4818
5212
  }
4819
- dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
4820
5213
  this.state = appStates.MOUNTING;
4821
5214
  cloneContainer(this.source.html, this.container, !this.umdMode);
4822
- (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.start({
5215
+ (_e = this.sandBox) === null || _e === void 0 ? void 0 : _e.start({
4823
5216
  umdMode: this.umdMode,
4824
5217
  baseroute,
4825
5218
  useMemoryRouter,
@@ -4831,6 +5224,7 @@ class CreateApp {
4831
5224
  let hasDispatchMountedEvent = false;
4832
5225
  // if all js are executed, param isFinished will be true
4833
5226
  execScripts(this, (isFinished) => {
5227
+ var _a;
4834
5228
  if (!this.umdMode) {
4835
5229
  const { mount, unmount } = this.getUmdLibraryHooks();
4836
5230
  /**
@@ -4844,9 +5238,9 @@ class CreateApp {
4844
5238
  this.umdMode = true;
4845
5239
  if (this.sandBox)
4846
5240
  this.sandBox.proxyWindow.__MICRO_APP_UMD_MODE__ = true;
4847
- // this.sandBox?.recordUmdSnapshot()
5241
+ // this.sandBox?.recordEffectSnapshot()
4848
5242
  try {
4849
- umdHookMountResult = this.umdHookMount();
5243
+ umdHookMountResult = this.umdHookMount(microApp.getData(this.name, true));
4850
5244
  }
4851
5245
  catch (e) {
4852
5246
  logError('an error occurred in the mount function \n', this.name, e);
@@ -4855,12 +5249,19 @@ class CreateApp {
4855
5249
  }
4856
5250
  if (!hasDispatchMountedEvent && (isFinished === true || this.umdMode)) {
4857
5251
  hasDispatchMountedEvent = true;
4858
- this.handleMounted(umdHookMountResult);
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();
5256
+ }
5257
+ else {
5258
+ dispatchMounted();
5259
+ }
4859
5260
  }
4860
5261
  });
4861
5262
  }
4862
5263
  else {
4863
- (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.rebuildUmdSnapshot();
5264
+ (_f = this.sandBox) === null || _f === void 0 ? void 0 : _f.rebuildEffectSnapshot();
4864
5265
  try {
4865
5266
  umdHookMountResult = this.umdHookMount();
4866
5267
  }
@@ -4890,6 +5291,9 @@ class CreateApp {
4890
5291
  dispatchMountedEvent() {
4891
5292
  if (appStates.UNMOUNT !== this.state) {
4892
5293
  this.state = appStates.MOUNTED;
5294
+ // call window.onmount of child app
5295
+ callFnWithTryCatch(this.getGlobalEventListener(microGlobalEvent.ONMOUNT), this.name, `window.${microGlobalEvent.ONMOUNT}`, microApp.getData(this.name, true));
5296
+ // dispatch event mounted to parent
4893
5297
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.MOUNTED);
4894
5298
  }
4895
5299
  }
@@ -4897,9 +5301,11 @@ class CreateApp {
4897
5301
  * unmount app
4898
5302
  * NOTE: Do not add any params on account of unmountApp
4899
5303
  * @param destroy completely destroy, delete cache resources
5304
+ * @param clearData clear data of dateCenter
5305
+ * @param keepRouteState keep route state when unmount, default is false
4900
5306
  * @param unmountcb callback of unmount
4901
5307
  */
4902
- unmount(destroy, unmountcb) {
5308
+ unmount({ destroy, clearData, keepRouteState, unmountcb, }) {
4903
5309
  if (this.state === appStates.LOAD_FAILED) {
4904
5310
  destroy = true;
4905
5311
  }
@@ -4914,38 +5320,50 @@ class CreateApp {
4914
5320
  */
4915
5321
  if (isFunction(this.umdHookUnmount)) {
4916
5322
  try {
4917
- umdHookUnmountResult = this.umdHookUnmount();
5323
+ umdHookUnmountResult = this.umdHookUnmount(microApp.getData(this.name, true));
4918
5324
  }
4919
5325
  catch (e) {
4920
5326
  logError('an error occurred in the unmount function \n', this.name, e);
4921
5327
  }
4922
5328
  }
5329
+ // call window.onunmount of child app
5330
+ callFnWithTryCatch(this.getGlobalEventListener(microGlobalEvent.ONUNMOUNT), this.name, `window.${microGlobalEvent.ONUNMOUNT}`);
4923
5331
  // dispatch unmount event to micro app
4924
5332
  dispatchCustomEventToMicroApp('unmount', this.name);
4925
- this.handleUnmounted(destroy, umdHookUnmountResult, unmountcb);
5333
+ this.handleUnmounted(destroy, clearData, keepRouteState, umdHookUnmountResult, unmountcb);
4926
5334
  }
4927
5335
  /**
4928
5336
  * handle for promise umdHookUnmount
4929
5337
  * @param destroy completely destroy, delete cache resources
5338
+ * @param clearData clear data of dateCenter
5339
+ * @param keepRouteState keep route state when unmount, default is false
4930
5340
  * @param umdHookUnmountResult result of umdHookUnmount
4931
5341
  * @param unmountcb callback of unmount
4932
5342
  */
4933
- handleUnmounted(destroy, umdHookUnmountResult, unmountcb) {
5343
+ handleUnmounted(destroy, clearData, keepRouteState, umdHookUnmountResult, unmountcb) {
5344
+ const unmountParam = {
5345
+ destroy,
5346
+ clearData,
5347
+ keepRouteState,
5348
+ unmountcb,
5349
+ };
4934
5350
  if (isPromise(umdHookUnmountResult)) {
4935
5351
  umdHookUnmountResult
4936
- .then(() => this.actionsForUnmount(destroy, unmountcb))
4937
- .catch(() => this.actionsForUnmount(destroy, unmountcb));
5352
+ .then(() => this.actionsForUnmount(unmountParam))
5353
+ .catch(() => this.actionsForUnmount(unmountParam));
4938
5354
  }
4939
5355
  else {
4940
- this.actionsForUnmount(destroy, unmountcb);
5356
+ this.actionsForUnmount(unmountParam);
4941
5357
  }
4942
5358
  }
4943
5359
  /**
4944
5360
  * actions for unmount app
4945
5361
  * @param destroy completely destroy, delete cache resources
5362
+ * @param clearData clear data of dateCenter
5363
+ * @param keepRouteState keep route state when unmount, default is false
4946
5364
  * @param unmountcb callback of unmount
4947
5365
  */
4948
- actionsForUnmount(destroy, unmountcb) {
5366
+ actionsForUnmount({ destroy, clearData, keepRouteState, unmountcb }) {
4949
5367
  var _a, _b;
4950
5368
  if (destroy) {
4951
5369
  this.actionsForCompletelyDestroy();
@@ -4954,7 +5372,7 @@ class CreateApp {
4954
5372
  cloneContainer(this.container, this.source.html, false);
4955
5373
  }
4956
5374
  if (this.umdMode) {
4957
- (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordUmdSnapshot();
5375
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordEffectSnapshot();
4958
5376
  }
4959
5377
  /**
4960
5378
  * this.container maybe contains micro-app element, stop sandbox should exec after cloneContainer
@@ -4964,27 +5382,34 @@ class CreateApp {
4964
5382
  */
4965
5383
  (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.stop({
4966
5384
  umdMode: this.umdMode,
4967
- keepRouteState: this.keepRouteState && !destroy,
5385
+ keepRouteState: keepRouteState && !destroy,
4968
5386
  clearEventSource: !this.umdMode || destroy,
5387
+ clearData: clearData || destroy,
4969
5388
  });
4970
5389
  if (!getActiveApps().length) {
4971
5390
  releasePatchSetAttribute();
4972
5391
  }
4973
5392
  // dispatch unmount event to base app
4974
5393
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.UNMOUNT);
5394
+ this.resetConfig();
5395
+ unmountcb && unmountcb();
5396
+ }
5397
+ resetConfig() {
4975
5398
  this.container.innerHTML = '';
4976
5399
  this.container = null;
4977
- unmountcb && unmountcb();
5400
+ this.isPrerender = false;
5401
+ this.preRenderEvent = undefined;
4978
5402
  }
4979
5403
  // actions for completely destroy
4980
5404
  actionsForCompletelyDestroy() {
4981
5405
  if (!this.useSandbox && this.umdMode) {
4982
5406
  delete window[this.libraryName];
4983
5407
  }
5408
+ sourceCenter.script.deleteInlineInfo(this.source.scripts);
4984
5409
  appInstanceMap.delete(this.name);
4985
5410
  }
4986
5411
  // hidden app when disconnectedCallback called with keep-alive
4987
- hiddenKeepAliveApp() {
5412
+ hiddenKeepAliveApp(callback) {
4988
5413
  var _a;
4989
5414
  const oldContainer = this.container;
4990
5415
  cloneContainer(this.container, this.keepAliveContainer ? this.keepAliveContainer : (this.keepAliveContainer = document.createElement('div')), false);
@@ -4997,12 +5422,17 @@ class CreateApp {
4997
5422
  });
4998
5423
  // dispatch afterHidden event to base app
4999
5424
  dispatchLifecyclesEvent(oldContainer, this.name, lifeCycles.AFTERHIDDEN);
5000
- // called after lifeCyclesEvent
5001
- (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.removeRouteInfoForKeepAliveApp();
5425
+ if (this.useMemoryRouter) {
5426
+ // called after lifeCyclesEvent
5427
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.removeRouteInfoForKeepAliveApp();
5428
+ }
5429
+ this.recordAndReleaseEffect();
5430
+ callback && callback();
5002
5431
  }
5003
5432
  // show app when connectedCallback called with keep-alive
5004
5433
  showKeepAliveApp(container) {
5005
- var _a;
5434
+ var _a, _b;
5435
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.rebuildEffectSnapshot();
5006
5436
  // dispatch beforeShow event to micro-app
5007
5437
  dispatchCustomEventToMicroApp('appstate-change', this.name, {
5008
5438
  appState: 'beforeshow',
@@ -5012,8 +5442,10 @@ class CreateApp {
5012
5442
  cloneContainer(this.container, container, false);
5013
5443
  this.container = container;
5014
5444
  this.keepAliveState = keepAliveStates.KEEP_ALIVE_SHOW;
5015
- // called before lifeCyclesEvent
5016
- (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.setRouteInfoForKeepAliveApp();
5445
+ if (this.useMemoryRouter) {
5446
+ // called before lifeCyclesEvent
5447
+ (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.setRouteInfoForKeepAliveApp();
5448
+ }
5017
5449
  // dispatch afterShow event to micro-app
5018
5450
  dispatchCustomEventToMicroApp('appstate-change', this.name, {
5019
5451
  appState: 'aftershow',
@@ -5053,6 +5485,23 @@ class CreateApp {
5053
5485
  }
5054
5486
  return {};
5055
5487
  }
5488
+ getGlobalEventListener(eventName) {
5489
+ var _a;
5490
+ // @ts-ignore
5491
+ const listener = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow[eventName];
5492
+ return isFunction(listener) ? listener : null;
5493
+ }
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();
5504
+ }
5056
5505
  }
5057
5506
 
5058
5507
  /**
@@ -5088,7 +5537,7 @@ function defineElement(tagName) {
5088
5537
  keepAliveStates.KEEP_ALIVE_HIDDEN !== existApp.getKeepAliveState() &&
5089
5538
  !existApp.isPrefetch) {
5090
5539
  this.setAttribute('name', this.appName);
5091
- return logError(`app name conflict, an app named ${formatAttrName} is running`, this.appName);
5540
+ return logError(`app name conflict, an app named ${formatAttrName} is running`);
5092
5541
  }
5093
5542
  }
5094
5543
  if (formatAttrName !== this.appName || formatAttrUrl !== this.appUrl) {
@@ -5135,25 +5584,56 @@ function defineElement(tagName) {
5135
5584
  * In some special scenes, such as vue's keep-alive, the micro-app will be inserted and deleted twice in an instant
5136
5585
  * So we execute the mount method async and record connectState to prevent repeated rendering
5137
5586
  */
5587
+ const effectiveApp = this.appName && this.appUrl;
5138
5588
  defer(() => {
5139
5589
  if (this.connectStateMap.get(cacheCount)) {
5140
5590
  dispatchLifecyclesEvent(this, this.appName, lifeCycles.CREATED);
5141
- this.initialMount();
5591
+ /**
5592
+ * If insert micro-app element without name or url, and set them in next action like angular,
5593
+ * handleConnected will be executed twice, causing the app render repeatedly,
5594
+ * so we only execute handleConnected() if url and name exist when connectedCallback
5595
+ */
5596
+ effectiveApp && this.handleConnected();
5142
5597
  }
5143
5598
  });
5144
5599
  }
5145
5600
  disconnectedCallback() {
5146
5601
  this.connectStateMap.set(this.connectedCount, false);
5602
+ this.handleDisconnected();
5603
+ }
5604
+ /**
5605
+ * Re render app from the command line
5606
+ * MicroAppElement.reload(destroy)
5607
+ */
5608
+ reload(destroy) {
5609
+ return new Promise((resolve) => {
5610
+ const handleAfterReload = () => {
5611
+ this.removeEventListener(lifeCycles.MOUNTED, handleAfterReload);
5612
+ this.removeEventListener(lifeCycles.AFTERSHOW, handleAfterReload);
5613
+ resolve(true);
5614
+ };
5615
+ this.addEventListener(lifeCycles.MOUNTED, handleAfterReload);
5616
+ this.addEventListener(lifeCycles.AFTERSHOW, handleAfterReload);
5617
+ this.handleDisconnected(destroy, () => {
5618
+ this.handleConnected();
5619
+ });
5620
+ });
5621
+ }
5622
+ /**
5623
+ * common action for unmount
5624
+ * @param destroy reload param
5625
+ */
5626
+ handleDisconnected(destroy = false, callback) {
5147
5627
  const app = appInstanceMap.get(this.appName);
5148
5628
  if (app &&
5149
5629
  app.getAppState() !== appStates.UNMOUNT &&
5150
5630
  app.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN) {
5151
5631
  // keep-alive
5152
- if (this.getKeepAliveModeResult()) {
5153
- this.handleHiddenKeepAliveApp();
5632
+ if (this.getKeepAliveModeResult() && !destroy) {
5633
+ this.handleHiddenKeepAliveApp(callback);
5154
5634
  }
5155
5635
  else {
5156
- this.handleUnmount(this.getDestroyCompatibleResult());
5636
+ this.handleUnmount(destroy || this.getDestroyCompatibleResult(), callback);
5157
5637
  }
5158
5638
  }
5159
5639
  }
@@ -5191,12 +5671,12 @@ function defineElement(tagName) {
5191
5671
  }
5192
5672
  // handle for connectedCallback run before attributeChangedCallback
5193
5673
  handleInitialNameAndUrl() {
5194
- this.connectStateMap.get(this.connectedCount) && this.initialMount();
5674
+ this.connectStateMap.get(this.connectedCount) && this.handleConnected();
5195
5675
  }
5196
5676
  /**
5197
5677
  * first mount of this app
5198
5678
  */
5199
- initialMount() {
5679
+ handleConnected() {
5200
5680
  if (!this.appName || !this.appUrl)
5201
5681
  return;
5202
5682
  if (this.getDisposeResult('shadowDOM') && !this.shadowRoot && isFunction(this.attachShadow)) {
@@ -5223,7 +5703,7 @@ function defineElement(tagName) {
5223
5703
  this.handleAppMount(app);
5224
5704
  }
5225
5705
  else if (app.isPrefetch || app.getAppState() === appStates.UNMOUNT) {
5226
- if (process.env.NODE_ENV !== 'production' &&
5706
+ if ((process.env.NODE_ENV !== 'production') &&
5227
5707
  app.scopecss === this.isScopecss() &&
5228
5708
  app.useSandbox === this.isSandbox()) {
5229
5709
  /**
@@ -5234,7 +5714,7 @@ function defineElement(tagName) {
5234
5714
  this.handleCreateApp();
5235
5715
  }
5236
5716
  else {
5237
- logError(`app name conflict, an app named: ${this.appName} with url: ${existAppUrl} is running`, this.appName);
5717
+ logError(`app name conflict, an app named: ${this.appName} with url: ${existAppUrl} is running`);
5238
5718
  }
5239
5719
  }
5240
5720
  else {
@@ -5269,7 +5749,7 @@ function defineElement(tagName) {
5269
5749
  }
5270
5750
  else {
5271
5751
  // the hidden keep-alive app is still active
5272
- logError(`app name conflict, an app named ${this.appName} is running`, this.appName);
5752
+ logError(`app name conflict, an app named ${this.appName} is running`);
5273
5753
  }
5274
5754
  }
5275
5755
  else if (existApp.url === this.appUrl && existApp.ssrUrl === this.ssrUrl) {
@@ -5296,6 +5776,27 @@ function defineElement(tagName) {
5296
5776
  }
5297
5777
  return true;
5298
5778
  }
5779
+ // create app instance
5780
+ handleCreateApp() {
5781
+ var _a;
5782
+ /**
5783
+ * actions for destroy old app
5784
+ * fix of unmounted umd app with disableSandbox
5785
+ */
5786
+ if (appInstanceMap.has(this.appName)) {
5787
+ appInstanceMap.get(this.appName).actionsForCompletelyDestroy();
5788
+ }
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
+ }
5299
5800
  /**
5300
5801
  * mount app
5301
5802
  * some serious note before mount:
@@ -5315,55 +5816,38 @@ function defineElement(tagName) {
5315
5816
  app.mount({
5316
5817
  container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
5317
5818
  inline: this.getDisposeResult('inline'),
5318
- esmodule: this.getDisposeResult('esmodule'),
5319
5819
  useMemoryRouter: !this.getDisposeResult('disable-memory-router'),
5320
- baseroute: this.getBaseRouteCompatible(),
5321
- keepRouteState: this.getDisposeResult('keep-router-state'),
5322
5820
  defaultPage: this.getDefaultPageValue(),
5323
- hiddenRouter: this.getDisposeResult('hidden-router'),
5821
+ baseroute: this.getBaseRouteCompatible(),
5324
5822
  disablePatchRequest: this.getDisposeResult('disable-patch-request'),
5325
5823
  fiber: this.getDisposeResult('fiber'),
5326
- });
5327
- }
5328
- // create app instance
5329
- handleCreateApp() {
5330
- var _a;
5331
- /**
5332
- * actions for destroy old app
5333
- * fix of unmounted umd app with disableSandbox
5334
- */
5335
- if (appInstanceMap.has(this.appName)) {
5336
- appInstanceMap.get(this.appName).actionsForCompletelyDestroy();
5337
- }
5338
- new CreateApp({
5339
- name: this.appName,
5340
- url: this.appUrl,
5341
- scopecss: this.isScopecss(),
5342
- useSandbox: this.isSandbox(),
5343
- inline: this.getDisposeResult('inline'),
5344
5824
  esmodule: this.getDisposeResult('esmodule'),
5345
- container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
5346
- ssrUrl: this.ssrUrl,
5347
5825
  });
5348
5826
  }
5349
5827
  /**
5350
5828
  * unmount app
5351
5829
  * @param destroy delete cache resources when unmount
5352
5830
  */
5353
- handleUnmount(destroy, unmountCb) {
5831
+ handleUnmount(destroy, unmountcb) {
5354
5832
  const app = appInstanceMap.get(this.appName);
5355
5833
  if (app &&
5356
5834
  app.getAppState() !== appStates.UNMOUNT) {
5357
- app.unmount(destroy, unmountCb);
5835
+ app.unmount({
5836
+ destroy,
5837
+ clearData: this.getDisposeResult('clear-data'),
5838
+ keepRouteState: this.getDisposeResult('keep-router-state'),
5839
+ unmountcb,
5840
+ });
5358
5841
  }
5359
5842
  }
5360
5843
  // hidden app when disconnectedCallback called with keep-alive
5361
- handleHiddenKeepAliveApp() {
5844
+ handleHiddenKeepAliveApp(callback) {
5362
5845
  const app = appInstanceMap.get(this.appName);
5363
5846
  if (app &&
5364
5847
  app.getAppState() !== appStates.UNMOUNT &&
5365
- app.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN)
5366
- app.hiddenKeepAliveApp();
5848
+ app.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN) {
5849
+ app.hiddenKeepAliveApp(callback);
5850
+ }
5367
5851
  }
5368
5852
  // show app when connectedCallback called with keep-alive
5369
5853
  handleShowKeepAliveApp(app) {
@@ -5480,7 +5964,7 @@ function defineElement(tagName) {
5480
5964
  return null;
5481
5965
  }
5482
5966
  }
5483
- window.customElements.define(tagName, MicroAppElement);
5967
+ globalEnv.rawWindow.customElements.define(tagName, MicroAppElement);
5484
5968
  }
5485
5969
 
5486
5970
  /**
@@ -5488,30 +5972,57 @@ function defineElement(tagName) {
5488
5972
  * {
5489
5973
  * name: string,
5490
5974
  * url: string,
5491
- * disableScopecss?: boolean,
5492
- * disableSandbox?: boolean,
5493
- * disableMemoryRouter?: boolean,
5975
+ * esmodule: boolean,
5976
+ * inline: boolean,
5977
+ * 'disable-scopecss': boolean,
5978
+ * 'disable-sandbox': boolean,
5979
+ * level: number,
5980
+ * 'default-page': string,
5981
+ * 'disable-patch-request': boolean,
5494
5982
  * },
5495
5983
  * ...
5496
5984
  * ])
5497
5985
  * Note:
5498
- * 1: preFetch is asynchronous and is performed only when the browser is idle
5499
- * 2: disableScopecss, disableSandbox, disableMemoryRouter must be same with micro-app element, if conflict, the one who executes first shall prevail
5500
- * @param apps micro apps
5986
+ * 1: preFetch is async and is performed only when the browser is idle
5987
+ * 2: options of prefetch preferably match the config of the micro-app element, although this is not required
5988
+ * @param apps micro app options
5989
+ * @param delay delay time
5501
5990
  */
5502
- function preFetch(apps) {
5991
+ function preFetch(apps, delay) {
5503
5992
  if (!isBrowser) {
5504
5993
  return logError('preFetch is only supported in browser environment');
5505
5994
  }
5506
5995
  requestIdleCallback(() => {
5507
- isFunction(apps) && (apps = apps());
5508
- if (isArray(apps)) {
5509
- apps.reduce((pre, next) => pre.then(() => preFetchInSerial(next)), Promise.resolve());
5510
- }
5996
+ const delayTime = isNumber(delay) ? delay : microApp.options.prefetchDelay;
5997
+ /**
5998
+ * TODO: remove setTimeout
5999
+ * Is there a better way?
6000
+ */
6001
+ setTimeout(() => {
6002
+ // releasePrefetchEffect()
6003
+ preFetchInSerial(apps);
6004
+ }, isNumber(delayTime) ? delayTime : 3000);
5511
6005
  });
6006
+ // const handleOnLoad = (): void => {
6007
+ // releasePrefetchEffect()
6008
+ // requestIdleCallback(() => {
6009
+ // preFetchInSerial(apps)
6010
+ // })
6011
+ // }
6012
+ // const releasePrefetchEffect = (): void => {
6013
+ // window.removeEventListener('load', handleOnLoad)
6014
+ // clearTimeout(preFetchTime)
6015
+ // }
6016
+ // window.addEventListener('load', handleOnLoad)
6017
+ }
6018
+ function preFetchInSerial(apps) {
6019
+ isFunction(apps) && (apps = apps());
6020
+ if (isArray(apps)) {
6021
+ apps.reduce((pre, next) => pre.then(() => preFetchAction(next)), Promise.resolve());
6022
+ }
5512
6023
  }
5513
6024
  // sequential preload app
5514
- function preFetchInSerial(options) {
6025
+ function preFetchAction(options) {
5515
6026
  return promiseRequestIdle((resolve) => {
5516
6027
  var _a, _b, _c, _d, _e, _f;
5517
6028
  if (isPlainObject(options) && navigator.onLine) {
@@ -5521,21 +6032,22 @@ function preFetchInSerial(options) {
5521
6032
  const app = new CreateApp({
5522
6033
  name: options.name,
5523
6034
  url: options.url,
6035
+ isPrefetch: true,
5524
6036
  scopecss: !((_b = (_a = options['disable-scopecss']) !== null && _a !== void 0 ? _a : options.disableScopecss) !== null && _b !== void 0 ? _b : microApp.options['disable-scopecss']),
5525
6037
  useSandbox: !((_d = (_c = options['disable-sandbox']) !== null && _c !== void 0 ? _c : options.disableSandbox) !== null && _d !== void 0 ? _d : microApp.options['disable-sandbox']),
5526
6038
  inline: (_e = options.inline) !== null && _e !== void 0 ? _e : microApp.options.inline,
5527
6039
  esmodule: (_f = options.esmodule) !== null && _f !== void 0 ? _f : microApp.options.esmodule,
5528
- isPrefetch: true,
6040
+ prefetchLevel: options.level && PREFETCH_LEVEL.includes(options.level) ? options.level : microApp.options.prefetchLevel && PREFETCH_LEVEL.includes(microApp.options.prefetchLevel) ? microApp.options.prefetchLevel : 2,
5529
6041
  });
5530
6042
  const oldOnload = app.onLoad;
5531
6043
  const oldOnLoadError = app.onLoadError;
5532
6044
  app.onLoad = (html) => {
5533
6045
  resolve();
5534
- oldOnload.call(app, html);
6046
+ oldOnload.call(app, html, options['default-page'], options['disable-patch-request']);
5535
6047
  };
5536
- app.onLoadError = (e) => {
6048
+ app.onLoadError = (...rests) => {
5537
6049
  resolve();
5538
- oldOnLoadError.call(app, e);
6050
+ oldOnLoadError.call(app, ...rests);
5539
6051
  };
5540
6052
  }
5541
6053
  else {
@@ -5593,13 +6105,14 @@ function fetchGlobalResources(resources, suffix, sourceHandler) {
5593
6105
  /**
5594
6106
  * if app not prefetch & not unmount, then app is active
5595
6107
  * @param excludeHiddenApp exclude hidden keep-alive app, default is false
6108
+ * @param excludePreRender exclude pre render app
5596
6109
  * @returns active apps
5597
6110
  */
5598
- function getActiveApps(excludeHiddenApp = false) {
6111
+ function getActiveApps({ excludeHiddenApp = false, excludePreRender = false, } = {}) {
5599
6112
  const activeApps = [];
5600
6113
  appInstanceMap.forEach((app, appName) => {
5601
6114
  if (appStates.UNMOUNT !== app.getAppState() &&
5602
- !app.isPrefetch &&
6115
+ (!app.isPrefetch || (app.isPrerender && !excludePreRender)) &&
5603
6116
  (!excludeHiddenApp ||
5604
6117
  keepAliveStates.KEEP_ALIVE_HIDDEN !== app.getKeepAliveState())) {
5605
6118
  activeApps.push(appName);
@@ -5625,33 +6138,43 @@ function unmountApp(appName, options) {
5625
6138
  if (options === null || options === void 0 ? void 0 : options.destroy) {
5626
6139
  app.actionsForCompletelyDestroy();
5627
6140
  }
5628
- resolve();
6141
+ resolve(true);
5629
6142
  }
5630
6143
  else if (app.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN) {
5631
6144
  if (options === null || options === void 0 ? void 0 : options.destroy) {
5632
- app.unmount(true, resolve);
6145
+ app.unmount({
6146
+ destroy: true,
6147
+ clearData: true,
6148
+ keepRouteState: true,
6149
+ unmountcb: resolve.bind(null, true)
6150
+ });
5633
6151
  }
5634
6152
  else if (options === null || options === void 0 ? void 0 : options.clearAliveState) {
5635
- app.unmount(false, resolve);
6153
+ app.unmount({
6154
+ destroy: false,
6155
+ clearData: !!options.clearData,
6156
+ keepRouteState: true,
6157
+ unmountcb: resolve.bind(null, true)
6158
+ });
5636
6159
  }
5637
6160
  else {
5638
- resolve();
6161
+ resolve(true);
5639
6162
  }
5640
6163
  }
5641
6164
  else {
5642
6165
  const container = getRootContainer(app.container);
5643
6166
  const unmountHandler = () => {
5644
- container.removeEventListener('unmount', unmountHandler);
5645
- container.removeEventListener('afterhidden', afterhiddenHandler);
5646
- resolve();
6167
+ container.removeEventListener(lifeCycles.UNMOUNT, unmountHandler);
6168
+ container.removeEventListener(lifeCycles.AFTERHIDDEN, afterhiddenHandler);
6169
+ resolve(true);
5647
6170
  };
5648
6171
  const afterhiddenHandler = () => {
5649
- container.removeEventListener('unmount', unmountHandler);
5650
- container.removeEventListener('afterhidden', afterhiddenHandler);
5651
- resolve();
6172
+ container.removeEventListener(lifeCycles.UNMOUNT, unmountHandler);
6173
+ container.removeEventListener(lifeCycles.AFTERHIDDEN, afterhiddenHandler);
6174
+ resolve(true);
5652
6175
  };
5653
- container.addEventListener('unmount', unmountHandler);
5654
- container.addEventListener('afterhidden', afterhiddenHandler);
6176
+ container.addEventListener(lifeCycles.UNMOUNT, unmountHandler);
6177
+ container.addEventListener(lifeCycles.AFTERHIDDEN, afterhiddenHandler);
5655
6178
  if (options === null || options === void 0 ? void 0 : options.destroy) {
5656
6179
  let destroyAttrValue, destoryAttrValue;
5657
6180
  container.hasAttribute('destroy') && (destroyAttrValue = container.getAttribute('destroy'));
@@ -5659,37 +6182,131 @@ function unmountApp(appName, options) {
5659
6182
  container.setAttribute('destroy', 'true');
5660
6183
  container.parentNode.removeChild(container);
5661
6184
  container.removeAttribute('destroy');
5662
- typeof destroyAttrValue === 'string' && container.setAttribute('destroy', destroyAttrValue);
5663
- typeof destoryAttrValue === 'string' && container.setAttribute('destory', destoryAttrValue);
6185
+ isString(destroyAttrValue) && container.setAttribute('destroy', destroyAttrValue);
6186
+ isString(destoryAttrValue) && container.setAttribute('destory', destoryAttrValue);
5664
6187
  }
5665
6188
  else if ((options === null || options === void 0 ? void 0 : options.clearAliveState) && container.hasAttribute('keep-alive')) {
5666
6189
  const keepAliveAttrValue = container.getAttribute('keep-alive');
5667
6190
  container.removeAttribute('keep-alive');
6191
+ let clearDataAttrValue;
6192
+ if (options.clearData) {
6193
+ clearDataAttrValue = container.getAttribute('clear-data');
6194
+ container.setAttribute('clear-data', 'true');
6195
+ }
5668
6196
  container.parentNode.removeChild(container);
5669
6197
  container.setAttribute('keep-alive', keepAliveAttrValue);
6198
+ isString(clearDataAttrValue) && container.setAttribute('clear-data', clearDataAttrValue);
5670
6199
  }
5671
6200
  else {
6201
+ let clearDataAttrValue;
6202
+ if (options === null || options === void 0 ? void 0 : options.clearData) {
6203
+ clearDataAttrValue = container.getAttribute('clear-data');
6204
+ container.setAttribute('clear-data', 'true');
6205
+ }
5672
6206
  container.parentNode.removeChild(container);
6207
+ isString(clearDataAttrValue) && container.setAttribute('clear-data', clearDataAttrValue);
5673
6208
  }
5674
6209
  }
5675
6210
  }
5676
6211
  else {
5677
6212
  logWarn(`app ${appName} does not exist`);
5678
- resolve();
6213
+ resolve(false);
5679
6214
  }
5680
6215
  });
5681
6216
  }
5682
6217
  // unmount all apps in turn
5683
6218
  function unmountAllApps(options) {
5684
- return Array.from(appInstanceMap.keys()).reduce((pre, next) => pre.then(() => unmountApp(next, options)), Promise.resolve());
6219
+ return Array.from(appInstanceMap.keys()).reduce((pre, next) => pre.then(() => unmountApp(next, options)), Promise.resolve(true));
6220
+ }
6221
+ /**
6222
+ * Re render app from the command line
6223
+ * microApp.reload(destroy)
6224
+ * @param appName app.name
6225
+ * @param destroy unmount app with destroy mode
6226
+ * @returns Promise<boolean>
6227
+ */
6228
+ function reload(appName, destroy) {
6229
+ return new Promise((resolve) => {
6230
+ const app = appInstanceMap.get(formatAppName(appName));
6231
+ if (app) {
6232
+ const rootContainer = app.container && getRootContainer(app.container);
6233
+ if (rootContainer) {
6234
+ resolve(rootContainer.reload(destroy));
6235
+ }
6236
+ else {
6237
+ logWarn(`app ${appName} is not rendered, cannot use reload`);
6238
+ resolve(false);
6239
+ }
6240
+ }
6241
+ else {
6242
+ logWarn(`app ${appName} does not exist`);
6243
+ resolve(false);
6244
+ }
6245
+ });
6246
+ }
6247
+ /**
6248
+ * Manually render app
6249
+ * @param options RenderAppOptions
6250
+ * @returns Promise<boolean>
6251
+ */
6252
+ function renderApp(options) {
6253
+ return new Promise((resolve) => {
6254
+ if (!isPlainObject(options))
6255
+ return logError('renderApp options must be an object');
6256
+ const container = isElement(options.container) ? options.container : isString(options.container) ? document.querySelector(options.container) : null;
6257
+ if (!isElement(container))
6258
+ return logError('Target container is not a DOM element.');
6259
+ const microAppElement = pureCreateElement(microApp.tagName);
6260
+ for (const attr in options) {
6261
+ if (attr === 'onDataChange') {
6262
+ if (isFunction(options[attr])) {
6263
+ microAppElement.addEventListener('datachange', options[attr]);
6264
+ }
6265
+ }
6266
+ else if (attr === 'lifeCycles') {
6267
+ const lifeCycleConfig = options[attr];
6268
+ if (isPlainObject(lifeCycleConfig)) {
6269
+ for (const lifeName in lifeCycleConfig) {
6270
+ if (lifeName.toUpperCase() in lifeCycles && isFunction(lifeCycleConfig[lifeName])) {
6271
+ microAppElement.addEventListener(lifeName.toLowerCase(), lifeCycleConfig[lifeName]);
6272
+ }
6273
+ }
6274
+ }
6275
+ }
6276
+ else if (attr !== 'container') {
6277
+ microAppElement.setAttribute(attr, options[attr]);
6278
+ }
6279
+ }
6280
+ const handleMount = () => {
6281
+ releaseListener();
6282
+ resolve(true);
6283
+ };
6284
+ const handleError = () => {
6285
+ releaseListener();
6286
+ resolve(false);
6287
+ };
6288
+ const releaseListener = () => {
6289
+ microAppElement.removeEventListener(lifeCycles.MOUNTED, handleMount);
6290
+ microAppElement.removeEventListener(lifeCycles.ERROR, handleError);
6291
+ };
6292
+ microAppElement.addEventListener(lifeCycles.MOUNTED, handleMount);
6293
+ microAppElement.addEventListener(lifeCycles.ERROR, handleError);
6294
+ container.appendChild(microAppElement);
6295
+ });
5685
6296
  }
5686
6297
  class MicroApp extends EventCenterForBaseApp {
5687
6298
  constructor() {
5688
6299
  super(...arguments);
5689
6300
  this.tagName = 'micro-app';
5690
6301
  this.options = {};
5691
- this.preFetch = preFetch;
5692
6302
  this.router = router;
6303
+ this.preFetch = preFetch;
6304
+ this.unmountApp = unmountApp;
6305
+ this.unmountAllApps = unmountAllApps;
6306
+ this.getActiveApps = getActiveApps;
6307
+ this.getAllApps = getAllApps;
6308
+ this.reload = reload;
6309
+ this.renderApp = renderApp;
5693
6310
  }
5694
6311
  start(options) {
5695
6312
  var _a, _b;
@@ -5733,8 +6350,8 @@ class MicroApp extends EventCenterForBaseApp {
5733
6350
  defineElement(this.tagName);
5734
6351
  }
5735
6352
  }
5736
- var microApp = new MicroApp();
6353
+ const microApp = new MicroApp();
5737
6354
 
5738
6355
  export default microApp;
5739
- export { EventCenterForMicroApp, MicroApp, getActiveApps, getAllApps, preFetch, pureCreateElement, removeDomScope, unmountAllApps, unmountApp, version };
6356
+ export { EventCenterForMicroApp, MicroApp, getActiveApps, getAllApps, preFetch, pureCreateElement, reload, removeDomScope, renderApp, unmountAllApps, unmountApp, version };
5740
6357
  //# sourceMappingURL=index.esm.js.map