@micro-zoe/micro-app 1.0.0-rc.5 → 1.0.0-rc.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- const version = '1.0.0-rc.5';
1
+ const version = '1.0.0-rc.7';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -56,7 +56,8 @@ function isPromise(target) {
56
56
  }
57
57
  // is bind function
58
58
  function isBoundFunction(target) {
59
- return isFunction(target) && target.name.indexOf('bound ') === 0 && !target.hasOwnProperty('prototype');
59
+ var _a;
60
+ return isFunction(target) && ((_a = target.name) === null || _a === void 0 ? void 0 : _a.indexOf('bound ')) === 0 && !target.hasOwnProperty('prototype');
60
61
  }
61
62
  // is constructor function
62
63
  function isConstructor(target) {
@@ -109,9 +110,15 @@ function isImageElement(target) {
109
110
  function isBaseElement(target) {
110
111
  return toTypeString(target) === '[object HTMLBaseElement]';
111
112
  }
113
+ function isDocumentFragment(target) {
114
+ return toTypeString(target) === '[object DocumentFragment]';
115
+ }
112
116
  function isMicroAppBody(target) {
113
117
  return isElement(target) && target.tagName.toUpperCase() === 'MICRO-APP-BODY';
114
118
  }
119
+ function isMicroAppHead(target) {
120
+ return isElement(target) && target.tagName.toUpperCase() === 'MICRO-APP-HEAD';
121
+ }
115
122
  // is ProxyDocument
116
123
  function isProxyDocument(target) {
117
124
  return toTypeString(target) === '[object ProxyDocument]';
@@ -180,6 +187,14 @@ function logWarn(msg, appName = null, ...rest) {
180
187
  function defer(fn, ...args) {
181
188
  Promise.resolve().then(fn.bind(null, ...args));
182
189
  }
190
+ /**
191
+ * async execution with macro task
192
+ * @param fn callback
193
+ * @param args params
194
+ */
195
+ function macro(fn, delay = 0, ...args) {
196
+ setTimeout(fn.bind(null, ...args), delay);
197
+ }
183
198
  /**
184
199
  * create URL as MicroLocation
185
200
  */
@@ -244,8 +259,7 @@ function formatAppName(name) {
244
259
  function getEffectivePath(url) {
245
260
  const { origin, pathname } = createURL(url);
246
261
  if (/\.(\w+)$/.test(pathname)) {
247
- const fullPath = `${origin}${pathname}`;
248
- const pathArr = fullPath.split('/');
262
+ const pathArr = `${origin}${pathname}`.split('/');
249
263
  pathArr.pop();
250
264
  return pathArr.join('/') + '/';
251
265
  }
@@ -344,33 +358,65 @@ function promiseRequestIdle(callback) {
344
358
  /**
345
359
  * Record the currently running app.name
346
360
  */
347
- let currentMicroAppName = null;
361
+ let currentAppName = null;
348
362
  function setCurrentAppName(appName) {
349
- currentMicroAppName = appName;
363
+ currentAppName = appName;
350
364
  }
351
365
  // get the currently running app.name
352
366
  function getCurrentAppName() {
353
- return currentMicroAppName;
367
+ return currentAppName;
354
368
  }
355
- // Clear appName
356
- let preventSetAppName = false;
357
- function removeDomScope(force) {
358
- setCurrentAppName(null);
359
- if (force && !preventSetAppName) {
360
- preventSetAppName = true;
369
+ function throttleDeferForSetAppName(appName) {
370
+ if (currentAppName !== appName && !getPreventSetState()) {
371
+ setCurrentAppName(appName);
361
372
  defer(() => {
362
- preventSetAppName = false;
373
+ setCurrentAppName(null);
363
374
  });
364
375
  }
365
376
  }
366
- function throttleDeferForSetAppName(appName) {
367
- if (currentMicroAppName !== appName && !preventSetAppName) {
368
- setCurrentAppName(appName);
377
+ // only for iframe document.body(head).querySelector(querySelectorAll)
378
+ let iframeCurrentAppName = null;
379
+ function setIframeCurrentAppName(appName) {
380
+ iframeCurrentAppName = appName;
381
+ }
382
+ function getIframeCurrentAppName() {
383
+ return iframeCurrentAppName;
384
+ }
385
+ function throttleDeferForIframeAppName(appName) {
386
+ if (iframeCurrentAppName !== appName && !getPreventSetState()) {
387
+ setIframeCurrentAppName(appName);
369
388
  defer(() => {
370
- setCurrentAppName(null);
389
+ setIframeCurrentAppName(null);
371
390
  });
372
391
  }
373
392
  }
393
+ // prevent set app name
394
+ let preventSetState = false;
395
+ function getPreventSetState() {
396
+ return preventSetState;
397
+ }
398
+ /**
399
+ * prevent set appName
400
+ * usage:
401
+ * removeDomScope(true)
402
+ * -----> element scope point to base app <-----
403
+ * removeDomScope(false)
404
+ */
405
+ function removeDomScope(force) {
406
+ if (force !== false) {
407
+ setCurrentAppName(null);
408
+ setIframeCurrentAppName(null);
409
+ if (force && !preventSetState) {
410
+ preventSetState = true;
411
+ defer(() => {
412
+ preventSetState = false;
413
+ });
414
+ }
415
+ }
416
+ else {
417
+ preventSetState = false;
418
+ }
419
+ }
374
420
  /**
375
421
  * Create pure elements
376
422
  */
@@ -563,13 +609,21 @@ function clearDOM($dom) {
563
609
  $dom.removeChild($dom.firstChild);
564
610
  }
565
611
  }
566
- /**
567
- * get HTMLElement from base app
568
- * @returns HTMLElement
569
- */
570
- function getBaseHTMLElement() {
571
- var _a;
572
- return (((_a = window.rawWindow) === null || _a === void 0 ? void 0 : _a.HTMLElement) || window.HTMLElement);
612
+ function instanceOf(instance, constructor) {
613
+ if (instance === null || instance === undefined) {
614
+ return false;
615
+ }
616
+ else if (!isFunction(constructor)) {
617
+ throw new TypeError("Right-hand side of 'instanceof' is not callable");
618
+ }
619
+ let proto = Object.getPrototypeOf(instance);
620
+ while (proto) {
621
+ if (proto === constructor.prototype) {
622
+ return true;
623
+ }
624
+ proto = Object.getPrototypeOf(proto);
625
+ }
626
+ return false;
573
627
  }
574
628
  /**
575
629
  * Format event name
@@ -582,6 +636,13 @@ const formatEventList = ['mounted', 'unmount'];
582
636
  function formatEventType(type, appName) {
583
637
  return formatEventList.includes(type) ? `${type}-${appName}` : type;
584
638
  }
639
+ /**
640
+ * Is the object empty
641
+ * target maybe number, string, array ...
642
+ */
643
+ function isEmptyObject(target) {
644
+ return isPlainObject(target) ? !Object.keys(target).length : true;
645
+ }
585
646
 
586
647
  function formatEventInfo(event, element) {
587
648
  Object.defineProperties(event, {
@@ -608,7 +669,7 @@ function formatEventInfo(event, element) {
608
669
  function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
609
670
  var _a;
610
671
  if (!element) {
611
- return logError(`element does not exist in lifecycle ${lifecycleName}`, appName);
672
+ return logWarn(`element does not exist in lifecycle ${lifecycleName}`, appName);
612
673
  }
613
674
  element = getRootContainer(element);
614
675
  // clear dom scope before dispatch lifeCycles event to base app, especially mounted & unmount
@@ -815,9 +876,24 @@ class CSSParser {
815
876
  const m = this.commonMatch(/^[^{]+/, skip);
816
877
  if (!m)
817
878
  return false;
879
+ /**
880
+ * NOTE:
881
+ * 1. :is(h1, h2, h3):has(+ h2, + h3, + h4) {}
882
+ * should be ==> micro-app[name=xxx] :is(h1, h2, h3):has(+ h2, + h3, + h4) {}
883
+ * 2. :dir(ltr) {}
884
+ * should be ==> micro-app[name=xxx] :dir(ltr) {}
885
+ * 3. body :not(div, .fancy) {}
886
+ * should be ==> micro-app[name=xxx] micro-app-body :not(div, .fancy) {}
887
+ * 4. .a, .b, li:nth-child(3)
888
+ * should be ==> micro-app[name=xxx] .a, micro-app[name=xxx] .b, micro-app[name=xxx] li:nth-child(3)
889
+ * 5. :is(.a, .b, .c) a {}
890
+ * should be ==> micro-app[name=xxx] :is(.a, .b, .c) a {}
891
+ * 6. :where(.a, .b, .c) a {}
892
+ * should be ==> micro-app[name=xxx] :where(.a, .b, .c) a {}
893
+ */
818
894
  return m[0].replace(/(^|,[\n\s]*)([^,]+)/g, (_, separator, selector) => {
819
895
  selector = trim(selector);
820
- if (!(this.scopecssDisableNextLine ||
896
+ if (selector && !(this.scopecssDisableNextLine ||
821
897
  (this.scopecssDisable && (!this.scopecssDisableSelectors.length ||
822
898
  this.scopecssDisableSelectors.includes(selector))) ||
823
899
  rootSelectorREG.test(selector))) {
@@ -840,7 +916,7 @@ class CSSParser {
840
916
  return parseError("Declaration missing '}'", this.linkPath);
841
917
  return true;
842
918
  }
843
- matchAllDeclarations(nesting = 1) {
919
+ matchAllDeclarations(nesting = 0) {
844
920
  let cssValue = this.commonMatch(/^(?:url\(["']?(?:[^)"'}]+)["']?\)|[^{}/])*/, true)[0];
845
921
  if (cssValue) {
846
922
  if (!this.scopecssDisableNextLine &&
@@ -862,14 +938,6 @@ class CSSParser {
862
938
  this.scopecssDisableNextLine = false;
863
939
  if (!this.cssText)
864
940
  return;
865
- if (this.cssText.charAt(0) === '}') {
866
- if (!nesting)
867
- return;
868
- if (nesting > 1) {
869
- this.commonMatch(/}+/);
870
- }
871
- return this.matchAllDeclarations(nesting - 1);
872
- }
873
941
  // extract comments in declarations
874
942
  if (this.cssText.charAt(0) === '/') {
875
943
  if (this.cssText.charAt(1) === '*') {
@@ -879,10 +947,16 @@ class CSSParser {
879
947
  this.commonMatch(/\/+/);
880
948
  }
881
949
  }
882
- if (this.cssText.charAt(0) === '{') {
883
- this.commonMatch(/{+\s*/);
950
+ else if (this.cssText.charAt(0) === '{') {
951
+ this.matchOpenBrace();
884
952
  nesting++;
885
953
  }
954
+ else if (this.cssText.charAt(0) === '}') {
955
+ if (nesting < 1)
956
+ return;
957
+ this.matchCloseBrace();
958
+ nesting--;
959
+ }
886
960
  return this.matchAllDeclarations(nesting);
887
961
  }
888
962
  matchAtRule() {
@@ -901,7 +975,8 @@ class CSSParser {
901
975
  this.documentRule() ||
902
976
  this.pageRule() ||
903
977
  this.hostRule() ||
904
- this.fontFaceRule();
978
+ this.fontFaceRule() ||
979
+ this.layerRule();
905
980
  }
906
981
  // :global is CSS Modules rule, it will be converted to normal syntax
907
982
  // private matchGlobalRule (): boolean | void {
@@ -963,6 +1038,19 @@ class CSSParser {
963
1038
  return false;
964
1039
  return this.commonHandlerForAtRuleWithSelfRule('font-face');
965
1040
  }
1041
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/@layer
1042
+ layerRule() {
1043
+ if (!this.commonMatch(/^@layer\s*([^{;]+)/))
1044
+ return false;
1045
+ if (!this.matchOpenBrace())
1046
+ return !!this.commonMatch(/^[;]+/);
1047
+ this.matchComments();
1048
+ this.matchRules();
1049
+ if (!this.matchCloseBrace())
1050
+ return parseError('@layer missing \'}\'', this.linkPath);
1051
+ this.matchLeadingSpaces();
1052
+ return true;
1053
+ }
966
1054
  // common matcher for @media, @supports, @document, @host, :global, @container
967
1055
  createMatcherForRuleWithChildRule(reg, name) {
968
1056
  return () => {
@@ -1058,7 +1146,7 @@ class CSSParser {
1058
1146
  return this.commonMatch(/^{\s*/);
1059
1147
  }
1060
1148
  matchCloseBrace() {
1061
- return this.commonMatch(/^}/);
1149
+ return this.commonMatch(/^}\s*/);
1062
1150
  }
1063
1151
  // match and slice the leading spaces
1064
1152
  matchLeadingSpaces() {
@@ -1272,7 +1360,7 @@ function extractLinkFromHtml(link, parent, app, isDynamic = false) {
1272
1360
  return { address: href, linkInfo };
1273
1361
  }
1274
1362
  }
1275
- else if (rel && ['prefetch', 'preload', 'prerender', 'modulepreload'].includes(rel)) {
1363
+ else if (rel && ['prefetch', 'preload', 'prerender', 'modulepreload', 'icon'].includes(rel)) {
1276
1364
  // preload prefetch prerender ....
1277
1365
  if (isDynamic) {
1278
1366
  replaceComment = document.createComment(`link element with rel=${rel}${href ? ' & href=' + href : ''} removed by micro-app`);
@@ -1486,7 +1574,6 @@ var MicroAppConfig;
1486
1574
  MicroAppConfig["DISABLE_MEMORY_ROUTER"] = "disable-memory-router";
1487
1575
  MicroAppConfig["DISABLE_PATCH_REQUEST"] = "disable-patch-request";
1488
1576
  MicroAppConfig["KEEP_ROUTER_STATE"] = "keep-router-state";
1489
- MicroAppConfig["HIDDEN_ROUTER"] = "hidden-router";
1490
1577
  MicroAppConfig["KEEP_ALIVE"] = "keep-alive";
1491
1578
  MicroAppConfig["CLEAR_DATA"] = "clear-data";
1492
1579
  MicroAppConfig["SSR"] = "ssr";
@@ -1535,7 +1622,6 @@ const BASE_SCOPE_WINDOW_EVENT = [
1535
1622
  'popstate',
1536
1623
  'hashchange',
1537
1624
  'load',
1538
- 'beforeunload',
1539
1625
  'unload',
1540
1626
  'unmount',
1541
1627
  'appstate-change',
@@ -1547,6 +1633,7 @@ const SCOPE_WINDOW_EVENT_OF_WITH = BASE_SCOPE_WINDOW_EVENT;
1547
1633
  // bind event of iframe sandbox
1548
1634
  const SCOPE_WINDOW_EVENT_OF_IFRAME = BASE_SCOPE_WINDOW_EVENT.concat([
1549
1635
  'unhandledrejection',
1636
+ 'message'
1550
1637
  ]);
1551
1638
  // on event bound to child app window
1552
1639
  // TODO: with和iframe处理方式不同,需修改
@@ -1554,9 +1641,9 @@ const BASE_SCOPE_WINDOW_ON_EVENT = [
1554
1641
  'onpopstate',
1555
1642
  'onhashchange',
1556
1643
  'onload',
1557
- 'onbeforeunload',
1558
1644
  'onunload',
1559
1645
  'onerror'
1646
+ // 'onbeforeunload', // remove at 2024.5.30 by cangdu
1560
1647
  ];
1561
1648
  // bind on event of with sandbox
1562
1649
  const SCOPE_WINDOW_ON_EVENT_OF_WITH = BASE_SCOPE_WINDOW_ON_EVENT;
@@ -1986,6 +2073,7 @@ function execScripts(app, initHook) {
1986
2073
  * @param callback callback of module script
1987
2074
  */
1988
2075
  function runScript(address, app, scriptInfo, callback, replaceElement) {
2076
+ var _a;
1989
2077
  try {
1990
2078
  actionsBeforeRunScript(app);
1991
2079
  const appSpaceData = scriptInfo.appSpace[app.name];
@@ -2016,7 +2104,7 @@ function runScript(address, app, scriptInfo, callback, replaceElement) {
2016
2104
  */
2017
2105
  if (!replaceElement) {
2018
2106
  // TEST IGNORE
2019
- const parent = app.iframe ? app.sandBox.microBody : app.querySelector('micro-app-body');
2107
+ const parent = app.iframe ? (_a = app.sandBox) === null || _a === void 0 ? void 0 : _a.microBody : app.querySelector('micro-app-body');
2020
2108
  parent === null || parent === void 0 ? void 0 : parent.appendChild(scriptElement);
2021
2109
  }
2022
2110
  }
@@ -2038,7 +2126,7 @@ function runScript(address, app, scriptInfo, callback, replaceElement) {
2038
2126
  * @param originScript origin script element
2039
2127
  */
2040
2128
  function runDynamicRemoteScript(address, app, scriptInfo, originScript) {
2041
- const replaceElement = isInlineMode(app, scriptInfo) ? pureCreateElement('script') : document.createComment('dynamic script extract by micro-app');
2129
+ const replaceElement = isInlineMode(app, scriptInfo) ? pureCreateElement('script') : document.createComment(`dynamic script with src='${address}' extract by micro-app`);
2042
2130
  const dispatchScriptOnLoadEvent = () => dispatchOnLoadEvent(originScript);
2043
2131
  const runDynamicScript = () => {
2044
2132
  const descriptor = Object.getOwnPropertyDescriptor(globalEnv.rawDocument, 'currentScript');
@@ -2072,7 +2160,7 @@ function runDynamicRemoteScript(address, app, scriptInfo, originScript) {
2072
2160
  * @param scriptInfo scriptInfo
2073
2161
  */
2074
2162
  function runDynamicInlineScript(address, app, scriptInfo) {
2075
- const replaceElement = isInlineMode(app, scriptInfo) ? pureCreateElement('script') : document.createComment('dynamic script extract by micro-app');
2163
+ const replaceElement = isInlineMode(app, scriptInfo) ? pureCreateElement('script') : document.createComment('dynamic inline script extract by micro-app');
2076
2164
  runScript(address, app, scriptInfo, void 0, replaceElement);
2077
2165
  return replaceElement;
2078
2166
  }
@@ -2250,7 +2338,7 @@ function extractSourceDom(htmlStr, app) {
2250
2338
  const fiberStyleTasks = app.isPrefetch || app.fiber ? [] : null;
2251
2339
  flatChildren(wrapElement, app, microAppHead, fiberStyleTasks);
2252
2340
  /**
2253
- * Style and link are parallel, because it takes a lot of time for link to request resources. During this period, style processing can be performed to improve efficiency.
2341
+ * Style and link are parallel, as it takes a lot of time for link to request resources. During this period, style processing can be performed to improve efficiency.
2254
2342
  */
2255
2343
  const fiberStyleResult = serialExecFiberTasks(fiberStyleTasks);
2256
2344
  if (app.source.links.size) {
@@ -2445,7 +2533,7 @@ const eventCenter = new EventCenter();
2445
2533
  function createEventName(appName, fromBaseApp) {
2446
2534
  if (!isString(appName) || !appName)
2447
2535
  return '';
2448
- return fromBaseApp ? `__from_base_app_${appName}__` : `__from_micro_app_${appName}__`;
2536
+ return fromBaseApp ? `__${appName}_from_base_app__` : `__${appName}_from_micro_app__`;
2449
2537
  }
2450
2538
  // Global data
2451
2539
  class EventCenterForGlobal {
@@ -2744,7 +2832,15 @@ function isConstructorFunction(value) {
2744
2832
  }
2745
2833
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
2746
2834
  function bindFunctionToRawTarget(value, rawTarget, key = 'WINDOW') {
2747
- if (isFunction(value) && !isConstructorFunction(value) && !isBoundedFunction(value)) {
2835
+ /**
2836
+ * In safari, nest app like: A -> B -> C
2837
+ * if B is iframe sandbox, and C is with sandbox, same property of document in C is abnormal
2838
+ * e.g:
2839
+ * document.all:
2840
+ * - typeof document.all ==> 'function'
2841
+ * - document.all.bind ==> undefined
2842
+ */
2843
+ if (isFunction(value) && !isConstructorFunction(value) && !isBoundedFunction(value) && value.bind) {
2748
2844
  const cacheKey = `__MICRO_APP_BOUND_${key}_FUNCTION__`;
2749
2845
  if (value[cacheKey])
2750
2846
  return value[cacheKey];
@@ -2765,6 +2861,198 @@ function bindFunctionToRawTarget(value, rawTarget, key = 'WINDOW') {
2765
2861
  return value;
2766
2862
  }
2767
2863
 
2864
+ class BaseSandbox {
2865
+ constructor(appName, url) {
2866
+ // keys that can only assigned to rawWindow
2867
+ this.rawWindowScopeKeyList = [
2868
+ 'location',
2869
+ ];
2870
+ // keys that can escape to rawWindow
2871
+ this.staticEscapeProperties = [
2872
+ 'System',
2873
+ '__cjsWrapper',
2874
+ ];
2875
+ // keys that scoped in child app
2876
+ this.staticScopeProperties = [
2877
+ 'webpackJsonp',
2878
+ 'webpackHotUpdate',
2879
+ 'Vue',
2880
+ // TODO: 是否可以和constants/SCOPE_WINDOW_ON_EVENT合并
2881
+ 'onpopstate',
2882
+ 'onhashchange',
2883
+ ];
2884
+ // Properties that can only get and set in microAppWindow, will not escape to rawWindow
2885
+ this.scopeProperties = Array.from(this.staticScopeProperties);
2886
+ // Properties that can be escape to rawWindow
2887
+ this.escapeProperties = [];
2888
+ // Properties newly added to microAppWindow
2889
+ this.injectedKeys = new Set();
2890
+ // Properties escape to rawWindow, cleared when unmount
2891
+ this.escapeKeys = new Set();
2892
+ this.appName = appName;
2893
+ this.url = url;
2894
+ this.injectReactHMRProperty();
2895
+ }
2896
+ // adapter for react
2897
+ injectReactHMRProperty() {
2898
+ if ((process.env.NODE_ENV !== 'production')) {
2899
+ // react child in non-react env
2900
+ this.staticEscapeProperties.push('__REACT_ERROR_OVERLAY_GLOBAL_HOOK__');
2901
+ // in react parent
2902
+ if (globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__) {
2903
+ this.staticScopeProperties = this.staticScopeProperties.concat([
2904
+ '__REACT_ERROR_OVERLAY_GLOBAL_HOOK__',
2905
+ '__reactRefreshInjected',
2906
+ ]);
2907
+ }
2908
+ }
2909
+ }
2910
+ }
2911
+ /**
2912
+ * TODO:
2913
+ * 1、将class Adapter去掉,改为CustomWindow,或者让CustomWindow继承Adapter
2914
+ * 2、with沙箱中的常量放入CustomWindow,虽然和iframe沙箱不一致,但更合理
2915
+ * 修改时机:在iframe沙箱支持插件后再修改
2916
+ */
2917
+ class CustomWindow {
2918
+ }
2919
+ // Fix conflict of babel-polyfill@6.x
2920
+ function fixBabelPolyfill6() {
2921
+ if (globalEnv.rawWindow._babelPolyfill)
2922
+ globalEnv.rawWindow._babelPolyfill = false;
2923
+ }
2924
+ /**
2925
+ * Fix error of hot reload when parent&child created by create-react-app in development environment
2926
+ * Issue: https://github.com/micro-zoe/micro-app/issues/382
2927
+ */
2928
+ function fixReactHMRConflict(app) {
2929
+ var _a;
2930
+ if ((process.env.NODE_ENV !== 'production')) {
2931
+ const rawReactErrorHook = globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__;
2932
+ const childReactErrorHook = (_a = app.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__;
2933
+ if (rawReactErrorHook && childReactErrorHook) {
2934
+ globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__ = childReactErrorHook;
2935
+ defer(() => {
2936
+ globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__ = rawReactErrorHook;
2937
+ });
2938
+ }
2939
+ }
2940
+ }
2941
+ /**
2942
+ * update dom tree of target dom
2943
+ * @param container target dom
2944
+ * @param appName app name
2945
+ */
2946
+ function patchElementTree(container, appName) {
2947
+ const children = Array.from(container.childNodes);
2948
+ children.length && children.forEach((child) => {
2949
+ patchElementTree(child, appName);
2950
+ });
2951
+ updateElementInfo(container, appName);
2952
+ }
2953
+ /**
2954
+ * rewrite baseURI, ownerDocument, __MICRO_APP_NAME__ of target node
2955
+ * @param node target node
2956
+ * @param appName app name
2957
+ * @returns target node
2958
+ */
2959
+ function updateElementInfo(node, appName) {
2960
+ var _a, _b;
2961
+ if (appName &&
2962
+ isNode(node) &&
2963
+ node.__MICRO_APP_NAME__ !== appName &&
2964
+ !node.__PURE_ELEMENT__ &&
2965
+ !getPreventSetState()) {
2966
+ /**
2967
+ * TODO:
2968
+ * 1. 测试baseURI和ownerDocument在with沙箱中是否正确
2969
+ * 经过验证with沙箱不能重写ownerDocument,否则react点击事件会触发两次
2970
+ */
2971
+ rawDefineProperties(node, {
2972
+ __MICRO_APP_NAME__: {
2973
+ configurable: true,
2974
+ enumerable: true,
2975
+ writable: true,
2976
+ value: appName,
2977
+ },
2978
+ });
2979
+ /**
2980
+ * In FireFox, iframe Node.prototype will point to native Node.prototype after insert to document
2981
+ *
2982
+ * Performance:
2983
+ * iframe element.__proto__ === browser HTMLElement.prototype // Chrome: false, FireFox: true
2984
+ * iframe element.__proto__ === iframe HTMLElement.prototype // Chrome: true, FireFox: false
2985
+ *
2986
+ * NOTE:
2987
+ * 1. Node.prototype.baseURI
2988
+ * 2. Node.prototype.ownerDocument
2989
+ * 3. Node.prototype.parentNode
2990
+ * 4. Node.prototype.getRootNode
2991
+ * 5. Node.prototype.cloneNode
2992
+ * 6. Element.prototype.innerHTML
2993
+ * 7. Image
2994
+ */
2995
+ if (isIframeSandbox(appName)) {
2996
+ const proxyWindow = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.sandBox) === null || _b === void 0 ? void 0 : _b.proxyWindow;
2997
+ if (proxyWindow) {
2998
+ rawDefineProperties(node, {
2999
+ baseURI: {
3000
+ configurable: true,
3001
+ enumerable: true,
3002
+ get: () => proxyWindow.location.href,
3003
+ },
3004
+ ownerDocument: {
3005
+ configurable: true,
3006
+ enumerable: true,
3007
+ get: () => node !== proxyWindow.document ? proxyWindow.document : null,
3008
+ },
3009
+ parentNode: getIframeParentNodeDesc(appName, globalEnv.rawParentNodeDesc),
3010
+ getRootNode: {
3011
+ configurable: true,
3012
+ enumerable: true,
3013
+ writable: true,
3014
+ value: function getRootNode() {
3015
+ return proxyWindow.document;
3016
+ }
3017
+ },
3018
+ });
3019
+ }
3020
+ }
3021
+ }
3022
+ return node;
3023
+ }
3024
+ /**
3025
+ * get Descriptor of Node.prototype.parentNode for iframe
3026
+ * @param appName app name
3027
+ * @param parentNode parentNode Descriptor of iframe or browser
3028
+ */
3029
+ function getIframeParentNodeDesc(appName, parentNodeDesc) {
3030
+ return {
3031
+ configurable: true,
3032
+ enumerable: true,
3033
+ get() {
3034
+ var _a, _b, _c, _d;
3035
+ throttleDeferForIframeAppName(appName);
3036
+ const result = (_a = parentNodeDesc.get) === null || _a === void 0 ? void 0 : _a.call(this);
3037
+ /**
3038
+ * If parentNode is <micro-app-body>, return rawDocument.body
3039
+ * Scenes:
3040
+ * 1. element-ui@2/lib/utils/vue-popper.js
3041
+ * if (this.popperElm.parentNode === document.body) ...
3042
+ * e.g.:
3043
+ * 1. element-ui@2.x el-dropdown
3044
+ * WARNING:
3045
+ * Will it cause other problems ?
3046
+ * e.g. target.parentNode.remove(target)
3047
+ */
3048
+ if (isMicroAppBody(result) && ((_b = appInstanceMap.get(appName)) === null || _b === void 0 ? void 0 : _b.container)) {
3049
+ return ((_d = (_c = microApp.options).getRootElementParentNode) === null || _d === void 0 ? void 0 : _d.call(_c, this, appName)) || globalEnv.rawDocument.body;
3050
+ }
3051
+ return result;
3052
+ }
3053
+ };
3054
+ }
3055
+
2768
3056
  /**
2769
3057
  * create proxyDocument and MicroDocument, rewrite document of child app
2770
3058
  * @param appName app name
@@ -2805,13 +3093,11 @@ function createProxyDocument(appName, sandbox) {
2805
3093
  const { rawDocument, rawCreateElement, rawCreateElementNS, rawAddEventListener, rawRemoveEventListener, } = globalEnv;
2806
3094
  function createElement(tagName, options) {
2807
3095
  const element = rawCreateElement.call(rawDocument, tagName, options);
2808
- element.__MICRO_APP_NAME__ = appName;
2809
- return element;
3096
+ return updateElementInfo(element, appName);
2810
3097
  }
2811
3098
  function createElementNS(namespaceURI, name, options) {
2812
3099
  const element = rawCreateElementNS.call(rawDocument, namespaceURI, name, options);
2813
- element.__MICRO_APP_NAME__ = appName;
2814
- return element;
3100
+ return updateElementInfo(element, appName);
2815
3101
  }
2816
3102
  /**
2817
3103
  * TODO:
@@ -3310,11 +3596,11 @@ function setMicroState(appName, microState, targetLocation) {
3310
3596
  const additionalState = {
3311
3597
  __MICRO_APP_STATE__: assign({}, rawState === null || rawState === void 0 ? void 0 : rawState.__MICRO_APP_STATE__, {
3312
3598
  [appName]: {
3313
- fullPath: targetLocation.pathname + targetLocation.search + targetLocation.hash,
3314
- state: microState,
3599
+ fullPath: targetLocation ? targetLocation.pathname + targetLocation.search + targetLocation.hash : null,
3600
+ state: microState !== null && microState !== void 0 ? microState : null,
3315
3601
  mode: getRouterMode(appName),
3316
3602
  }
3317
- })
3603
+ }),
3318
3604
  };
3319
3605
  // create new state object
3320
3606
  return assign({}, rawState, additionalState);
@@ -3329,14 +3615,19 @@ function removeMicroState(appName, rawState) {
3329
3615
  delete rawState.__MICRO_APP_STATE__;
3330
3616
  }
3331
3617
  }
3332
- return assign({}, rawState);
3618
+ return !isEmptyObject(rawState) ? assign({}, rawState) : null;
3333
3619
  }
3334
3620
  // get micro app state form origin state
3335
3621
  function getMicroState(appName) {
3336
3622
  var _a, _b;
3337
3623
  const rawState = globalEnv.rawWindow.history.state;
3338
- // rawState?.__MICRO_APP_STATE__?.[appName]?.state || (isRouterModeCustom(appName) ? rawState : null)
3339
- return ((_b = (_a = rawState === null || rawState === void 0 ? void 0 : rawState.__MICRO_APP_STATE__) === null || _a === void 0 ? void 0 : _a[appName]) === null || _b === void 0 ? void 0 : _b.state) || (isRouterModeCustom(appName) ? rawState : null);
3624
+ return ((_b = (_a = rawState === null || rawState === void 0 ? void 0 : rawState.__MICRO_APP_STATE__) === null || _a === void 0 ? void 0 : _a[appName]) === null || _b === void 0 ? void 0 : _b.state) || null;
3625
+ }
3626
+ // get micro app router info state form origin state
3627
+ function getMicroRouterInfoState(appName) {
3628
+ var _a;
3629
+ const rawState = globalEnv.rawWindow.history.state;
3630
+ return ((_a = rawState === null || rawState === void 0 ? void 0 : rawState.__MICRO_APP_STATE__) === null || _a === void 0 ? void 0 : _a[appName]) || null;
3340
3631
  }
3341
3632
  const ENC_AD_RE = /&/g; // %M1
3342
3633
  const ENC_EQ_RE = /=/g; // %M2
@@ -3385,7 +3676,7 @@ function getMicroPathFromURL(appName) {
3385
3676
  * NOTE:
3386
3677
  * 1. state mode: all base on __MICRO_APP_STATE__
3387
3678
  * 2. pure mode: navigate by location.xxx may contain one-time information in __MICRO_APP_STATE__
3388
- * 3. native/scope mode: vue-router@4 will exec replaceState base on state before pushState, like:
3679
+ * 3. native mode: vue-router@4 will exec replaceState with history.state before pushState, like:
3389
3680
  * history.replaceState(
3390
3681
  * assign({}, history.state, {...}),
3391
3682
  * title,
@@ -3393,8 +3684,13 @@ function getMicroPathFromURL(appName) {
3393
3684
  * )
3394
3685
  * when base app jump to another page from child page, it will replace child path with base app path
3395
3686
  * e.g: base-home --> child-home --> child-about(will replace with child-home before jump to base-home) --> base-home, when go back, it will back to child-home not child-about
3396
- * So we take the fullPath as the standard
3687
+ * So we take the fullPath as standard
3397
3688
  */
3689
+ // 问题:1、同一个页面多个子应用,一个修改后... --- native模式不支持多个子应用同时渲染,多个子应用推荐使用其它模式
3690
+ // if (isRouterModeCustom(appName)) {
3691
+ // return rawLocation.pathname + rawLocation.search + rawLocation.hash
3692
+ // }
3693
+ // return rawState?.__MICRO_APP_STATE__?.[appName]?.fullPath || null
3398
3694
  return ((_d = (_c = rawState === null || rawState === void 0 ? void 0 : rawState.__MICRO_APP_STATE__) === null || _c === void 0 ? void 0 : _c[appName]) === null || _d === void 0 ? void 0 : _d.fullPath) || (isRouterModeCustom(appName) ? rawLocation.pathname + rawLocation.search + rawLocation.hash : null);
3399
3695
  }
3400
3696
  /**
@@ -3590,6 +3886,7 @@ function addHistoryListener(appName) {
3590
3886
  const rawWindow = globalEnv.rawWindow;
3591
3887
  // handle popstate event and distribute to child app
3592
3888
  const popStateHandler = (e) => {
3889
+ var _a, _b, _c;
3593
3890
  /**
3594
3891
  * 1. unmount app & hidden keep-alive app will not receive popstate event
3595
3892
  * 2. filter out onlyForBrowser
@@ -3600,20 +3897,27 @@ function addHistoryListener(appName) {
3600
3897
  }).includes(appName) &&
3601
3898
  !e.onlyForBrowser) {
3602
3899
  /**
3603
- * TODO: vue-router@4 navigate async when receive popstateEvent, but child may respond to popstateEvent immediately(vue2, react), so when go back throw browser child will not unmount sync, and will respond to popstateEvent before base app, this will cause some problems
3604
- * __MICRO_APP_BASE_ROUTE__不可控,用户设置的值是随机的且不一定使用,用它作为判断依据太过危险
3605
- */
3606
- // const microAppWindow = appInstanceMap.get(appName)!.sandBox!.microAppWindow
3607
- // const rawLocation = globalEnv.rawWindow.location
3608
- // if (
3609
- // !isRouterModeCustom(appName) ||
3610
- // !microAppWindow.__MICRO_APP_BASE_ROUTE__ ||
3611
- // // history、子hash,主、子都是hash如何处理
3612
- // microAppWindow.__MICRO_APP_BASE_ROUTE__.includes('#') ||
3613
- // `${rawLocation.pathname}/`.startsWith(('/' + microAppWindow.__MICRO_APP_BASE_ROUTE__).replace(/^\/+/, '/'))
3614
- // ) {
3615
- updateMicroLocationWithEvent(appName, getMicroPathFromURL(appName));
3616
- // }
3900
+ * base app may respond to popstateEvent async(lazy load page & browser back/forward), but child app will respond to popstateEvent immediately(vue2, react), this will cause some problems
3901
+ * 2 solutions:
3902
+ * 1. child app respond to popstateEvent async -- router-event-delay
3903
+ * 2. child app will not respond to popstateEvent in some scenarios (history.state===null || history.state?__MICRO_APP_STATE__[appName])
3904
+ * NOTE 1:
3905
+ * 1. browser back/forward
3906
+ * 2. location.hash/search/pathname = xxx
3907
+ * 3. <a href="/#/xxx">, <a href="/xxx">
3908
+ * 4. history.back/go/forward
3909
+ * 5. history.pushState/replaceState
3910
+ *
3911
+ * NOTE2:
3912
+ * 1、react16 hash mode navigate by location.hash = xxx, history.state is always null, but react16 respond to popstateEvent sync
3913
+ * 2、multiple child apps may has problems
3914
+ */
3915
+ if (!isRouterModeCustom(appName) ||
3916
+ !globalEnv.rawWindow.history.state ||
3917
+ getMicroRouterInfoState(appName)) {
3918
+ const container = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container;
3919
+ macro(() => updateMicroLocationWithEvent(appName, getMicroPathFromURL(appName)), (_c = (_b = (container && getRootContainer(container))) === null || _b === void 0 ? void 0 : _b.getRouterEventDelay()) !== null && _c !== void 0 ? _c : 0);
3920
+ }
3617
3921
  }
3618
3922
  };
3619
3923
  rawWindow.addEventListener('popstate', popStateHandler);
@@ -3631,24 +3935,26 @@ function addHistoryListener(appName) {
3631
3935
  */
3632
3936
  function updateMicroLocationWithEvent(appName, targetFullPath) {
3633
3937
  const app = appInstanceMap.get(appName);
3634
- const proxyWindow = app.sandBox.proxyWindow;
3635
- const microAppWindow = app.sandBox.microAppWindow;
3636
- let isHashChange = false;
3637
- // for hashChangeEvent
3638
- const oldHref = proxyWindow.location.href;
3639
- // Do not attach micro state to url when targetFullPath is empty
3640
- if (targetFullPath) {
3641
- const oldHash = proxyWindow.location.hash;
3642
- updateMicroLocation(appName, targetFullPath, microAppWindow.location);
3643
- isHashChange = proxyWindow.location.hash !== oldHash;
3644
- }
3645
- // dispatch formatted popStateEvent to child
3646
- dispatchPopStateEventToMicroApp(appName, proxyWindow, microAppWindow);
3647
- // dispatch formatted hashChangeEvent to child when hash change
3648
- if (isHashChange)
3649
- dispatchHashChangeEventToMicroApp(appName, proxyWindow, microAppWindow, oldHref);
3650
- // clear element scope before trigger event of next app
3651
- removeDomScope();
3938
+ if (app === null || app === void 0 ? void 0 : app.sandBox) {
3939
+ const proxyWindow = app.sandBox.proxyWindow;
3940
+ const microAppWindow = app.sandBox.microAppWindow;
3941
+ let isHashChange = false;
3942
+ // for hashChangeEvent
3943
+ const oldHref = proxyWindow.location.href;
3944
+ // Do not attach micro state to url when targetFullPath is empty
3945
+ if (targetFullPath) {
3946
+ const oldHash = proxyWindow.location.hash;
3947
+ updateMicroLocation(appName, targetFullPath, microAppWindow.location);
3948
+ isHashChange = proxyWindow.location.hash !== oldHash;
3949
+ }
3950
+ // dispatch formatted popStateEvent to child
3951
+ dispatchPopStateEventToMicroApp(appName, proxyWindow, microAppWindow);
3952
+ // dispatch formatted hashChangeEvent to child when hash change
3953
+ if (isHashChange)
3954
+ dispatchHashChangeEventToMicroApp(appName, proxyWindow, microAppWindow, oldHref);
3955
+ // clear element scope before trigger event of next app
3956
+ removeDomScope();
3957
+ }
3652
3958
  }
3653
3959
  /**
3654
3960
  * dispatch formatted popstate event to microApp
@@ -3747,20 +4053,16 @@ function createMicroHistory(appName, microLocation) {
3747
4053
  return function (...rests) {
3748
4054
  var _a, _b, _c;
3749
4055
  // TODO: 测试iframe的URL兼容isURL的情况
3750
- if (isString(rests[2]) || isURL(rests[2])) {
3751
- const targetLocation = createURL(rests[2], microLocation.href);
3752
- const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3753
- if (!isRouterModePure(appName)) {
3754
- navigateWithNativeEvent(appName, methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rests[0], targetLocation), rests[1]);
3755
- }
3756
- if (targetFullPath !== microLocation.fullPath) {
3757
- updateMicroLocation(appName, targetFullPath, microLocation);
3758
- }
3759
- (_c = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : (_b = _a.sandBox).updateIframeBase) === null || _c === void 0 ? void 0 : _c.call(_b);
4056
+ rests[2] = isUndefined(rests[2]) || isNull(rests[2]) || ('' + rests[2] === '') ? microLocation.href : '' + rests[2];
4057
+ const targetLocation = createURL(rests[2], microLocation.href);
4058
+ const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
4059
+ if (!isRouterModePure(appName)) {
4060
+ navigateWithNativeEvent(appName, methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rests[0], targetLocation), rests[1]);
3760
4061
  }
3761
- else {
3762
- nativeHistoryNavigate(appName, methodName, rests[2], rests[0], rests[1]);
4062
+ if (targetFullPath !== microLocation.fullPath) {
4063
+ updateMicroLocation(appName, targetFullPath, microLocation);
3763
4064
  }
4065
+ (_c = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : (_b = _a.sandBox).updateIframeBase) === null || _c === void 0 ? void 0 : _c.call(_b);
3764
4066
  };
3765
4067
  }
3766
4068
  const pushState = getMicroHistoryMethod('pushState');
@@ -3769,6 +4071,9 @@ function createMicroHistory(appName, microLocation) {
3769
4071
  return {
3770
4072
  pushState,
3771
4073
  replaceState,
4074
+ go(delta) {
4075
+ return rawHistory.go(delta);
4076
+ }
3772
4077
  };
3773
4078
  }
3774
4079
  return new Proxy(rawHistory, {
@@ -3881,10 +4186,24 @@ function reWriteHistoryMethod(method) {
3881
4186
  excludeHiddenApp: true,
3882
4187
  excludePreRender: true,
3883
4188
  }).forEach(appName => {
4189
+ // TODO: 大部分情况下,history.pushState 都是先执行,micro-app后卸载,所以会产生一种情况:跳转到新地址后,search模式会在url上添加参数,卸载后再将参数删除,所以会导致浏览器地址闪烁,是否需要去掉这个功能
3884
4190
  if ((isRouterModeSearch(appName) || isRouterModeState(appName)) && !getMicroPathFromURL(appName)) {
3885
4191
  const app = appInstanceMap.get(appName);
3886
4192
  attachRouteToBrowserURL(appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName), app.sandBox.proxyWindow.location));
3887
4193
  }
4194
+ if (isRouterModeCustom(appName) && !getMicroRouterInfoState(appName)) {
4195
+ nativeHistoryNavigate(appName, 'replaceState', rawWindow.location.href, setMicroState(appName));
4196
+ }
4197
+ // if (isRouterModeCustom(appName) || isRouterModeSearch(appName)) {
4198
+ /**
4199
+ * history.pushState/replaceState后主动触发子应用响应
4200
+ * 问题:子应用的卸载可能是异步的,而跳转的地址不一定在基础路径中,太快响应pushState可能会导致url地址被子应用改变或者子应用404,Promise太快卸载时出问题、setTimeout太慢keep-alive二次渲染后出问题
4201
+ * 1、history.pushState/replaceState执行后,子应用以异步的形式被主应用卸载,Promise响应时子应用还在,导致子应用跳转404后者浏览器url被子应用修改,产生异常
4202
+ * 2、keep-alive应用二次渲染时,由于setTimeout响应过慢,子应用在渲染后才接受到popstate事件,响应新的url,从而导致状态丢失
4203
+ * 3、同一个页面多个子应用,修改地址响应
4204
+ * 4、vue3跳转前会执行一次replace,有没有影响?
4205
+ */
4206
+ // }
3888
4207
  });
3889
4208
  // fix bug for nest app
3890
4209
  removeDomScope();
@@ -4415,12 +4734,12 @@ function autoTriggerNavigationGuard(appName, microLocation) {
4415
4734
  * @param microLocation micro app location
4416
4735
  * @param type auto prevent
4417
4736
  */
4418
- function updateMicroLocation(appName, path, microLocation, type) {
4737
+ function updateMicroLocation(appName, targetFullPath, microLocation, type) {
4419
4738
  var _a;
4420
4739
  // record old values of microLocation to `from`
4421
4740
  const from = createGuardLocation(appName, microLocation);
4422
4741
  // if is iframeSandbox, microLocation muse be rawLocation of iframe, not proxyLocation
4423
- const newLocation = createURL(path, microLocation.href);
4742
+ const newLocation = createURL(targetFullPath, microLocation.href);
4424
4743
  if (isIframeSandbox(appName)) {
4425
4744
  const microAppWindow = appInstanceMap.get(appName).sandBox.microAppWindow;
4426
4745
  (_a = microAppWindow.rawReplaceState) === null || _a === void 0 ? void 0 : _a.call(microAppWindow.history, getMicroState(appName), '', newLocation.href);
@@ -4432,6 +4751,20 @@ function updateMicroLocation(appName, path, microLocation, type) {
4432
4751
  }
4433
4752
  microLocation.self.href = targetHref;
4434
4753
  }
4754
+ /**
4755
+ * The native mode also base of history.state, and the modification of the browser url cannot be controlled. It is very likely that the browser url and __MICRO_APP_STATE__ are different.
4756
+ * Especially during init of child or forward and backward of browser, because vue-router@4 will actively modify the browser URL, the above situation often occurs
4757
+ * To solve this problem, after child app is initialized and responds to the popstateEvent, it is determined whether __MICRO_APP_STATE__ and the browser url are different. If they are different, the browser url will updated to the address of __MICRO_APP_STATE__
4758
+ * NOTE:
4759
+ * 1. If __MICRO_APP_STATE__ is different from the URL, then the operation of updating the URL is correct, otherwise there will be a problem of inconsistency between the URL and the rendered page
4760
+ * 2. When there are multiple child app in native mode, if one of them changes the URL address, the other one will not change __MICRO_APP_STATE__, and refresh browser will cause problems
4761
+ */
4762
+ const rawLocation = globalEnv.rawWindow.location;
4763
+ if (isRouterModeCustom(appName) &&
4764
+ (targetFullPath !== rawLocation.pathname + rawLocation.search + rawLocation.hash) &&
4765
+ type !== 'prevent') {
4766
+ nativeHistoryNavigate(appName, 'replaceState', targetFullPath, globalEnv.rawWindow.history.state);
4767
+ }
4435
4768
  // update latest values of microLocation to `to`
4436
4769
  const to = createGuardLocation(appName, microLocation);
4437
4770
  // The hook called only when fullPath changed
@@ -4515,130 +4848,6 @@ function removePathFromBrowser(appName) {
4515
4848
  attachRouteToBrowserURL(appName, removeMicroPathFromURL(appName), removeMicroState(appName, globalEnv.rawWindow.history.state));
4516
4849
  }
4517
4850
 
4518
- class BaseSandbox {
4519
- constructor() {
4520
- // keys that can only assigned to rawWindow
4521
- this.rawWindowScopeKeyList = [
4522
- 'location',
4523
- ];
4524
- // keys that can escape to rawWindow
4525
- this.staticEscapeProperties = [
4526
- 'System',
4527
- '__cjsWrapper',
4528
- ];
4529
- // keys that scoped in child app
4530
- this.staticScopeProperties = [
4531
- 'webpackJsonp',
4532
- 'webpackHotUpdate',
4533
- 'Vue',
4534
- // TODO: 是否可以和constants/SCOPE_WINDOW_ON_EVENT合并
4535
- 'onpopstate',
4536
- 'onhashchange',
4537
- ];
4538
- // Properties that can only get and set in microAppWindow, will not escape to rawWindow
4539
- this.scopeProperties = Array.from(this.staticScopeProperties);
4540
- // Properties that can be escape to rawWindow
4541
- this.escapeProperties = [];
4542
- // Properties newly added to microAppWindow
4543
- this.injectedKeys = new Set();
4544
- // Properties escape to rawWindow, cleared when unmount
4545
- this.escapeKeys = new Set();
4546
- this.injectReactHMRProperty();
4547
- }
4548
- // adapter for react
4549
- injectReactHMRProperty() {
4550
- if ((process.env.NODE_ENV !== 'production')) {
4551
- // react child in non-react env
4552
- this.staticEscapeProperties.push('__REACT_ERROR_OVERLAY_GLOBAL_HOOK__');
4553
- // in react parent
4554
- if (globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__) {
4555
- this.staticScopeProperties = this.staticScopeProperties.concat([
4556
- '__REACT_ERROR_OVERLAY_GLOBAL_HOOK__',
4557
- '__reactRefreshInjected',
4558
- ]);
4559
- }
4560
- }
4561
- }
4562
- }
4563
- /**
4564
- * TODO:
4565
- * 1、将class Adapter去掉,改为CustomWindow,或者让CustomWindow继承Adapter
4566
- * 2、with沙箱中的常量放入CustomWindow,虽然和iframe沙箱不一致,但更合理
4567
- * 修改时机:在iframe沙箱支持插件后再修改
4568
- */
4569
- class CustomWindow {
4570
- }
4571
- // Fix conflict of babel-polyfill@6.x
4572
- function fixBabelPolyfill6() {
4573
- if (globalEnv.rawWindow._babelPolyfill)
4574
- globalEnv.rawWindow._babelPolyfill = false;
4575
- }
4576
- /**
4577
- * Fix error of hot reload when parent&child created by create-react-app in development environment
4578
- * Issue: https://github.com/micro-zoe/micro-app/issues/382
4579
- */
4580
- function fixReactHMRConflict(app) {
4581
- var _a;
4582
- if ((process.env.NODE_ENV !== 'production')) {
4583
- const rawReactErrorHook = globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__;
4584
- const childReactErrorHook = (_a = app.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__;
4585
- if (rawReactErrorHook && childReactErrorHook) {
4586
- globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__ = childReactErrorHook;
4587
- defer(() => {
4588
- globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__ = rawReactErrorHook;
4589
- });
4590
- }
4591
- }
4592
- }
4593
- /**
4594
- * update dom tree of target dom
4595
- * @param container target dom
4596
- * @param appName app name
4597
- */
4598
- function patchElementTree(container, appName) {
4599
- const children = Array.from(container.children);
4600
- children.length && children.forEach((child) => {
4601
- patchElementTree(child, appName);
4602
- });
4603
- for (const child of children) {
4604
- updateElementInfo(child, appName);
4605
- }
4606
- }
4607
- /**
4608
- * rewrite baseURI, ownerDocument, __MICRO_APP_NAME__ of target node
4609
- * @param node target node
4610
- * @param appName app name
4611
- * @returns target node
4612
- */
4613
- function updateElementInfo(node, appName) {
4614
- var _a, _b;
4615
- const proxyWindow = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.sandBox) === null || _b === void 0 ? void 0 : _b.proxyWindow;
4616
- if (isNode(node) &&
4617
- !node.__MICRO_APP_NAME__ &&
4618
- !node.__PURE_ELEMENT__ &&
4619
- proxyWindow) {
4620
- /**
4621
- * TODO:
4622
- * 1. 测试baseURI和ownerDocument在with沙箱中是否正确
4623
- * 经过验证with沙箱不能重写ownerDocument,否则react点击事件会触发两次
4624
- * 2. with沙箱所有node设置__MICRO_APP_NAME__都使用updateElementInfo
4625
- */
4626
- rawDefineProperties(node, {
4627
- baseURI: {
4628
- configurable: true,
4629
- // if disable-memory-router or router-mode='disable', href point to base app
4630
- get: () => proxyWindow.location.href,
4631
- },
4632
- __MICRO_APP_NAME__: {
4633
- configurable: true,
4634
- writable: true,
4635
- value: appName,
4636
- },
4637
- });
4638
- }
4639
- return node;
4640
- }
4641
-
4642
4851
  /**
4643
4852
  * https://developer.mozilla.org/en-US/docs/Web/API/fetch
4644
4853
  * Promise<Response> fetch(input[, init])
@@ -4743,7 +4952,7 @@ function useMicroEventSource() {
4743
4952
  const { createMicroEventSource, clearMicroEventSource } = useMicroEventSource();
4744
4953
  class WithSandBox extends BaseSandbox {
4745
4954
  constructor(appName, url) {
4746
- super();
4955
+ super(appName, url);
4747
4956
  this.active = false;
4748
4957
  this.microAppWindow = new CustomWindow(); // Proxy target
4749
4958
  this.patchWith((resolve) => {
@@ -4824,6 +5033,7 @@ class WithSandBox extends BaseSandbox {
4824
5033
  * 1. injectedKeys and escapeKeys must be placed at the back
4825
5034
  * 2. if key in initial microAppWindow, and then rewrite, this key will be delete from microAppWindow when stop, and lost when restart
4826
5035
  * 3. umd mode will not delete global keys
5036
+ * 4. mount & unmount hook should delete in default mode when stop
4827
5037
  */
4828
5038
  if (!umdMode || destroy) {
4829
5039
  clearMicroEventSource(this.microAppWindow.__MICRO_APP_NAME__);
@@ -4835,6 +5045,7 @@ class WithSandBox extends BaseSandbox {
4835
5045
  Reflect.deleteProperty(globalEnv.rawWindow, key);
4836
5046
  });
4837
5047
  this.escapeKeys.clear();
5048
+ this.clearHijackUmdHooks();
4838
5049
  }
4839
5050
  if (--globalEnv.activeSandbox === 0) {
4840
5051
  releasePatchElementAndDocument();
@@ -4867,6 +5078,7 @@ class WithSandBox extends BaseSandbox {
4867
5078
  microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
4868
5079
  removeDomScope,
4869
5080
  pureCreateElement,
5081
+ location: microAppWindow.location,
4870
5082
  router,
4871
5083
  });
4872
5084
  }
@@ -5148,10 +5360,49 @@ class WithSandBox extends BaseSandbox {
5148
5360
  * action before exec scripts when mount
5149
5361
  * Actions:
5150
5362
  * 1. patch static elements from html
5363
+ * 2. hijack umd hooks -- mount, unmount, micro-app-appName
5151
5364
  * @param container micro app container
5152
5365
  */
5153
- actionBeforeExecScripts(container) {
5366
+ actionsBeforeExecScripts(container, handleUmdHooks) {
5154
5367
  this.patchStaticElement(container);
5368
+ this.clearHijackUmdHooks = this.hijackUmdHooks(this.appName, this.microAppWindow, handleUmdHooks);
5369
+ }
5370
+ // hijack mount, unmount, micro-app-appName hook to microAppWindow
5371
+ hijackUmdHooks(appName, microAppWindow, handleUmdHooks) {
5372
+ let mount, unmount, microAppLibrary;
5373
+ rawDefineProperties(microAppWindow, {
5374
+ mount: {
5375
+ configurable: true,
5376
+ get: () => mount,
5377
+ set: (value) => {
5378
+ if (this.active && isFunction(value) && !mount) {
5379
+ handleUmdHooks(mount = value, unmount);
5380
+ }
5381
+ }
5382
+ },
5383
+ unmount: {
5384
+ configurable: true,
5385
+ get: () => unmount,
5386
+ set: (value) => {
5387
+ if (this.active && isFunction(value) && !unmount) {
5388
+ handleUmdHooks(mount, unmount = value);
5389
+ }
5390
+ }
5391
+ },
5392
+ [`micro-app-${appName}`]: {
5393
+ configurable: true,
5394
+ get: () => microAppLibrary,
5395
+ set: (value) => {
5396
+ if (this.active && isPlainObject(value) && !microAppLibrary) {
5397
+ microAppLibrary = value;
5398
+ handleUmdHooks(microAppLibrary.mount, microAppLibrary.unmount);
5399
+ }
5400
+ }
5401
+ }
5402
+ });
5403
+ return () => {
5404
+ mount = unmount = microAppLibrary = null;
5405
+ };
5155
5406
  }
5156
5407
  setStaticAppState(state) {
5157
5408
  this.microAppWindow.__MICRO_APP_STATE__ = state;
@@ -5196,9 +5447,10 @@ function patchRouter(appName, url, microAppWindow, browserHost) {
5196
5447
 
5197
5448
  const escape2RawWindowKeys = [
5198
5449
  'getComputedStyle',
5450
+ // FIX ISSUE: https://github.com/micro-zoe/micro-app/issues/1292
5451
+ 'DOMParser',
5199
5452
  'visualViewport',
5200
5453
  'matchMedia',
5201
- // 'DOMParser',
5202
5454
  'ResizeObserver',
5203
5455
  'IntersectionObserver',
5204
5456
  ];
@@ -5207,7 +5459,6 @@ const escape2RawWindowRegExpKeys = [
5207
5459
  /mutationObserver$/i,
5208
5460
  /height$|width$/i,
5209
5461
  /offset$/i,
5210
- // /event$/i,
5211
5462
  /selection$/i,
5212
5463
  /^range/i,
5213
5464
  /^screen/i,
@@ -5220,7 +5471,7 @@ const uniqueDocumentElement = [
5220
5471
  'html',
5221
5472
  'title',
5222
5473
  ];
5223
- // shadowRoot则代理到shadowRoot否则代理到原生document上 (属性)
5474
+ // proxy to shadowRoot or rawDocument (property)
5224
5475
  const proxy2RawDocOrShadowKeys = [
5225
5476
  'childElementCount',
5226
5477
  'children',
@@ -5233,7 +5484,7 @@ const proxy2RawDocOrShadowKeys = [
5233
5484
  'pointerLockElement',
5234
5485
  'styleSheets',
5235
5486
  ];
5236
- // shadowRoot则代理到shadowRoot否则代理到原生document上 (方法)
5487
+ // proxy to shadowRoot or rawDocument (method)
5237
5488
  const proxy2RawDocOrShadowMethods = [
5238
5489
  'append',
5239
5490
  'contains',
@@ -5244,7 +5495,7 @@ const proxy2RawDocOrShadowMethods = [
5244
5495
  'elementsFromPoint',
5245
5496
  'getAnimations',
5246
5497
  ];
5247
- // 直接代理到原生document上 (属性)
5498
+ // proxy to rawDocument (property)
5248
5499
  const proxy2RawDocumentKeys = [
5249
5500
  'characterSet',
5250
5501
  'compatMode',
@@ -5264,7 +5515,7 @@ const proxy2RawDocumentKeys = [
5264
5515
  'visibilityState',
5265
5516
  'fonts',
5266
5517
  ];
5267
- // 直接代理到原生document上 (方法)
5518
+ // proxy to rawDocument (method)
5268
5519
  const proxy2RawDocumentMethods = [
5269
5520
  'execCommand',
5270
5521
  'createRange',
@@ -5322,6 +5573,40 @@ function patchWindowProperty$1(appName, microAppWindow) {
5322
5573
  }
5323
5574
  return false;
5324
5575
  });
5576
+ /**
5577
+ * In FireFox, iframe Element.prototype will point to native Element.prototype after insert to document
5578
+ * Rewrite all constructor's Symbol.hasInstance of iframeWindow
5579
+ * NOTE:
5580
+ * 1. native event instanceof iframe window.Event
5581
+ * 2. native node instanceof iframe window.Node
5582
+ * 3. native element instanceof iframe window.Element
5583
+ * 4. native url instanceof iframe window.URL
5584
+ * ...
5585
+ */
5586
+ if (isConstructor(microAppWindow[key]) && key in rawWindow) {
5587
+ rawDefineProperty(microAppWindow[key], Symbol.hasInstance, {
5588
+ configurable: true,
5589
+ enumerable: false,
5590
+ value(target) {
5591
+ return target instanceof rawWindow[key] || instanceOf(target, microAppWindow[key]);
5592
+ },
5593
+ });
5594
+ }
5595
+ // hijackInstanceOfWindowRegExpKeys.some((reg: RegExp) => {
5596
+ // if (reg.test(key) && key in rawWindow) {
5597
+ // rawDefineProperty(microAppWindow[key], Symbol.hasInstance, {
5598
+ // configurable: true,
5599
+ // enumerable: false,
5600
+ // value: (target: unknown) => {
5601
+ // return target instanceof rawWindow[key]
5602
+ // ? true
5603
+ // : instanceOf(target, microAppWindow[key])
5604
+ // },
5605
+ // })
5606
+ // return true
5607
+ // }
5608
+ // return false
5609
+ // })
5325
5610
  return /^on/.test(key) && !SCOPE_WINDOW_ON_EVENT_OF_IFRAME.includes(key);
5326
5611
  })
5327
5612
  .forEach((eventName) => {
@@ -5407,10 +5692,14 @@ function createProxyWindow$1(microAppWindow, sandbox) {
5407
5692
  sandbox.proxyWindow = proxyWindow;
5408
5693
  }
5409
5694
  function patchWindowEffect$1(microAppWindow) {
5410
- const { rawWindow, rawAddEventListener, rawRemoveEventListener } = globalEnv;
5695
+ const { rawWindow, rawAddEventListener, rawRemoveEventListener, rawDispatchEvent } = globalEnv;
5411
5696
  const eventListenerMap = new Map();
5412
5697
  const sstEventListenerMap = new Map();
5413
5698
  function getEventTarget(type) {
5699
+ /**
5700
+ * TODO: SCOPE_WINDOW_EVENT_OF_IFRAME的事件非常少,有可能导致问题
5701
+ * 1、一些未知的需要绑定到iframe的事件被错误的绑定到原生window上
5702
+ */
5414
5703
  return SCOPE_WINDOW_EVENT_OF_IFRAME.includes(type) ? microAppWindow : rawWindow;
5415
5704
  }
5416
5705
  // TODO: listener 是否需要绑定microAppWindow,否则函数中的this指向原生window
@@ -5432,6 +5721,9 @@ function patchWindowEffect$1(microAppWindow) {
5432
5721
  }
5433
5722
  rawRemoveEventListener.call(getEventTarget(type), type, listener, options);
5434
5723
  };
5724
+ microAppWindow.dispatchEvent = function (event) {
5725
+ return rawDispatchEvent.call(getEventTarget(event === null || event === void 0 ? void 0 : event.type), event);
5726
+ };
5435
5727
  const reset = () => {
5436
5728
  sstEventListenerMap.clear();
5437
5729
  };
@@ -5543,34 +5835,55 @@ function patchDocumentPrototype(appName, microAppWindow) {
5543
5835
  const element = rawMicroCreateComment.call(this, data);
5544
5836
  return updateElementInfo(element, appName);
5545
5837
  };
5546
- function getDefaultRawTarget(target) {
5547
- return microDocument !== target ? target : rawDocument;
5838
+ function getBindTarget(target) {
5839
+ /**
5840
+ * handler for:
5841
+ * 1. document.getElementsByTagName('head')[0].querySelector('script')
5842
+ * 2. document.querySelector('body').querySelectorAll('script')
5843
+ * ...
5844
+ */
5845
+ throttleDeferForIframeAppName(appName);
5846
+ // DOMParser.document !== microDocument
5847
+ return microDocument === target ? rawDocument : target;
5548
5848
  }
5549
5849
  // query element👇
5550
5850
  function querySelector(selectors) {
5551
- var _a, _b;
5851
+ var _a;
5852
+ const _this = getBindTarget(this);
5552
5853
  if (!selectors ||
5553
5854
  isUniqueElement(selectors) ||
5554
- microDocument !== this) {
5555
- const _this = getDefaultRawTarget(this);
5855
+ rawDocument !== _this) {
5556
5856
  return rawMicroQuerySelector.call(_this, selectors);
5557
5857
  }
5558
- return (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.querySelector(selectors)) !== null && _b !== void 0 ? _b : null;
5858
+ /**
5859
+ * The child app cannot query the base element inside iframe
5860
+ * Same for querySelectorAll
5861
+ *
5862
+ * Scenes:
5863
+ * 1. vue-router@4.x --> createWebHistory(base?: string)
5864
+ * const baseEl = document.querySelector('base')
5865
+ * base = (baseEl && baseEl.getAttribute('href')) || '/'
5866
+ *
5867
+ * Issue: https://github.com/micro-zoe/micro-app/issues/1335
5868
+ */
5869
+ const result = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.querySelector(selectors);
5870
+ return result || selectors === 'base' ? result : rawMicroQuerySelector.call(microDocument, selectors);
5559
5871
  }
5560
5872
  function querySelectorAll(selectors) {
5561
5873
  var _a, _b;
5874
+ const _this = getBindTarget(this);
5562
5875
  if (!selectors ||
5563
5876
  isUniqueElement(selectors) ||
5564
- microDocument !== this) {
5565
- const _this = getDefaultRawTarget(this);
5877
+ rawDocument !== _this) {
5566
5878
  return rawMicroQuerySelectorAll.call(_this, selectors);
5567
5879
  }
5568
- return (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.querySelectorAll(selectors)) !== null && _b !== void 0 ? _b : [];
5880
+ const result = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.querySelectorAll(selectors)) !== null && _b !== void 0 ? _b : [];
5881
+ return result.length || selectors === 'base' ? result : rawMicroQuerySelectorAll.call(microDocument, selectors);
5569
5882
  }
5570
5883
  microRootDocument.prototype.querySelector = querySelector;
5571
5884
  microRootDocument.prototype.querySelectorAll = querySelectorAll;
5572
5885
  microRootDocument.prototype.getElementById = function getElementById(key) {
5573
- const _this = getDefaultRawTarget(this);
5886
+ const _this = getBindTarget(this);
5574
5887
  if (isInvalidQuerySelectorKey(key)) {
5575
5888
  return rawMicroGetElementById.call(_this, key);
5576
5889
  }
@@ -5582,7 +5895,7 @@ function patchDocumentPrototype(appName, microAppWindow) {
5582
5895
  }
5583
5896
  };
5584
5897
  microRootDocument.prototype.getElementsByClassName = function getElementsByClassName(key) {
5585
- const _this = getDefaultRawTarget(this);
5898
+ const _this = getBindTarget(this);
5586
5899
  if (isInvalidQuerySelectorKey(key)) {
5587
5900
  return rawMicroGetElementsByClassName.call(_this, key);
5588
5901
  }
@@ -5594,12 +5907,13 @@ function patchDocumentPrototype(appName, microAppWindow) {
5594
5907
  }
5595
5908
  };
5596
5909
  microRootDocument.prototype.getElementsByTagName = function getElementsByTagName(key) {
5597
- const _this = getDefaultRawTarget(this);
5910
+ const _this = getBindTarget(this);
5598
5911
  if (isUniqueElement(key) ||
5599
5912
  isInvalidQuerySelectorKey(key)) {
5600
5913
  return rawMicroGetElementsByTagName.call(_this, key);
5914
+ // just script, not base
5601
5915
  }
5602
- else if (/^script|base$/i.test(key)) {
5916
+ else if (/^script$/i.test(key)) {
5603
5917
  return rawMicroGetElementsByTagName.call(microDocument, key);
5604
5918
  }
5605
5919
  try {
@@ -5610,7 +5924,7 @@ function patchDocumentPrototype(appName, microAppWindow) {
5610
5924
  }
5611
5925
  };
5612
5926
  microRootDocument.prototype.getElementsByName = function getElementsByName(key) {
5613
- const _this = getDefaultRawTarget(this);
5927
+ const _this = getBindTarget(this);
5614
5928
  if (isInvalidQuerySelectorKey(key)) {
5615
5929
  return rawMicroGetElementsByName.call(_this, key);
5616
5930
  }
@@ -5677,7 +5991,7 @@ function patchDocumentProperty(appName, microAppWindow, sandbox) {
5677
5991
  enumerable: true,
5678
5992
  configurable: true,
5679
5993
  get: () => {
5680
- throttleDeferForSetAppName(appName);
5994
+ throttleDeferForIframeAppName(appName);
5681
5995
  return rawDocument[tagName];
5682
5996
  },
5683
5997
  set: (value) => { rawDocument[tagName] = value; },
@@ -5685,7 +5999,7 @@ function patchDocumentProperty(appName, microAppWindow, sandbox) {
5685
5999
  });
5686
6000
  }
5687
6001
  function patchDocumentEffect(appName, microAppWindow) {
5688
- const { rawDocument, rawAddEventListener, rawRemoveEventListener } = globalEnv;
6002
+ const { rawDocument, rawAddEventListener, rawRemoveEventListener, rawDispatchEvent } = globalEnv;
5689
6003
  const eventListenerMap = new Map();
5690
6004
  const sstEventListenerMap = new Map();
5691
6005
  let onClickHandler = null;
@@ -5715,6 +6029,9 @@ function patchDocumentEffect(appName, microAppWindow) {
5715
6029
  const handler = (listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_BOUND_FUNCTION__) || listener;
5716
6030
  rawRemoveEventListener.call(getEventTarget(type, this), type, handler, options);
5717
6031
  };
6032
+ microRootDocument.prototype.dispatchEvent = function (event) {
6033
+ return rawDispatchEvent.call(getEventTarget(event === null || event === void 0 ? void 0 : event.type, this), event);
6034
+ };
5718
6035
  // 重新定义microRootDocument.prototype 上的on开头方法
5719
6036
  function createSetterHandler(eventName) {
5720
6037
  if (eventName === 'onclick') {
@@ -5835,12 +6152,18 @@ function patchElement(appName, url, microAppWindow, sandbox) {
5835
6152
  patchIframeNode(appName, microAppWindow, sandbox);
5836
6153
  patchIframeAttribute(url, microAppWindow);
5837
6154
  }
6155
+ /**
6156
+ * patch iframe Node/Element
6157
+ *
6158
+ */
5838
6159
  function patchIframeNode(appName, microAppWindow, sandbox) {
5839
6160
  const rawRootElement = globalEnv.rawRootElement; // native root Element
6161
+ const rawRootNode = globalEnv.rawRootNode;
5840
6162
  const rawDocument = globalEnv.rawDocument;
5841
6163
  const microDocument = microAppWindow.document;
5842
6164
  const microRootNode = microAppWindow.Node;
5843
6165
  const microRootElement = microAppWindow.Element;
6166
+ const microDocumentFragment = microAppWindow.DocumentFragment;
5844
6167
  // const rawMicroGetRootNode = microRootNode.prototype.getRootNode
5845
6168
  const rawMicroAppendChild = microRootNode.prototype.appendChild;
5846
6169
  const rawMicroInsertBefore = microRootNode.prototype.insertBefore;
@@ -5848,6 +6171,8 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5848
6171
  const rawMicroRemoveChild = microRootNode.prototype.removeChild;
5849
6172
  const rawMicroAppend = microRootElement.prototype.append;
5850
6173
  const rawMicroPrepend = microRootElement.prototype.prepend;
6174
+ const rawMicroFragmentAppend = microDocumentFragment.prototype.append;
6175
+ const rawMicroFragmentPrepend = microDocumentFragment.prototype.prepend;
5851
6176
  const rawMicroInsertAdjacentElement = microRootElement.prototype.insertAdjacentElement;
5852
6177
  const rawMicroCloneNode = microRootNode.prototype.cloneNode;
5853
6178
  const rawInnerHTMLDesc = Object.getOwnPropertyDescriptor(microRootElement.prototype, 'innerHTML');
@@ -5865,42 +6190,34 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5865
6190
  }
5866
6191
  return parent;
5867
6192
  };
5868
- microRootNode.prototype.getRootNode = function getRootNode() {
5869
- return microDocument;
5870
- // TODO: 什么情况下返回原生document?
5871
- // const rootNode = rawMicroGetRootNode.call(this, options)
5872
- // if (rootNode === appInstanceMap.get(appName)?.container) return microDocument
5873
- // return rootNode
5874
- };
5875
6193
  microRootNode.prototype.appendChild = function appendChild(node) {
5876
- // TODO: 有必要执行这么多次updateElementInfo?
5877
6194
  updateElementInfo(node, appName);
5878
6195
  if (isPureNode(node)) {
5879
6196
  return rawMicroAppendChild.call(this, node);
5880
6197
  }
5881
- return rawRootElement.prototype.appendChild.call(getRawTarget(this), node);
6198
+ return rawRootNode.prototype.appendChild.call(getRawTarget(this), node);
5882
6199
  };
5883
6200
  microRootNode.prototype.insertBefore = function insertBefore(node, child) {
5884
6201
  updateElementInfo(node, appName);
5885
6202
  if (isPureNode(node)) {
5886
6203
  return rawMicroInsertBefore.call(this, node, child);
5887
6204
  }
5888
- return rawRootElement.prototype.insertBefore.call(getRawTarget(this), node, child);
6205
+ return rawRootNode.prototype.insertBefore.call(getRawTarget(this), node, child);
5889
6206
  };
5890
6207
  microRootNode.prototype.replaceChild = function replaceChild(node, child) {
5891
6208
  updateElementInfo(node, appName);
5892
6209
  if (isPureNode(node)) {
5893
6210
  return rawMicroReplaceChild.call(this, node, child);
5894
6211
  }
5895
- return rawRootElement.prototype.replaceChild.call(getRawTarget(this), node, child);
6212
+ return rawRootNode.prototype.replaceChild.call(getRawTarget(this), node, child);
5896
6213
  };
5897
6214
  microRootNode.prototype.removeChild = function removeChild(oldChild) {
5898
6215
  if (isPureNode(oldChild) || this.contains(oldChild)) {
5899
6216
  return rawMicroRemoveChild.call(this, oldChild);
5900
6217
  }
5901
- return rawRootElement.prototype.removeChild.call(getRawTarget(this), oldChild);
6218
+ return rawRootNode.prototype.removeChild.call(getRawTarget(this), oldChild);
5902
6219
  };
5903
- microRootElement.prototype.append = function append(...nodes) {
6220
+ microDocumentFragment.prototype.append = microRootElement.prototype.append = function append(...nodes) {
5904
6221
  let i = 0;
5905
6222
  let hasPureNode = false;
5906
6223
  while (i < nodes.length) {
@@ -5910,11 +6227,11 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5910
6227
  i++;
5911
6228
  }
5912
6229
  if (hasPureNode) {
5913
- return rawMicroAppend.call(this, ...nodes);
6230
+ return (isDocumentFragment(this) ? rawMicroFragmentAppend : rawMicroAppend).call(this, ...nodes);
5914
6231
  }
5915
6232
  return rawRootElement.prototype.append.call(getRawTarget(this), ...nodes);
5916
6233
  };
5917
- microRootElement.prototype.prepend = function prepend(...nodes) {
6234
+ microDocumentFragment.prototype.prepend = microRootElement.prototype.prepend = function prepend(...nodes) {
5918
6235
  let i = 0;
5919
6236
  let hasPureNode = false;
5920
6237
  while (i < nodes.length) {
@@ -5924,7 +6241,7 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5924
6241
  i++;
5925
6242
  }
5926
6243
  if (hasPureNode) {
5927
- return rawMicroPrepend.call(this, ...nodes);
6244
+ return (isDocumentFragment(this) ? rawMicroFragmentPrepend : rawMicroPrepend).call(this, ...nodes);
5928
6245
  }
5929
6246
  return rawRootElement.prototype.prepend.call(getRawTarget(this), ...nodes);
5930
6247
  };
@@ -5940,28 +6257,53 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5940
6257
  }
5941
6258
  return rawRootElement.prototype.insertAdjacentElement.call(getRawTarget(this), where, element);
5942
6259
  };
5943
- // patch cloneNode
5944
- microRootNode.prototype.cloneNode = function cloneNode(deep) {
5945
- const clonedNode = rawMicroCloneNode.call(this, deep);
5946
- return updateElementInfo(clonedNode, appName);
5947
- };
6260
+ /**
6261
+ * Specific prototype properties:
6262
+ * 1. baseURI
6263
+ * 2. ownerDocument
6264
+ * 3. parentNode
6265
+ * 4. innerHTML
6266
+ */
6267
+ rawDefineProperty(microRootNode.prototype, 'baseURI', {
6268
+ configurable: true,
6269
+ enumerable: true,
6270
+ get() {
6271
+ return sandbox.proxyWindow.location.href;
6272
+ },
6273
+ });
5948
6274
  rawDefineProperty(microRootNode.prototype, 'ownerDocument', {
5949
6275
  configurable: true,
5950
6276
  enumerable: true,
5951
6277
  get() {
6278
+ var _a;
5952
6279
  return this.__PURE_ELEMENT__ || this === microDocument
5953
- ? rawOwnerDocumentDesc.get.call(this)
5954
- : microDocument;
6280
+ ? (_a = rawOwnerDocumentDesc.get) === null || _a === void 0 ? void 0 : _a.call(this) : microDocument;
5955
6281
  },
5956
6282
  });
6283
+ // patch parentNode
6284
+ rawDefineProperty(microRootNode.prototype, 'parentNode', getIframeParentNodeDesc(appName, rawParentNodeDesc));
6285
+ microRootNode.prototype.getRootNode = function getRootNode() {
6286
+ return microDocument;
6287
+ // TODO: any case return document?
6288
+ // const rootNode = rawMicroGetRootNode.call(this, options)
6289
+ // if (rootNode === appInstanceMap.get(appName)?.container) return microDocument
6290
+ // return rootNode
6291
+ };
6292
+ // patch cloneNode
6293
+ microRootNode.prototype.cloneNode = function cloneNode(deep) {
6294
+ const clonedNode = rawMicroCloneNode.call(this, deep);
6295
+ return updateElementInfo(clonedNode, appName);
6296
+ };
5957
6297
  rawDefineProperty(microRootElement.prototype, 'innerHTML', {
5958
6298
  configurable: true,
5959
6299
  enumerable: true,
5960
6300
  get() {
5961
- return rawInnerHTMLDesc.get.call(this);
6301
+ var _a;
6302
+ return (_a = rawInnerHTMLDesc.get) === null || _a === void 0 ? void 0 : _a.call(this);
5962
6303
  },
5963
6304
  set(code) {
5964
- rawInnerHTMLDesc.set.call(this, code);
6305
+ var _a;
6306
+ (_a = rawInnerHTMLDesc.set) === null || _a === void 0 ? void 0 : _a.call(this, code);
5965
6307
  Array.from(this.children).forEach((child) => {
5966
6308
  if (isElement(child)) {
5967
6309
  updateElementInfo(child, appName);
@@ -5969,34 +6311,6 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5969
6311
  });
5970
6312
  }
5971
6313
  });
5972
- // patch parentNode
5973
- rawDefineProperty(microRootNode.prototype, 'parentNode', {
5974
- configurable: true,
5975
- enumerable: true,
5976
- get() {
5977
- var _a, _b, _c;
5978
- /**
5979
- * set current appName for hijack parentNode of html
5980
- * NOTE:
5981
- * 1. Is there a problem with setting the current appName in iframe mode
5982
- */
5983
- throttleDeferForSetAppName(appName);
5984
- const result = rawParentNodeDesc.get.call(this);
5985
- /**
5986
- * If parentNode is <micro-app-body>, return rawDocument.body
5987
- * Scenes:
5988
- * 1. element-ui@2/lib/utils/vue-popper.js
5989
- * if (this.popperElm.parentNode === document.body) ...
5990
- * WARNING:
5991
- * Will it cause other problems ?
5992
- * e.g. target.parentNode.remove(target)
5993
- */
5994
- if (isMicroAppBody(result) && ((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container)) {
5995
- return ((_c = (_b = microApp.options).getRootElementParentNode) === null || _c === void 0 ? void 0 : _c.call(_b, this, appName)) || globalEnv.rawDocument.body;
5996
- }
5997
- return result;
5998
- }
5999
- });
6000
6314
  // Adapt to new image(...) scene
6001
6315
  const ImageProxy = new Proxy(microAppWindow.Image, {
6002
6316
  construct(Target, args) {
@@ -6015,16 +6329,24 @@ function patchIframeAttribute(url, microAppWindow) {
6015
6329
  const microRootElement = microAppWindow.Element;
6016
6330
  const rawMicroSetAttribute = microRootElement.prototype.setAttribute;
6017
6331
  microRootElement.prototype.setAttribute = function setAttribute(key, value) {
6018
- if (((key === 'src' || key === 'srcset') && /^(img|script)$/i.test(this.tagName)) ||
6019
- (key === 'href' && /^link$/i.test(this.tagName))) {
6020
- value = CompletionPath(value, url);
6332
+ if (/^micro-app(-\S+)?/i.test(this.tagName) &&
6333
+ key === 'data' &&
6334
+ this.setAttribute !== microRootElement.prototype.setAttribute) {
6335
+ this.setAttribute(key, value);
6336
+ }
6337
+ else {
6338
+ if (((key === 'src' || key === 'srcset') && /^(img|script|video|audio|source|embed)$/i.test(this.tagName)) ||
6339
+ (key === 'href' && /^(link|image)$/i.test(this.tagName))) {
6340
+ value = CompletionPath(value, url);
6341
+ }
6342
+ rawMicroSetAttribute.call(this, key, value);
6021
6343
  }
6022
- rawMicroSetAttribute.call(this, key, value);
6023
6344
  };
6024
6345
  const protoAttrList = [
6025
6346
  [microAppWindow.HTMLImageElement.prototype, 'src'],
6026
6347
  [microAppWindow.HTMLScriptElement.prototype, 'src'],
6027
6348
  [microAppWindow.HTMLLinkElement.prototype, 'href'],
6349
+ [microAppWindow.SVGImageElement.prototype, 'href'],
6028
6350
  ];
6029
6351
  /**
6030
6352
  * element.setAttribute does not trigger this actions:
@@ -6057,7 +6379,7 @@ class IframeSandbox {
6057
6379
  this.escapeProperties = [];
6058
6380
  // Properties escape to rawWindow, cleared when unmount
6059
6381
  this.escapeKeys = new Set();
6060
- // 初始化和每次跳转时都要更新basehref
6382
+ // Update the base.href when initial and each redirect
6061
6383
  this.updateIframeBase = () => {
6062
6384
  var _a;
6063
6385
  // origin must be child app origin
@@ -6101,9 +6423,10 @@ class IframeSandbox {
6101
6423
  createIframeElement(appName, browserPath) {
6102
6424
  this.iframe = pureCreateElement('iframe');
6103
6425
  const iframeAttrs = {
6426
+ id: appName,
6104
6427
  src: microApp.options.iframeSrc || browserPath,
6105
6428
  style: 'display: none',
6106
- id: appName,
6429
+ 'powered-by': 'micro-app',
6107
6430
  };
6108
6431
  Object.keys(iframeAttrs).forEach((key) => this.iframe.setAttribute(key, iframeAttrs[key]));
6109
6432
  // effect action during construct
@@ -6137,15 +6460,6 @@ class IframeSandbox {
6137
6460
  * 1. iframe router and browser router are separated, we should update iframe router manually
6138
6461
  * 2. withSandbox location is browser location when disable memory-router, so no need to do anything
6139
6462
  */
6140
- /**
6141
- * TODO:
6142
- * 1. iframe关闭虚拟路由系统后,default-page无法使用,推荐用户直接使用浏览器地址控制首页渲染
6143
- * 补充:keep-router-state 也无法配置,因为keep-router-state一定为true。
6144
- * 2. 导航拦截、current.route 可以正常使用
6145
- * 3. 可以正常控制子应用跳转,方式还是自上而下(也可以是子应用内部跳转,这种方式更好一点,减小对基座的影响,不会导致vue的循环刷新)
6146
- * 4. 关闭虚拟路由以后会对应 route-mode='custom' 模式,包括with沙箱也会这么做
6147
- * 5. 关闭虚拟路由是指尽可能模拟没有虚拟路由的情况,子应用直接获取浏览器location和history,控制浏览器跳转
6148
- */
6149
6463
  this.initRouteState(defaultPage);
6150
6464
  // unique listener of popstate event for child app
6151
6465
  this.removeHistoryListener = addHistoryListener(this.microAppWindow.__MICRO_APP_NAME__);
@@ -6168,6 +6482,7 @@ class IframeSandbox {
6168
6482
  }
6169
6483
  stop({ umdMode, keepRouteState, destroy, clearData, }) {
6170
6484
  var _a;
6485
+ // sandbox.stop may exec before sandbox.start, e.g: iframe sandbox + default mode + remount
6171
6486
  if (!this.active)
6172
6487
  return;
6173
6488
  this.recordAndReleaseEffect({ clearData }, !umdMode || destroy);
@@ -6183,6 +6498,7 @@ class IframeSandbox {
6183
6498
  Reflect.deleteProperty(globalEnv.rawWindow, key);
6184
6499
  });
6185
6500
  this.escapeKeys.clear();
6501
+ this.clearHijackUmdHooks();
6186
6502
  }
6187
6503
  if (--globalEnv.activeSandbox === 0) {
6188
6504
  releasePatchElementAndDocument();
@@ -6401,10 +6717,49 @@ class IframeSandbox {
6401
6717
  * action before exec scripts when mount
6402
6718
  * Actions:
6403
6719
  * 1. patch static elements from html
6720
+ * 2. hijack umd hooks -- mount, unmount, micro-app-appName
6404
6721
  * @param container micro app container
6405
6722
  */
6406
- actionBeforeExecScripts(container) {
6723
+ actionsBeforeExecScripts(container, handleUmdHooks) {
6407
6724
  this.patchStaticElement(container);
6725
+ this.clearHijackUmdHooks = this.hijackUmdHooks(this.appName, this.microAppWindow, handleUmdHooks);
6726
+ }
6727
+ // hijack mount, unmount, micro-app-appName hook to microAppWindow
6728
+ hijackUmdHooks(appName, microAppWindow, handleUmdHooks) {
6729
+ let mount, unmount, microAppLibrary;
6730
+ rawDefineProperties(microAppWindow, {
6731
+ mount: {
6732
+ configurable: true,
6733
+ get: () => mount,
6734
+ set: (value) => {
6735
+ if (this.active && isFunction(value) && !mount) {
6736
+ handleUmdHooks(mount = value, unmount);
6737
+ }
6738
+ }
6739
+ },
6740
+ unmount: {
6741
+ configurable: true,
6742
+ get: () => unmount,
6743
+ set: (value) => {
6744
+ if (this.active && isFunction(value) && !unmount) {
6745
+ handleUmdHooks(mount, unmount = value);
6746
+ }
6747
+ }
6748
+ },
6749
+ [`micro-app-${appName}`]: {
6750
+ configurable: true,
6751
+ get: () => microAppLibrary,
6752
+ set: (value) => {
6753
+ if (this.active && isPlainObject(value) && !microAppLibrary) {
6754
+ microAppLibrary = value;
6755
+ handleUmdHooks(microAppLibrary.mount, microAppLibrary.unmount);
6756
+ }
6757
+ }
6758
+ }
6759
+ });
6760
+ return () => {
6761
+ mount = unmount = microAppLibrary = null;
6762
+ };
6408
6763
  }
6409
6764
  setStaticAppState(state) {
6410
6765
  this.microAppWindow.__MICRO_APP_STATE__ = state;
@@ -6421,7 +6776,6 @@ class CreateApp {
6421
6776
  this.loadSourceLevel = 0;
6422
6777
  this.umdHookMount = null;
6423
6778
  this.umdHookUnmount = null;
6424
- this.lifeCycleState = null;
6425
6779
  this.umdMode = false;
6426
6780
  // TODO: 类型优化,加上iframe沙箱
6427
6781
  this.sandBox = null;
@@ -6466,7 +6820,9 @@ class CreateApp {
6466
6820
  var _a;
6467
6821
  if (++this.loadSourceLevel === 2) {
6468
6822
  this.source.html = html;
6469
- if (!this.isPrefetch && !this.isUnmounted()) {
6823
+ if (this.isUnmounted())
6824
+ return;
6825
+ if (!this.isPrefetch) {
6470
6826
  getRootContainer(this.container).mount(this);
6471
6827
  }
6472
6828
  else if (this.isPrerender) {
@@ -6589,7 +6945,6 @@ class CreateApp {
6589
6945
  this.fiber = fiber;
6590
6946
  this.routerMode = routerMode;
6591
6947
  const dispatchBeforeMount = () => {
6592
- this.setLifeCycleState(lifeCycles.BEFOREMOUNT);
6593
6948
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
6594
6949
  };
6595
6950
  if (this.isPrerender) {
@@ -6603,7 +6958,7 @@ class CreateApp {
6603
6958
  dispatchCustomEventToMicroApp(this, 'statechange', {
6604
6959
  appState: appStates.MOUNTING
6605
6960
  });
6606
- // TODO: 将所有cloneContainer中的'as Element'去掉,兼容shadowRoot的场景
6961
+ // TODO: 兼容shadowRoot的场景
6607
6962
  this.cloneContainer(this.container, this.source.html, !this.umdMode);
6608
6963
  (_e = this.sandBox) === null || _e === void 0 ? void 0 : _e.start({
6609
6964
  umdMode: this.umdMode,
@@ -6612,38 +6967,35 @@ class CreateApp {
6612
6967
  disablePatchRequest,
6613
6968
  });
6614
6969
  if (!this.umdMode) {
6615
- // update element info of html
6616
- (_f = this.sandBox) === null || _f === void 0 ? void 0 : _f.actionBeforeExecScripts(this.container);
6617
- // if all js are executed, param isFinished will be true
6618
- execScripts(this, (isFinished) => {
6619
- if (!this.umdMode) {
6620
- const { mount, unmount } = this.getUmdLibraryHooks();
6621
- /**
6622
- * umdHookUnmount can works in default mode
6623
- * register through window.unmount
6624
- */
6625
- // TODO: 不对,这里要改,因为unmount不一定是函数
6626
- this.umdHookUnmount = unmount;
6970
+ // patch element info of html
6971
+ (_f = this.sandBox) === null || _f === void 0 ? void 0 : _f.actionsBeforeExecScripts(this.container, (mount, unmount) => {
6972
+ var _a;
6973
+ if (!this.umdMode && !this.isUnmounted()) {
6974
+ this.umdHookMount = isFunction(mount) ? mount : null;
6975
+ // umdHookUnmount can works in default mode, register by window.unmount
6976
+ this.umdHookUnmount = isFunction(unmount) ? unmount : null;
6627
6977
  // if mount & unmount is function, the sub app is umd mode
6628
- if (isFunction(mount) && isFunction(unmount)) {
6629
- this.umdHookMount = mount;
6630
- // sandbox must exist
6631
- this.sandBox.markUmdMode(this.umdMode = true);
6978
+ if (isFunction(this.umdHookMount) && isFunction(this.umdHookUnmount)) {
6979
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.markUmdMode(this.umdMode = true);
6632
6980
  try {
6633
- this.handleMounted(this.umdHookMount(microApp.getData(this.name, true)));
6981
+ // if appState is mounted, it means that isFinished is true and this.handleMounted has already been executed, just exec this.umdHookMount
6982
+ if (this.getAppState() === appStates.MOUNTED) {
6983
+ this.umdHookMount(microApp.getData(this.name, true));
6984
+ }
6985
+ else {
6986
+ this.handleMounted(this.umdHookMount(microApp.getData(this.name, true)));
6987
+ }
6634
6988
  }
6635
6989
  catch (e) {
6636
- /**
6637
- * TODO:
6638
- * 1. 是否应该直接抛出错误
6639
- * 2. 是否应该触发error生命周期
6640
- */
6641
- logError('An error occurred in window.mount \n', this.name, e);
6990
+ logError('An error occurred when mount \n', this.name, e);
6642
6991
  }
6643
6992
  }
6644
- else if (isFinished === true) {
6645
- this.handleMounted();
6646
- }
6993
+ }
6994
+ });
6995
+ // if all js are executed, param isFinished will be true
6996
+ execScripts(this, (isFinished) => {
6997
+ if (!this.umdMode && isFinished === true) {
6998
+ this.handleMounted();
6647
6999
  }
6648
7000
  });
6649
7001
  }
@@ -6653,13 +7005,22 @@ class CreateApp {
6653
7005
  this.handleMounted(this.umdHookMount(microApp.getData(this.name, true)));
6654
7006
  }
6655
7007
  catch (e) {
6656
- logError('An error occurred in window.mount \n', this.name, e);
7008
+ logError('An error occurred when mount \n', this.name, e);
6657
7009
  }
6658
7010
  }
6659
7011
  }
6660
7012
  };
7013
+ /**
7014
+ * Initialization of sandbox is async, especially iframe sandbox are macro tasks
7015
+ * when child apps switch quickly, we need to pay attention to the following points:
7016
+ * NOTE:
7017
+ * 1. unmount app before exec nextAction (especially: iframe sandbox + default mode + remount)
7018
+ * this.container is null, this.sandBox will not start
7019
+ * 2. remount app of note 1
7020
+ * 3. unmount app during exec js
7021
+ */
6661
7022
  // TODO: 可优化?
6662
- this.sandBox ? this.sandBox.sandboxReady.then(nextAction) : nextAction();
7023
+ this.sandBox ? this.sandBox.sandboxReady.then(() => !this.isUnmounted() && nextAction()) : nextAction();
6663
7024
  }
6664
7025
  /**
6665
7026
  * handle for promise umdHookMount
@@ -6668,16 +7029,17 @@ class CreateApp {
6668
7029
  handleMounted(umdHookMountResult) {
6669
7030
  var _a, _b;
6670
7031
  const dispatchAction = () => {
7032
+ const nextAction = () => this.actionsAfterMounted();
6671
7033
  if (isPromise(umdHookMountResult)) {
6672
7034
  umdHookMountResult
6673
- .then(() => this.dispatchMountedEvent())
7035
+ .then(nextAction)
6674
7036
  .catch((e) => {
6675
7037
  logError('An error occurred in window.mount \n', this.name, e);
6676
- this.dispatchMountedEvent();
7038
+ nextAction();
6677
7039
  });
6678
7040
  }
6679
7041
  else {
6680
- this.dispatchMountedEvent();
7042
+ nextAction();
6681
7043
  }
6682
7044
  };
6683
7045
  if (this.isPrerender) {
@@ -6691,7 +7053,7 @@ class CreateApp {
6691
7053
  /**
6692
7054
  * dispatch mounted event when app run finished
6693
7055
  */
6694
- dispatchMountedEvent() {
7056
+ actionsAfterMounted() {
6695
7057
  var _a;
6696
7058
  if (!this.isUnmounted()) {
6697
7059
  this.setAppState(appStates.MOUNTED);
@@ -6703,7 +7065,6 @@ class CreateApp {
6703
7065
  });
6704
7066
  // dispatch mounted event to micro app
6705
7067
  dispatchCustomEventToMicroApp(this, 'mounted');
6706
- this.setLifeCycleState(lifeCycles.MOUNTED);
6707
7068
  // dispatch event mounted to parent
6708
7069
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.MOUNTED);
6709
7070
  /**
@@ -6726,6 +7087,8 @@ class CreateApp {
6726
7087
  * unmount app
6727
7088
  * NOTE:
6728
7089
  * 1. do not add any params on account of unmountApp
7090
+ * 2. this.container maybe null: Initialization of sandbox is async, child app may unmount before exec nextAction of mount
7091
+ * 3. unmount app when loading files (this.container is not null)
6729
7092
  * @param destroy completely destroy, delete cache resources
6730
7093
  * @param clearData clear data of dateCenter
6731
7094
  * @param keepRouteState keep route state when unmount, default is false
@@ -6735,29 +7098,12 @@ class CreateApp {
6735
7098
  var _a;
6736
7099
  destroy = destroy || this.state === appStates.LOAD_FAILED;
6737
7100
  this.setAppState(appStates.UNMOUNT);
6738
- let umdHookUnmountResult = null;
6739
7101
  try {
6740
- // call umd unmount hook before the sandbox is cleared
6741
- umdHookUnmountResult = (_a = this.umdHookUnmount) === null || _a === void 0 ? void 0 : _a.call(this, microApp.getData(this.name, true));
7102
+ this.handleUnmounted(destroy, clearData, keepRouteState, unmountcb, (_a = this.umdHookUnmount) === null || _a === void 0 ? void 0 : _a.call(this, microApp.getData(this.name, true)));
6742
7103
  }
6743
7104
  catch (e) {
6744
- logError('An error occurred in window.unmount \n', this.name, e);
7105
+ logError('An error occurred when unmount \n', this.name, e);
6745
7106
  }
6746
- // dispatch state event to micro app
6747
- dispatchCustomEventToMicroApp(this, 'statechange', {
6748
- appState: appStates.UNMOUNT
6749
- });
6750
- // dispatch unmount event to micro app
6751
- dispatchCustomEventToMicroApp(this, 'unmount');
6752
- // call window.onunmount of child app
6753
- execMicroAppGlobalHook(this.getMicroAppGlobalHook(microGlobalEvent.ONUNMOUNT), this.name, microGlobalEvent.ONUNMOUNT);
6754
- this.handleUnmounted({
6755
- destroy,
6756
- clearData,
6757
- keepRouteState,
6758
- unmountcb,
6759
- umdHookUnmountResult,
6760
- });
6761
7107
  }
6762
7108
  /**
6763
7109
  * handle for promise umdHookUnmount
@@ -6767,8 +7113,16 @@ class CreateApp {
6767
7113
  * @param unmountcb callback of unmount
6768
7114
  * @param umdHookUnmountResult result of umdHookUnmount
6769
7115
  */
6770
- handleUnmounted({ destroy, clearData, keepRouteState, unmountcb, umdHookUnmountResult, }) {
6771
- const nextAction = () => this.actionsForUnmount({
7116
+ handleUnmounted(destroy, clearData, keepRouteState, unmountcb, umdHookUnmountResult) {
7117
+ // dispatch state event to micro app
7118
+ dispatchCustomEventToMicroApp(this, 'statechange', {
7119
+ appState: appStates.UNMOUNT
7120
+ });
7121
+ // dispatch unmount event to micro app
7122
+ dispatchCustomEventToMicroApp(this, 'unmount');
7123
+ // call window.onunmount of child app
7124
+ execMicroAppGlobalHook(this.getMicroAppGlobalHook(microGlobalEvent.ONUNMOUNT), this.name, microGlobalEvent.ONUNMOUNT);
7125
+ const nextAction = () => this.actionsAfterUnmounted({
6772
7126
  destroy,
6773
7127
  clearData,
6774
7128
  keepRouteState,
@@ -6777,7 +7131,12 @@ class CreateApp {
6777
7131
  if (isPromise(umdHookUnmountResult)) {
6778
7132
  // async window.unmount will cause appName bind error in nest app
6779
7133
  removeDomScope();
6780
- umdHookUnmountResult.then(nextAction).catch(nextAction);
7134
+ umdHookUnmountResult
7135
+ .then(nextAction)
7136
+ .catch((e) => {
7137
+ logError('An error occurred in window.unmount \n', this.name, e);
7138
+ nextAction();
7139
+ });
6781
7140
  }
6782
7141
  else {
6783
7142
  nextAction();
@@ -6790,7 +7149,7 @@ class CreateApp {
6790
7149
  * @param keepRouteState keep route state when unmount, default is false
6791
7150
  * @param unmountcb callback of unmount
6792
7151
  */
6793
- actionsForUnmount({ destroy, clearData, keepRouteState, unmountcb, }) {
7152
+ actionsAfterUnmounted({ destroy, clearData, keepRouteState, unmountcb, }) {
6794
7153
  var _a;
6795
7154
  if (this.umdMode && this.container && !destroy) {
6796
7155
  this.cloneContainer(this.source.html, this.container, false);
@@ -6807,20 +7166,33 @@ class CreateApp {
6807
7166
  destroy,
6808
7167
  clearData: clearData || destroy,
6809
7168
  });
6810
- this.setLifeCycleState(lifeCycles.UNMOUNT);
6811
7169
  // dispatch unmount event to base app
6812
7170
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.UNMOUNT);
6813
7171
  this.clearOptions(destroy);
6814
7172
  unmountcb === null || unmountcb === void 0 ? void 0 : unmountcb();
6815
7173
  }
6816
7174
  clearOptions(destroy) {
6817
- this.container.innerHTML = '';
6818
- this.container = null;
7175
+ var _a, _b;
6819
7176
  this.isPrerender = false;
6820
7177
  this.preRenderEvents = null;
6821
7178
  this.setKeepAliveState(null);
7179
+ if (this.container) {
7180
+ this.container.innerHTML = '';
7181
+ this.container = null;
7182
+ }
7183
+ else if (!this.umdMode) {
7184
+ /**
7185
+ * this.container is null means sandBox.start has not exec, so sandBox.stop won't exec either
7186
+ * we should remove iframeElement in default mode manually
7187
+ */
7188
+ (_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.deleteIframeElement) === null || _b === void 0 ? void 0 : _b.call(_a);
7189
+ }
6822
7190
  // in iframe sandbox & default mode, delete the sandbox & iframeElement
6823
- // TODO: with沙箱与iframe沙箱保持一致:with沙箱默认模式下删除 或者 iframe沙箱umd模式下保留
7191
+ /**
7192
+ * TODO:
7193
+ * 1. with沙箱与iframe沙箱保持一致:with沙箱默认模式下删除 或者 iframe沙箱umd模式下保留
7194
+ * 2. 接1.0,this.sandBox置空,还需要注意后续app.sandBox相关操作,比如 scripts.ts --> app.iframe ? app.sandBox!.microBody : app.querySelector('micro-app-body'),如果是fiber或者预加载,会存在卸载后js还在处理的情况
7195
+ */
6824
7196
  if (this.iframe && !this.umdMode)
6825
7197
  this.sandBox = null;
6826
7198
  if (destroy)
@@ -6849,7 +7221,6 @@ class CreateApp {
6849
7221
  dispatchCustomEventToMicroApp(this, 'appstate-change', {
6850
7222
  appState: 'afterhidden',
6851
7223
  });
6852
- this.setLifeCycleState(lifeCycles.AFTERHIDDEN);
6853
7224
  // dispatch afterHidden event to base app
6854
7225
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.AFTERHIDDEN);
6855
7226
  if (isRouterModeSearch(this.name)) {
@@ -6891,7 +7262,7 @@ class CreateApp {
6891
7262
  /**
6892
7263
  * TODO:
6893
7264
  * 问题:当路由模式为custom时,keep-alive应用在重新展示,是否需要根据子应用location信息更新浏览器地址?
6894
- * 暂时不这么做吧,因为无法确定二次展示时新旧地址是否相同,是否带有特殊信息
7265
+ * 暂时不这么做,因为无法确定二次展示时新旧地址是否相同,是否带有特殊信息
6895
7266
  */
6896
7267
  if (isRouterModeSearch(this.name)) {
6897
7268
  // called before lifeCyclesEvent
@@ -6901,7 +7272,6 @@ class CreateApp {
6901
7272
  dispatchCustomEventToMicroApp(this, 'appstate-change', {
6902
7273
  appState: 'aftershow',
6903
7274
  });
6904
- this.setLifeCycleState(lifeCycles.AFTERSHOW);
6905
7275
  // dispatch afterShow event to base app
6906
7276
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.AFTERSHOW);
6907
7277
  }
@@ -6910,7 +7280,6 @@ class CreateApp {
6910
7280
  * @param e Error
6911
7281
  */
6912
7282
  onerror(e) {
6913
- this.setLifeCycleState(lifeCycles.ERROR);
6914
7283
  // dispatch state event to micro app
6915
7284
  dispatchCustomEventToMicroApp(this, 'statechange', {
6916
7285
  appState: appStates.LOAD_FAILED
@@ -6931,8 +7300,8 @@ class CreateApp {
6931
7300
  }
6932
7301
  /**
6933
7302
  * clone origin elements to target
6934
- * @param origin Cloned element
6935
7303
  * @param target Accept cloned elements
7304
+ * @param origin Cloned element
6936
7305
  * @param deep deep clone or transfer dom
6937
7306
  */
6938
7307
  cloneContainer(target, origin, deep) {
@@ -6967,14 +7336,6 @@ class CreateApp {
6967
7336
  getAppState() {
6968
7337
  return this.state;
6969
7338
  }
6970
- // set app lifeCycleState
6971
- setLifeCycleState(state) {
6972
- this.lifeCycleState = state;
6973
- }
6974
- // get app lifeCycleState
6975
- getLifeCycleState() {
6976
- return this.lifeCycleState || '';
6977
- }
6978
7339
  // set keep-alive state
6979
7340
  setKeepAliveState(state) {
6980
7341
  this.keepAliveState = state;
@@ -6991,23 +7352,6 @@ class CreateApp {
6991
7352
  isHidden() {
6992
7353
  return keepAliveStates.KEEP_ALIVE_HIDDEN === this.keepAliveState;
6993
7354
  }
6994
- // get umd library, if it not exist, return empty object
6995
- getUmdLibraryHooks() {
6996
- // after execScripts, the app maybe unmounted
6997
- if (!this.isUnmounted() && this.sandBox) {
6998
- const libraryName = getRootContainer(this.container).getAttribute('library') || `micro-app-${this.name}`;
6999
- const proxyWindow = this.sandBox.proxyWindow;
7000
- // compatible with pre versions
7001
- if (isObject(proxyWindow[libraryName])) {
7002
- return proxyWindow[libraryName];
7003
- }
7004
- return {
7005
- mount: proxyWindow.mount,
7006
- unmount: proxyWindow.unmount,
7007
- };
7008
- }
7009
- return {};
7010
- }
7011
7355
  getMicroAppGlobalHook(eventName) {
7012
7356
  var _a, _b;
7013
7357
  const listener = (_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow) === null || _b === void 0 ? void 0 : _b[eventName];
@@ -7113,11 +7457,11 @@ function handleNewNode(child, app) {
7113
7457
  * @param app app
7114
7458
  * @param method raw method
7115
7459
  * @param parent parent node
7116
- * @param targetChild target node
7117
- * @param passiveChild second param of insertBefore and replaceChild
7460
+ * @param targetNode target node
7461
+ * @param passiveNode second param of insertBefore and replaceChild
7118
7462
  */
7119
- function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
7120
- const hijackParent = getHijackParent(parent, targetChild, app);
7463
+ function invokePrototypeMethod(app, rawMethod, parent, targetNode, passiveNode) {
7464
+ const hijackParent = getHijackParent(parent, targetNode, app);
7121
7465
  if (hijackParent) {
7122
7466
  /**
7123
7467
  * If parentNode is <micro-app-body>, return rawDocument.body
@@ -7134,9 +7478,9 @@ function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild
7134
7478
  if (!isIframeSandbox(app.name) &&
7135
7479
  isMicroAppBody(hijackParent) &&
7136
7480
  rawMethod !== globalEnv.rawRemoveChild) {
7137
- const descriptor = Object.getOwnPropertyDescriptor(targetChild, 'parentNode');
7138
- if ((!descriptor || descriptor.configurable) && !targetChild.__MICRO_APP_HAS_DPN__) {
7139
- rawDefineProperties(targetChild, {
7481
+ const descriptor = Object.getOwnPropertyDescriptor(targetNode, 'parentNode');
7482
+ if ((!descriptor || descriptor.configurable) && !targetNode.__MICRO_APP_HAS_DPN__) {
7483
+ rawDefineProperties(targetNode, {
7140
7484
  parentNode: {
7141
7485
  configurable: true,
7142
7486
  get() {
@@ -7157,68 +7501,84 @@ function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild
7157
7501
  }
7158
7502
  }
7159
7503
  if ((process.env.NODE_ENV !== 'production') &&
7160
- isIFrameElement(targetChild) &&
7504
+ isIFrameElement(targetNode) &&
7161
7505
  rawMethod === globalEnv.rawAppendChild) {
7162
7506
  fixReactHMRConflict(app);
7163
7507
  }
7164
7508
  /**
7165
- * 1. If passiveChild exists, it must be insertBefore or replaceChild
7166
- * 2. When removeChild, targetChild may not be in microAppHead or head
7509
+ * 1. If passiveNode exists, it must be insertBefore or replaceChild
7510
+ * 2. When removeChild, targetNode may not be in microAppHead or head
7167
7511
  * NOTE:
7168
- * 1. If passiveChild not in hijackParent, insertBefore replaceChild will be degraded to appendChild
7169
- * E.g: document.head.replaceChild(targetChild, document.scripts[0])
7170
- * 2. If passiveChild not in hijackParent but in parent and method is insertBefore, try insert it into the position corresponding to hijackParent
7171
- * E.g: document.head.insertBefore(targetChild, document.head.childNodes[0])
7512
+ * 1. If passiveNode not in hijackParent, insertBefore replaceChild will be degraded to appendChild
7513
+ * E.g: document.head.replaceChild(targetNode, document.scripts[0])
7514
+ * 2. If passiveNode not in hijackParent but in parent and method is insertBefore, try insert it into the position corresponding to hijackParent
7515
+ * E.g: document.head.insertBefore(targetNode, document.head.childNodes[0])
7172
7516
  * ISSUE: https://github.com/micro-zoe/micro-app/issues/1071
7173
7517
  */
7174
- if (passiveChild && !hijackParent.contains(passiveChild)) {
7175
- if (rawMethod === globalEnv.rawInsertBefore && parent.contains(passiveChild)) {
7176
- const indexOfParent = Array.from(parent.childNodes).indexOf(passiveChild);
7518
+ if (passiveNode && !hijackParent.contains(passiveNode)) {
7519
+ if (rawMethod === globalEnv.rawInsertBefore && parent.contains(passiveNode)) {
7520
+ const indexOfParent = Array.from(parent.childNodes).indexOf(passiveNode);
7177
7521
  if (hijackParent.childNodes[indexOfParent]) {
7178
- return invokeRawMethod(rawMethod, hijackParent, targetChild, hijackParent.childNodes[indexOfParent]);
7522
+ return invokeRawMethod(rawMethod, hijackParent, targetNode, hijackParent.childNodes[indexOfParent], app);
7179
7523
  }
7180
7524
  }
7181
- return globalEnv.rawAppendChild.call(hijackParent, targetChild);
7525
+ return globalEnv.rawAppendChild.call(hijackParent, targetNode);
7182
7526
  }
7183
- else if (rawMethod === globalEnv.rawRemoveChild && !hijackParent.contains(targetChild)) {
7184
- if (parent.contains(targetChild)) {
7185
- return rawMethod.call(parent, targetChild);
7527
+ else if (rawMethod === globalEnv.rawRemoveChild && !hijackParent.contains(targetNode)) {
7528
+ if (parent.contains(targetNode)) {
7529
+ return rawMethod.call(parent, targetNode);
7186
7530
  }
7187
- return targetChild;
7531
+ return targetNode;
7188
7532
  }
7189
- return invokeRawMethod(rawMethod, hijackParent, targetChild, passiveChild);
7533
+ return invokeRawMethod(rawMethod, hijackParent, targetNode, passiveNode, app);
7190
7534
  }
7191
- return invokeRawMethod(rawMethod, parent, targetChild, passiveChild);
7535
+ return invokeRawMethod(rawMethod, parent, targetNode, passiveNode, app);
7192
7536
  }
7193
7537
  // head/body map to micro-app-head/micro-app-body
7194
- function getHijackParent(parent, targetChild, app) {
7538
+ function getHijackParent(parent, targetNode, app) {
7195
7539
  if (app) {
7196
7540
  if (parent === document.head) {
7197
- if (app.iframe && isScriptElement(targetChild)) {
7541
+ if (app.iframe && isScriptElement(targetNode)) {
7198
7542
  return app.sandBox.microHead;
7199
7543
  }
7200
7544
  return app.querySelector('micro-app-head');
7201
7545
  }
7202
7546
  if (parent === document.body || parent === document.body.parentNode) {
7203
- if (app.iframe && isScriptElement(targetChild)) {
7547
+ if (app.iframe && isScriptElement(targetNode)) {
7204
7548
  return app.sandBox.microBody;
7205
7549
  }
7206
7550
  return app.querySelector('micro-app-body');
7207
7551
  }
7208
- if (app.iframe && isScriptElement(targetChild)) {
7552
+ if (app.iframe && isScriptElement(targetNode)) {
7209
7553
  return app.sandBox.microBody;
7210
7554
  }
7211
7555
  }
7212
7556
  return null;
7213
7557
  }
7214
- function invokeRawMethod(rawMethod, parent, targetChild, passiveChild) {
7558
+ function invokeRawMethod(rawMethod, parent, targetNode, passiveNode, app) {
7215
7559
  if (isPendMethod(rawMethod)) {
7216
- return rawMethod.call(parent, targetChild);
7560
+ /**
7561
+ * In iframe sandbox, script will pend to iframe.body, so we should reset rawMethod, because:
7562
+ * Element.prototype.append === DocumentFragment.prototype.append ==> false
7563
+ * Element.prototype.prepend === DocumentFragment.prototype.prepend ==> false
7564
+ */
7565
+ if ((app === null || app === void 0 ? void 0 : app.iframe) && isScriptElement(targetNode)) {
7566
+ if (rawMethod === globalEnv.rawFragmentAppend) {
7567
+ rawMethod = globalEnv.rawAppend;
7568
+ }
7569
+ else if (rawMethod === globalEnv.rawFragmentPrepend) {
7570
+ rawMethod = globalEnv.rawPrepend;
7571
+ }
7572
+ }
7573
+ return rawMethod.call(parent, targetNode);
7217
7574
  }
7218
- return rawMethod.call(parent, targetChild, passiveChild);
7575
+ return rawMethod.call(parent, targetNode, passiveNode);
7219
7576
  }
7220
7577
  function isPendMethod(method) {
7221
- return method === globalEnv.rawAppend || method === globalEnv.rawPrepend;
7578
+ return (method === globalEnv.rawAppend ||
7579
+ method === globalEnv.rawPrepend ||
7580
+ method === globalEnv.rawFragmentAppend ||
7581
+ method === globalEnv.rawFragmentPrepend);
7222
7582
  }
7223
7583
  /**
7224
7584
  * Attempt to complete the static resource address again before insert the node
@@ -7235,7 +7595,7 @@ function completePathDynamic(app, newChild) {
7235
7595
  globalEnv.rawSetAttribute.call(newChild, 'srcset', CompletionPath(newChild.getAttribute('srcset'), app.url));
7236
7596
  }
7237
7597
  }
7238
- else if (/^link$/i.test(newChild.tagName) && newChild.hasAttribute('href')) {
7598
+ else if (/^(link|image)$/i.test(newChild.tagName) && newChild.hasAttribute('href')) {
7239
7599
  globalEnv.rawSetAttribute.call(newChild, 'href', CompletionPath(newChild.getAttribute('href'), app.url));
7240
7600
  }
7241
7601
  }
@@ -7244,31 +7604,26 @@ function completePathDynamic(app, newChild) {
7244
7604
  * method of handle new node
7245
7605
  * @param parent parent node
7246
7606
  * @param newChild new node
7247
- * @param passiveChild passive node
7607
+ * @param passiveNode passive node
7248
7608
  * @param rawMethod method
7249
7609
  */
7250
- function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
7610
+ function commonElementHandler(parent, newChild, passiveNode, rawMethod) {
7251
7611
  const currentAppName = getCurrentAppName();
7252
7612
  if (isNode(newChild) &&
7253
7613
  !newChild.__PURE_ELEMENT__ &&
7254
7614
  (newChild.__MICRO_APP_NAME__ ||
7255
7615
  currentAppName)) {
7256
- newChild.__MICRO_APP_NAME__ = newChild.__MICRO_APP_NAME__ || currentAppName;
7616
+ updateElementInfo(newChild, newChild.__MICRO_APP_NAME__ || currentAppName);
7257
7617
  const app = appInstanceMap.get(newChild.__MICRO_APP_NAME__);
7258
- if (isStyleElement(newChild)) {
7259
- const isShadowNode = parent.getRootNode();
7260
- const isShadowEnvironment = isShadowNode instanceof ShadowRoot;
7261
- isShadowEnvironment && newChild.setAttribute('ignore', 'true');
7262
- }
7263
7618
  if (app === null || app === void 0 ? void 0 : app.container) {
7619
+ if (isStyleElement(newChild)) {
7620
+ parent.getRootNode() instanceof ShadowRoot && newChild.setAttribute('ignore', 'true');
7621
+ }
7264
7622
  completePathDynamic(app, newChild);
7265
- return invokePrototypeMethod(app, rawMethod, parent, handleNewNode(newChild, app), passiveChild && getMappingNode(passiveChild));
7623
+ return invokePrototypeMethod(app, rawMethod, parent, handleNewNode(newChild, app), passiveNode && getMappingNode(passiveNode));
7266
7624
  }
7267
7625
  }
7268
- if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
7269
- return rawMethod.call(parent, newChild);
7270
- }
7271
- return rawMethod.call(parent, newChild, passiveChild);
7626
+ return invokeRawMethod(rawMethod, parent, newChild, passiveNode);
7272
7627
  }
7273
7628
  /**
7274
7629
  * Rewrite element prototype method
@@ -7277,36 +7632,19 @@ function patchElementAndDocument() {
7277
7632
  patchDocument$2();
7278
7633
  const rawRootElement = globalEnv.rawRootElement;
7279
7634
  const rawRootNode = globalEnv.rawRootNode;
7635
+ const rawDocumentFragment = globalEnv.rawDocumentFragment;
7280
7636
  // prototype methods of add element👇
7281
- rawRootElement.prototype.appendChild = function appendChild(newChild) {
7637
+ rawRootNode.prototype.appendChild = function appendChild(newChild) {
7282
7638
  return commonElementHandler(this, newChild, null, globalEnv.rawAppendChild);
7283
7639
  };
7284
- rawRootElement.prototype.insertBefore = function insertBefore(newChild, refChild) {
7640
+ rawRootNode.prototype.insertBefore = function insertBefore(newChild, refChild) {
7285
7641
  return commonElementHandler(this, newChild, refChild, globalEnv.rawInsertBefore);
7286
7642
  };
7287
- rawRootElement.prototype.replaceChild = function replaceChild(newChild, oldChild) {
7643
+ rawRootNode.prototype.replaceChild = function replaceChild(newChild, oldChild) {
7288
7644
  return commonElementHandler(this, newChild, oldChild, globalEnv.rawReplaceChild);
7289
7645
  };
7290
- rawRootElement.prototype.append = function append(...nodes) {
7291
- let i = 0;
7292
- while (i < nodes.length) {
7293
- let node = nodes[i];
7294
- node = isNode(node) ? node : globalEnv.rawCreateTextNode.call(globalEnv.rawDocument, node);
7295
- commonElementHandler(this, markElement(node), null, globalEnv.rawAppend);
7296
- i++;
7297
- }
7298
- };
7299
- rawRootElement.prototype.prepend = function prepend(...nodes) {
7300
- let i = nodes.length;
7301
- while (i > 0) {
7302
- let node = nodes[i - 1];
7303
- node = isNode(node) ? node : globalEnv.rawCreateTextNode.call(globalEnv.rawDocument, node);
7304
- commonElementHandler(this, markElement(node), null, globalEnv.rawPrepend);
7305
- i--;
7306
- }
7307
- };
7308
7646
  // prototype methods of delete element👇
7309
- rawRootElement.prototype.removeChild = function removeChild(oldChild) {
7647
+ rawRootNode.prototype.removeChild = function removeChild(oldChild) {
7310
7648
  if (oldChild === null || oldChild === void 0 ? void 0 : oldChild.__MICRO_APP_NAME__) {
7311
7649
  const app = appInstanceMap.get(oldChild.__MICRO_APP_NAME__);
7312
7650
  if (app === null || app === void 0 ? void 0 : app.container) {
@@ -7321,6 +7659,24 @@ function patchElementAndDocument() {
7321
7659
  }
7322
7660
  return globalEnv.rawRemoveChild.call(this, oldChild);
7323
7661
  };
7662
+ rawDocumentFragment.prototype.append = rawRootElement.prototype.append = function append(...nodes) {
7663
+ let i = 0;
7664
+ while (i < nodes.length) {
7665
+ let node = nodes[i];
7666
+ node = isNode(node) ? node : globalEnv.rawCreateTextNode.call(globalEnv.rawDocument, node);
7667
+ commonElementHandler(this, markElement(node), null, isDocumentFragment(this) ? globalEnv.rawFragmentAppend : globalEnv.rawAppend);
7668
+ i++;
7669
+ }
7670
+ };
7671
+ rawDocumentFragment.prototype.prepend = rawRootElement.prototype.prepend = function prepend(...nodes) {
7672
+ let i = nodes.length;
7673
+ while (i > 0) {
7674
+ let node = nodes[i - 1];
7675
+ node = isNode(node) ? node : globalEnv.rawCreateTextNode.call(globalEnv.rawDocument, node);
7676
+ commonElementHandler(this, markElement(node), null, isDocumentFragment(this) ? globalEnv.rawFragmentPrepend : globalEnv.rawPrepend);
7677
+ i--;
7678
+ }
7679
+ };
7324
7680
  /**
7325
7681
  * The insertAdjacentElement method of the Element interface inserts a given element node at a given position relative to the element it is invoked upon.
7326
7682
  * NOTE:
@@ -7340,52 +7696,81 @@ function patchElementAndDocument() {
7340
7696
  }
7341
7697
  return globalEnv.rawInsertAdjacentElement.call(this, where, element);
7342
7698
  };
7343
- // patch cloneNode
7344
- rawRootElement.prototype.cloneNode = function cloneNode(deep) {
7345
- const clonedNode = globalEnv.rawCloneNode.call(this, deep);
7346
- this.__MICRO_APP_NAME__ && (clonedNode.__MICRO_APP_NAME__ = this.__MICRO_APP_NAME__);
7347
- return clonedNode;
7348
- };
7349
7699
  /**
7350
7700
  * document.body(head).querySelector(querySelectorAll) hijack to microAppBody(microAppHead).querySelector(querySelectorAll)
7351
7701
  * NOTE:
7352
7702
  * 1. May cause some problems!
7353
7703
  * 2. Add config options?
7354
7704
  */
7355
- function getQueryTarget(target) {
7356
- const currentAppName = getCurrentAppName();
7357
- if ((target === document.body || target === document.head) && currentAppName) {
7705
+ function getElementQueryTarget(targetNode) {
7706
+ const currentAppName = getIframeCurrentAppName() || getCurrentAppName();
7707
+ if ((targetNode === document.body || targetNode === document.head) && currentAppName) {
7358
7708
  const app = appInstanceMap.get(currentAppName);
7359
7709
  if (app === null || app === void 0 ? void 0 : app.container) {
7360
- if (target === document.body) {
7710
+ if (targetNode === document.body) {
7361
7711
  return app.querySelector('micro-app-body');
7362
7712
  }
7363
- else if (target === document.head) {
7713
+ else if (targetNode === document.head) {
7364
7714
  return app.querySelector('micro-app-head');
7365
7715
  }
7366
7716
  }
7367
7717
  }
7368
- return target;
7718
+ return targetNode;
7719
+ }
7720
+ /**
7721
+ * In iframe sandbox, script will render to iframe instead of micro-app-body
7722
+ * So when query elements, we need to search both micro-app and iframe
7723
+ * @param isEmpty get empty result
7724
+ * @param targetNode targetNode element
7725
+ * @param result origin result
7726
+ * @param selectors selectors
7727
+ * @param methodName querySelector or querySelectorAll
7728
+ */
7729
+ function getElementQueryResult(isEmpty, targetNode, result, selectors, methodName) {
7730
+ if (isEmpty) {
7731
+ const currentAppName = getIframeCurrentAppName() || getCurrentAppName();
7732
+ if (currentAppName && isIframeSandbox(currentAppName)) {
7733
+ const app = appInstanceMap.get(currentAppName);
7734
+ if (isMicroAppHead(targetNode)) {
7735
+ return app.sandBox.microHead[methodName](selectors);
7736
+ }
7737
+ if (isMicroAppBody(targetNode)) {
7738
+ return app.sandBox.microBody[methodName](selectors);
7739
+ }
7740
+ }
7741
+ }
7742
+ return result;
7369
7743
  }
7370
7744
  rawRootElement.prototype.querySelector = function querySelector(selectors) {
7371
7745
  var _a;
7372
- return globalEnv.rawElementQuerySelector.call((_a = getQueryTarget(this)) !== null && _a !== void 0 ? _a : this, selectors);
7746
+ const _this = (_a = getElementQueryTarget(this)) !== null && _a !== void 0 ? _a : this;
7747
+ const result = globalEnv.rawElementQuerySelector.call(_this, selectors);
7748
+ return getElementQueryResult(isNull(result) && _this !== this, _this, result, selectors, 'querySelector');
7373
7749
  };
7374
7750
  rawRootElement.prototype.querySelectorAll = function querySelectorAll(selectors) {
7375
7751
  var _a;
7376
- return globalEnv.rawElementQuerySelectorAll.call((_a = getQueryTarget(this)) !== null && _a !== void 0 ? _a : this, selectors);
7752
+ const _this = (_a = getElementQueryTarget(this)) !== null && _a !== void 0 ? _a : this;
7753
+ const result = globalEnv.rawElementQuerySelectorAll.call(_this, selectors);
7754
+ return getElementQueryResult(!result.length && _this !== this, _this, result, selectors, 'querySelectorAll');
7377
7755
  };
7378
7756
  // rewrite setAttribute, complete resource address
7379
7757
  rawRootElement.prototype.setAttribute = function setAttribute(key, value) {
7380
- const appName = this.__MICRO_APP_NAME__ || getCurrentAppName();
7381
- if (appName &&
7382
- appInstanceMap.has(appName) &&
7383
- (((key === 'src' || key === 'srcset') && /^(img|script|video|audio|source|embed)$/i.test(this.tagName)) ||
7384
- (key === 'href' && /^link$/i.test(this.tagName)))) {
7385
- const app = appInstanceMap.get(appName);
7386
- value = CompletionPath(value, app.url);
7758
+ if (/^micro-app(-\S+)?/i.test(this.tagName) &&
7759
+ key === 'data' &&
7760
+ this.setAttribute !== rawRootElement.prototype.setAttribute) {
7761
+ this.setAttribute(key, value);
7762
+ }
7763
+ else {
7764
+ const appName = this.__MICRO_APP_NAME__ || getCurrentAppName();
7765
+ if (appName &&
7766
+ appInstanceMap.has(appName) &&
7767
+ (((key === 'src' || key === 'srcset') && /^(img|script|video|audio|source|embed)$/i.test(this.tagName)) ||
7768
+ (key === 'href' && /^(link|image)$/i.test(this.tagName)))) {
7769
+ const app = appInstanceMap.get(appName);
7770
+ value = CompletionPath(value, app.url);
7771
+ }
7772
+ globalEnv.rawSetAttribute.call(this, key, value);
7387
7773
  }
7388
- globalEnv.rawSetAttribute.call(this, key, value);
7389
7774
  };
7390
7775
  /**
7391
7776
  * TODO: 兼容直接通过img.src等操作设置的资源
@@ -7412,7 +7797,7 @@ function patchElementAndDocument() {
7412
7797
  // return get?.call(this)
7413
7798
  // },
7414
7799
  // set: function (value) {
7415
- // const currentAppName = getCurrentAppName()
7800
+ // const currentAppName = this.__MICRO_APP_NAME__ || getCurrentAppName()
7416
7801
  // if (currentAppName && appInstanceMap.has(currentAppName)) {
7417
7802
  // const app = appInstanceMap.get(currentAppName)
7418
7803
  // value = CompletionPath(value, app!.url)
@@ -7421,41 +7806,25 @@ function patchElementAndDocument() {
7421
7806
  // },
7422
7807
  // })
7423
7808
  // })
7424
- rawDefineProperty(rawRootElement.prototype, 'innerHTML', {
7425
- configurable: true,
7426
- enumerable: true,
7427
- get() {
7428
- return globalEnv.rawInnerHTMLDesc.get.call(this);
7429
- },
7430
- set(code) {
7431
- globalEnv.rawInnerHTMLDesc.set.call(this, code);
7432
- const currentAppName = getCurrentAppName();
7433
- Array.from(this.children).forEach((child) => {
7434
- if (isElement(child) && currentAppName) {
7435
- // TODO: 使用updateElementInfo进行更新
7436
- child.__MICRO_APP_NAME__ = currentAppName;
7437
- }
7438
- });
7439
- }
7440
- });
7441
7809
  rawDefineProperty(rawRootNode.prototype, 'parentNode', {
7442
7810
  configurable: true,
7443
7811
  enumerable: true,
7444
7812
  get() {
7445
7813
  var _a, _b, _c;
7446
7814
  /**
7447
- * hijack parentNode of html
7815
+ * hijack parentNode of html for with sandbox
7448
7816
  * Scenes:
7449
7817
  * 1. element-ui@2/lib/utils/popper.js
7450
7818
  * // root is child app window, so root.document is proxyDocument or microDocument
7451
7819
  * if (element.parentNode === root.document) ...
7452
7820
  */
7453
- const currentAppName = getCurrentAppName();
7821
+ const currentAppName = getIframeCurrentAppName() || getCurrentAppName();
7454
7822
  if (currentAppName && this === globalEnv.rawDocument.firstElementChild) {
7455
7823
  const microDocument = (_c = (_b = (_a = appInstanceMap.get(currentAppName)) === null || _a === void 0 ? void 0 : _a.sandBox) === null || _b === void 0 ? void 0 : _b.proxyWindow) === null || _c === void 0 ? void 0 : _c.document;
7456
7824
  if (microDocument)
7457
7825
  return microDocument;
7458
7826
  }
7827
+ // NOTE: run after hijack html.parentNode
7459
7828
  const result = globalEnv.rawParentNodeDesc.get.call(this);
7460
7829
  /**
7461
7830
  * If parentNode is <micro-app-body>, return rawDocument.body
@@ -7474,16 +7843,34 @@ function patchElementAndDocument() {
7474
7843
  return result;
7475
7844
  },
7476
7845
  });
7846
+ rawDefineProperty(rawRootElement.prototype, 'innerHTML', {
7847
+ configurable: true,
7848
+ enumerable: true,
7849
+ get() {
7850
+ return globalEnv.rawInnerHTMLDesc.get.call(this);
7851
+ },
7852
+ set(code) {
7853
+ globalEnv.rawInnerHTMLDesc.set.call(this, code);
7854
+ const currentAppName = this.__MICRO_APP_NAME__ || getCurrentAppName();
7855
+ Array.from(this.children).forEach((child) => {
7856
+ if (isElement(child) && currentAppName) {
7857
+ updateElementInfo(child, currentAppName);
7858
+ }
7859
+ });
7860
+ }
7861
+ });
7862
+ // patch cloneNode
7863
+ rawRootNode.prototype.cloneNode = function cloneNode(deep) {
7864
+ const clonedNode = globalEnv.rawCloneNode.call(this, deep);
7865
+ return updateElementInfo(clonedNode, this.__MICRO_APP_NAME__);
7866
+ };
7477
7867
  }
7478
7868
  /**
7479
7869
  * Mark the newly created element in the micro application
7480
7870
  * @param element new element
7481
7871
  */
7482
7872
  function markElement(element) {
7483
- const currentAppName = getCurrentAppName();
7484
- if (currentAppName)
7485
- element.__MICRO_APP_NAME__ = currentAppName;
7486
- return element;
7873
+ return updateElementInfo(element, getCurrentAppName());
7487
7874
  }
7488
7875
  // methods of document
7489
7876
  function patchDocument$2() {
@@ -7614,18 +8001,18 @@ function releasePatchElementAndDocument() {
7614
8001
  releasePatchDocument();
7615
8002
  const rawRootElement = globalEnv.rawRootElement;
7616
8003
  const rawRootNode = globalEnv.rawRootNode;
7617
- rawRootElement.prototype.appendChild = globalEnv.rawAppendChild;
7618
- rawRootElement.prototype.insertBefore = globalEnv.rawInsertBefore;
7619
- rawRootElement.prototype.replaceChild = globalEnv.rawReplaceChild;
7620
- rawRootElement.prototype.removeChild = globalEnv.rawRemoveChild;
8004
+ rawRootNode.prototype.appendChild = globalEnv.rawAppendChild;
8005
+ rawRootNode.prototype.insertBefore = globalEnv.rawInsertBefore;
8006
+ rawRootNode.prototype.replaceChild = globalEnv.rawReplaceChild;
8007
+ rawRootNode.prototype.removeChild = globalEnv.rawRemoveChild;
8008
+ rawRootNode.prototype.cloneNode = globalEnv.rawCloneNode;
7621
8009
  rawRootElement.prototype.append = globalEnv.rawAppend;
7622
8010
  rawRootElement.prototype.prepend = globalEnv.rawPrepend;
7623
- rawRootElement.prototype.cloneNode = globalEnv.rawCloneNode;
7624
8011
  rawRootElement.prototype.querySelector = globalEnv.rawElementQuerySelector;
7625
8012
  rawRootElement.prototype.querySelectorAll = globalEnv.rawElementQuerySelectorAll;
7626
8013
  rawRootElement.prototype.setAttribute = globalEnv.rawSetAttribute;
7627
- rawDefineProperty(rawRootElement.prototype, 'innerHTML', globalEnv.rawInnerHTMLDesc);
7628
8014
  rawDefineProperty(rawRootNode.prototype, 'parentNode', globalEnv.rawParentNodeDesc);
8015
+ rawDefineProperty(rawRootElement.prototype, 'innerHTML', globalEnv.rawInnerHTMLDesc);
7629
8016
  }
7630
8017
  // Set the style of micro-app-head and micro-app-body
7631
8018
  let hasRejectMicroAppStyle = false;
@@ -7655,15 +8042,18 @@ function initGlobalEnv() {
7655
8042
  const rawRootElement = rawWindow.Element;
7656
8043
  const rawRootNode = rawWindow.Node;
7657
8044
  const rawRootEventTarget = rawWindow.EventTarget;
8045
+ const rawDocumentFragment = rawWindow.DocumentFragment;
7658
8046
  // save patch raw methods, pay attention to this binding
8047
+ const rawAppendChild = rawRootNode.prototype.appendChild;
8048
+ const rawInsertBefore = rawRootNode.prototype.insertBefore;
8049
+ const rawReplaceChild = rawRootNode.prototype.replaceChild;
8050
+ const rawRemoveChild = rawRootNode.prototype.removeChild;
7659
8051
  const rawSetAttribute = rawRootElement.prototype.setAttribute;
7660
- const rawAppendChild = rawRootElement.prototype.appendChild;
7661
- const rawInsertBefore = rawRootElement.prototype.insertBefore;
7662
- const rawReplaceChild = rawRootElement.prototype.replaceChild;
7663
- const rawRemoveChild = rawRootElement.prototype.removeChild;
7664
8052
  const rawAppend = rawRootElement.prototype.append;
7665
8053
  const rawPrepend = rawRootElement.prototype.prepend;
7666
- const rawCloneNode = rawRootElement.prototype.cloneNode;
8054
+ const rawFragmentAppend = rawDocumentFragment.prototype.append;
8055
+ const rawFragmentPrepend = rawDocumentFragment.prototype.prepend;
8056
+ const rawCloneNode = rawRootNode.prototype.cloneNode;
7667
8057
  const rawElementQuerySelector = rawRootElement.prototype.querySelector;
7668
8058
  const rawElementQuerySelectorAll = rawRootElement.prototype.querySelectorAll;
7669
8059
  const rawInsertAdjacentElement = rawRootElement.prototype.insertAdjacentElement;
@@ -7681,13 +8071,10 @@ function initGlobalEnv() {
7681
8071
  const rawGetElementsByClassName = rawRootDocument.prototype.getElementsByClassName;
7682
8072
  const rawGetElementsByTagName = rawRootDocument.prototype.getElementsByTagName;
7683
8073
  const rawGetElementsByName = rawRootDocument.prototype.getElementsByName;
7684
- const ImageProxy = new Proxy(Image, {
8074
+ // TODO: 将ImageProxy移出去
8075
+ const ImageProxy = new Proxy(rawWindow.Image, {
7685
8076
  construct(Target, args) {
7686
- const elementImage = new Target(...args);
7687
- const currentAppName = getCurrentAppName();
7688
- if (currentAppName)
7689
- elementImage.__MICRO_APP_NAME__ = currentAppName;
7690
- return elementImage;
8077
+ return updateElementInfo(new Target(...args), getCurrentAppName());
7691
8078
  },
7692
8079
  });
7693
8080
  /**
@@ -7713,6 +8100,7 @@ function initGlobalEnv() {
7713
8100
  rawRootDocument,
7714
8101
  rawRootElement,
7715
8102
  rawRootNode,
8103
+ rawDocumentFragment,
7716
8104
  // source/patch
7717
8105
  rawSetAttribute,
7718
8106
  rawAppendChild,
@@ -7721,6 +8109,8 @@ function initGlobalEnv() {
7721
8109
  rawRemoveChild,
7722
8110
  rawAppend,
7723
8111
  rawPrepend,
8112
+ rawFragmentAppend,
8113
+ rawFragmentPrepend,
7724
8114
  rawCloneNode,
7725
8115
  rawElementQuerySelector,
7726
8116
  rawElementQuerySelectorAll,
@@ -7760,7 +8150,7 @@ function initGlobalEnv() {
7760
8150
  * @param tagName element name
7761
8151
  */
7762
8152
  function defineElement(tagName) {
7763
- class MicroAppElement extends getBaseHTMLElement() {
8153
+ class MicroAppElement extends HTMLElement {
7764
8154
  constructor() {
7765
8155
  super(...arguments);
7766
8156
  this.isWaiting = false;
@@ -7825,6 +8215,13 @@ function defineElement(tagName) {
7825
8215
  // baseRoute: route prefix, default is ''
7826
8216
  // keep-alive: open keep-alive mode
7827
8217
  connectedCallback() {
8218
+ /**
8219
+ * In FireFox, iframe Node.prototype will point to native Node.prototype after insert to document
8220
+ * If <micro-app>.prototype is not MicroAppElement.prototype, we should reset it
8221
+ */
8222
+ if (Object.getPrototypeOf(this) !== MicroAppElement.prototype) {
8223
+ Object.setPrototypeOf(this, MicroAppElement.prototype);
8224
+ }
7828
8225
  const cacheCount = ++this.connectedCount;
7829
8226
  this.connectStateMap.set(cacheCount, true);
7830
8227
  /**
@@ -8263,6 +8660,17 @@ function defineElement(tagName) {
8263
8660
  globalEnv.rawSetAttribute.call(this, key, value);
8264
8661
  }
8265
8662
  }
8663
+ /**
8664
+ * get delay time of router event
8665
+ * @returns delay time
8666
+ */
8667
+ getRouterEventDelay() {
8668
+ let delay = parseInt(this.getAttribute('router-event-delay'));
8669
+ if (isNaN(delay)) {
8670
+ delay = parseInt((isFunction(microApp.options['router-event-delay']) ? microApp.options['router-event-delay'](this.appName) : microApp.options['router-event-delay']));
8671
+ }
8672
+ return !isNaN(delay) ? delay : 0;
8673
+ }
8266
8674
  /**
8267
8675
  * Data from the base application
8268
8676
  */
@@ -8299,7 +8707,7 @@ function defineElement(tagName) {
8299
8707
  return this.getBaseRouteCompatible();
8300
8708
  }
8301
8709
  }
8302
- globalEnv.rawWindow.customElements.define(tagName, MicroAppElement);
8710
+ window.customElements.define(tagName, MicroAppElement);
8303
8711
  }
8304
8712
 
8305
8713
  /**
@@ -8331,7 +8739,7 @@ function preFetch(apps, delay) {
8331
8739
  const delayTime = isNumber(delay) ? delay : microApp.options.prefetchDelay;
8332
8740
  /**
8333
8741
  * TODO: remove setTimeout
8334
- * Is there a better way?
8742
+ * 如果要保留setTimeout,则需要考虑清空定时器的情况
8335
8743
  */
8336
8744
  setTimeout(() => {
8337
8745
  // releasePrefetchEffect()
@@ -8566,7 +8974,7 @@ function unmountApp(appName, options) {
8566
8974
  }
8567
8975
  }
8568
8976
  else {
8569
- logWarn(`app ${appName} does not exist`);
8977
+ logWarn(`app ${appName} does not exist when unmountApp`);
8570
8978
  resolve(false);
8571
8979
  }
8572
8980
  });
@@ -8596,7 +9004,7 @@ function reload(appName, destroy) {
8596
9004
  }
8597
9005
  }
8598
9006
  else {
8599
- logWarn(`app ${appName} does not exist`);
9007
+ logWarn(`app ${appName} does not exist when reload app`);
8600
9008
  resolve(false);
8601
9009
  }
8602
9010
  });
@@ -8651,20 +9059,6 @@ function renderApp(options) {
8651
9059
  container.appendChild(microAppElement);
8652
9060
  });
8653
9061
  }
8654
- /**
8655
- * get app state
8656
- * @param appName app.name
8657
- * @returns app.state
8658
- */
8659
- function getAppStatus(appName) {
8660
- const app = appInstanceMap.get(formatAppName(appName));
8661
- if (app) {
8662
- return app.getLifeCycleState();
8663
- }
8664
- else {
8665
- logWarn(`app ${appName} does not exist`);
8666
- }
8667
- }
8668
9062
  class MicroApp extends EventCenterForBaseApp {
8669
9063
  constructor() {
8670
9064
  super(...arguments);
@@ -8679,7 +9073,6 @@ class MicroApp extends EventCenterForBaseApp {
8679
9073
  this.getAllApps = getAllApps;
8680
9074
  this.reload = reload;
8681
9075
  this.renderApp = renderApp;
8682
- this.getAppStatus = getAppStatus;
8683
9076
  }
8684
9077
  start(options) {
8685
9078
  var _a, _b;
@@ -8704,7 +9097,7 @@ class MicroApp extends EventCenterForBaseApp {
8704
9097
  }
8705
9098
  }
8706
9099
  initGlobalEnv();
8707
- if (globalEnv.rawWindow.customElements.get(this.tagName)) {
9100
+ if (window.customElements.get(this.tagName)) {
8708
9101
  return logWarn(`element ${this.tagName} is already defined`);
8709
9102
  }
8710
9103
  if (isPlainObject(options)) {
@@ -8735,5 +9128,5 @@ class MicroApp extends EventCenterForBaseApp {
8735
9128
  const microApp = new MicroApp();
8736
9129
 
8737
9130
  export default microApp;
8738
- export { EventCenterForMicroApp, MicroApp, getActiveApps, getAllApps, getAppStatus, preFetch, pureCreateElement, reload, removeDomScope, renderApp, unmountAllApps, unmountApp, version };
9131
+ export { EventCenterForMicroApp, MicroApp, getActiveApps, getAllApps, preFetch, pureCreateElement, reload, removeDomScope, renderApp, unmountAllApps, unmountApp, version };
8739
9132
  //# sourceMappingURL=index.esm.js.map