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

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.6';
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
+ * native模式从state中取值,而浏览器地址的修改无法控制,很可能出现浏览器地址和__MICRO_APP_STATE__不一致的情况
4756
+ * 尤其是在初始化和前进后退时,由于vue-router4会主动修改url地址,倒是上述情况经常出现
4757
+ * 为了结局这个问题,在子应用初始化和响应popstate事件后,判断__MICRO_APP_STATE__和浏览器地址是否一致,如果不一致,则将浏览器地址更新为__MICRO_APP_STATE__的地址
4758
+ * 说明:
4759
+ * 1、如果__MICRO_APP_STATE__和url不一样,那么更新url的操作是对的,否则会产生url和渲染页面不一致的问题,开发者会更加困惑
4760
+ * 2、当native模式有多个子应用同时存在,其中一个修改url地址,另外一个并不会改变__MICRO_APP_STATE__,刷新就产生问题,不一致,第二是根据谁更新url?
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,43 @@ 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, _b, _c;
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
+ return (_c = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.querySelector(selectors)) !== null && _b !== void 0 ? _b : rawMicroQuerySelector.call(microDocument, selectors)) !== null && _c !== void 0 ? _c : null;
5559
5859
  }
5560
5860
  function querySelectorAll(selectors) {
5561
5861
  var _a, _b;
5862
+ const _this = getBindTarget(this);
5562
5863
  if (!selectors ||
5563
5864
  isUniqueElement(selectors) ||
5564
- microDocument !== this) {
5565
- const _this = getDefaultRawTarget(this);
5865
+ rawDocument !== _this) {
5566
5866
  return rawMicroQuerySelectorAll.call(_this, selectors);
5567
5867
  }
5568
- return (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.querySelectorAll(selectors)) !== null && _b !== void 0 ? _b : [];
5868
+ const result = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.querySelectorAll(selectors)) !== null && _b !== void 0 ? _b : [];
5869
+ return result.length ? result : rawMicroQuerySelectorAll.call(microDocument, selectors);
5569
5870
  }
5570
5871
  microRootDocument.prototype.querySelector = querySelector;
5571
5872
  microRootDocument.prototype.querySelectorAll = querySelectorAll;
5572
5873
  microRootDocument.prototype.getElementById = function getElementById(key) {
5573
- const _this = getDefaultRawTarget(this);
5874
+ const _this = getBindTarget(this);
5574
5875
  if (isInvalidQuerySelectorKey(key)) {
5575
5876
  return rawMicroGetElementById.call(_this, key);
5576
5877
  }
@@ -5582,7 +5883,7 @@ function patchDocumentPrototype(appName, microAppWindow) {
5582
5883
  }
5583
5884
  };
5584
5885
  microRootDocument.prototype.getElementsByClassName = function getElementsByClassName(key) {
5585
- const _this = getDefaultRawTarget(this);
5886
+ const _this = getBindTarget(this);
5586
5887
  if (isInvalidQuerySelectorKey(key)) {
5587
5888
  return rawMicroGetElementsByClassName.call(_this, key);
5588
5889
  }
@@ -5594,7 +5895,7 @@ function patchDocumentPrototype(appName, microAppWindow) {
5594
5895
  }
5595
5896
  };
5596
5897
  microRootDocument.prototype.getElementsByTagName = function getElementsByTagName(key) {
5597
- const _this = getDefaultRawTarget(this);
5898
+ const _this = getBindTarget(this);
5598
5899
  if (isUniqueElement(key) ||
5599
5900
  isInvalidQuerySelectorKey(key)) {
5600
5901
  return rawMicroGetElementsByTagName.call(_this, key);
@@ -5610,7 +5911,7 @@ function patchDocumentPrototype(appName, microAppWindow) {
5610
5911
  }
5611
5912
  };
5612
5913
  microRootDocument.prototype.getElementsByName = function getElementsByName(key) {
5613
- const _this = getDefaultRawTarget(this);
5914
+ const _this = getBindTarget(this);
5614
5915
  if (isInvalidQuerySelectorKey(key)) {
5615
5916
  return rawMicroGetElementsByName.call(_this, key);
5616
5917
  }
@@ -5677,7 +5978,7 @@ function patchDocumentProperty(appName, microAppWindow, sandbox) {
5677
5978
  enumerable: true,
5678
5979
  configurable: true,
5679
5980
  get: () => {
5680
- throttleDeferForSetAppName(appName);
5981
+ throttleDeferForIframeAppName(appName);
5681
5982
  return rawDocument[tagName];
5682
5983
  },
5683
5984
  set: (value) => { rawDocument[tagName] = value; },
@@ -5685,7 +5986,7 @@ function patchDocumentProperty(appName, microAppWindow, sandbox) {
5685
5986
  });
5686
5987
  }
5687
5988
  function patchDocumentEffect(appName, microAppWindow) {
5688
- const { rawDocument, rawAddEventListener, rawRemoveEventListener } = globalEnv;
5989
+ const { rawDocument, rawAddEventListener, rawRemoveEventListener, rawDispatchEvent } = globalEnv;
5689
5990
  const eventListenerMap = new Map();
5690
5991
  const sstEventListenerMap = new Map();
5691
5992
  let onClickHandler = null;
@@ -5715,6 +6016,9 @@ function patchDocumentEffect(appName, microAppWindow) {
5715
6016
  const handler = (listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_BOUND_FUNCTION__) || listener;
5716
6017
  rawRemoveEventListener.call(getEventTarget(type, this), type, handler, options);
5717
6018
  };
6019
+ microRootDocument.prototype.dispatchEvent = function (event) {
6020
+ return rawDispatchEvent.call(getEventTarget(event === null || event === void 0 ? void 0 : event.type, this), event);
6021
+ };
5718
6022
  // 重新定义microRootDocument.prototype 上的on开头方法
5719
6023
  function createSetterHandler(eventName) {
5720
6024
  if (eventName === 'onclick') {
@@ -5835,12 +6139,18 @@ function patchElement(appName, url, microAppWindow, sandbox) {
5835
6139
  patchIframeNode(appName, microAppWindow, sandbox);
5836
6140
  patchIframeAttribute(url, microAppWindow);
5837
6141
  }
6142
+ /**
6143
+ * patch iframe Node/Element
6144
+ *
6145
+ */
5838
6146
  function patchIframeNode(appName, microAppWindow, sandbox) {
5839
6147
  const rawRootElement = globalEnv.rawRootElement; // native root Element
6148
+ const rawRootNode = globalEnv.rawRootNode;
5840
6149
  const rawDocument = globalEnv.rawDocument;
5841
6150
  const microDocument = microAppWindow.document;
5842
6151
  const microRootNode = microAppWindow.Node;
5843
6152
  const microRootElement = microAppWindow.Element;
6153
+ const microDocumentFragment = microAppWindow.DocumentFragment;
5844
6154
  // const rawMicroGetRootNode = microRootNode.prototype.getRootNode
5845
6155
  const rawMicroAppendChild = microRootNode.prototype.appendChild;
5846
6156
  const rawMicroInsertBefore = microRootNode.prototype.insertBefore;
@@ -5848,6 +6158,8 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5848
6158
  const rawMicroRemoveChild = microRootNode.prototype.removeChild;
5849
6159
  const rawMicroAppend = microRootElement.prototype.append;
5850
6160
  const rawMicroPrepend = microRootElement.prototype.prepend;
6161
+ const rawMicroFragmentAppend = microDocumentFragment.prototype.append;
6162
+ const rawMicroFragmentPrepend = microDocumentFragment.prototype.prepend;
5851
6163
  const rawMicroInsertAdjacentElement = microRootElement.prototype.insertAdjacentElement;
5852
6164
  const rawMicroCloneNode = microRootNode.prototype.cloneNode;
5853
6165
  const rawInnerHTMLDesc = Object.getOwnPropertyDescriptor(microRootElement.prototype, 'innerHTML');
@@ -5865,42 +6177,34 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5865
6177
  }
5866
6178
  return parent;
5867
6179
  };
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
6180
  microRootNode.prototype.appendChild = function appendChild(node) {
5876
- // TODO: 有必要执行这么多次updateElementInfo?
5877
6181
  updateElementInfo(node, appName);
5878
6182
  if (isPureNode(node)) {
5879
6183
  return rawMicroAppendChild.call(this, node);
5880
6184
  }
5881
- return rawRootElement.prototype.appendChild.call(getRawTarget(this), node);
6185
+ return rawRootNode.prototype.appendChild.call(getRawTarget(this), node);
5882
6186
  };
5883
6187
  microRootNode.prototype.insertBefore = function insertBefore(node, child) {
5884
6188
  updateElementInfo(node, appName);
5885
6189
  if (isPureNode(node)) {
5886
6190
  return rawMicroInsertBefore.call(this, node, child);
5887
6191
  }
5888
- return rawRootElement.prototype.insertBefore.call(getRawTarget(this), node, child);
6192
+ return rawRootNode.prototype.insertBefore.call(getRawTarget(this), node, child);
5889
6193
  };
5890
6194
  microRootNode.prototype.replaceChild = function replaceChild(node, child) {
5891
6195
  updateElementInfo(node, appName);
5892
6196
  if (isPureNode(node)) {
5893
6197
  return rawMicroReplaceChild.call(this, node, child);
5894
6198
  }
5895
- return rawRootElement.prototype.replaceChild.call(getRawTarget(this), node, child);
6199
+ return rawRootNode.prototype.replaceChild.call(getRawTarget(this), node, child);
5896
6200
  };
5897
6201
  microRootNode.prototype.removeChild = function removeChild(oldChild) {
5898
6202
  if (isPureNode(oldChild) || this.contains(oldChild)) {
5899
6203
  return rawMicroRemoveChild.call(this, oldChild);
5900
6204
  }
5901
- return rawRootElement.prototype.removeChild.call(getRawTarget(this), oldChild);
6205
+ return rawRootNode.prototype.removeChild.call(getRawTarget(this), oldChild);
5902
6206
  };
5903
- microRootElement.prototype.append = function append(...nodes) {
6207
+ microDocumentFragment.prototype.append = microRootElement.prototype.append = function append(...nodes) {
5904
6208
  let i = 0;
5905
6209
  let hasPureNode = false;
5906
6210
  while (i < nodes.length) {
@@ -5910,11 +6214,11 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5910
6214
  i++;
5911
6215
  }
5912
6216
  if (hasPureNode) {
5913
- return rawMicroAppend.call(this, ...nodes);
6217
+ return (isDocumentFragment(this) ? rawMicroFragmentAppend : rawMicroAppend).call(this, ...nodes);
5914
6218
  }
5915
6219
  return rawRootElement.prototype.append.call(getRawTarget(this), ...nodes);
5916
6220
  };
5917
- microRootElement.prototype.prepend = function prepend(...nodes) {
6221
+ microDocumentFragment.prototype.prepend = microRootElement.prototype.prepend = function prepend(...nodes) {
5918
6222
  let i = 0;
5919
6223
  let hasPureNode = false;
5920
6224
  while (i < nodes.length) {
@@ -5924,7 +6228,7 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5924
6228
  i++;
5925
6229
  }
5926
6230
  if (hasPureNode) {
5927
- return rawMicroPrepend.call(this, ...nodes);
6231
+ return (isDocumentFragment(this) ? rawMicroFragmentPrepend : rawMicroPrepend).call(this, ...nodes);
5928
6232
  }
5929
6233
  return rawRootElement.prototype.prepend.call(getRawTarget(this), ...nodes);
5930
6234
  };
@@ -5940,28 +6244,53 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5940
6244
  }
5941
6245
  return rawRootElement.prototype.insertAdjacentElement.call(getRawTarget(this), where, element);
5942
6246
  };
5943
- // patch cloneNode
5944
- microRootNode.prototype.cloneNode = function cloneNode(deep) {
5945
- const clonedNode = rawMicroCloneNode.call(this, deep);
5946
- return updateElementInfo(clonedNode, appName);
5947
- };
6247
+ /**
6248
+ * Specific prototype properties:
6249
+ * 1. baseURI
6250
+ * 2. ownerDocument
6251
+ * 3. parentNode
6252
+ * 4. innerHTML
6253
+ */
6254
+ rawDefineProperty(microRootNode.prototype, 'baseURI', {
6255
+ configurable: true,
6256
+ enumerable: true,
6257
+ get() {
6258
+ return sandbox.proxyWindow.location.href;
6259
+ },
6260
+ });
5948
6261
  rawDefineProperty(microRootNode.prototype, 'ownerDocument', {
5949
6262
  configurable: true,
5950
6263
  enumerable: true,
5951
6264
  get() {
6265
+ var _a;
5952
6266
  return this.__PURE_ELEMENT__ || this === microDocument
5953
- ? rawOwnerDocumentDesc.get.call(this)
5954
- : microDocument;
6267
+ ? (_a = rawOwnerDocumentDesc.get) === null || _a === void 0 ? void 0 : _a.call(this) : microDocument;
5955
6268
  },
5956
6269
  });
6270
+ // patch parentNode
6271
+ rawDefineProperty(microRootNode.prototype, 'parentNode', getIframeParentNodeDesc(appName, rawParentNodeDesc));
6272
+ microRootNode.prototype.getRootNode = function getRootNode() {
6273
+ return microDocument;
6274
+ // TODO: any case return document?
6275
+ // const rootNode = rawMicroGetRootNode.call(this, options)
6276
+ // if (rootNode === appInstanceMap.get(appName)?.container) return microDocument
6277
+ // return rootNode
6278
+ };
6279
+ // patch cloneNode
6280
+ microRootNode.prototype.cloneNode = function cloneNode(deep) {
6281
+ const clonedNode = rawMicroCloneNode.call(this, deep);
6282
+ return updateElementInfo(clonedNode, appName);
6283
+ };
5957
6284
  rawDefineProperty(microRootElement.prototype, 'innerHTML', {
5958
6285
  configurable: true,
5959
6286
  enumerable: true,
5960
6287
  get() {
5961
- return rawInnerHTMLDesc.get.call(this);
6288
+ var _a;
6289
+ return (_a = rawInnerHTMLDesc.get) === null || _a === void 0 ? void 0 : _a.call(this);
5962
6290
  },
5963
6291
  set(code) {
5964
- rawInnerHTMLDesc.set.call(this, code);
6292
+ var _a;
6293
+ (_a = rawInnerHTMLDesc.set) === null || _a === void 0 ? void 0 : _a.call(this, code);
5965
6294
  Array.from(this.children).forEach((child) => {
5966
6295
  if (isElement(child)) {
5967
6296
  updateElementInfo(child, appName);
@@ -5969,34 +6298,6 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5969
6298
  });
5970
6299
  }
5971
6300
  });
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
6301
  // Adapt to new image(...) scene
6001
6302
  const ImageProxy = new Proxy(microAppWindow.Image, {
6002
6303
  construct(Target, args) {
@@ -6015,16 +6316,24 @@ function patchIframeAttribute(url, microAppWindow) {
6015
6316
  const microRootElement = microAppWindow.Element;
6016
6317
  const rawMicroSetAttribute = microRootElement.prototype.setAttribute;
6017
6318
  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);
6319
+ if (/^micro-app(-\S+)?/i.test(this.tagName) &&
6320
+ key === 'data' &&
6321
+ this.setAttribute !== microRootElement.prototype.setAttribute) {
6322
+ this.setAttribute(key, value);
6323
+ }
6324
+ else {
6325
+ if (((key === 'src' || key === 'srcset') && /^(img|script|video|audio|source|embed)$/i.test(this.tagName)) ||
6326
+ (key === 'href' && /^(link|image)$/i.test(this.tagName))) {
6327
+ value = CompletionPath(value, url);
6328
+ }
6329
+ rawMicroSetAttribute.call(this, key, value);
6021
6330
  }
6022
- rawMicroSetAttribute.call(this, key, value);
6023
6331
  };
6024
6332
  const protoAttrList = [
6025
6333
  [microAppWindow.HTMLImageElement.prototype, 'src'],
6026
6334
  [microAppWindow.HTMLScriptElement.prototype, 'src'],
6027
6335
  [microAppWindow.HTMLLinkElement.prototype, 'href'],
6336
+ [microAppWindow.SVGImageElement.prototype, 'href'],
6028
6337
  ];
6029
6338
  /**
6030
6339
  * element.setAttribute does not trigger this actions:
@@ -6101,9 +6410,10 @@ class IframeSandbox {
6101
6410
  createIframeElement(appName, browserPath) {
6102
6411
  this.iframe = pureCreateElement('iframe');
6103
6412
  const iframeAttrs = {
6413
+ id: appName,
6104
6414
  src: microApp.options.iframeSrc || browserPath,
6105
6415
  style: 'display: none',
6106
- id: appName,
6416
+ 'powered-by': 'micro-app',
6107
6417
  };
6108
6418
  Object.keys(iframeAttrs).forEach((key) => this.iframe.setAttribute(key, iframeAttrs[key]));
6109
6419
  // effect action during construct
@@ -6137,15 +6447,6 @@ class IframeSandbox {
6137
6447
  * 1. iframe router and browser router are separated, we should update iframe router manually
6138
6448
  * 2. withSandbox location is browser location when disable memory-router, so no need to do anything
6139
6449
  */
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
6450
  this.initRouteState(defaultPage);
6150
6451
  // unique listener of popstate event for child app
6151
6452
  this.removeHistoryListener = addHistoryListener(this.microAppWindow.__MICRO_APP_NAME__);
@@ -6168,6 +6469,7 @@ class IframeSandbox {
6168
6469
  }
6169
6470
  stop({ umdMode, keepRouteState, destroy, clearData, }) {
6170
6471
  var _a;
6472
+ // sandbox.stop may exec before sandbox.start, e.g: iframe sandbox + default mode + remount
6171
6473
  if (!this.active)
6172
6474
  return;
6173
6475
  this.recordAndReleaseEffect({ clearData }, !umdMode || destroy);
@@ -6183,6 +6485,7 @@ class IframeSandbox {
6183
6485
  Reflect.deleteProperty(globalEnv.rawWindow, key);
6184
6486
  });
6185
6487
  this.escapeKeys.clear();
6488
+ this.clearHijackUmdHooks();
6186
6489
  }
6187
6490
  if (--globalEnv.activeSandbox === 0) {
6188
6491
  releasePatchElementAndDocument();
@@ -6401,10 +6704,49 @@ class IframeSandbox {
6401
6704
  * action before exec scripts when mount
6402
6705
  * Actions:
6403
6706
  * 1. patch static elements from html
6707
+ * 2. hijack umd hooks -- mount, unmount, micro-app-appName
6404
6708
  * @param container micro app container
6405
6709
  */
6406
- actionBeforeExecScripts(container) {
6710
+ actionsBeforeExecScripts(container, handleUmdHooks) {
6407
6711
  this.patchStaticElement(container);
6712
+ this.clearHijackUmdHooks = this.hijackUmdHooks(this.appName, this.microAppWindow, handleUmdHooks);
6713
+ }
6714
+ // hijack mount, unmount, micro-app-appName hook to microAppWindow
6715
+ hijackUmdHooks(appName, microAppWindow, handleUmdHooks) {
6716
+ let mount, unmount, microAppLibrary;
6717
+ rawDefineProperties(microAppWindow, {
6718
+ mount: {
6719
+ configurable: true,
6720
+ get: () => mount,
6721
+ set: (value) => {
6722
+ if (this.active && isFunction(value) && !mount) {
6723
+ handleUmdHooks(mount = value, unmount);
6724
+ }
6725
+ }
6726
+ },
6727
+ unmount: {
6728
+ configurable: true,
6729
+ get: () => unmount,
6730
+ set: (value) => {
6731
+ if (this.active && isFunction(value) && !unmount) {
6732
+ handleUmdHooks(mount, unmount = value);
6733
+ }
6734
+ }
6735
+ },
6736
+ [`micro-app-${appName}`]: {
6737
+ configurable: true,
6738
+ get: () => microAppLibrary,
6739
+ set: (value) => {
6740
+ if (this.active && isPlainObject(value) && !microAppLibrary) {
6741
+ microAppLibrary = value;
6742
+ handleUmdHooks(microAppLibrary.mount, microAppLibrary.unmount);
6743
+ }
6744
+ }
6745
+ }
6746
+ });
6747
+ return () => {
6748
+ mount = unmount = microAppLibrary = null;
6749
+ };
6408
6750
  }
6409
6751
  setStaticAppState(state) {
6410
6752
  this.microAppWindow.__MICRO_APP_STATE__ = state;
@@ -6421,7 +6763,6 @@ class CreateApp {
6421
6763
  this.loadSourceLevel = 0;
6422
6764
  this.umdHookMount = null;
6423
6765
  this.umdHookUnmount = null;
6424
- this.lifeCycleState = null;
6425
6766
  this.umdMode = false;
6426
6767
  // TODO: 类型优化,加上iframe沙箱
6427
6768
  this.sandBox = null;
@@ -6466,7 +6807,9 @@ class CreateApp {
6466
6807
  var _a;
6467
6808
  if (++this.loadSourceLevel === 2) {
6468
6809
  this.source.html = html;
6469
- if (!this.isPrefetch && !this.isUnmounted()) {
6810
+ if (this.isUnmounted())
6811
+ return;
6812
+ if (!this.isPrefetch) {
6470
6813
  getRootContainer(this.container).mount(this);
6471
6814
  }
6472
6815
  else if (this.isPrerender) {
@@ -6589,7 +6932,6 @@ class CreateApp {
6589
6932
  this.fiber = fiber;
6590
6933
  this.routerMode = routerMode;
6591
6934
  const dispatchBeforeMount = () => {
6592
- this.setLifeCycleState(lifeCycles.BEFOREMOUNT);
6593
6935
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
6594
6936
  };
6595
6937
  if (this.isPrerender) {
@@ -6603,7 +6945,7 @@ class CreateApp {
6603
6945
  dispatchCustomEventToMicroApp(this, 'statechange', {
6604
6946
  appState: appStates.MOUNTING
6605
6947
  });
6606
- // TODO: 将所有cloneContainer中的'as Element'去掉,兼容shadowRoot的场景
6948
+ // TODO: 兼容shadowRoot的场景
6607
6949
  this.cloneContainer(this.container, this.source.html, !this.umdMode);
6608
6950
  (_e = this.sandBox) === null || _e === void 0 ? void 0 : _e.start({
6609
6951
  umdMode: this.umdMode,
@@ -6612,38 +6954,35 @@ class CreateApp {
6612
6954
  disablePatchRequest,
6613
6955
  });
6614
6956
  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;
6957
+ // patch element info of html
6958
+ (_f = this.sandBox) === null || _f === void 0 ? void 0 : _f.actionsBeforeExecScripts(this.container, (mount, unmount) => {
6959
+ var _a;
6960
+ if (!this.umdMode && !this.isUnmounted()) {
6961
+ this.umdHookMount = isFunction(mount) ? mount : null;
6962
+ // umdHookUnmount can works in default mode, register by window.unmount
6963
+ this.umdHookUnmount = isFunction(unmount) ? unmount : null;
6627
6964
  // 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);
6965
+ if (isFunction(this.umdHookMount) && isFunction(this.umdHookUnmount)) {
6966
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.markUmdMode(this.umdMode = true);
6632
6967
  try {
6633
- this.handleMounted(this.umdHookMount(microApp.getData(this.name, true)));
6968
+ // if appState is mounted, it means that isFinished is true and this.handleMounted has already been executed, just exec this.umdHookMount
6969
+ if (this.getAppState() === appStates.MOUNTED) {
6970
+ this.umdHookMount(microApp.getData(this.name, true));
6971
+ }
6972
+ else {
6973
+ this.handleMounted(this.umdHookMount(microApp.getData(this.name, true)));
6974
+ }
6634
6975
  }
6635
6976
  catch (e) {
6636
- /**
6637
- * TODO:
6638
- * 1. 是否应该直接抛出错误
6639
- * 2. 是否应该触发error生命周期
6640
- */
6641
- logError('An error occurred in window.mount \n', this.name, e);
6977
+ logError('An error occurred when mount \n', this.name, e);
6642
6978
  }
6643
6979
  }
6644
- else if (isFinished === true) {
6645
- this.handleMounted();
6646
- }
6980
+ }
6981
+ });
6982
+ // if all js are executed, param isFinished will be true
6983
+ execScripts(this, (isFinished) => {
6984
+ if (!this.umdMode && isFinished === true) {
6985
+ this.handleMounted();
6647
6986
  }
6648
6987
  });
6649
6988
  }
@@ -6653,13 +6992,22 @@ class CreateApp {
6653
6992
  this.handleMounted(this.umdHookMount(microApp.getData(this.name, true)));
6654
6993
  }
6655
6994
  catch (e) {
6656
- logError('An error occurred in window.mount \n', this.name, e);
6995
+ logError('An error occurred when mount \n', this.name, e);
6657
6996
  }
6658
6997
  }
6659
6998
  }
6660
6999
  };
7000
+ /**
7001
+ * Initialization of sandbox is async, especially iframe sandbox are macro tasks
7002
+ * when child apps switch quickly, we need to pay attention to the following points:
7003
+ * NOTE:
7004
+ * 1. unmount app before exec nextAction (especially: iframe sandbox + default mode + remount)
7005
+ * this.container is null, this.sandBox will not start
7006
+ * 2. remount app of note 1
7007
+ * 3. unmount app during exec js
7008
+ */
6661
7009
  // TODO: 可优化?
6662
- this.sandBox ? this.sandBox.sandboxReady.then(nextAction) : nextAction();
7010
+ this.sandBox ? this.sandBox.sandboxReady.then(() => !this.isUnmounted() && nextAction()) : nextAction();
6663
7011
  }
6664
7012
  /**
6665
7013
  * handle for promise umdHookMount
@@ -6668,16 +7016,17 @@ class CreateApp {
6668
7016
  handleMounted(umdHookMountResult) {
6669
7017
  var _a, _b;
6670
7018
  const dispatchAction = () => {
7019
+ const nextAction = () => this.actionsAfterMounted();
6671
7020
  if (isPromise(umdHookMountResult)) {
6672
7021
  umdHookMountResult
6673
- .then(() => this.dispatchMountedEvent())
7022
+ .then(nextAction)
6674
7023
  .catch((e) => {
6675
7024
  logError('An error occurred in window.mount \n', this.name, e);
6676
- this.dispatchMountedEvent();
7025
+ nextAction();
6677
7026
  });
6678
7027
  }
6679
7028
  else {
6680
- this.dispatchMountedEvent();
7029
+ nextAction();
6681
7030
  }
6682
7031
  };
6683
7032
  if (this.isPrerender) {
@@ -6691,7 +7040,7 @@ class CreateApp {
6691
7040
  /**
6692
7041
  * dispatch mounted event when app run finished
6693
7042
  */
6694
- dispatchMountedEvent() {
7043
+ actionsAfterMounted() {
6695
7044
  var _a;
6696
7045
  if (!this.isUnmounted()) {
6697
7046
  this.setAppState(appStates.MOUNTED);
@@ -6703,7 +7052,6 @@ class CreateApp {
6703
7052
  });
6704
7053
  // dispatch mounted event to micro app
6705
7054
  dispatchCustomEventToMicroApp(this, 'mounted');
6706
- this.setLifeCycleState(lifeCycles.MOUNTED);
6707
7055
  // dispatch event mounted to parent
6708
7056
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.MOUNTED);
6709
7057
  /**
@@ -6726,6 +7074,8 @@ class CreateApp {
6726
7074
  * unmount app
6727
7075
  * NOTE:
6728
7076
  * 1. do not add any params on account of unmountApp
7077
+ * 2. this.container maybe null: Initialization of sandbox is async, child app may unmount before exec nextAction of mount
7078
+ * 3. unmount app when loading files (this.container is not null)
6729
7079
  * @param destroy completely destroy, delete cache resources
6730
7080
  * @param clearData clear data of dateCenter
6731
7081
  * @param keepRouteState keep route state when unmount, default is false
@@ -6735,29 +7085,12 @@ class CreateApp {
6735
7085
  var _a;
6736
7086
  destroy = destroy || this.state === appStates.LOAD_FAILED;
6737
7087
  this.setAppState(appStates.UNMOUNT);
6738
- let umdHookUnmountResult = null;
6739
7088
  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));
7089
+ this.handleUnmounted(destroy, clearData, keepRouteState, unmountcb, (_a = this.umdHookUnmount) === null || _a === void 0 ? void 0 : _a.call(this, microApp.getData(this.name, true)));
6742
7090
  }
6743
7091
  catch (e) {
6744
- logError('An error occurred in window.unmount \n', this.name, e);
7092
+ logError('An error occurred when unmount \n', this.name, e);
6745
7093
  }
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
7094
  }
6762
7095
  /**
6763
7096
  * handle for promise umdHookUnmount
@@ -6767,8 +7100,16 @@ class CreateApp {
6767
7100
  * @param unmountcb callback of unmount
6768
7101
  * @param umdHookUnmountResult result of umdHookUnmount
6769
7102
  */
6770
- handleUnmounted({ destroy, clearData, keepRouteState, unmountcb, umdHookUnmountResult, }) {
6771
- const nextAction = () => this.actionsForUnmount({
7103
+ handleUnmounted(destroy, clearData, keepRouteState, unmountcb, umdHookUnmountResult) {
7104
+ // dispatch state event to micro app
7105
+ dispatchCustomEventToMicroApp(this, 'statechange', {
7106
+ appState: appStates.UNMOUNT
7107
+ });
7108
+ // dispatch unmount event to micro app
7109
+ dispatchCustomEventToMicroApp(this, 'unmount');
7110
+ // call window.onunmount of child app
7111
+ execMicroAppGlobalHook(this.getMicroAppGlobalHook(microGlobalEvent.ONUNMOUNT), this.name, microGlobalEvent.ONUNMOUNT);
7112
+ const nextAction = () => this.actionsAfterUnmounted({
6772
7113
  destroy,
6773
7114
  clearData,
6774
7115
  keepRouteState,
@@ -6777,7 +7118,12 @@ class CreateApp {
6777
7118
  if (isPromise(umdHookUnmountResult)) {
6778
7119
  // async window.unmount will cause appName bind error in nest app
6779
7120
  removeDomScope();
6780
- umdHookUnmountResult.then(nextAction).catch(nextAction);
7121
+ umdHookUnmountResult
7122
+ .then(nextAction)
7123
+ .catch((e) => {
7124
+ logError('An error occurred in window.unmount \n', this.name, e);
7125
+ nextAction();
7126
+ });
6781
7127
  }
6782
7128
  else {
6783
7129
  nextAction();
@@ -6790,7 +7136,7 @@ class CreateApp {
6790
7136
  * @param keepRouteState keep route state when unmount, default is false
6791
7137
  * @param unmountcb callback of unmount
6792
7138
  */
6793
- actionsForUnmount({ destroy, clearData, keepRouteState, unmountcb, }) {
7139
+ actionsAfterUnmounted({ destroy, clearData, keepRouteState, unmountcb, }) {
6794
7140
  var _a;
6795
7141
  if (this.umdMode && this.container && !destroy) {
6796
7142
  this.cloneContainer(this.source.html, this.container, false);
@@ -6807,20 +7153,33 @@ class CreateApp {
6807
7153
  destroy,
6808
7154
  clearData: clearData || destroy,
6809
7155
  });
6810
- this.setLifeCycleState(lifeCycles.UNMOUNT);
6811
7156
  // dispatch unmount event to base app
6812
7157
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.UNMOUNT);
6813
7158
  this.clearOptions(destroy);
6814
7159
  unmountcb === null || unmountcb === void 0 ? void 0 : unmountcb();
6815
7160
  }
6816
7161
  clearOptions(destroy) {
6817
- this.container.innerHTML = '';
6818
- this.container = null;
7162
+ var _a, _b;
6819
7163
  this.isPrerender = false;
6820
7164
  this.preRenderEvents = null;
6821
7165
  this.setKeepAliveState(null);
7166
+ if (this.container) {
7167
+ this.container.innerHTML = '';
7168
+ this.container = null;
7169
+ }
7170
+ else if (!this.umdMode) {
7171
+ /**
7172
+ * this.container is null means sandBox.start has not exec, so sandBox.stop won't exec either
7173
+ * we should remove iframeElement in default mode manually
7174
+ */
7175
+ (_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.deleteIframeElement) === null || _b === void 0 ? void 0 : _b.call(_a);
7176
+ }
6822
7177
  // in iframe sandbox & default mode, delete the sandbox & iframeElement
6823
- // TODO: with沙箱与iframe沙箱保持一致:with沙箱默认模式下删除 或者 iframe沙箱umd模式下保留
7178
+ /**
7179
+ * TODO:
7180
+ * 1. with沙箱与iframe沙箱保持一致:with沙箱默认模式下删除 或者 iframe沙箱umd模式下保留
7181
+ * 2. 接1.0,this.sandBox置空,还需要注意后续app.sandBox相关操作,比如 scripts.ts --> app.iframe ? app.sandBox!.microBody : app.querySelector('micro-app-body'),如果是fiber或者预加载,会存在卸载后js还在处理的情况
7182
+ */
6824
7183
  if (this.iframe && !this.umdMode)
6825
7184
  this.sandBox = null;
6826
7185
  if (destroy)
@@ -6849,7 +7208,6 @@ class CreateApp {
6849
7208
  dispatchCustomEventToMicroApp(this, 'appstate-change', {
6850
7209
  appState: 'afterhidden',
6851
7210
  });
6852
- this.setLifeCycleState(lifeCycles.AFTERHIDDEN);
6853
7211
  // dispatch afterHidden event to base app
6854
7212
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.AFTERHIDDEN);
6855
7213
  if (isRouterModeSearch(this.name)) {
@@ -6891,7 +7249,7 @@ class CreateApp {
6891
7249
  /**
6892
7250
  * TODO:
6893
7251
  * 问题:当路由模式为custom时,keep-alive应用在重新展示,是否需要根据子应用location信息更新浏览器地址?
6894
- * 暂时不这么做吧,因为无法确定二次展示时新旧地址是否相同,是否带有特殊信息
7252
+ * 暂时不这么做,因为无法确定二次展示时新旧地址是否相同,是否带有特殊信息
6895
7253
  */
6896
7254
  if (isRouterModeSearch(this.name)) {
6897
7255
  // called before lifeCyclesEvent
@@ -6901,7 +7259,6 @@ class CreateApp {
6901
7259
  dispatchCustomEventToMicroApp(this, 'appstate-change', {
6902
7260
  appState: 'aftershow',
6903
7261
  });
6904
- this.setLifeCycleState(lifeCycles.AFTERSHOW);
6905
7262
  // dispatch afterShow event to base app
6906
7263
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.AFTERSHOW);
6907
7264
  }
@@ -6910,7 +7267,6 @@ class CreateApp {
6910
7267
  * @param e Error
6911
7268
  */
6912
7269
  onerror(e) {
6913
- this.setLifeCycleState(lifeCycles.ERROR);
6914
7270
  // dispatch state event to micro app
6915
7271
  dispatchCustomEventToMicroApp(this, 'statechange', {
6916
7272
  appState: appStates.LOAD_FAILED
@@ -6931,8 +7287,8 @@ class CreateApp {
6931
7287
  }
6932
7288
  /**
6933
7289
  * clone origin elements to target
6934
- * @param origin Cloned element
6935
7290
  * @param target Accept cloned elements
7291
+ * @param origin Cloned element
6936
7292
  * @param deep deep clone or transfer dom
6937
7293
  */
6938
7294
  cloneContainer(target, origin, deep) {
@@ -6967,14 +7323,6 @@ class CreateApp {
6967
7323
  getAppState() {
6968
7324
  return this.state;
6969
7325
  }
6970
- // set app lifeCycleState
6971
- setLifeCycleState(state) {
6972
- this.lifeCycleState = state;
6973
- }
6974
- // get app lifeCycleState
6975
- getLifeCycleState() {
6976
- return this.lifeCycleState || '';
6977
- }
6978
7326
  // set keep-alive state
6979
7327
  setKeepAliveState(state) {
6980
7328
  this.keepAliveState = state;
@@ -6991,23 +7339,6 @@ class CreateApp {
6991
7339
  isHidden() {
6992
7340
  return keepAliveStates.KEEP_ALIVE_HIDDEN === this.keepAliveState;
6993
7341
  }
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
7342
  getMicroAppGlobalHook(eventName) {
7012
7343
  var _a, _b;
7013
7344
  const listener = (_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow) === null || _b === void 0 ? void 0 : _b[eventName];
@@ -7113,11 +7444,11 @@ function handleNewNode(child, app) {
7113
7444
  * @param app app
7114
7445
  * @param method raw method
7115
7446
  * @param parent parent node
7116
- * @param targetChild target node
7117
- * @param passiveChild second param of insertBefore and replaceChild
7447
+ * @param targetNode target node
7448
+ * @param passiveNode second param of insertBefore and replaceChild
7118
7449
  */
7119
- function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
7120
- const hijackParent = getHijackParent(parent, targetChild, app);
7450
+ function invokePrototypeMethod(app, rawMethod, parent, targetNode, passiveNode) {
7451
+ const hijackParent = getHijackParent(parent, targetNode, app);
7121
7452
  if (hijackParent) {
7122
7453
  /**
7123
7454
  * If parentNode is <micro-app-body>, return rawDocument.body
@@ -7134,9 +7465,9 @@ function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild
7134
7465
  if (!isIframeSandbox(app.name) &&
7135
7466
  isMicroAppBody(hijackParent) &&
7136
7467
  rawMethod !== globalEnv.rawRemoveChild) {
7137
- const descriptor = Object.getOwnPropertyDescriptor(targetChild, 'parentNode');
7138
- if ((!descriptor || descriptor.configurable) && !targetChild.__MICRO_APP_HAS_DPN__) {
7139
- rawDefineProperties(targetChild, {
7468
+ const descriptor = Object.getOwnPropertyDescriptor(targetNode, 'parentNode');
7469
+ if ((!descriptor || descriptor.configurable) && !targetNode.__MICRO_APP_HAS_DPN__) {
7470
+ rawDefineProperties(targetNode, {
7140
7471
  parentNode: {
7141
7472
  configurable: true,
7142
7473
  get() {
@@ -7157,68 +7488,84 @@ function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild
7157
7488
  }
7158
7489
  }
7159
7490
  if ((process.env.NODE_ENV !== 'production') &&
7160
- isIFrameElement(targetChild) &&
7491
+ isIFrameElement(targetNode) &&
7161
7492
  rawMethod === globalEnv.rawAppendChild) {
7162
7493
  fixReactHMRConflict(app);
7163
7494
  }
7164
7495
  /**
7165
- * 1. If passiveChild exists, it must be insertBefore or replaceChild
7166
- * 2. When removeChild, targetChild may not be in microAppHead or head
7496
+ * 1. If passiveNode exists, it must be insertBefore or replaceChild
7497
+ * 2. When removeChild, targetNode may not be in microAppHead or head
7167
7498
  * 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])
7499
+ * 1. If passiveNode not in hijackParent, insertBefore replaceChild will be degraded to appendChild
7500
+ * E.g: document.head.replaceChild(targetNode, document.scripts[0])
7501
+ * 2. If passiveNode not in hijackParent but in parent and method is insertBefore, try insert it into the position corresponding to hijackParent
7502
+ * E.g: document.head.insertBefore(targetNode, document.head.childNodes[0])
7172
7503
  * ISSUE: https://github.com/micro-zoe/micro-app/issues/1071
7173
7504
  */
7174
- if (passiveChild && !hijackParent.contains(passiveChild)) {
7175
- if (rawMethod === globalEnv.rawInsertBefore && parent.contains(passiveChild)) {
7176
- const indexOfParent = Array.from(parent.childNodes).indexOf(passiveChild);
7505
+ if (passiveNode && !hijackParent.contains(passiveNode)) {
7506
+ if (rawMethod === globalEnv.rawInsertBefore && parent.contains(passiveNode)) {
7507
+ const indexOfParent = Array.from(parent.childNodes).indexOf(passiveNode);
7177
7508
  if (hijackParent.childNodes[indexOfParent]) {
7178
- return invokeRawMethod(rawMethod, hijackParent, targetChild, hijackParent.childNodes[indexOfParent]);
7509
+ return invokeRawMethod(rawMethod, hijackParent, targetNode, hijackParent.childNodes[indexOfParent], app);
7179
7510
  }
7180
7511
  }
7181
- return globalEnv.rawAppendChild.call(hijackParent, targetChild);
7512
+ return globalEnv.rawAppendChild.call(hijackParent, targetNode);
7182
7513
  }
7183
- else if (rawMethod === globalEnv.rawRemoveChild && !hijackParent.contains(targetChild)) {
7184
- if (parent.contains(targetChild)) {
7185
- return rawMethod.call(parent, targetChild);
7514
+ else if (rawMethod === globalEnv.rawRemoveChild && !hijackParent.contains(targetNode)) {
7515
+ if (parent.contains(targetNode)) {
7516
+ return rawMethod.call(parent, targetNode);
7186
7517
  }
7187
- return targetChild;
7518
+ return targetNode;
7188
7519
  }
7189
- return invokeRawMethod(rawMethod, hijackParent, targetChild, passiveChild);
7520
+ return invokeRawMethod(rawMethod, hijackParent, targetNode, passiveNode, app);
7190
7521
  }
7191
- return invokeRawMethod(rawMethod, parent, targetChild, passiveChild);
7522
+ return invokeRawMethod(rawMethod, parent, targetNode, passiveNode, app);
7192
7523
  }
7193
7524
  // head/body map to micro-app-head/micro-app-body
7194
- function getHijackParent(parent, targetChild, app) {
7525
+ function getHijackParent(parent, targetNode, app) {
7195
7526
  if (app) {
7196
7527
  if (parent === document.head) {
7197
- if (app.iframe && isScriptElement(targetChild)) {
7528
+ if (app.iframe && isScriptElement(targetNode)) {
7198
7529
  return app.sandBox.microHead;
7199
7530
  }
7200
7531
  return app.querySelector('micro-app-head');
7201
7532
  }
7202
7533
  if (parent === document.body || parent === document.body.parentNode) {
7203
- if (app.iframe && isScriptElement(targetChild)) {
7534
+ if (app.iframe && isScriptElement(targetNode)) {
7204
7535
  return app.sandBox.microBody;
7205
7536
  }
7206
7537
  return app.querySelector('micro-app-body');
7207
7538
  }
7208
- if (app.iframe && isScriptElement(targetChild)) {
7539
+ if (app.iframe && isScriptElement(targetNode)) {
7209
7540
  return app.sandBox.microBody;
7210
7541
  }
7211
7542
  }
7212
7543
  return null;
7213
7544
  }
7214
- function invokeRawMethod(rawMethod, parent, targetChild, passiveChild) {
7545
+ function invokeRawMethod(rawMethod, parent, targetNode, passiveNode, app) {
7215
7546
  if (isPendMethod(rawMethod)) {
7216
- return rawMethod.call(parent, targetChild);
7547
+ /**
7548
+ * In iframe sandbox, script will pend to iframe.body, so we should reset rawMethod, because:
7549
+ * Element.prototype.append === DocumentFragment.prototype.append ==> false
7550
+ * Element.prototype.prepend === DocumentFragment.prototype.prepend ==> false
7551
+ */
7552
+ if ((app === null || app === void 0 ? void 0 : app.iframe) && isScriptElement(targetNode)) {
7553
+ if (rawMethod === globalEnv.rawFragmentAppend) {
7554
+ rawMethod = globalEnv.rawAppend;
7555
+ }
7556
+ else if (rawMethod === globalEnv.rawFragmentPrepend) {
7557
+ rawMethod = globalEnv.rawPrepend;
7558
+ }
7559
+ }
7560
+ return rawMethod.call(parent, targetNode);
7217
7561
  }
7218
- return rawMethod.call(parent, targetChild, passiveChild);
7562
+ return rawMethod.call(parent, targetNode, passiveNode);
7219
7563
  }
7220
7564
  function isPendMethod(method) {
7221
- return method === globalEnv.rawAppend || method === globalEnv.rawPrepend;
7565
+ return (method === globalEnv.rawAppend ||
7566
+ method === globalEnv.rawPrepend ||
7567
+ method === globalEnv.rawFragmentAppend ||
7568
+ method === globalEnv.rawFragmentPrepend);
7222
7569
  }
7223
7570
  /**
7224
7571
  * Attempt to complete the static resource address again before insert the node
@@ -7235,7 +7582,7 @@ function completePathDynamic(app, newChild) {
7235
7582
  globalEnv.rawSetAttribute.call(newChild, 'srcset', CompletionPath(newChild.getAttribute('srcset'), app.url));
7236
7583
  }
7237
7584
  }
7238
- else if (/^link$/i.test(newChild.tagName) && newChild.hasAttribute('href')) {
7585
+ else if (/^(link|image)$/i.test(newChild.tagName) && newChild.hasAttribute('href')) {
7239
7586
  globalEnv.rawSetAttribute.call(newChild, 'href', CompletionPath(newChild.getAttribute('href'), app.url));
7240
7587
  }
7241
7588
  }
@@ -7244,31 +7591,26 @@ function completePathDynamic(app, newChild) {
7244
7591
  * method of handle new node
7245
7592
  * @param parent parent node
7246
7593
  * @param newChild new node
7247
- * @param passiveChild passive node
7594
+ * @param passiveNode passive node
7248
7595
  * @param rawMethod method
7249
7596
  */
7250
- function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
7597
+ function commonElementHandler(parent, newChild, passiveNode, rawMethod) {
7251
7598
  const currentAppName = getCurrentAppName();
7252
7599
  if (isNode(newChild) &&
7253
7600
  !newChild.__PURE_ELEMENT__ &&
7254
7601
  (newChild.__MICRO_APP_NAME__ ||
7255
7602
  currentAppName)) {
7256
- newChild.__MICRO_APP_NAME__ = newChild.__MICRO_APP_NAME__ || currentAppName;
7603
+ updateElementInfo(newChild, newChild.__MICRO_APP_NAME__ || currentAppName);
7257
7604
  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
7605
  if (app === null || app === void 0 ? void 0 : app.container) {
7606
+ if (isStyleElement(newChild)) {
7607
+ parent.getRootNode() instanceof ShadowRoot && newChild.setAttribute('ignore', 'true');
7608
+ }
7264
7609
  completePathDynamic(app, newChild);
7265
- return invokePrototypeMethod(app, rawMethod, parent, handleNewNode(newChild, app), passiveChild && getMappingNode(passiveChild));
7610
+ return invokePrototypeMethod(app, rawMethod, parent, handleNewNode(newChild, app), passiveNode && getMappingNode(passiveNode));
7266
7611
  }
7267
7612
  }
7268
- if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
7269
- return rawMethod.call(parent, newChild);
7270
- }
7271
- return rawMethod.call(parent, newChild, passiveChild);
7613
+ return invokeRawMethod(rawMethod, parent, newChild, passiveNode);
7272
7614
  }
7273
7615
  /**
7274
7616
  * Rewrite element prototype method
@@ -7277,36 +7619,19 @@ function patchElementAndDocument() {
7277
7619
  patchDocument$2();
7278
7620
  const rawRootElement = globalEnv.rawRootElement;
7279
7621
  const rawRootNode = globalEnv.rawRootNode;
7622
+ const rawDocumentFragment = globalEnv.rawDocumentFragment;
7280
7623
  // prototype methods of add element👇
7281
- rawRootElement.prototype.appendChild = function appendChild(newChild) {
7624
+ rawRootNode.prototype.appendChild = function appendChild(newChild) {
7282
7625
  return commonElementHandler(this, newChild, null, globalEnv.rawAppendChild);
7283
7626
  };
7284
- rawRootElement.prototype.insertBefore = function insertBefore(newChild, refChild) {
7627
+ rawRootNode.prototype.insertBefore = function insertBefore(newChild, refChild) {
7285
7628
  return commonElementHandler(this, newChild, refChild, globalEnv.rawInsertBefore);
7286
7629
  };
7287
- rawRootElement.prototype.replaceChild = function replaceChild(newChild, oldChild) {
7630
+ rawRootNode.prototype.replaceChild = function replaceChild(newChild, oldChild) {
7288
7631
  return commonElementHandler(this, newChild, oldChild, globalEnv.rawReplaceChild);
7289
7632
  };
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
7633
  // prototype methods of delete element👇
7309
- rawRootElement.prototype.removeChild = function removeChild(oldChild) {
7634
+ rawRootNode.prototype.removeChild = function removeChild(oldChild) {
7310
7635
  if (oldChild === null || oldChild === void 0 ? void 0 : oldChild.__MICRO_APP_NAME__) {
7311
7636
  const app = appInstanceMap.get(oldChild.__MICRO_APP_NAME__);
7312
7637
  if (app === null || app === void 0 ? void 0 : app.container) {
@@ -7321,6 +7646,24 @@ function patchElementAndDocument() {
7321
7646
  }
7322
7647
  return globalEnv.rawRemoveChild.call(this, oldChild);
7323
7648
  };
7649
+ rawDocumentFragment.prototype.append = rawRootElement.prototype.append = function append(...nodes) {
7650
+ let i = 0;
7651
+ while (i < nodes.length) {
7652
+ let node = nodes[i];
7653
+ node = isNode(node) ? node : globalEnv.rawCreateTextNode.call(globalEnv.rawDocument, node);
7654
+ commonElementHandler(this, markElement(node), null, isDocumentFragment(this) ? globalEnv.rawFragmentAppend : globalEnv.rawAppend);
7655
+ i++;
7656
+ }
7657
+ };
7658
+ rawDocumentFragment.prototype.prepend = rawRootElement.prototype.prepend = function prepend(...nodes) {
7659
+ let i = nodes.length;
7660
+ while (i > 0) {
7661
+ let node = nodes[i - 1];
7662
+ node = isNode(node) ? node : globalEnv.rawCreateTextNode.call(globalEnv.rawDocument, node);
7663
+ commonElementHandler(this, markElement(node), null, isDocumentFragment(this) ? globalEnv.rawFragmentPrepend : globalEnv.rawPrepend);
7664
+ i--;
7665
+ }
7666
+ };
7324
7667
  /**
7325
7668
  * 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
7669
  * NOTE:
@@ -7340,52 +7683,81 @@ function patchElementAndDocument() {
7340
7683
  }
7341
7684
  return globalEnv.rawInsertAdjacentElement.call(this, where, element);
7342
7685
  };
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
7686
  /**
7350
7687
  * document.body(head).querySelector(querySelectorAll) hijack to microAppBody(microAppHead).querySelector(querySelectorAll)
7351
7688
  * NOTE:
7352
7689
  * 1. May cause some problems!
7353
7690
  * 2. Add config options?
7354
7691
  */
7355
- function getQueryTarget(target) {
7356
- const currentAppName = getCurrentAppName();
7357
- if ((target === document.body || target === document.head) && currentAppName) {
7692
+ function getElementQueryTarget(targetNode) {
7693
+ const currentAppName = getIframeCurrentAppName() || getCurrentAppName();
7694
+ if ((targetNode === document.body || targetNode === document.head) && currentAppName) {
7358
7695
  const app = appInstanceMap.get(currentAppName);
7359
7696
  if (app === null || app === void 0 ? void 0 : app.container) {
7360
- if (target === document.body) {
7697
+ if (targetNode === document.body) {
7361
7698
  return app.querySelector('micro-app-body');
7362
7699
  }
7363
- else if (target === document.head) {
7700
+ else if (targetNode === document.head) {
7364
7701
  return app.querySelector('micro-app-head');
7365
7702
  }
7366
7703
  }
7367
7704
  }
7368
- return target;
7705
+ return targetNode;
7706
+ }
7707
+ /**
7708
+ * In iframe sandbox, script will render to iframe instead of micro-app-body
7709
+ * So when query elements, we need to search both micro-app and iframe
7710
+ * @param isEmpty get empty result
7711
+ * @param targetNode targetNode element
7712
+ * @param result origin result
7713
+ * @param selectors selectors
7714
+ * @param methodName querySelector or querySelectorAll
7715
+ */
7716
+ function getElementQueryResult(isEmpty, targetNode, result, selectors, methodName) {
7717
+ if (isEmpty) {
7718
+ const currentAppName = getIframeCurrentAppName() || getCurrentAppName();
7719
+ if (currentAppName && isIframeSandbox(currentAppName)) {
7720
+ const app = appInstanceMap.get(currentAppName);
7721
+ if (isMicroAppHead(targetNode)) {
7722
+ return app.sandBox.microHead[methodName](selectors);
7723
+ }
7724
+ if (isMicroAppBody(targetNode)) {
7725
+ return app.sandBox.microBody[methodName](selectors);
7726
+ }
7727
+ }
7728
+ }
7729
+ return result;
7369
7730
  }
7370
7731
  rawRootElement.prototype.querySelector = function querySelector(selectors) {
7371
7732
  var _a;
7372
- return globalEnv.rawElementQuerySelector.call((_a = getQueryTarget(this)) !== null && _a !== void 0 ? _a : this, selectors);
7733
+ const _this = (_a = getElementQueryTarget(this)) !== null && _a !== void 0 ? _a : this;
7734
+ const result = globalEnv.rawElementQuerySelector.call(_this, selectors);
7735
+ return getElementQueryResult(isNull(result) && _this !== this, _this, result, selectors, 'querySelector');
7373
7736
  };
7374
7737
  rawRootElement.prototype.querySelectorAll = function querySelectorAll(selectors) {
7375
7738
  var _a;
7376
- return globalEnv.rawElementQuerySelectorAll.call((_a = getQueryTarget(this)) !== null && _a !== void 0 ? _a : this, selectors);
7739
+ const _this = (_a = getElementQueryTarget(this)) !== null && _a !== void 0 ? _a : this;
7740
+ const result = globalEnv.rawElementQuerySelectorAll.call(_this, selectors);
7741
+ return getElementQueryResult(!result.length && _this !== this, _this, result, selectors, 'querySelectorAll');
7377
7742
  };
7378
7743
  // rewrite setAttribute, complete resource address
7379
7744
  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);
7745
+ if (/^micro-app(-\S+)?/i.test(this.tagName) &&
7746
+ key === 'data' &&
7747
+ this.setAttribute !== rawRootElement.prototype.setAttribute) {
7748
+ this.setAttribute(key, value);
7749
+ }
7750
+ else {
7751
+ const appName = this.__MICRO_APP_NAME__ || getCurrentAppName();
7752
+ if (appName &&
7753
+ appInstanceMap.has(appName) &&
7754
+ (((key === 'src' || key === 'srcset') && /^(img|script|video|audio|source|embed)$/i.test(this.tagName)) ||
7755
+ (key === 'href' && /^(link|image)$/i.test(this.tagName)))) {
7756
+ const app = appInstanceMap.get(appName);
7757
+ value = CompletionPath(value, app.url);
7758
+ }
7759
+ globalEnv.rawSetAttribute.call(this, key, value);
7387
7760
  }
7388
- globalEnv.rawSetAttribute.call(this, key, value);
7389
7761
  };
7390
7762
  /**
7391
7763
  * TODO: 兼容直接通过img.src等操作设置的资源
@@ -7412,7 +7784,7 @@ function patchElementAndDocument() {
7412
7784
  // return get?.call(this)
7413
7785
  // },
7414
7786
  // set: function (value) {
7415
- // const currentAppName = getCurrentAppName()
7787
+ // const currentAppName = this.__MICRO_APP_NAME__ || getCurrentAppName()
7416
7788
  // if (currentAppName && appInstanceMap.has(currentAppName)) {
7417
7789
  // const app = appInstanceMap.get(currentAppName)
7418
7790
  // value = CompletionPath(value, app!.url)
@@ -7421,41 +7793,25 @@ function patchElementAndDocument() {
7421
7793
  // },
7422
7794
  // })
7423
7795
  // })
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
7796
  rawDefineProperty(rawRootNode.prototype, 'parentNode', {
7442
7797
  configurable: true,
7443
7798
  enumerable: true,
7444
7799
  get() {
7445
7800
  var _a, _b, _c;
7446
7801
  /**
7447
- * hijack parentNode of html
7802
+ * hijack parentNode of html for with sandbox
7448
7803
  * Scenes:
7449
7804
  * 1. element-ui@2/lib/utils/popper.js
7450
7805
  * // root is child app window, so root.document is proxyDocument or microDocument
7451
7806
  * if (element.parentNode === root.document) ...
7452
7807
  */
7453
- const currentAppName = getCurrentAppName();
7808
+ const currentAppName = getIframeCurrentAppName() || getCurrentAppName();
7454
7809
  if (currentAppName && this === globalEnv.rawDocument.firstElementChild) {
7455
7810
  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
7811
  if (microDocument)
7457
7812
  return microDocument;
7458
7813
  }
7814
+ // NOTE: run after hijack html.parentNode
7459
7815
  const result = globalEnv.rawParentNodeDesc.get.call(this);
7460
7816
  /**
7461
7817
  * If parentNode is <micro-app-body>, return rawDocument.body
@@ -7474,16 +7830,34 @@ function patchElementAndDocument() {
7474
7830
  return result;
7475
7831
  },
7476
7832
  });
7833
+ rawDefineProperty(rawRootElement.prototype, 'innerHTML', {
7834
+ configurable: true,
7835
+ enumerable: true,
7836
+ get() {
7837
+ return globalEnv.rawInnerHTMLDesc.get.call(this);
7838
+ },
7839
+ set(code) {
7840
+ globalEnv.rawInnerHTMLDesc.set.call(this, code);
7841
+ const currentAppName = this.__MICRO_APP_NAME__ || getCurrentAppName();
7842
+ Array.from(this.children).forEach((child) => {
7843
+ if (isElement(child) && currentAppName) {
7844
+ updateElementInfo(child, currentAppName);
7845
+ }
7846
+ });
7847
+ }
7848
+ });
7849
+ // patch cloneNode
7850
+ rawRootNode.prototype.cloneNode = function cloneNode(deep) {
7851
+ const clonedNode = globalEnv.rawCloneNode.call(this, deep);
7852
+ return updateElementInfo(clonedNode, this.__MICRO_APP_NAME__);
7853
+ };
7477
7854
  }
7478
7855
  /**
7479
7856
  * Mark the newly created element in the micro application
7480
7857
  * @param element new element
7481
7858
  */
7482
7859
  function markElement(element) {
7483
- const currentAppName = getCurrentAppName();
7484
- if (currentAppName)
7485
- element.__MICRO_APP_NAME__ = currentAppName;
7486
- return element;
7860
+ return updateElementInfo(element, getCurrentAppName());
7487
7861
  }
7488
7862
  // methods of document
7489
7863
  function patchDocument$2() {
@@ -7614,18 +7988,18 @@ function releasePatchElementAndDocument() {
7614
7988
  releasePatchDocument();
7615
7989
  const rawRootElement = globalEnv.rawRootElement;
7616
7990
  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;
7991
+ rawRootNode.prototype.appendChild = globalEnv.rawAppendChild;
7992
+ rawRootNode.prototype.insertBefore = globalEnv.rawInsertBefore;
7993
+ rawRootNode.prototype.replaceChild = globalEnv.rawReplaceChild;
7994
+ rawRootNode.prototype.removeChild = globalEnv.rawRemoveChild;
7995
+ rawRootNode.prototype.cloneNode = globalEnv.rawCloneNode;
7621
7996
  rawRootElement.prototype.append = globalEnv.rawAppend;
7622
7997
  rawRootElement.prototype.prepend = globalEnv.rawPrepend;
7623
- rawRootElement.prototype.cloneNode = globalEnv.rawCloneNode;
7624
7998
  rawRootElement.prototype.querySelector = globalEnv.rawElementQuerySelector;
7625
7999
  rawRootElement.prototype.querySelectorAll = globalEnv.rawElementQuerySelectorAll;
7626
8000
  rawRootElement.prototype.setAttribute = globalEnv.rawSetAttribute;
7627
- rawDefineProperty(rawRootElement.prototype, 'innerHTML', globalEnv.rawInnerHTMLDesc);
7628
8001
  rawDefineProperty(rawRootNode.prototype, 'parentNode', globalEnv.rawParentNodeDesc);
8002
+ rawDefineProperty(rawRootElement.prototype, 'innerHTML', globalEnv.rawInnerHTMLDesc);
7629
8003
  }
7630
8004
  // Set the style of micro-app-head and micro-app-body
7631
8005
  let hasRejectMicroAppStyle = false;
@@ -7655,15 +8029,18 @@ function initGlobalEnv() {
7655
8029
  const rawRootElement = rawWindow.Element;
7656
8030
  const rawRootNode = rawWindow.Node;
7657
8031
  const rawRootEventTarget = rawWindow.EventTarget;
8032
+ const rawDocumentFragment = rawWindow.DocumentFragment;
7658
8033
  // save patch raw methods, pay attention to this binding
8034
+ const rawAppendChild = rawRootNode.prototype.appendChild;
8035
+ const rawInsertBefore = rawRootNode.prototype.insertBefore;
8036
+ const rawReplaceChild = rawRootNode.prototype.replaceChild;
8037
+ const rawRemoveChild = rawRootNode.prototype.removeChild;
7659
8038
  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
8039
  const rawAppend = rawRootElement.prototype.append;
7665
8040
  const rawPrepend = rawRootElement.prototype.prepend;
7666
- const rawCloneNode = rawRootElement.prototype.cloneNode;
8041
+ const rawFragmentAppend = rawDocumentFragment.prototype.append;
8042
+ const rawFragmentPrepend = rawDocumentFragment.prototype.prepend;
8043
+ const rawCloneNode = rawRootNode.prototype.cloneNode;
7667
8044
  const rawElementQuerySelector = rawRootElement.prototype.querySelector;
7668
8045
  const rawElementQuerySelectorAll = rawRootElement.prototype.querySelectorAll;
7669
8046
  const rawInsertAdjacentElement = rawRootElement.prototype.insertAdjacentElement;
@@ -7681,13 +8058,10 @@ function initGlobalEnv() {
7681
8058
  const rawGetElementsByClassName = rawRootDocument.prototype.getElementsByClassName;
7682
8059
  const rawGetElementsByTagName = rawRootDocument.prototype.getElementsByTagName;
7683
8060
  const rawGetElementsByName = rawRootDocument.prototype.getElementsByName;
7684
- const ImageProxy = new Proxy(Image, {
8061
+ // TODO: 将ImageProxy移出去
8062
+ const ImageProxy = new Proxy(rawWindow.Image, {
7685
8063
  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;
8064
+ return updateElementInfo(new Target(...args), getCurrentAppName());
7691
8065
  },
7692
8066
  });
7693
8067
  /**
@@ -7713,6 +8087,7 @@ function initGlobalEnv() {
7713
8087
  rawRootDocument,
7714
8088
  rawRootElement,
7715
8089
  rawRootNode,
8090
+ rawDocumentFragment,
7716
8091
  // source/patch
7717
8092
  rawSetAttribute,
7718
8093
  rawAppendChild,
@@ -7721,6 +8096,8 @@ function initGlobalEnv() {
7721
8096
  rawRemoveChild,
7722
8097
  rawAppend,
7723
8098
  rawPrepend,
8099
+ rawFragmentAppend,
8100
+ rawFragmentPrepend,
7724
8101
  rawCloneNode,
7725
8102
  rawElementQuerySelector,
7726
8103
  rawElementQuerySelectorAll,
@@ -7760,7 +8137,7 @@ function initGlobalEnv() {
7760
8137
  * @param tagName element name
7761
8138
  */
7762
8139
  function defineElement(tagName) {
7763
- class MicroAppElement extends getBaseHTMLElement() {
8140
+ class MicroAppElement extends HTMLElement {
7764
8141
  constructor() {
7765
8142
  super(...arguments);
7766
8143
  this.isWaiting = false;
@@ -7825,6 +8202,13 @@ function defineElement(tagName) {
7825
8202
  // baseRoute: route prefix, default is ''
7826
8203
  // keep-alive: open keep-alive mode
7827
8204
  connectedCallback() {
8205
+ /**
8206
+ * In FireFox, iframe Node.prototype will point to native Node.prototype after insert to document
8207
+ * If <micro-app>.prototype is not MicroAppElement.prototype, we should reset it
8208
+ */
8209
+ if (Object.getPrototypeOf(this) !== MicroAppElement.prototype) {
8210
+ Object.setPrototypeOf(this, MicroAppElement.prototype);
8211
+ }
7828
8212
  const cacheCount = ++this.connectedCount;
7829
8213
  this.connectStateMap.set(cacheCount, true);
7830
8214
  /**
@@ -8263,6 +8647,17 @@ function defineElement(tagName) {
8263
8647
  globalEnv.rawSetAttribute.call(this, key, value);
8264
8648
  }
8265
8649
  }
8650
+ /**
8651
+ * get delay time of router event
8652
+ * @returns delay time
8653
+ */
8654
+ getRouterEventDelay() {
8655
+ let delay = parseInt(this.getAttribute('router-event-delay'));
8656
+ if (isNaN(delay)) {
8657
+ delay = parseInt((isFunction(microApp.options['router-event-delay']) ? microApp.options['router-event-delay'](this.appName) : microApp.options['router-event-delay']));
8658
+ }
8659
+ return !isNaN(delay) ? delay : 0;
8660
+ }
8266
8661
  /**
8267
8662
  * Data from the base application
8268
8663
  */
@@ -8299,7 +8694,7 @@ function defineElement(tagName) {
8299
8694
  return this.getBaseRouteCompatible();
8300
8695
  }
8301
8696
  }
8302
- globalEnv.rawWindow.customElements.define(tagName, MicroAppElement);
8697
+ window.customElements.define(tagName, MicroAppElement);
8303
8698
  }
8304
8699
 
8305
8700
  /**
@@ -8331,7 +8726,7 @@ function preFetch(apps, delay) {
8331
8726
  const delayTime = isNumber(delay) ? delay : microApp.options.prefetchDelay;
8332
8727
  /**
8333
8728
  * TODO: remove setTimeout
8334
- * Is there a better way?
8729
+ * 如果要保留setTimeout,则需要考虑清空定时器的情况
8335
8730
  */
8336
8731
  setTimeout(() => {
8337
8732
  // releasePrefetchEffect()
@@ -8566,7 +8961,7 @@ function unmountApp(appName, options) {
8566
8961
  }
8567
8962
  }
8568
8963
  else {
8569
- logWarn(`app ${appName} does not exist`);
8964
+ logWarn(`app ${appName} does not exist when unmountApp`);
8570
8965
  resolve(false);
8571
8966
  }
8572
8967
  });
@@ -8596,7 +8991,7 @@ function reload(appName, destroy) {
8596
8991
  }
8597
8992
  }
8598
8993
  else {
8599
- logWarn(`app ${appName} does not exist`);
8994
+ logWarn(`app ${appName} does not exist when reload app`);
8600
8995
  resolve(false);
8601
8996
  }
8602
8997
  });
@@ -8651,20 +9046,6 @@ function renderApp(options) {
8651
9046
  container.appendChild(microAppElement);
8652
9047
  });
8653
9048
  }
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
9049
  class MicroApp extends EventCenterForBaseApp {
8669
9050
  constructor() {
8670
9051
  super(...arguments);
@@ -8679,7 +9060,6 @@ class MicroApp extends EventCenterForBaseApp {
8679
9060
  this.getAllApps = getAllApps;
8680
9061
  this.reload = reload;
8681
9062
  this.renderApp = renderApp;
8682
- this.getAppStatus = getAppStatus;
8683
9063
  }
8684
9064
  start(options) {
8685
9065
  var _a, _b;
@@ -8704,7 +9084,7 @@ class MicroApp extends EventCenterForBaseApp {
8704
9084
  }
8705
9085
  }
8706
9086
  initGlobalEnv();
8707
- if (globalEnv.rawWindow.customElements.get(this.tagName)) {
9087
+ if (window.customElements.get(this.tagName)) {
8708
9088
  return logWarn(`element ${this.tagName} is already defined`);
8709
9089
  }
8710
9090
  if (isPlainObject(options)) {
@@ -8735,5 +9115,5 @@ class MicroApp extends EventCenterForBaseApp {
8735
9115
  const microApp = new MicroApp();
8736
9116
 
8737
9117
  export default microApp;
8738
- export { EventCenterForMicroApp, MicroApp, getActiveApps, getAllApps, getAppStatus, preFetch, pureCreateElement, reload, removeDomScope, renderApp, unmountAllApps, unmountApp, version };
9118
+ export { EventCenterForMicroApp, MicroApp, getActiveApps, getAllApps, preFetch, pureCreateElement, reload, removeDomScope, renderApp, unmountAllApps, unmountApp, version };
8739
9119
  //# sourceMappingURL=index.esm.js.map