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

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.3';
1
+ const version = '1.0.0-rc.5';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -116,6 +116,34 @@ function isMicroAppBody(target) {
116
116
  function isProxyDocument(target) {
117
117
  return toTypeString(target) === '[object ProxyDocument]';
118
118
  }
119
+ function isTargetExtension(path, suffix) {
120
+ try {
121
+ return createURL(path).pathname.split('.').pop() === suffix;
122
+ }
123
+ catch (_a) {
124
+ return false;
125
+ }
126
+ }
127
+ function includes(target, searchElement, fromIndex) {
128
+ if (target == null) {
129
+ throw new TypeError('includes target is null or undefined');
130
+ }
131
+ const O = Object(target);
132
+ const len = parseInt(O.length, 10) || 0;
133
+ if (len === 0)
134
+ return false;
135
+ // @ts-ignore
136
+ fromIndex = parseInt(fromIndex, 10) || 0;
137
+ let i = Math.max(fromIndex >= 0 ? fromIndex : len + fromIndex, 0);
138
+ while (i < len) {
139
+ // NaN !== NaN
140
+ if (searchElement === O[i] || (searchElement !== searchElement && O[i] !== O[i])) {
141
+ return true;
142
+ }
143
+ i++;
144
+ }
145
+ return false;
146
+ }
119
147
  /**
120
148
  * format error log
121
149
  * @param msg message
@@ -179,13 +207,13 @@ function formatAppURL(url, appName = null) {
179
207
  if (!isString(url) || !url)
180
208
  return '';
181
209
  try {
182
- const { origin, pathname, search } = createURL(addProtocol(url));
183
- // If it ends with .html/.node/.php/.net/.etc, don’t need to add /
184
- if (/\.(\w+)$/.test(pathname)) {
185
- return `${origin}${pathname}${search}`;
186
- }
187
- const fullPath = `${origin}${pathname}/`.replace(/\/\/$/, '/');
188
- return /^https?:\/\//.test(fullPath) ? `${fullPath}${search}` : '';
210
+ const { origin, pathname, search } = createURL(addProtocol(url), (window.rawWindow || window).location.href);
211
+ /**
212
+ * keep the original url unchanged, such as .html .node .php .net .etc, search, except hash
213
+ * BUG FIX: Never using '/' to complete url, refer to https://github.com/micro-zoe/micro-app/issues/1147
214
+ */
215
+ const fullPath = `${origin}${pathname}${search}`;
216
+ return /^https?:\/\//.test(fullPath) ? fullPath : '';
189
217
  }
190
218
  catch (e) {
191
219
  logError(e, appName);
@@ -208,7 +236,9 @@ function formatAppName(name) {
208
236
  return name.replace(/(^\d+)|([^\w\d-_])/gi, '');
209
237
  }
210
238
  /**
211
- * Get valid address, such as https://xxx/xx/xx.html to https://xxx/xx/
239
+ * Get valid address, such as
240
+ * 1. https://domain/xx/xx.html to https://domain/xx/
241
+ * 2. https://domain/xx to https://domain/xx/
212
242
  * @param url app.url
213
243
  */
214
244
  function getEffectivePath(url) {
@@ -318,21 +348,28 @@ let currentMicroAppName = null;
318
348
  function setCurrentAppName(appName) {
319
349
  currentMicroAppName = appName;
320
350
  }
321
- function throttleDeferForSetAppName(appName) {
322
- if (currentMicroAppName !== appName) {
323
- setCurrentAppName(appName);
324
- defer(() => {
325
- setCurrentAppName(null);
326
- });
327
- }
328
- }
329
351
  // get the currently running app.name
330
352
  function getCurrentAppName() {
331
353
  return currentMicroAppName;
332
354
  }
333
355
  // Clear appName
334
- function removeDomScope() {
356
+ let preventSetAppName = false;
357
+ function removeDomScope(force) {
335
358
  setCurrentAppName(null);
359
+ if (force && !preventSetAppName) {
360
+ preventSetAppName = true;
361
+ defer(() => {
362
+ preventSetAppName = false;
363
+ });
364
+ }
365
+ }
366
+ function throttleDeferForSetAppName(appName) {
367
+ if (currentMicroAppName !== appName && !preventSetAppName) {
368
+ setCurrentAppName(appName);
369
+ defer(() => {
370
+ setCurrentAppName(null);
371
+ });
372
+ }
336
373
  }
337
374
  /**
338
375
  * Create pure elements
@@ -344,33 +381,6 @@ function pureCreateElement(tagName, options) {
344
381
  element.__PURE_ELEMENT__ = true;
345
382
  return element;
346
383
  }
347
- /**
348
- * clone origin elements to target
349
- * @param origin Cloned element
350
- * @param target Accept cloned elements
351
- * @param deep deep clone or transfer dom
352
- */
353
- function cloneContainer(target, origin, deep) {
354
- // 在基座接受到afterhidden方法后立即执行unmount,彻底destroy应用时,因为unmount时同步执行,所以this.container为null后才执行cloneContainer
355
- if (origin) {
356
- target.innerHTML = '';
357
- if (deep) {
358
- // TODO: ShadowRoot兼容,ShadowRoot不能直接使用cloneNode
359
- const clonedNode = origin.cloneNode(true);
360
- const fragment = document.createDocumentFragment();
361
- Array.from(clonedNode.childNodes).forEach((node) => {
362
- fragment.appendChild(node);
363
- });
364
- target.appendChild(fragment);
365
- }
366
- else {
367
- Array.from(origin.childNodes).forEach((node) => {
368
- target.appendChild(node);
369
- });
370
- }
371
- }
372
- return target;
373
- }
374
384
  // is invalid key of querySelector
375
385
  function isInvalidQuerySelectorKey(key) {
376
386
  return !key || /(^\d)|([^\w\d-_\u4e00-\u9fa5])/gi.test(key);
@@ -561,6 +571,17 @@ function getBaseHTMLElement() {
561
571
  var _a;
562
572
  return (((_a = window.rawWindow) === null || _a === void 0 ? void 0 : _a.HTMLElement) || window.HTMLElement);
563
573
  }
574
+ /**
575
+ * Format event name
576
+ * In with sandbox, child event and lifeCycles bind to microAppElement, there are two events with same name - mounted unmount, it should be handled specifically to prevent conflicts
577
+ * Issue: https://github.com/micro-zoe/micro-app/issues/1161
578
+ * @param type event name
579
+ * @param appName app name
580
+ */
581
+ const formatEventList = ['mounted', 'unmount'];
582
+ function formatEventType(type, appName) {
583
+ return formatEventList.includes(type) ? `${type}-${appName}` : type;
584
+ }
564
585
 
565
586
  function formatEventInfo(event, element) {
566
587
  Object.defineProperties(event, {
@@ -604,19 +625,19 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
604
625
  formatEventInfo(event, element);
605
626
  // global hooks
606
627
  if (isFunction((_a = microApp.options.lifeCycles) === null || _a === void 0 ? void 0 : _a[lifecycleName])) {
607
- microApp.options.lifeCycles[lifecycleName](event);
628
+ microApp.options.lifeCycles[lifecycleName](event, appName);
608
629
  }
609
630
  element.dispatchEvent(event);
610
631
  }
611
632
  /**
612
633
  * Dispatch custom event to micro app
613
634
  * @param app app
614
- * @param eventName event name ['unmount', 'appstate-change']
635
+ * @param eventName event name ['mounted', 'unmount', 'appstate-change', 'statechange']
615
636
  * @param detail event detail
616
637
  */
617
638
  function dispatchCustomEventToMicroApp(app, eventName, detail = {}) {
618
639
  var _a;
619
- const event = new CustomEvent(eventName, {
640
+ const event = new CustomEvent(formatEventType(eventName, app.name), {
620
641
  detail,
621
642
  });
622
643
  (_a = app.sandBox) === null || _a === void 0 ? void 0 : _a.microAppWindow.dispatchEvent(event);
@@ -660,7 +681,8 @@ class HTMLLoader {
660
681
  run(app, successCb) {
661
682
  const appName = app.name;
662
683
  const htmlUrl = app.ssrUrl || app.url;
663
- const htmlPromise = htmlUrl.includes('.js')
684
+ const isJsResource = isTargetExtension(htmlUrl, 'js');
685
+ const htmlPromise = isJsResource
664
686
  ? Promise.resolve(`<micro-app-head><script src='${htmlUrl}'></script></micro-app-head><micro-app-body></micro-app-body>`)
665
687
  : fetchSource(htmlUrl, appName, { cache: 'no-cache' });
666
688
  htmlPromise.then((htmlStr) => {
@@ -824,7 +846,7 @@ class CSSParser {
824
846
  if (!this.scopecssDisableNextLine &&
825
847
  (!this.scopecssDisable || this.scopecssDisableSelectors.length)) {
826
848
  cssValue = cssValue.replace(/url\(["']?([^)"']+)["']?\)/gm, (all, $1) => {
827
- if (/^((data|blob):|#)/.test($1) || /^(https?:)?\/\//.test($1)) {
849
+ if (/^((data|blob):|#|%23)/.test($1) || /^(https?:)?\/\//.test($1)) {
828
850
  return all;
829
851
  }
830
852
  // ./a/b.png ../a/b.png a/b.png
@@ -1250,7 +1272,7 @@ function extractLinkFromHtml(link, parent, app, isDynamic = false) {
1250
1272
  return { address: href, linkInfo };
1251
1273
  }
1252
1274
  }
1253
- else if (rel && ['prefetch', 'preload', 'prerender'].includes(rel)) {
1275
+ else if (rel && ['prefetch', 'preload', 'prerender', 'modulepreload'].includes(rel)) {
1254
1276
  // preload prefetch prerender ....
1255
1277
  if (isDynamic) {
1256
1278
  replaceComment = document.createComment(`link element with rel=${rel}${href ? ' & href=' + href : ''} removed by micro-app`);
@@ -1295,12 +1317,12 @@ function fetchLinksFromHtml(wrapElement, app, microAppHead, fiberStyleResult) {
1295
1317
  */
1296
1318
  if (fiberStyleResult) {
1297
1319
  fiberStyleResult.then(() => {
1298
- fiberLinkTasks.push(() => Promise.resolve(app.onLoad(wrapElement)));
1320
+ fiberLinkTasks.push(() => Promise.resolve(app.onLoad({ html: wrapElement })));
1299
1321
  serialExecFiberTasks(fiberLinkTasks);
1300
1322
  });
1301
1323
  }
1302
1324
  else {
1303
- app.onLoad(wrapElement);
1325
+ app.onLoad({ html: wrapElement });
1304
1326
  }
1305
1327
  });
1306
1328
  }
@@ -1470,19 +1492,46 @@ var MicroAppConfig;
1470
1492
  MicroAppConfig["SSR"] = "ssr";
1471
1493
  MicroAppConfig["FIBER"] = "fiber";
1472
1494
  })(MicroAppConfig || (MicroAppConfig = {}));
1495
+ /**
1496
+ * global key must be static key, they can not rewrite
1497
+ * e.g.
1498
+ * window.Promise = newValue
1499
+ * new Promise ==> still get old value, not newValue, because they are cached by top function
1500
+ * NOTE:
1501
+ * 1. Do not add fetch, XMLHttpRequest, EventSource
1502
+ */
1503
+ const GLOBAL_CACHED_KEY = 'window,self,globalThis,document,Document,Array,Object,String,Boolean,Math,Number,Symbol,Date,Function,Proxy,WeakMap,WeakSet,Set,Map,Reflect,Element,Node,RegExp,Error,TypeError,JSON,isNaN,parseFloat,parseInt,performance,console,decodeURI,encodeURI,decodeURIComponent,encodeURIComponent,navigator,undefined,location,history';
1473
1504
  // prefetch level
1474
1505
  const PREFETCH_LEVEL = [1, 2, 3];
1475
- // memory router constants
1506
+ /**
1507
+ * memory router modes
1508
+ * NOTE:
1509
+ * 1. The only difference between native and native-scope is location.origin, in native-scope mode location.origin point to child app
1510
+ * 2. native mode equal to disable-memory-router
1511
+ */
1512
+ // 临时注释,1.0版本放开,默认模式切换为state
1513
+ // // default mode, sync child app router info to history.state
1514
+ // export const DEFAULT_ROUTER_MODE = 'state'
1515
+ // // sync child app router info to browser url as search
1516
+ // export const ROUTER_MODE_SEARCH = 'search'
1517
+ // 临时放开,1.0版本去除
1518
+ const ROUTER_MODE_STATE = 'state';
1476
1519
  const DEFAULT_ROUTER_MODE = 'search';
1477
- const ROUTER_MODE_HISTORY = 'history';
1478
- const ROUTER_MODE_CUSTOM = 'custom';
1520
+ // render base on browser url, and location.origin location.href point to base app
1521
+ const ROUTER_MODE_NATIVE = 'native';
1522
+ // render base on browser url, but location.origin location.href point to child app
1523
+ const ROUTER_MODE_NATIVE_SCOPE = 'native-scope';
1524
+ // search mode, but child router info will not sync to browser url
1525
+ const ROUTER_MODE_PURE = 'pure';
1479
1526
  const ROUTER_MODE_LIST = [
1480
1527
  DEFAULT_ROUTER_MODE,
1481
- ROUTER_MODE_HISTORY,
1482
- ROUTER_MODE_CUSTOM,
1528
+ ROUTER_MODE_STATE,
1529
+ ROUTER_MODE_NATIVE,
1530
+ ROUTER_MODE_NATIVE_SCOPE,
1531
+ ROUTER_MODE_PURE,
1483
1532
  ];
1484
1533
  // event bound to child app window
1485
- const SCOPE_WINDOW_EVENT = [
1534
+ const BASE_SCOPE_WINDOW_EVENT = [
1486
1535
  'popstate',
1487
1536
  'hashchange',
1488
1537
  'load',
@@ -1493,8 +1542,15 @@ const SCOPE_WINDOW_EVENT = [
1493
1542
  'statechange',
1494
1543
  'mounted',
1495
1544
  ];
1545
+ // bind event of with sandbox
1546
+ const SCOPE_WINDOW_EVENT_OF_WITH = BASE_SCOPE_WINDOW_EVENT;
1547
+ // bind event of iframe sandbox
1548
+ const SCOPE_WINDOW_EVENT_OF_IFRAME = BASE_SCOPE_WINDOW_EVENT.concat([
1549
+ 'unhandledrejection',
1550
+ ]);
1496
1551
  // on event bound to child app window
1497
- const SCOPE_WINDOW_ON_EVENT = [
1552
+ // TODO: with和iframe处理方式不同,需修改
1553
+ const BASE_SCOPE_WINDOW_ON_EVENT = [
1498
1554
  'onpopstate',
1499
1555
  'onhashchange',
1500
1556
  'onload',
@@ -1502,6 +1558,12 @@ const SCOPE_WINDOW_ON_EVENT = [
1502
1558
  'onunload',
1503
1559
  'onerror'
1504
1560
  ];
1561
+ // bind on event of with sandbox
1562
+ const SCOPE_WINDOW_ON_EVENT_OF_WITH = BASE_SCOPE_WINDOW_ON_EVENT;
1563
+ // bind on event of iframe sandbox
1564
+ const SCOPE_WINDOW_ON_EVENT_OF_IFRAME = BASE_SCOPE_WINDOW_ON_EVENT.concat([
1565
+ 'onunhandledrejection',
1566
+ ]);
1505
1567
  // event bound to child app document
1506
1568
  const SCOPE_DOCUMENT_EVENT = [
1507
1569
  'DOMContentLoaded',
@@ -1518,15 +1580,13 @@ const GLOBAL_KEY_TO_WINDOW = [
1518
1580
  'globalThis',
1519
1581
  ];
1520
1582
  const RAW_GLOBAL_TARGET = ['rawWindow', 'rawDocument'];
1521
- /**
1522
- * global key must be static key, they can not rewrite
1523
- * e.g.
1524
- * window.Promise = newValue
1525
- * new Promise ==> still get old value, not newValue, because they are cached by top function
1526
- * NOTE:
1527
- * 1. Do not add fetch, XMLHttpRequest, EventSource
1528
- */
1529
- const GLOBAL_CACHED_KEY = 'window,self,globalThis,document,Document,Array,Object,String,Boolean,Math,Number,Symbol,Date,Function,Proxy,WeakMap,WeakSet,Set,Map,Reflect,Element,Node,RegExp,Error,TypeError,JSON,isNaN,parseFloat,parseInt,performance,console,decodeURI,encodeURI,decodeURIComponent,encodeURIComponent,navigator,undefined,location,history';
1583
+ const HIJACK_LOCATION_KEYS = [
1584
+ 'host',
1585
+ 'hostname',
1586
+ 'port',
1587
+ 'protocol',
1588
+ 'origin',
1589
+ ];
1530
1590
 
1531
1591
  const scriptTypes = ['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module', 'systemjs-module', 'systemjs-importmap'];
1532
1592
  // whether use type='module' script
@@ -1790,16 +1850,16 @@ function fetchScriptsFromHtml(wrapElement, app) {
1790
1850
  logError(err, app.name);
1791
1851
  }, () => {
1792
1852
  if (fiberScriptTasks) {
1793
- fiberScriptTasks.push(() => Promise.resolve(app.onLoad(wrapElement)));
1853
+ fiberScriptTasks.push(() => Promise.resolve(app.onLoad({ html: wrapElement })));
1794
1854
  serialExecFiberTasks(fiberScriptTasks);
1795
1855
  }
1796
1856
  else {
1797
- app.onLoad(wrapElement);
1857
+ app.onLoad({ html: wrapElement });
1798
1858
  }
1799
1859
  });
1800
1860
  }
1801
1861
  else {
1802
- app.onLoad(wrapElement);
1862
+ app.onLoad({ html: wrapElement });
1803
1863
  }
1804
1864
  }
1805
1865
  /**
@@ -2123,15 +2183,6 @@ function processCode(configs, code, address) {
2123
2183
  }, code);
2124
2184
  }
2125
2185
 
2126
- /**
2127
- * transform html string to dom
2128
- * @param str string dom
2129
- */
2130
- function getWrapElement(str) {
2131
- const wrapDiv = pureCreateElement('div');
2132
- wrapDiv.innerHTML = str;
2133
- return wrapDiv;
2134
- }
2135
2186
  /**
2136
2187
  * Recursively process each child element
2137
2188
  * @param parent parent element
@@ -2188,7 +2239,7 @@ function flatChildren(parent, app, microAppHead, fiberStyleTasks) {
2188
2239
  * @param app app
2189
2240
  */
2190
2241
  function extractSourceDom(htmlStr, app) {
2191
- const wrapElement = getWrapElement(htmlStr);
2242
+ const wrapElement = app.parseHtmlString(htmlStr);
2192
2243
  const microAppHead = globalEnv.rawElementQuerySelector.call(wrapElement, 'micro-app-head');
2193
2244
  const microAppBody = globalEnv.rawElementQuerySelector.call(wrapElement, 'micro-app-body');
2194
2245
  if (!microAppHead || !microAppBody) {
@@ -2206,16 +2257,16 @@ function extractSourceDom(htmlStr, app) {
2206
2257
  fetchLinksFromHtml(wrapElement, app, microAppHead, fiberStyleResult);
2207
2258
  }
2208
2259
  else if (fiberStyleResult) {
2209
- fiberStyleResult.then(() => app.onLoad(wrapElement));
2260
+ fiberStyleResult.then(() => app.onLoad({ html: wrapElement }));
2210
2261
  }
2211
2262
  else {
2212
- app.onLoad(wrapElement);
2263
+ app.onLoad({ html: wrapElement });
2213
2264
  }
2214
2265
  if (app.source.scripts.size) {
2215
2266
  fetchScriptsFromHtml(wrapElement, app);
2216
2267
  }
2217
2268
  else {
2218
- app.onLoad(wrapElement);
2269
+ app.onLoad({ html: wrapElement });
2219
2270
  }
2220
2271
  }
2221
2272
 
@@ -2751,12 +2802,17 @@ function createProxyDocument(appName, sandbox) {
2751
2802
  const sstEventListenerMap = new Map();
2752
2803
  let onClickHandler = null;
2753
2804
  let sstOnClickHandler = null;
2754
- const { rawDocument, rawCreateElement, rawAddEventListener, rawRemoveEventListener, } = globalEnv;
2805
+ const { rawDocument, rawCreateElement, rawCreateElementNS, rawAddEventListener, rawRemoveEventListener, } = globalEnv;
2755
2806
  function createElement(tagName, options) {
2756
2807
  const element = rawCreateElement.call(rawDocument, tagName, options);
2757
2808
  element.__MICRO_APP_NAME__ = appName;
2758
2809
  return element;
2759
2810
  }
2811
+ function createElementNS(namespaceURI, name, options) {
2812
+ const element = rawCreateElementNS.call(rawDocument, namespaceURI, name, options);
2813
+ element.__MICRO_APP_NAME__ = appName;
2814
+ return element;
2815
+ }
2760
2816
  /**
2761
2817
  * TODO:
2762
2818
  * 1. listener 是否需要绑定proxyDocument,否则函数中的this指向原生window
@@ -2872,6 +2928,8 @@ function createProxyDocument(appName, sandbox) {
2872
2928
  // TODO: 转换成数据形式,类似iframe的方式
2873
2929
  if (key === 'createElement')
2874
2930
  return createElement;
2931
+ if (key === 'createElementNS')
2932
+ return createElementNS;
2875
2933
  if (key === Symbol.toStringTag)
2876
2934
  return 'ProxyDocument';
2877
2935
  if (key === 'defaultView')
@@ -2968,7 +3026,7 @@ function createMicroDocument(appName, proxyDocument) {
2968
3026
  function patchWindow(appName, microAppWindow, sandbox) {
2969
3027
  patchWindowProperty(microAppWindow);
2970
3028
  createProxyWindow(appName, microAppWindow, sandbox);
2971
- return patchWindowEffect(microAppWindow);
3029
+ return patchWindowEffect(microAppWindow, appName);
2972
3030
  }
2973
3031
  /**
2974
3032
  * rewrite special properties of window
@@ -2979,7 +3037,7 @@ function patchWindowProperty(microAppWindow) {
2979
3037
  const rawWindow = globalEnv.rawWindow;
2980
3038
  Object.getOwnPropertyNames(rawWindow)
2981
3039
  .filter((key) => {
2982
- return /^on/.test(key) && !SCOPE_WINDOW_ON_EVENT.includes(key);
3040
+ return /^on/.test(key) && !SCOPE_WINDOW_ON_EVENT_OF_WITH.includes(key);
2983
3041
  })
2984
3042
  .forEach((eventName) => {
2985
3043
  const { enumerable, writable, set } = Object.getOwnPropertyDescriptor(rawWindow, eventName) || {
@@ -3009,22 +3067,22 @@ function createProxyWindow(appName, microAppWindow, sandbox) {
3009
3067
  throttleDeferForSetAppName(appName);
3010
3068
  if (Reflect.has(target, key) ||
3011
3069
  (isString(key) && /^__MICRO_APP_/.test(key)) ||
3012
- sandbox.scopeProperties.includes(key)) {
3013
- if (RAW_GLOBAL_TARGET.includes(key))
3070
+ includes(sandbox.scopeProperties, key)) {
3071
+ if (includes(RAW_GLOBAL_TARGET, key))
3014
3072
  removeDomScope();
3015
3073
  return Reflect.get(target, key);
3016
3074
  }
3017
3075
  return bindFunctionToRawTarget(Reflect.get(rawWindow, key), rawWindow);
3018
3076
  },
3019
3077
  set: (target, key, value) => {
3020
- if (sandbox.adapter.escapeSetterKeyList.includes(key)) {
3078
+ if (includes(sandbox.rawWindowScopeKeyList, key)) {
3021
3079
  Reflect.set(rawWindow, key, value);
3022
3080
  }
3023
3081
  else if (
3024
3082
  // target.hasOwnProperty has been rewritten
3025
3083
  !rawHasOwnProperty.call(target, key) &&
3026
3084
  rawHasOwnProperty.call(rawWindow, key) &&
3027
- !sandbox.scopeProperties.includes(key)) {
3085
+ !includes(sandbox.scopeProperties, key)) {
3028
3086
  const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
3029
3087
  const { configurable, enumerable, writable, set } = descriptor;
3030
3088
  // set value because it can be set
@@ -3037,32 +3095,37 @@ function createProxyWindow(appName, microAppWindow, sandbox) {
3037
3095
  sandbox.injectedKeys.add(key);
3038
3096
  }
3039
3097
  else {
3040
- !Reflect.has(target, key) && sandbox.injectedKeys.add(key);
3098
+ // all scopeProperties will add to injectedKeys, use for key in window (Proxy.has)
3099
+ if (!Reflect.has(target, key) || includes(sandbox.scopeProperties, key)) {
3100
+ sandbox.injectedKeys.add(key);
3101
+ }
3041
3102
  Reflect.set(target, key, value);
3042
3103
  }
3043
- if ((sandbox.escapeProperties.includes(key) ||
3044
- (sandbox.adapter.staticEscapeProperties.includes(key) &&
3104
+ if ((includes(sandbox.escapeProperties, key) ||
3105
+ (
3106
+ // TODO: staticEscapeProperties 合并到 escapeProperties
3107
+ includes(sandbox.staticEscapeProperties, key) &&
3045
3108
  !Reflect.has(rawWindow, key))) &&
3046
- !sandbox.scopeProperties.includes(key)) {
3109
+ !includes(sandbox.scopeProperties, key)) {
3047
3110
  !Reflect.has(rawWindow, key) && sandbox.escapeKeys.add(key);
3048
3111
  Reflect.set(rawWindow, key, value);
3049
3112
  }
3050
3113
  return true;
3051
3114
  },
3052
3115
  has: (target, key) => {
3053
- if (sandbox.scopeProperties.includes(key)) {
3054
- /**
3055
- * Some keywords, such as Vue, need to meet two conditions at the same time:
3056
- * 1. 'Vue' in window --> false
3057
- * 2. Vue (top level variable) // undefined
3058
- * Issue https://github.com/micro-zoe/micro-app/issues/686
3059
- */
3060
- if (sandbox.adapter.staticScopeProperties.includes(key)) {
3061
- return !!target[key];
3116
+ /**
3117
+ * Some keywords, such as Vue, need to meet two conditions at the same time:
3118
+ * 1. window.Vue --> undefined
3119
+ * 2. 'Vue' in window --> false
3120
+ * Issue https://github.com/micro-zoe/micro-app/issues/686
3121
+ */
3122
+ if (includes(sandbox.scopeProperties, key)) {
3123
+ if (sandbox.injectedKeys.has(key)) {
3124
+ return Reflect.has(target, key); // true
3062
3125
  }
3063
- return key in target;
3126
+ return !!target[key]; // false
3064
3127
  }
3065
- return key in target || key in rawWindow;
3128
+ return Reflect.has(target, key) || Reflect.has(rawWindow, key);
3066
3129
  },
3067
3130
  // Object.getOwnPropertyDescriptor(window, key)
3068
3131
  getOwnPropertyDescriptor: (target, key) => {
@@ -3107,14 +3170,26 @@ function createProxyWindow(appName, microAppWindow, sandbox) {
3107
3170
  * Rewrite side-effect events
3108
3171
  * @param microAppWindow micro window
3109
3172
  */
3110
- function patchWindowEffect(microAppWindow) {
3173
+ function patchWindowEffect(microAppWindow, appName) {
3111
3174
  const eventListenerMap = new Map();
3112
3175
  const sstEventListenerMap = new Map();
3113
3176
  const intervalIdMap = new Map();
3114
3177
  const timeoutIdMap = new Map();
3115
3178
  const { rawWindow, rawAddEventListener, rawRemoveEventListener, rawDispatchEvent, rawSetInterval, rawSetTimeout, rawClearInterval, rawClearTimeout, } = globalEnv;
3179
+ /**
3180
+ * All events will bind to microAppElement or rawWindow
3181
+ * Some special events, such as popstate、load、unmount、appstate-change、statechange..., bind to microAppElement, others bind to rawWindow
3182
+ * NOTE:
3183
+ * 1、At first, microAppWindow = new EventTarget(), but it can not compatible with iOS 14 or below, so microAppElement was used instead. (2024.1.22)
3184
+ * @param type event name
3185
+ * @returns microAppElement/rawWindow
3186
+ */
3116
3187
  function getEventTarget(type) {
3117
- return SCOPE_WINDOW_EVENT.includes(type) ? microAppWindow : rawWindow;
3188
+ var _a;
3189
+ if (SCOPE_WINDOW_EVENT_OF_WITH.includes(type) && ((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container)) {
3190
+ return getRootContainer(appInstanceMap.get(appName).container);
3191
+ }
3192
+ return rawWindow;
3118
3193
  }
3119
3194
  /**
3120
3195
  * listener may be null, e.g test-passive
@@ -3124,6 +3199,7 @@ function patchWindowEffect(microAppWindow) {
3124
3199
  * window.addEventListener.call(非window, type, listener, options)
3125
3200
  */
3126
3201
  microAppWindow.addEventListener = function (type, listener, options) {
3202
+ type = formatEventType(type, appName);
3127
3203
  const listenerList = eventListenerMap.get(type);
3128
3204
  if (listenerList) {
3129
3205
  listenerList.add(listener);
@@ -3135,6 +3211,7 @@ function patchWindowEffect(microAppWindow) {
3135
3211
  rawAddEventListener.call(getEventTarget(type), type, listener, options);
3136
3212
  };
3137
3213
  microAppWindow.removeEventListener = function (type, listener, options) {
3214
+ type = formatEventType(type, appName);
3138
3215
  const listenerList = eventListenerMap.get(type);
3139
3216
  if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
3140
3217
  listenerList.delete(listener);
@@ -3227,42 +3304,39 @@ function patchWindowEffect(microAppWindow) {
3227
3304
  }
3228
3305
 
3229
3306
  // set micro app state to origin state
3230
- function setMicroState(appName, microState) {
3231
- if (!isRouterModeCustom(appName)) {
3232
- const rawState = globalEnv.rawWindow.history.state;
3233
- const additionalState = {
3234
- microAppState: assign({}, rawState === null || rawState === void 0 ? void 0 : rawState.microAppState, {
3235
- [appName]: microState
3236
- })
3237
- };
3238
- // create new state object
3239
- return assign({}, rawState, additionalState);
3240
- }
3241
- return microState;
3307
+ function setMicroState(appName, microState, targetLocation) {
3308
+ // TODO: 验证native模式下修改state nextjs路由是否正常
3309
+ const rawState = globalEnv.rawWindow.history.state;
3310
+ const additionalState = {
3311
+ __MICRO_APP_STATE__: assign({}, rawState === null || rawState === void 0 ? void 0 : rawState.__MICRO_APP_STATE__, {
3312
+ [appName]: {
3313
+ fullPath: targetLocation.pathname + targetLocation.search + targetLocation.hash,
3314
+ state: microState,
3315
+ mode: getRouterMode(appName),
3316
+ }
3317
+ })
3318
+ };
3319
+ // create new state object
3320
+ return assign({}, rawState, additionalState);
3242
3321
  }
3243
3322
  // delete micro app state form origin state
3244
3323
  function removeMicroState(appName, rawState) {
3245
- if (!isRouterModeCustom(appName)) {
3246
- if (isPlainObject(rawState === null || rawState === void 0 ? void 0 : rawState.microAppState)) {
3247
- if (!isUndefined(rawState.microAppState[appName])) {
3248
- delete rawState.microAppState[appName];
3249
- }
3250
- if (!Object.keys(rawState.microAppState).length) {
3251
- delete rawState.microAppState;
3252
- }
3324
+ if (isPlainObject(rawState === null || rawState === void 0 ? void 0 : rawState.__MICRO_APP_STATE__)) {
3325
+ if (!isUndefined(rawState.__MICRO_APP_STATE__[appName])) {
3326
+ delete rawState.__MICRO_APP_STATE__[appName];
3327
+ }
3328
+ if (!Object.keys(rawState.__MICRO_APP_STATE__).length) {
3329
+ delete rawState.__MICRO_APP_STATE__;
3253
3330
  }
3254
- return assign({}, rawState);
3255
3331
  }
3256
- return rawState;
3332
+ return assign({}, rawState);
3257
3333
  }
3258
3334
  // get micro app state form origin state
3259
3335
  function getMicroState(appName) {
3260
- var _a;
3336
+ var _a, _b;
3261
3337
  const rawState = globalEnv.rawWindow.history.state;
3262
- if (!isRouterModeCustom(appName)) {
3263
- return ((_a = rawState === null || rawState === void 0 ? void 0 : rawState.microAppState) === null || _a === void 0 ? void 0 : _a[appName]) || null;
3264
- }
3265
- return rawState;
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);
3266
3340
  }
3267
3341
  const ENC_AD_RE = /&/g; // %M1
3268
3342
  const ENC_EQ_RE = /=/g; // %M2
@@ -3298,14 +3372,30 @@ function formatQueryAppName(appName) {
3298
3372
  * @param appName app.name
3299
3373
  */
3300
3374
  function getMicroPathFromURL(appName) {
3301
- var _a, _b;
3375
+ var _a, _b, _c, _d;
3302
3376
  const rawLocation = globalEnv.rawWindow.location;
3303
- if (!isRouterModeCustom(appName)) {
3377
+ const rawState = globalEnv.rawWindow.history.state;
3378
+ if (isRouterModeSearch(appName)) {
3304
3379
  const queryObject = getQueryObjectFromURL(rawLocation.search, rawLocation.hash);
3305
3380
  const microPath = ((_a = queryObject.hashQuery) === null || _a === void 0 ? void 0 : _a[formatQueryAppName(appName)]) || ((_b = queryObject.searchQuery) === null || _b === void 0 ? void 0 : _b[formatQueryAppName(appName)]);
3306
3381
  return isString(microPath) ? decodeMicroPath(microPath) : null;
3307
3382
  }
3308
- return rawLocation.pathname + rawLocation.search + rawLocation.hash;
3383
+ /**
3384
+ * Get fullPath from __MICRO_APP_STATE__
3385
+ * NOTE:
3386
+ * 1. state mode: all base on __MICRO_APP_STATE__
3387
+ * 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:
3389
+ * history.replaceState(
3390
+ * assign({}, history.state, {...}),
3391
+ * title,
3392
+ * history.state.current, <---
3393
+ * )
3394
+ * when base app jump to another page from child page, it will replace child path with base app path
3395
+ * 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
3397
+ */
3398
+ 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);
3309
3399
  }
3310
3400
  /**
3311
3401
  * Attach child app fullPath to browser url
@@ -3313,10 +3403,11 @@ function getMicroPathFromURL(appName) {
3313
3403
  * @param targetLocation location of child app or rawLocation of window
3314
3404
  */
3315
3405
  function setMicroPathToURL(appName, targetLocation) {
3316
- const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3406
+ const rawLocation = globalEnv.rawWindow.location;
3407
+ let targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3317
3408
  let isAttach2Hash = false;
3318
- if (!isRouterModeCustom(appName)) {
3319
- let { pathname, search, hash } = globalEnv.rawWindow.location;
3409
+ if (isRouterModeSearch(appName)) {
3410
+ let { pathname, search, hash } = rawLocation;
3320
3411
  const queryObject = getQueryObjectFromURL(search, hash);
3321
3412
  const encodedMicroPath = encodeMicroPath(targetFullPath);
3322
3413
  /**
@@ -3326,6 +3417,7 @@ function setMicroPathToURL(appName, targetLocation) {
3326
3417
  // If hash exists and search does not exist, it is considered as a hash route
3327
3418
  if (hash && !search) {
3328
3419
  isAttach2Hash = true;
3420
+ // TODO: 这里和下面的if判断可以简化一下
3329
3421
  if (queryObject.hashQuery) {
3330
3422
  queryObject.hashQuery[formatQueryAppName(appName)] = encodedMicroPath;
3331
3423
  }
@@ -3353,6 +3445,9 @@ function setMicroPathToURL(appName, targetLocation) {
3353
3445
  isAttach2Hash,
3354
3446
  };
3355
3447
  }
3448
+ if (isRouterModeState(appName) || isRouterModePure(appName)) {
3449
+ targetFullPath = rawLocation.pathname + rawLocation.search + rawLocation.hash;
3450
+ }
3356
3451
  return {
3357
3452
  fullPath: targetFullPath,
3358
3453
  isAttach2Hash,
@@ -3361,13 +3456,12 @@ function setMicroPathToURL(appName, targetLocation) {
3361
3456
  /**
3362
3457
  * Delete child app fullPath from browser url
3363
3458
  * @param appName app.name
3364
- * @param targetLocation target Location, default is rawLocation
3365
3459
  */
3366
- function removeMicroPathFromURL(appName, targetLocation) {
3460
+ function removeMicroPathFromURL(appName) {
3367
3461
  var _a, _b, _c, _d;
3368
- let { pathname, search, hash } = targetLocation || globalEnv.rawWindow.location;
3462
+ let { pathname, search, hash } = globalEnv.rawWindow.location;
3369
3463
  let isAttach2Hash = false;
3370
- if (!isRouterModeCustom(appName)) {
3464
+ if (isRouterModeSearch(appName)) {
3371
3465
  const queryObject = getQueryObjectFromURL(search, hash);
3372
3466
  if ((_a = queryObject.hashQuery) === null || _a === void 0 ? void 0 : _a[formatQueryAppName(appName)]) {
3373
3467
  isAttach2Hash = true;
@@ -3428,14 +3522,38 @@ function isEffectiveApp(appName) {
3428
3522
  return !!(app && !app.isPrefetch);
3429
3523
  }
3430
3524
  /**
3431
- * router mode is custom
3432
- * NOTE:
3433
- * 1. if sandbox disabled, router mode defaults to custom
3434
- * 2. if app not exist, router mode defaults to custom
3525
+ * get router mode of app
3526
+ * NOTE: app maybe undefined
3527
+ */
3528
+ function getRouterMode(appName) {
3529
+ var _a;
3530
+ return (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.routerMode;
3531
+ }
3532
+ // router mode is search
3533
+ function isRouterModeSearch(appName) {
3534
+ return getRouterMode(appName) === DEFAULT_ROUTER_MODE;
3535
+ }
3536
+ // router mode is state
3537
+ function isRouterModeState(appName) {
3538
+ return getRouterMode(appName) === ROUTER_MODE_STATE;
3539
+ }
3540
+ // router mode is history
3541
+ function isRouterModeNative(appName) {
3542
+ return getRouterMode(appName) === ROUTER_MODE_NATIVE;
3543
+ }
3544
+ // router mode is disable
3545
+ function isRouterModeNativeScope(appName) {
3546
+ return getRouterMode(appName) === ROUTER_MODE_NATIVE_SCOPE;
3547
+ }
3548
+ // router mode is pure
3549
+ function isRouterModePure(appName) {
3550
+ return getRouterMode(appName) === ROUTER_MODE_PURE;
3551
+ }
3552
+ /**
3553
+ * router mode is history or disable
3435
3554
  */
3436
3555
  function isRouterModeCustom(appName) {
3437
- const app = appInstanceMap.get(appName);
3438
- return !app || !app.sandBox || app.routerMode === ROUTER_MODE_CUSTOM;
3556
+ return isRouterModeNative(appName) || isRouterModeNativeScope(appName);
3439
3557
  }
3440
3558
  /**
3441
3559
  * get memory router mode of child app
@@ -3443,21 +3561,21 @@ function isRouterModeCustom(appName) {
3443
3561
  * 1. if microAppElement exists, it means the app render by the micro-app element
3444
3562
  * 2. if microAppElement not exists, it means it is prerender app
3445
3563
  * @param mode native config
3446
- * @param microAppElement micro-app element
3447
- * @returns mode
3564
+ * @param inlineDisableMemoryRouter disable-memory-router set by micro-app element or prerender
3565
+ * @returns router mode
3448
3566
  */
3449
- function getRouterMode(mode, microAppElement) {
3450
- let routerMode;
3567
+ function initRouterMode(mode, inlineDisableMemoryRouter) {
3451
3568
  /**
3452
3569
  * compatible with disable-memory-router in older versions
3453
- * if disable-memory-router is true, router-mode will be custom
3570
+ * if disable-memory-router is true, router-mode will be disable
3571
+ * Priority:
3572
+ * inline disable-memory-router > inline router-mode > global disable-memory-router > global router-mode
3454
3573
  */
3455
- if (microAppElement) {
3456
- routerMode = microAppElement.getDisposeResult('disable-memory-router') ? ROUTER_MODE_CUSTOM : mode || microApp.options['router-mode'] || '';
3457
- }
3458
- else {
3459
- routerMode = microApp.options['disable-memory-router'] ? ROUTER_MODE_CUSTOM : mode || microApp.options['router-mode'] || '';
3460
- }
3574
+ const routerMode = ((inlineDisableMemoryRouter && ROUTER_MODE_NATIVE) ||
3575
+ mode ||
3576
+ (microApp.options['disable-memory-router'] && ROUTER_MODE_NATIVE) ||
3577
+ microApp.options['router-mode'] ||
3578
+ DEFAULT_ROUTER_MODE);
3461
3579
  return ROUTER_MODE_LIST.includes(routerMode) ? routerMode : DEFAULT_ROUTER_MODE;
3462
3580
  }
3463
3581
 
@@ -3481,7 +3599,21 @@ function addHistoryListener(appName) {
3481
3599
  excludePreRender: true,
3482
3600
  }).includes(appName) &&
3483
3601
  !e.onlyForBrowser) {
3602
+ /**
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
+ // ) {
3484
3615
  updateMicroLocationWithEvent(appName, getMicroPathFromURL(appName));
3616
+ // }
3485
3617
  }
3486
3618
  };
3487
3619
  rawWindow.addEventListener('popstate', popStateHandler);
@@ -3529,14 +3661,18 @@ function dispatchPopStateEventToMicroApp(appName, proxyWindow, microAppWindow) {
3529
3661
  * TODO: test
3530
3662
  * angular14 takes e.type as type judgment
3531
3663
  * when e.type is popstate-appName popstate event will be invalid
3664
+ * Object.defineProperty(newPopStateEvent, 'type', {
3665
+ * value: 'popstate',
3666
+ * writable: true,
3667
+ * configurable: true,
3668
+ * enumerable: true,
3669
+ * })
3670
+ */
3671
+ /**
3672
+ * create PopStateEvent named popstate-appName with sub app state
3673
+ * TODO: feeling like there's something wrong, check carefully
3674
+ * In native mode, getMicroState(appName) return rawWindow.history.state when use microApp.router.push/replace or other scenes when state.__MICRO_APP_STATE__[appName] is null
3532
3675
  */
3533
- // Object.defineProperty(newPopStateEvent, 'type', {
3534
- // value: 'popstate',
3535
- // writable: true,
3536
- // configurable: true,
3537
- // enumerable: true,
3538
- // })
3539
- // create PopStateEvent named popstate-appName with sub app state
3540
3676
  const newPopStateEvent = new PopStateEvent('popstate', { state: getMicroState(appName) });
3541
3677
  microAppWindow.dispatchEvent(newPopStateEvent);
3542
3678
  if (!isIframeSandbox(appName)) {
@@ -3603,7 +3739,7 @@ function dispatchNativeEvent(appName, onlyForBrowser, oldHref) {
3603
3739
  * create proxyHistory for microApp
3604
3740
  * MDN https://developer.mozilla.org/en-US/docs/Web/API/History
3605
3741
  * @param appName app name
3606
- * @param microLocation microApp location
3742
+ * @param microLocation microApp location(with: proxyLocation iframe: iframeWindow.location)
3607
3743
  */
3608
3744
  function createMicroHistory(appName, microLocation) {
3609
3745
  const rawHistory = globalEnv.rawWindow.history;
@@ -3614,7 +3750,9 @@ function createMicroHistory(appName, microLocation) {
3614
3750
  if (isString(rests[2]) || isURL(rests[2])) {
3615
3751
  const targetLocation = createURL(rests[2], microLocation.href);
3616
3752
  const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3617
- navigateWithNativeEvent(appName, methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rests[0]), rests[1]);
3753
+ if (!isRouterModePure(appName)) {
3754
+ navigateWithNativeEvent(appName, methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rests[0], targetLocation), rests[1]);
3755
+ }
3618
3756
  if (targetFullPath !== microLocation.fullPath) {
3619
3757
  updateMicroLocation(appName, targetFullPath, microLocation);
3620
3758
  }
@@ -3627,8 +3765,12 @@ function createMicroHistory(appName, microLocation) {
3627
3765
  }
3628
3766
  const pushState = getMicroHistoryMethod('pushState');
3629
3767
  const replaceState = getMicroHistoryMethod('replaceState');
3630
- if (isIframeSandbox(appName))
3631
- return { pushState, replaceState };
3768
+ if (isIframeSandbox(appName)) {
3769
+ return {
3770
+ pushState,
3771
+ replaceState,
3772
+ };
3773
+ }
3632
3774
  return new Proxy(rawHistory, {
3633
3775
  get(target, key) {
3634
3776
  if (key === 'state') {
@@ -3691,12 +3833,8 @@ function navigateWithNativeEvent(appName, methodName, result, onlyForBrowser, st
3691
3833
  const oldHref = result.isAttach2Hash && oldFullPath !== result.fullPath ? rawLocation.href : null;
3692
3834
  // navigate with native history method
3693
3835
  nativeHistoryNavigate(appName, methodName, result.fullPath, state, title);
3694
- /**
3695
- * TODO:
3696
- * 1. 如果所有模式统一发送popstate事件,则!isRouterModeCustom(appName)要去掉
3697
- * 2. 如果发送事件,则会导致vue router-view :key='router.path'绑定,无限卸载应用,死循环
3698
- */
3699
- if (oldFullPath !== result.fullPath && !isRouterModeCustom(appName)) {
3836
+ // just search mode will dispatch native event
3837
+ if (oldFullPath !== result.fullPath && isRouterModeSearch(appName)) {
3700
3838
  dispatchNativeEvent(appName, onlyForBrowser, oldHref);
3701
3839
  }
3702
3840
  }
@@ -3712,22 +3850,22 @@ function attachRouteToBrowserURL(appName, result, state) {
3712
3850
  navigateWithNativeEvent(appName, 'replaceState', result, true, state);
3713
3851
  }
3714
3852
  /**
3715
- * When path is same, keep the microAppState in history.state
3716
- * Fix bug of missing microAppState when base app is next.js or angular
3853
+ * When path is same, keep the __MICRO_APP_STATE__ in history.state
3854
+ * Fix bug of missing __MICRO_APP_STATE__ when base app is next.js or angular
3717
3855
  * @param method history.pushState/replaceState
3718
3856
  */
3719
3857
  function reWriteHistoryMethod(method) {
3720
3858
  const rawWindow = globalEnv.rawWindow;
3721
3859
  return function (...rests) {
3722
3860
  var _a;
3723
- if (((_a = rawWindow.history.state) === null || _a === void 0 ? void 0 : _a.microAppState) &&
3724
- (!isPlainObject(rests[0]) || !rests[0].microAppState) &&
3861
+ if (((_a = rawWindow.history.state) === null || _a === void 0 ? void 0 : _a.__MICRO_APP_STATE__) &&
3862
+ (!isPlainObject(rests[0]) || !rests[0].__MICRO_APP_STATE__) &&
3725
3863
  (isString(rests[2]) || isURL(rests[2]))) {
3726
3864
  const currentHref = rawWindow.location.href;
3727
3865
  const targetLocation = createURL(rests[2], currentHref);
3728
3866
  if (targetLocation.href === currentHref) {
3729
3867
  rests[0] = assign({}, rests[0], {
3730
- microAppState: rawWindow.history.state.microAppState,
3868
+ __MICRO_APP_STATE__: rawWindow.history.state.__MICRO_APP_STATE__,
3731
3869
  });
3732
3870
  }
3733
3871
  }
@@ -3743,9 +3881,9 @@ function reWriteHistoryMethod(method) {
3743
3881
  excludeHiddenApp: true,
3744
3882
  excludePreRender: true,
3745
3883
  }).forEach(appName => {
3746
- if (!isRouterModeCustom(appName) && !getMicroPathFromURL(appName)) {
3884
+ if ((isRouterModeSearch(appName) || isRouterModeState(appName)) && !getMicroPathFromURL(appName)) {
3747
3885
  const app = appInstanceMap.get(appName);
3748
- attachRouteToBrowserURL(appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName)));
3886
+ attachRouteToBrowserURL(appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName), app.sandBox.proxyWindow.location));
3749
3887
  }
3750
3888
  });
3751
3889
  // fix bug for nest app
@@ -3754,7 +3892,7 @@ function reWriteHistoryMethod(method) {
3754
3892
  }
3755
3893
  /**
3756
3894
  * rewrite history.pushState/replaceState
3757
- * used to fix the problem that the microAppState maybe missing when mainApp navigate to same path
3895
+ * used to fix the problem that the __MICRO_APP_STATE__ maybe missing when mainApp navigate to same path
3758
3896
  * e.g: when nextjs, angular receive popstate event, they will use history.replaceState to update browser url with a new state object
3759
3897
  */
3760
3898
  function patchHistory() {
@@ -3777,7 +3915,7 @@ function createRouterApi() {
3777
3915
  * @param state to.state
3778
3916
  */
3779
3917
  function navigateWithRawHistory(appName, methodName, targetLocation, state) {
3780
- navigateWithNativeEvent(appName, methodName, setMicroPathToURL(appName, targetLocation), false, setMicroState(appName, state !== null && state !== void 0 ? state : null));
3918
+ navigateWithNativeEvent(appName, methodName, setMicroPathToURL(appName, targetLocation), false, setMicroState(appName, state !== null && state !== void 0 ? state : null, targetLocation));
3781
3919
  // clear element scope after navigate
3782
3920
  removeDomScope();
3783
3921
  }
@@ -3795,21 +3933,13 @@ function createRouterApi() {
3795
3933
  const currentFullPath = microLocation.pathname + microLocation.search + microLocation.hash;
3796
3934
  const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3797
3935
  if (currentFullPath !== targetFullPath || getMicroPathFromURL(appName) !== targetFullPath) {
3798
- const methodName = (replace && to.replace !== false) || to.replace === true ? 'replaceState' : 'pushState';
3799
- navigateWithRawHistory(appName, methodName, targetLocation, to.state);
3800
- /**
3801
- * TODO:
3802
- * 1. 关闭虚拟路由的跳转地址不同:baseRoute + 子应用地址,文档中要说明
3803
- * 2. 关闭虚拟路由时跳转方式不同:1、基座跳转但不发送popstate事件 2、控制子应用更新location,内部发送popstate事件。
3804
- * 核心思路:减小对基座的影响(子应用跳转不向基座发送popstate事件,其他操作一致),但这是必要的吗,只是多了一个触发popstate的操作
3805
- * 路由优化方案有两种:
3806
- * 1、减少对基座的影响,主要是解决vue循环刷新的问题
3807
- * 2、全局发送popstate事件,解决主、子都是vue3的冲突问题
3808
- * 两者选一个吧,如果选2,则下面这两行代码可以去掉
3809
- * NOTE1: history和search模式采用2,这样可以解决vue3的问题,custom采用1,避免vue循环刷新的问题,这样在用户出现问题时各有解决方案。但反过来说,每种方案又分别导致另外的问题,不统一,导致复杂度增高
3810
- * NOTE2: 关闭虚拟路由,同时发送popstate事件还是无法解决vue3的问题(毕竟history.state理论上还是会冲突),那么就没必要发送popstate事件了。
3811
- */
3812
- if (isRouterModeCustom(appName)) {
3936
+ // pure mode will not call history.pushState/replaceState
3937
+ if (!isRouterModePure(appName)) {
3938
+ const methodName = (replace && to.replace !== false) || to.replace === true ? 'replaceState' : 'pushState';
3939
+ navigateWithRawHistory(appName, methodName, targetLocation, to.state);
3940
+ }
3941
+ // only search mode will dispatch PopStateEvent to browser
3942
+ if (!isRouterModeSearch(appName)) {
3813
3943
  updateMicroLocationWithEvent(appName, targetFullPath);
3814
3944
  }
3815
3945
  }
@@ -3834,27 +3964,18 @@ function createRouterApi() {
3834
3964
  * 2. disable memory-router
3835
3965
  */
3836
3966
  /**
3837
- * TODO: 子应用开始渲染但是还没渲染完成
3838
- * 1、调用跳转改如何处理
3967
+ * TODO:
3968
+ * 1、子应用开始渲染但是还没渲染完成,调用跳转改如何处理
3839
3969
  * 2、iframe的沙箱还没初始化时执行跳转报错,如何处理。。。
3840
- * 3、hidden app 是否支持跳转
3970
+ * 3、hidden app、预渲染 app 是否支持跳转 --- 支持(这里还涉及子应用内部跳转的支持)
3841
3971
  */
3842
3972
  if (getActiveApps({ excludeHiddenApp: true, excludePreRender: true }).includes(appName)) {
3843
3973
  const app = appInstanceMap.get(appName);
3844
3974
  resolve(app.sandBox.sandboxReady.then(() => handleNavigate(appName, app, to, replace)));
3845
3975
  }
3846
3976
  else {
3847
- reject(logError('navigation failed, app does not exist or is inactive'));
3977
+ reject(logError('导航失败,请确保子应用渲染后再调用此方法'));
3848
3978
  }
3849
- // /**
3850
- // * app not exit or unmounted, update browser URL with replaceState
3851
- // * use base app location.origin as baseURL
3852
- // * 应用不存在或已卸载,依然使用replaceState来更新浏览器地址 -- 不合理
3853
- // */
3854
- // /**
3855
- // * TODO: 应用还没渲染或已经卸载最好不要支持跳转了,我知道这是因为解决一些特殊场景,但这么做是非常反直觉的
3856
- // * 并且在新版本中有多种路由模式,如果应用不存在,我们根本无法知道是哪种模式,那么这里的操作就无意义了。
3857
- // */
3858
3979
  // const rawLocation = globalEnv.rawWindow.location
3859
3980
  // const targetLocation = createURL(to.path, rawLocation.origin)
3860
3981
  // const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash
@@ -3927,9 +4048,9 @@ function createRouterApi() {
3927
4048
  * 3. router mode is custom
3928
4049
  */
3929
4050
  function commonHandlerForAttachToURL(appName) {
3930
- if (!isRouterModeCustom(appName)) {
4051
+ if (isRouterModeSearch(appName) || isRouterModeState(appName)) {
3931
4052
  const app = appInstanceMap.get(appName);
3932
- attachRouteToBrowserURL(appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName)));
4053
+ attachRouteToBrowserURL(appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName), app.sandBox.proxyWindow.location));
3933
4054
  }
3934
4055
  }
3935
4056
  /**
@@ -4028,94 +4149,6 @@ function createRouterApi() {
4028
4149
  }
4029
4150
  const { router, executeNavigationGuard, clearRouterWhenUnmount, } = createRouterApi();
4030
4151
 
4031
- const escape2RawWindowKeys = [
4032
- 'getComputedStyle',
4033
- 'visualViewport',
4034
- 'matchMedia',
4035
- // 'DOMParser',
4036
- 'ResizeObserver',
4037
- 'IntersectionObserver',
4038
- ];
4039
- const escape2RawWindowRegExpKeys = [
4040
- /animationFrame$/i,
4041
- /mutationObserver$/i,
4042
- /height$|width$/i,
4043
- /offset$/i,
4044
- // /event$/i,
4045
- /selection$/i,
4046
- /^range/i,
4047
- /^screen/i,
4048
- /^scroll/i,
4049
- /X$|Y$/,
4050
- ];
4051
- const uniqueDocumentElement = [
4052
- 'body',
4053
- 'head',
4054
- 'html',
4055
- 'title',
4056
- ];
4057
- const hijackMicroLocationKeys = [
4058
- 'host',
4059
- 'hostname',
4060
- 'port',
4061
- 'protocol',
4062
- 'origin',
4063
- ];
4064
- // 有shadowRoot则代理到shadowRoot否则代理到原生document上 (属性)
4065
- const proxy2RawDocOrShadowKeys = [
4066
- 'childElementCount',
4067
- 'children',
4068
- 'firstElementChild',
4069
- 'firstChild',
4070
- 'lastElementChild',
4071
- 'activeElement',
4072
- 'fullscreenElement',
4073
- 'pictureInPictureElement',
4074
- 'pointerLockElement',
4075
- 'styleSheets',
4076
- ];
4077
- // 有shadowRoot则代理到shadowRoot否则代理到原生document上 (方法)
4078
- const proxy2RawDocOrShadowMethods = [
4079
- 'append',
4080
- 'contains',
4081
- 'replaceChildren',
4082
- 'createRange',
4083
- 'getSelection',
4084
- 'elementFromPoint',
4085
- 'elementsFromPoint',
4086
- 'getAnimations',
4087
- ];
4088
- // 直接代理到原生document上 (属性)
4089
- const proxy2RawDocumentKeys = [
4090
- 'characterSet',
4091
- 'compatMode',
4092
- 'contentType',
4093
- 'designMode',
4094
- 'dir',
4095
- 'doctype',
4096
- 'embeds',
4097
- 'fullscreenEnabled',
4098
- 'hidden',
4099
- 'implementation',
4100
- 'lastModified',
4101
- 'pictureInPictureEnabled',
4102
- 'plugins',
4103
- 'readyState',
4104
- 'referrer',
4105
- 'visibilityState',
4106
- 'fonts',
4107
- ];
4108
- // 直接代理到原生document上 (方法)
4109
- const proxy2RawDocumentMethods = [
4110
- 'execCommand',
4111
- 'createRange',
4112
- 'exitFullscreen',
4113
- 'exitPictureInPicture',
4114
- 'getElementsByTagNameNS',
4115
- 'hasFocus',
4116
- 'prepend',
4117
- ];
4118
-
4119
4152
  // origin is readonly, so we ignore when updateMicroLocation
4120
4153
  const locationKeys = ['href', 'pathname', 'search', 'hash', 'host', 'hostname', 'port', 'protocol', 'search'];
4121
4154
  // origin, fullPath is necessary for guardLocation
@@ -4160,38 +4193,46 @@ function createMicroLocation(appName, url, microAppWindow, childStaticLocation,
4160
4193
  const setMicroPathResult = setMicroPathToURL(appName, targetLocation);
4161
4194
  // if disable memory-router, navigate directly through rawLocation
4162
4195
  if (!isRouterModeCustom(appName)) {
4196
+ methodName = isRouterModePure(appName) ? 'replaceState' : methodName;
4163
4197
  /**
4164
4198
  * change hash with location.href will not trigger the browser reload
4165
4199
  * so we use pushState & reload to imitate href behavior
4166
4200
  * NOTE:
4167
- * 1. if child app only change hash, it should not trigger browser reload
4168
- * 2. if address is same and has hash, it should not add route stack
4201
+ * 1. if child app only change hash, it will not reload browser
4202
+ * 2. if address is same and has hash, it will not add route stack
4169
4203
  */
4170
4204
  if (targetLocation.pathname === proxyLocation.pathname &&
4171
4205
  targetLocation.search === proxyLocation.search) {
4172
4206
  let oldHref = null;
4173
- if (targetLocation.hash !== proxyLocation.hash) {
4174
- if (setMicroPathResult.isAttach2Hash)
4207
+ // NOTE: if pathname & search is same, it should record router info to history.state in pure mode
4208
+ if (targetLocation.hash !== proxyLocation.hash || isRouterModePure(appName)) {
4209
+ // search mode only
4210
+ if (setMicroPathResult.isAttach2Hash) {
4175
4211
  oldHref = rawLocation.href;
4176
- nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath);
4212
+ }
4213
+ // if router mode is pure and targetLocation.hash exist, it will not call nativeHistoryNavigate
4214
+ if (!isRouterModePure(appName) || !targetLocation.hash) {
4215
+ nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath, !isRouterModeSearch(appName) ? setMicroState(appName, null, targetLocation) : null);
4216
+ }
4177
4217
  }
4178
4218
  if (targetLocation.hash) {
4179
- dispatchNativeEvent(appName, false, oldHref);
4219
+ if (isRouterModeSearch(appName)) {
4220
+ dispatchNativeEvent(appName, false, oldHref);
4221
+ }
4222
+ else {
4223
+ updateMicroLocationWithEvent(appName, targetLocation.pathname + targetLocation.search + targetLocation.hash);
4224
+ }
4180
4225
  }
4181
4226
  else {
4182
4227
  reload();
4183
4228
  }
4184
4229
  return void 0;
4185
- /**
4186
- * when baseApp is hash router, address change of child can not reload browser
4187
- * so we imitate behavior of browser (reload) manually
4188
- */
4189
- }
4190
- else if (setMicroPathResult.isAttach2Hash) {
4191
- nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath);
4192
- reload();
4193
- return void 0;
4194
4230
  }
4231
+ // when pathname or search change, simulate behavior of browser (reload) manually
4232
+ // TODO: state模式下pushState会带上上一个页面的state,会不会有问题,尤其是vue3,应不应该将主应用的state设置为null
4233
+ nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath, !isRouterModeSearch(appName) ? setMicroState(appName, null, targetLocation) : null);
4234
+ reload();
4235
+ return void 0;
4195
4236
  }
4196
4237
  return setMicroPathResult.fullPath;
4197
4238
  }
@@ -4216,7 +4257,9 @@ function createMicroLocation(appName, url, microAppWindow, childStaticLocation,
4216
4257
  * pathname: /path ==> /path#hash, /path ==> /path?query
4217
4258
  * search: ?query ==> ?query#hash
4218
4259
  */
4219
- nativeHistoryNavigate(appName, targetLocation[key] === proxyLocation[key] ? 'replaceState' : 'pushState', setMicroPathToURL(appName, targetLocation).fullPath);
4260
+ nativeHistoryNavigate(appName, (targetLocation[key] === proxyLocation[key] || isRouterModePure(appName))
4261
+ ? 'replaceState'
4262
+ : 'pushState', setMicroPathToURL(appName, targetLocation).fullPath, !isRouterModeSearch(appName) ? setMicroState(appName, null, targetLocation) : null);
4220
4263
  reload();
4221
4264
  }
4222
4265
  }
@@ -4245,16 +4288,6 @@ function createMicroLocation(appName, url, microAppWindow, childStaticLocation,
4245
4288
  const proxyLocation = new Proxy({}, {
4246
4289
  get: (_, key) => {
4247
4290
  const target = getTarget();
4248
- if (isIframe) {
4249
- // host hostname port protocol
4250
- if (hijackMicroLocationKeys.includes(key)) {
4251
- return childStaticLocation[key];
4252
- }
4253
- if (key === 'href') {
4254
- // do not use target, because target may be deleted
4255
- return target[key].replace(browserHost, childHost);
4256
- }
4257
- }
4258
4291
  if (key === 'assign')
4259
4292
  return assign;
4260
4293
  if (key === 'replace')
@@ -4263,21 +4296,47 @@ function createMicroLocation(appName, url, microAppWindow, childStaticLocation,
4263
4296
  return reload;
4264
4297
  if (key === 'self')
4265
4298
  return target;
4299
+ if (key === 'fullPath')
4300
+ return target.fullPath;
4301
+ /**
4302
+ * Special keys: host, hostname, port, protocol, origin, href
4303
+ * NOTE:
4304
+ * 1. In native mode this keys point to browser, in other mode this keys point to child app origin
4305
+ * 2. In iframe sandbox, iframe.src is base app address, so origin points to the browser by default, we need to replace it with child app origin
4306
+ * 3. In other modes, origin points to child app
4307
+ */
4308
+ if (HIJACK_LOCATION_KEYS.includes(key)) {
4309
+ if (isRouterModeNative(appName)) {
4310
+ return rawLocation[key];
4311
+ }
4312
+ if (isIframe) {
4313
+ return childStaticLocation[key];
4314
+ }
4315
+ }
4316
+ if (key === 'href') {
4317
+ if (isRouterModeNative(appName)) {
4318
+ return target[key].replace(target.origin, rawLocation.origin);
4319
+ }
4320
+ if (isIframe) {
4321
+ // target may be deleted
4322
+ return target[key].replace(browserHost, childHost);
4323
+ }
4324
+ }
4266
4325
  return bindFunctionToRawTarget(Reflect.get(target, key), target, 'LOCATION');
4267
4326
  },
4268
4327
  set: (_, key, value) => {
4269
4328
  if (isEffectiveApp(appName)) {
4270
4329
  const target = getTarget();
4271
4330
  if (key === 'href') {
4272
- const targetPath = commonHandler(value, 'pushState');
4273
4331
  /**
4274
4332
  * In vite, targetPath without origin will be completed with child origin
4275
4333
  * So we use browser origin to complete targetPath to avoid this problem
4276
- * But, why child app can affect browser jump?
4277
- * Guess(need check):
4278
- * 1. vite records the origin when init
4279
- * 2. listen for browser jump and automatically complete the address
4334
+ * NOTE:
4335
+ * 1. history mode & value is childOrigin + path ==> jump to browserOrigin + path
4336
+ * 2. disable mode & value is childOrigin + path ==> jump to childOrigin + path
4337
+ * 3. search mode & value is browserOrigin + path ==> jump to browserOrigin + path
4280
4338
  */
4339
+ const targetPath = commonHandler(value, 'pushState');
4281
4340
  if (targetPath) {
4282
4341
  rawLocation.href = createURL(targetPath, rawLocation.origin).href;
4283
4342
  }
@@ -4309,7 +4368,12 @@ function createMicroLocation(appName, url, microAppWindow, childStaticLocation,
4309
4368
  const targetLocation = createURL(targetPath, url);
4310
4369
  // The same hash will not trigger popStateEvent
4311
4370
  if (targetLocation.hash !== proxyLocation.hash) {
4312
- navigateWithNativeEvent(appName, 'pushState', setMicroPathToURL(appName, targetLocation), false);
4371
+ if (!isRouterModePure(appName)) {
4372
+ navigateWithNativeEvent(appName, 'pushState', setMicroPathToURL(appName, targetLocation), false, setMicroState(appName, null, targetLocation));
4373
+ }
4374
+ if (!isRouterModeSearch(appName)) {
4375
+ updateMicroLocationWithEvent(appName, targetLocation.pathname + targetLocation.search + targetLocation.hash);
4376
+ }
4313
4377
  }
4314
4378
  }
4315
4379
  }
@@ -4362,9 +4426,11 @@ function updateMicroLocation(appName, path, microLocation, type) {
4362
4426
  (_a = microAppWindow.rawReplaceState) === null || _a === void 0 ? void 0 : _a.call(microAppWindow.history, getMicroState(appName), '', newLocation.href);
4363
4427
  }
4364
4428
  else {
4365
- for (const key of locationKeys) {
4366
- microLocation.self[key] = newLocation[key];
4429
+ let targetHref = newLocation.href;
4430
+ if (microLocation.self.origin !== newLocation.origin) {
4431
+ targetHref = targetHref.replace(newLocation.origin, microLocation.self.origin);
4367
4432
  }
4433
+ microLocation.self.href = targetHref;
4368
4434
  }
4369
4435
  // update latest values of microLocation to `to`
4370
4436
  const to = createGuardLocation(appName, microLocation);
@@ -4374,15 +4440,6 @@ function updateMicroLocation(appName, path, microLocation, type) {
4374
4440
  }
4375
4441
  }
4376
4442
 
4377
- /**
4378
- * TODO: 关于关闭虚拟路由系统的临时笔记 - 即custom模式,虚拟路由不支持关闭
4379
- * 1. with沙箱关闭虚拟路由最好和iframe保持一致
4380
- * 2. default-page无法使用,但是用基座的地址可以实现一样的效果
4381
- * 3. keep-router-state功能失效,因为始终为true
4382
- * 4. 基座控制子应用跳转地址改变,正确的值为:baseRoute + 子应用地址,这要在文档中说明,否则很容易出错,确实也很难理解
4383
- * 5. 是否需要发送popstate事件,为了减小对基座的影响,现在不发送
4384
- * 6. 关闭后导致的vue3路由冲突问题需要在文档中明确指出(2处:在关闭虚拟路由系统的配置那里着重说明,在vue常见问题中说明)
4385
- */
4386
4443
  /**
4387
4444
  * The router system has two operations: read and write
4388
4445
  * Read through location and write through history & location
@@ -4407,6 +4464,9 @@ function initRouteStateWithURL(appName, microLocation, defaultPage) {
4407
4464
  const microPath = getMicroPathFromURL(appName);
4408
4465
  if (microPath) {
4409
4466
  updateMicroLocation(appName, microPath, microLocation, 'auto');
4467
+ if (isRouterModePure(appName)) {
4468
+ removePathFromBrowser(appName);
4469
+ }
4410
4470
  }
4411
4471
  else {
4412
4472
  updateBrowserURLWithLocation(appName, microLocation, defaultPage);
@@ -4422,8 +4482,10 @@ function updateBrowserURLWithLocation(appName, microLocation, defaultPage) {
4422
4482
  // update microLocation with defaultPage
4423
4483
  if (defaultPage)
4424
4484
  updateMicroLocation(appName, defaultPage, microLocation, 'prevent');
4425
- // attach microApp route info to browser URL
4426
- attachRouteToBrowserURL(appName, setMicroPathToURL(appName, microLocation), setMicroState(appName, null));
4485
+ if (!isRouterModePure(appName)) {
4486
+ // attach microApp route info to browser URL
4487
+ attachRouteToBrowserURL(appName, setMicroPathToURL(appName, microLocation), setMicroState(appName, null, microLocation));
4488
+ }
4427
4489
  // trigger guards after change browser URL
4428
4490
  autoTriggerNavigationGuard(appName, microLocation);
4429
4491
  }
@@ -4435,11 +4497,12 @@ function updateBrowserURLWithLocation(appName, microLocation, defaultPage) {
4435
4497
  * @param keepRouteState keep-router-state is only used to control whether to clear the location of microApp, default is false
4436
4498
  */
4437
4499
  function clearRouteStateFromURL(appName, url, microLocation, keepRouteState) {
4438
- if (!isRouterModeCustom(appName)) {
4439
- if (!keepRouteState) {
4440
- const { pathname, search, hash } = createURL(url);
4441
- updateMicroLocation(appName, pathname + search + hash, microLocation, 'prevent');
4442
- }
4500
+ // TODO: keep-router-state 功能太弱,是否可以增加优先级,或者去掉
4501
+ if (!keepRouteState && !isRouterModeCustom(appName)) {
4502
+ const { pathname, search, hash } = createURL(url);
4503
+ updateMicroLocation(appName, pathname + search + hash, microLocation, 'prevent');
4504
+ }
4505
+ if (!isRouterModePure(appName)) {
4443
4506
  removePathFromBrowser(appName);
4444
4507
  }
4445
4508
  clearRouterWhenUnmount(appName);
@@ -4452,10 +4515,10 @@ function removePathFromBrowser(appName) {
4452
4515
  attachRouteToBrowserURL(appName, removeMicroPathFromURL(appName), removeMicroState(appName, globalEnv.rawWindow.history.state));
4453
4516
  }
4454
4517
 
4455
- class Adapter {
4518
+ class BaseSandbox {
4456
4519
  constructor() {
4457
4520
  // keys that can only assigned to rawWindow
4458
- this.escapeSetterKeyList = [
4521
+ this.rawWindowScopeKeyList = [
4459
4522
  'location',
4460
4523
  ];
4461
4524
  // keys that can escape to rawWindow
@@ -4468,7 +4531,18 @@ class Adapter {
4468
4531
  'webpackJsonp',
4469
4532
  'webpackHotUpdate',
4470
4533
  'Vue',
4534
+ // TODO: 是否可以和constants/SCOPE_WINDOW_ON_EVENT合并
4535
+ 'onpopstate',
4536
+ 'onhashchange',
4471
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();
4472
4546
  this.injectReactHMRProperty();
4473
4547
  }
4474
4548
  // adapter for react
@@ -4486,6 +4560,14 @@ class Adapter {
4486
4560
  }
4487
4561
  }
4488
4562
  }
4563
+ /**
4564
+ * TODO:
4565
+ * 1、将class Adapter去掉,改为CustomWindow,或者让CustomWindow继承Adapter
4566
+ * 2、with沙箱中的常量放入CustomWindow,虽然和iframe沙箱不一致,但更合理
4567
+ * 修改时机:在iframe沙箱支持插件后再修改
4568
+ */
4569
+ class CustomWindow {
4570
+ }
4489
4571
  // Fix conflict of babel-polyfill@6.x
4490
4572
  function fixBabelPolyfill6() {
4491
4573
  if (globalEnv.rawWindow._babelPolyfill)
@@ -4544,6 +4626,7 @@ function updateElementInfo(node, appName) {
4544
4626
  rawDefineProperties(node, {
4545
4627
  baseURI: {
4546
4628
  configurable: true,
4629
+ // if disable-memory-router or router-mode='disable', href point to base app
4547
4630
  get: () => proxyWindow.location.href,
4548
4631
  },
4549
4632
  __MICRO_APP_NAME__: {
@@ -4552,58 +4635,9 @@ function updateElementInfo(node, appName) {
4552
4635
  value: appName,
4553
4636
  },
4554
4637
  });
4555
- if (isIframeSandbox(appName)) {
4556
- /**
4557
- * If HTML built-in node belongs to base app, it needs to be handled separately for parentNode
4558
- * Fix error for nuxt@2.x + ElementUI@2.x
4559
- */
4560
- if (node instanceof globalEnv.rawRootNode) {
4561
- rawDefineProperty(node, 'parentNode', {
4562
- configurable: true,
4563
- get: createGetterForIframeParentNode(appName, globalEnv.rawParentNodeDesc, true)
4564
- });
4565
- }
4566
- }
4567
4638
  }
4568
4639
  return node;
4569
4640
  }
4570
- /**
4571
- * patch iframe node parentNode
4572
- * Scenes:
4573
- * 1. iframe common node: patch Node.prototype.parentNode to hijack parentNode
4574
- * 2. iframe HTML built-in node: belongs to base app, we should rewrite parentNode for every built-in node
4575
- * NOTE:
4576
- * 1. HTML built-in node parentNode cannot point to raw body, otherwise Vue2 will render failed
4577
- * @param appName app name
4578
- * @param parentNode parentNode Descriptor of iframe or browser
4579
- * @param HTMLBuildInNode is HTML built-in node
4580
- */
4581
- function createGetterForIframeParentNode(appName, parentNodeDesc, HTMLBuildInNode) {
4582
- return function () {
4583
- var _a, _b, _c;
4584
- /**
4585
- * set current appName for hijack parentNode of html
4586
- * NOTE:
4587
- * 1. Is there a problem with setting the current appName in iframe mode
4588
- */
4589
- throttleDeferForSetAppName(appName);
4590
- const result = parentNodeDesc.get.call(this);
4591
- /**
4592
- * If parentNode is <micro-app-body>, return rawDocument.body
4593
- * Scenes:
4594
- * 1. element-ui@2/lib/utils/vue-popper.js
4595
- * if (this.popperElm.parentNode === document.body) ...
4596
- * WARNING:
4597
- * Will it cause other problems ?
4598
- * e.g. target.parentNode.remove(target)
4599
- */
4600
- if (!HTMLBuildInNode &&
4601
- isMicroAppBody(result) && ((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container)) {
4602
- return ((_c = (_b = microApp.options).getRootElementParentNode) === null || _c === void 0 ? void 0 : _c.call(_b, this, appName)) || globalEnv.rawDocument.body;
4603
- }
4604
- return result;
4605
- };
4606
- }
4607
4641
 
4608
4642
  /**
4609
4643
  * https://developer.mozilla.org/en-US/docs/Web/API/fetch
@@ -4707,23 +4741,12 @@ function useMicroEventSource() {
4707
4741
  }
4708
4742
 
4709
4743
  const { createMicroEventSource, clearMicroEventSource } = useMicroEventSource();
4710
- class WithSandBox {
4744
+ class WithSandBox extends BaseSandbox {
4711
4745
  constructor(appName, url) {
4746
+ super();
4712
4747
  this.active = false;
4713
- /**
4714
- * Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
4715
- * Fix https://github.com/micro-zoe/micro-app/issues/234
4716
- */
4717
- this.scopeProperties = [];
4718
- // Properties that can be escape to rawWindow
4719
- this.escapeProperties = [];
4720
- // Properties escape to rawWindow, cleared when unmount
4721
- this.escapeKeys = new Set();
4722
- // Properties newly added to microAppWindow
4723
- this.injectedKeys = new Set();
4724
- this.microAppWindow = new EventTarget(); // Proxy target
4748
+ this.microAppWindow = new CustomWindow(); // Proxy target
4725
4749
  this.patchWith((resolve) => {
4726
- this.adapter = new Adapter();
4727
4750
  // get scopeProperties and escapeProperties from plugins
4728
4751
  this.getSpecialProperties(appName);
4729
4752
  // create location, history for child app
@@ -4732,6 +4755,8 @@ class WithSandBox {
4732
4755
  this.windowEffect = patchWindow(appName, this.microAppWindow, this);
4733
4756
  // patch document of child app
4734
4757
  this.documentEffect = patchDocument(appName, this.microAppWindow, this);
4758
+ // properties associated with the native window
4759
+ this.setMappingPropertiesWithRawDescriptor(this.microAppWindow);
4735
4760
  // inject global properties
4736
4761
  this.initStaticGlobalKeys(appName, url, this.microAppWindow);
4737
4762
  resolve();
@@ -4820,6 +4845,7 @@ class WithSandBox {
4820
4845
  }
4821
4846
  /**
4822
4847
  * inject global properties to microAppWindow
4848
+ * TODO: 设置为只读变量
4823
4849
  * @param appName app name
4824
4850
  * @param url app url
4825
4851
  * @param microAppWindow micro window
@@ -4833,6 +4859,7 @@ class WithSandBox {
4833
4859
  microAppWindow.__MICRO_APP_WINDOW__ = microAppWindow;
4834
4860
  microAppWindow.__MICRO_APP_PRE_RENDER__ = false;
4835
4861
  microAppWindow.__MICRO_APP_UMD_MODE__ = false;
4862
+ microAppWindow.__MICRO_APP_PROXY_WINDOW__ = this.proxyWindow;
4836
4863
  microAppWindow.__MICRO_APP_SANDBOX__ = this;
4837
4864
  microAppWindow.__MICRO_APP_SANDBOX_TYPE__ = 'with';
4838
4865
  microAppWindow.rawWindow = globalEnv.rawWindow;
@@ -4842,7 +4869,6 @@ class WithSandBox {
4842
4869
  pureCreateElement,
4843
4870
  router,
4844
4871
  });
4845
- this.setMappingPropertiesWithRawDescriptor(microAppWindow);
4846
4872
  }
4847
4873
  /**
4848
4874
  * Record global effect and then release (effect: global event, timeout, data listener)
@@ -4925,7 +4951,6 @@ class WithSandBox {
4925
4951
  */
4926
4952
  getSpecialProperties(appName) {
4927
4953
  var _a;
4928
- this.scopeProperties = this.scopeProperties.concat(this.adapter.staticScopeProperties);
4929
4954
  if (isPlainObject(microApp.options.plugins)) {
4930
4955
  this.commonActionForSpecialProperties(microApp.options.plugins.global);
4931
4956
  this.commonActionForSpecialProperties((_a = microApp.options.plugins.modules) === null || _a === void 0 ? void 0 : _a[appName]);
@@ -4967,9 +4992,10 @@ class WithSandBox {
4967
4992
  topValue = rawWindow.top;
4968
4993
  parentValue = rawWindow.parent;
4969
4994
  }
4970
- // TODO: 用rawDefineProperties
4971
- rawDefineProperty(microAppWindow, 'top', this.createDescriptorForMicroAppWindow('top', topValue));
4972
- rawDefineProperty(microAppWindow, 'parent', this.createDescriptorForMicroAppWindow('parent', parentValue));
4995
+ rawDefineProperties(microAppWindow, {
4996
+ top: this.createDescriptorForMicroAppWindow('top', topValue),
4997
+ parent: this.createDescriptorForMicroAppWindow('parent', parentValue),
4998
+ });
4973
4999
  GLOBAL_KEY_TO_WINDOW.forEach((key) => {
4974
5000
  rawDefineProperty(microAppWindow, key, this.createDescriptorForMicroAppWindow(key, this.proxyWindow));
4975
5001
  });
@@ -5134,13 +5160,29 @@ class WithSandBox {
5134
5160
  WithSandBox.activeCount = 0; // number of active sandbox
5135
5161
 
5136
5162
  function patchRouter(appName, url, microAppWindow, browserHost) {
5137
- const childStaticLocation = new URL(url);
5163
+ const rawHistory = globalEnv.rawWindow.history;
5164
+ const childStaticLocation = createURL(url);
5138
5165
  const childHost = childStaticLocation.protocol + '//' + childStaticLocation.host;
5139
5166
  const childFullPath = childStaticLocation.pathname + childStaticLocation.search + childStaticLocation.hash;
5140
5167
  // rewrite microAppWindow.history
5141
5168
  const microHistory = microAppWindow.history;
5169
+ // save history.replaceState, it will be used in updateMicroLocation
5142
5170
  microAppWindow.rawReplaceState = microHistory.replaceState;
5171
+ // rewrite microAppWindow.history
5143
5172
  assign(microHistory, createMicroHistory(appName, microAppWindow.location));
5173
+ // scrollRestoration proxy to rawHistory
5174
+ rawDefineProperties(microHistory, {
5175
+ scrollRestoration: {
5176
+ configurable: true,
5177
+ enumerable: true,
5178
+ get() {
5179
+ return rawHistory.scrollRestoration;
5180
+ },
5181
+ set(value) {
5182
+ rawHistory.scrollRestoration = value;
5183
+ }
5184
+ }
5185
+ });
5144
5186
  /**
5145
5187
  * Init microLocation before exec sandbox.start
5146
5188
  * NOTE:
@@ -5152,6 +5194,87 @@ function patchRouter(appName, url, microAppWindow, browserHost) {
5152
5194
  return createMicroLocation(appName, url, microAppWindow, childStaticLocation, browserHost, childHost);
5153
5195
  }
5154
5196
 
5197
+ const escape2RawWindowKeys = [
5198
+ 'getComputedStyle',
5199
+ 'visualViewport',
5200
+ 'matchMedia',
5201
+ // 'DOMParser',
5202
+ 'ResizeObserver',
5203
+ 'IntersectionObserver',
5204
+ ];
5205
+ const escape2RawWindowRegExpKeys = [
5206
+ /animationFrame$/i,
5207
+ /mutationObserver$/i,
5208
+ /height$|width$/i,
5209
+ /offset$/i,
5210
+ // /event$/i,
5211
+ /selection$/i,
5212
+ /^range/i,
5213
+ /^screen/i,
5214
+ /^scroll/i,
5215
+ /X$|Y$/,
5216
+ ];
5217
+ const uniqueDocumentElement = [
5218
+ 'body',
5219
+ 'head',
5220
+ 'html',
5221
+ 'title',
5222
+ ];
5223
+ // 有shadowRoot则代理到shadowRoot否则代理到原生document上 (属性)
5224
+ const proxy2RawDocOrShadowKeys = [
5225
+ 'childElementCount',
5226
+ 'children',
5227
+ 'firstElementChild',
5228
+ 'firstChild',
5229
+ 'lastElementChild',
5230
+ 'activeElement',
5231
+ 'fullscreenElement',
5232
+ 'pictureInPictureElement',
5233
+ 'pointerLockElement',
5234
+ 'styleSheets',
5235
+ ];
5236
+ // 有shadowRoot则代理到shadowRoot否则代理到原生document上 (方法)
5237
+ const proxy2RawDocOrShadowMethods = [
5238
+ 'append',
5239
+ 'contains',
5240
+ 'replaceChildren',
5241
+ 'createRange',
5242
+ 'getSelection',
5243
+ 'elementFromPoint',
5244
+ 'elementsFromPoint',
5245
+ 'getAnimations',
5246
+ ];
5247
+ // 直接代理到原生document上 (属性)
5248
+ const proxy2RawDocumentKeys = [
5249
+ 'characterSet',
5250
+ 'compatMode',
5251
+ 'contentType',
5252
+ 'designMode',
5253
+ 'dir',
5254
+ 'doctype',
5255
+ 'embeds',
5256
+ 'fullscreenEnabled',
5257
+ 'hidden',
5258
+ 'implementation',
5259
+ 'lastModified',
5260
+ 'pictureInPictureEnabled',
5261
+ 'plugins',
5262
+ 'readyState',
5263
+ 'referrer',
5264
+ 'visibilityState',
5265
+ 'fonts',
5266
+ ];
5267
+ // 直接代理到原生document上 (方法)
5268
+ const proxy2RawDocumentMethods = [
5269
+ 'execCommand',
5270
+ 'createRange',
5271
+ 'exitFullscreen',
5272
+ 'exitPictureInPicture',
5273
+ 'getElementsByTagNameNS',
5274
+ 'hasFocus',
5275
+ 'prepend',
5276
+ ];
5277
+
5155
5278
  /**
5156
5279
  * patch window of child app
5157
5280
  * @param appName app name
@@ -5199,7 +5322,7 @@ function patchWindowProperty$1(appName, microAppWindow) {
5199
5322
  }
5200
5323
  return false;
5201
5324
  });
5202
- return /^on/.test(key) && !SCOPE_WINDOW_ON_EVENT.includes(key);
5325
+ return /^on/.test(key) && !SCOPE_WINDOW_ON_EVENT_OF_IFRAME.includes(key);
5203
5326
  })
5204
5327
  .forEach((eventName) => {
5205
5328
  const { enumerable, writable, set } = Object.getOwnPropertyDescriptor(microAppWindow, eventName) || {
@@ -5227,18 +5350,35 @@ function patchWindowProperty$1(appName, microAppWindow) {
5227
5350
  */
5228
5351
  function createProxyWindow$1(microAppWindow, sandbox) {
5229
5352
  const rawWindow = globalEnv.rawWindow;
5230
- const customProperties = [];
5353
+ const customProperties = new Set();
5354
+ /**
5355
+ * proxyWindow will only take effect in certain scenes, such as window.key
5356
+ * e.g:
5357
+ * 1. window.key in normal app --> fall into proxyWindow
5358
+ * 2. window.key in module app(vite), fall into microAppWindow(iframeWindow)
5359
+ * 3. if (key)... --> fall into microAppWindow(iframeWindow)
5360
+ */
5231
5361
  const proxyWindow = new Proxy(microAppWindow, {
5232
5362
  get: (target, key) => {
5233
5363
  if (key === 'location') {
5234
5364
  return sandbox.proxyLocation;
5235
5365
  }
5236
- if (GLOBAL_KEY_TO_WINDOW.includes(key.toString())) {
5366
+ if (includes(GLOBAL_KEY_TO_WINDOW, key)) {
5237
5367
  return proxyWindow;
5238
5368
  }
5239
- if (customProperties.includes(key)) {
5369
+ if (customProperties.has(key)) {
5240
5370
  return Reflect.get(target, key);
5241
5371
  }
5372
+ /**
5373
+ * Same as proxyWindow, escapeProperties will only take effect in certain scenes
5374
+ * e.g:
5375
+ * 1. window.key in normal app --> fall into proxyWindow, escapeProperties will effect
5376
+ * 2. window.key in module app(vite), fall into microAppWindow(iframeWindow), escapeProperties will not take effect
5377
+ * 3. if (key)... --> fall into microAppWindow(iframeWindow), escapeProperties will not take effect
5378
+ */
5379
+ if (includes(sandbox.escapeProperties, key) && !Reflect.has(target, key)) {
5380
+ return bindFunctionToRawTarget(Reflect.get(rawWindow, key), rawWindow);
5381
+ }
5242
5382
  return bindFunctionToRawTarget(Reflect.get(target, key), target);
5243
5383
  },
5244
5384
  set: (target, key, value) => {
@@ -5246,10 +5386,10 @@ function createProxyWindow$1(microAppWindow, sandbox) {
5246
5386
  return Reflect.set(rawWindow, key, value);
5247
5387
  }
5248
5388
  if (!Reflect.has(target, key)) {
5249
- customProperties.push(key);
5389
+ customProperties.add(key);
5250
5390
  }
5251
5391
  Reflect.set(target, key, value);
5252
- if (sandbox.escapeProperties.includes(key)) {
5392
+ if (includes(sandbox.escapeProperties, key)) {
5253
5393
  !Reflect.has(rawWindow, key) && sandbox.escapeKeys.add(key);
5254
5394
  Reflect.set(rawWindow, key, value);
5255
5395
  }
@@ -5271,7 +5411,7 @@ function patchWindowEffect$1(microAppWindow) {
5271
5411
  const eventListenerMap = new Map();
5272
5412
  const sstEventListenerMap = new Map();
5273
5413
  function getEventTarget(type) {
5274
- return SCOPE_WINDOW_EVENT.includes(type) ? microAppWindow : rawWindow;
5414
+ return SCOPE_WINDOW_EVENT_OF_IFRAME.includes(type) ? microAppWindow : rawWindow;
5275
5415
  }
5276
5416
  // TODO: listener 是否需要绑定microAppWindow,否则函数中的this指向原生window
5277
5417
  microAppWindow.addEventListener = function (type, listener, options) {
@@ -5364,7 +5504,9 @@ function patchDocumentPrototype(appName, microAppWindow) {
5364
5504
  const microRootDocument = microAppWindow.Document;
5365
5505
  const microDocument = microAppWindow.document;
5366
5506
  const rawMicroCreateElement = microRootDocument.prototype.createElement;
5507
+ const rawMicroCreateElementNS = microRootDocument.prototype.createElementNS;
5367
5508
  const rawMicroCreateTextNode = microRootDocument.prototype.createTextNode;
5509
+ const rawMicroCreateDocumentFragment = microRootDocument.prototype.createDocumentFragment;
5368
5510
  const rawMicroCreateComment = microRootDocument.prototype.createComment;
5369
5511
  const rawMicroQuerySelector = microRootDocument.prototype.querySelector;
5370
5512
  const rawMicroQuerySelectorAll = microRootDocument.prototype.querySelectorAll;
@@ -5385,10 +5527,18 @@ function patchDocumentPrototype(appName, microAppWindow) {
5385
5527
  const element = rawMicroCreateElement.call(this, tagName, options);
5386
5528
  return updateElementInfo(element, appName);
5387
5529
  };
5530
+ microRootDocument.prototype.createElementNS = function createElementNS(namespaceURI, name, options) {
5531
+ const element = rawMicroCreateElementNS.call(this, namespaceURI, name, options);
5532
+ return updateElementInfo(element, appName);
5533
+ };
5388
5534
  microRootDocument.prototype.createTextNode = function createTextNode(data) {
5389
5535
  const element = rawMicroCreateTextNode.call(this, data);
5390
5536
  return updateElementInfo(element, appName);
5391
5537
  };
5538
+ microRootDocument.prototype.createDocumentFragment = function createDocumentFragment() {
5539
+ const element = rawMicroCreateDocumentFragment.call(this);
5540
+ return updateElementInfo(element, appName);
5541
+ };
5392
5542
  microRootDocument.prototype.createComment = function createComment(data) {
5393
5543
  const element = rawMicroCreateComment.call(this, data);
5394
5544
  return updateElementInfo(element, appName);
@@ -5489,6 +5639,7 @@ function patchDocumentProperty(appName, microAppWindow, sandbox) {
5489
5639
  const createDescriptors = () => {
5490
5640
  const result = {};
5491
5641
  const descList = [
5642
+ // if disable-memory-router or router-mode='disable', href point to base app
5492
5643
  ['documentURI', () => sandbox.proxyLocation.href],
5493
5644
  ['URL', () => sandbox.proxyLocation.href],
5494
5645
  ['documentElement', () => rawDocument.documentElement],
@@ -5722,6 +5873,7 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5722
5873
  // return rootNode
5723
5874
  };
5724
5875
  microRootNode.prototype.appendChild = function appendChild(node) {
5876
+ // TODO: 有必要执行这么多次updateElementInfo?
5725
5877
  updateElementInfo(node, appName);
5726
5878
  if (isPureNode(node)) {
5727
5879
  return rawMicroAppendChild.call(this, node);
@@ -5797,7 +5949,7 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5797
5949
  configurable: true,
5798
5950
  enumerable: true,
5799
5951
  get() {
5800
- return this.__PURE_ELEMENT__
5952
+ return this.__PURE_ELEMENT__ || this === microDocument
5801
5953
  ? rawOwnerDocumentDesc.get.call(this)
5802
5954
  : microDocument;
5803
5955
  },
@@ -5821,7 +5973,29 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5821
5973
  rawDefineProperty(microRootNode.prototype, 'parentNode', {
5822
5974
  configurable: true,
5823
5975
  enumerable: true,
5824
- get: createGetterForIframeParentNode(appName, rawParentNodeDesc),
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
+ }
5825
5999
  });
5826
6000
  // Adapt to new image(...) scene
5827
6001
  const ImageProxy = new Proxy(microAppWindow.Image, {
@@ -5883,14 +6057,17 @@ class IframeSandbox {
5883
6057
  this.escapeProperties = [];
5884
6058
  // Properties escape to rawWindow, cleared when unmount
5885
6059
  this.escapeKeys = new Set();
5886
- // TODO: 初始化和每次跳转时都要更新base的href
6060
+ // 初始化和每次跳转时都要更新base的href
5887
6061
  this.updateIframeBase = () => {
5888
6062
  var _a;
5889
- (_a = this.baseElement) === null || _a === void 0 ? void 0 : _a.setAttribute('href', this.proxyLocation.protocol + '//' + this.proxyLocation.host + this.proxyLocation.pathname);
6063
+ // origin must be child app origin
6064
+ (_a = this.baseElement) === null || _a === void 0 ? void 0 : _a.setAttribute('href', createURL(this.url).origin + this.proxyLocation.pathname);
5890
6065
  };
6066
+ this.appName = appName;
6067
+ this.url = url;
5891
6068
  const rawLocation = globalEnv.rawWindow.location;
5892
6069
  const browserHost = rawLocation.protocol + '//' + rawLocation.host;
5893
- this.deleteIframeElement = this.createIframeElement(appName, browserHost);
6070
+ this.deleteIframeElement = this.createIframeElement(appName, browserHost + rawLocation.pathname);
5894
6071
  this.microAppWindow = this.iframe.contentWindow;
5895
6072
  this.patchIframe(this.microAppWindow, (resolve) => {
5896
6073
  // create new html to iframe
@@ -5918,13 +6095,13 @@ class IframeSandbox {
5918
6095
  /**
5919
6096
  * create iframe for sandbox
5920
6097
  * @param appName app name
5921
- * @param browserHost browser origin
6098
+ * @param browserPath browser origin
5922
6099
  * @returns release callback
5923
6100
  */
5924
- createIframeElement(appName, browserHost) {
6101
+ createIframeElement(appName, browserPath) {
5925
6102
  this.iframe = pureCreateElement('iframe');
5926
6103
  const iframeAttrs = {
5927
- src: browserHost,
6104
+ src: microApp.options.iframeSrc || browserPath,
5928
6105
  style: 'display: none',
5929
6106
  id: appName,
5930
6107
  };
@@ -6019,6 +6196,7 @@ class IframeSandbox {
6019
6196
  * NOTE:
6020
6197
  * 1. execute as early as possible
6021
6198
  * 2. run after patchRouter & createProxyWindow
6199
+ * TODO: 设置为只读变量
6022
6200
  */
6023
6201
  initStaticGlobalKeys(appName, url, microAppWindow) {
6024
6202
  microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
@@ -6220,6 +6398,7 @@ class IframeSandbox {
6220
6398
  patchElementTree(container, this.microAppWindow.__MICRO_APP_NAME__);
6221
6399
  }
6222
6400
  /**
6401
+ * action before exec scripts when mount
6223
6402
  * Actions:
6224
6403
  * 1. patch static elements from html
6225
6404
  * @param container micro app container
@@ -6279,8 +6458,11 @@ class CreateApp {
6279
6458
  }
6280
6459
  /**
6281
6460
  * When resource is loaded, mount app if it is not prefetch or unmount
6461
+ * defaultPage disablePatchRequest routerMode baseroute is only for prerender app
6282
6462
  */
6283
- onLoad(html, defaultPage, disablePatchRequest, routerMode, baseroute) {
6463
+ onLoad({ html,
6464
+ // below params is only for prerender app
6465
+ defaultPage, routerMode, baseroute, disablePatchRequest, }) {
6284
6466
  var _a;
6285
6467
  if (++this.loadSourceLevel === 2) {
6286
6468
  this.source.html = html;
@@ -6311,11 +6493,11 @@ class CreateApp {
6311
6493
  this.mount({
6312
6494
  container,
6313
6495
  inline: this.inline,
6314
- routerMode: routerMode,
6315
- baseroute: baseroute || '',
6316
6496
  fiber: true,
6317
6497
  defaultPage: defaultPage || '',
6318
6498
  disablePatchRequest: disablePatchRequest !== null && disablePatchRequest !== void 0 ? disablePatchRequest : false,
6499
+ routerMode: routerMode,
6500
+ baseroute: baseroute || '',
6319
6501
  });
6320
6502
  }
6321
6503
  }
@@ -6353,6 +6535,7 @@ class CreateApp {
6353
6535
  // mount before prerender exec mount (loading source), set isPrerender to false
6354
6536
  this.isPrerender = false;
6355
6537
  // dispatch state event to micro app
6538
+ // TODO: statechange 还是 state-change,保持一致
6356
6539
  dispatchCustomEventToMicroApp(this, 'statechange', {
6357
6540
  appState: appStates.LOADING
6358
6541
  });
@@ -6376,6 +6559,14 @@ class CreateApp {
6376
6559
  if (this.isPrerender &&
6377
6560
  isDivElement(this.container) &&
6378
6561
  this.container.hasAttribute('prerender')) {
6562
+ /**
6563
+ * current this.container is <div prerender='true'></div>
6564
+ * set this.container to <micro-app></micro-app>
6565
+ * NOTE:
6566
+ * 1. must exec before this.sandBox.rebuildEffectSnapshot
6567
+ * 2. must exec before this.preRenderEvents?.forEach((cb) => cb())
6568
+ */
6569
+ this.container = this.cloneContainer(container, this.container, false);
6379
6570
  /**
6380
6571
  * rebuild effect event of window, document, data center
6381
6572
  * explain:
@@ -6384,14 +6575,6 @@ class CreateApp {
6384
6575
  * 3. rebuild after js exec end, normal recovery effect event
6385
6576
  */
6386
6577
  (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.rebuildEffectSnapshot();
6387
- // current this.container is <div prerender='true'></div>
6388
- cloneContainer(container, this.container, false);
6389
- /**
6390
- * set this.container to <micro-app></micro-app>
6391
- * NOTE:
6392
- * must exec before this.preRenderEvents?.forEach((cb) => cb())
6393
- */
6394
- this.container = container;
6395
6578
  (_b = this.preRenderEvents) === null || _b === void 0 ? void 0 : _b.forEach((cb) => cb());
6396
6579
  // reset isPrerender config
6397
6580
  this.isPrerender = false;
@@ -6421,7 +6604,7 @@ class CreateApp {
6421
6604
  appState: appStates.MOUNTING
6422
6605
  });
6423
6606
  // TODO: 将所有cloneContainer中的'as Element'去掉,兼容shadowRoot的场景
6424
- cloneContainer(this.container, this.source.html, !this.umdMode);
6607
+ this.cloneContainer(this.container, this.source.html, !this.umdMode);
6425
6608
  (_e = this.sandBox) === null || _e === void 0 ? void 0 : _e.start({
6426
6609
  umdMode: this.umdMode,
6427
6610
  baseroute,
@@ -6439,6 +6622,7 @@ class CreateApp {
6439
6622
  * umdHookUnmount can works in default mode
6440
6623
  * register through window.unmount
6441
6624
  */
6625
+ // TODO: 不对,这里要改,因为unmount不一定是函数
6442
6626
  this.umdHookUnmount = unmount;
6443
6627
  // if mount & unmount is function, the sub app is umd mode
6444
6628
  if (isFunction(mount) && isFunction(unmount)) {
@@ -6609,7 +6793,7 @@ class CreateApp {
6609
6793
  actionsForUnmount({ destroy, clearData, keepRouteState, unmountcb, }) {
6610
6794
  var _a;
6611
6795
  if (this.umdMode && this.container && !destroy) {
6612
- cloneContainer(this.source.html, this.container, false);
6796
+ this.cloneContainer(this.source.html, this.container, false);
6613
6797
  }
6614
6798
  /**
6615
6799
  * this.container maybe contains micro-app element, stop sandbox should exec after cloneContainer
@@ -6636,6 +6820,7 @@ class CreateApp {
6636
6820
  this.preRenderEvents = null;
6637
6821
  this.setKeepAliveState(null);
6638
6822
  // in iframe sandbox & default mode, delete the sandbox & iframeElement
6823
+ // TODO: with沙箱与iframe沙箱保持一致:with沙箱默认模式下删除 或者 iframe沙箱umd模式下保留
6639
6824
  if (this.iframe && !this.umdMode)
6640
6825
  this.sandBox = null;
6641
6826
  if (destroy)
@@ -6667,7 +6852,7 @@ class CreateApp {
6667
6852
  this.setLifeCycleState(lifeCycles.AFTERHIDDEN);
6668
6853
  // dispatch afterHidden event to base app
6669
6854
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.AFTERHIDDEN);
6670
- if (this.routerMode !== ROUTER_MODE_CUSTOM) {
6855
+ if (isRouterModeSearch(this.name)) {
6671
6856
  // called after lifeCyclesEvent
6672
6857
  (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.removeRouteInfoForKeepAliveApp();
6673
6858
  }
@@ -6678,7 +6863,7 @@ class CreateApp {
6678
6863
  getRootContainer(this.container).unmount();
6679
6864
  }
6680
6865
  else {
6681
- this.container = cloneContainer(pureCreateElement('div'), this.container, false);
6866
+ this.container = this.cloneContainer(pureCreateElement('div'), this.container, false);
6682
6867
  (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.recordAndReleaseEffect({ keepAlive: true });
6683
6868
  }
6684
6869
  callback === null || callback === void 0 ? void 0 : callback();
@@ -6686,6 +6871,14 @@ class CreateApp {
6686
6871
  // show app when connectedCallback called with keep-alive
6687
6872
  showKeepAliveApp(container) {
6688
6873
  var _a, _b;
6874
+ /**
6875
+ * NOTE:
6876
+ * 1. this.container must set to container(micro-app element) before exec rebuildEffectSnapshot
6877
+ * ISSUE: https://github.com/micro-zoe/micro-app/issues/1115
6878
+ * 2. rebuildEffectSnapshot must exec before dispatch beforeshow event
6879
+ */
6880
+ const oldContainer = this.container;
6881
+ this.container = container;
6689
6882
  (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.rebuildEffectSnapshot();
6690
6883
  // dispatch beforeShow event to micro-app
6691
6884
  dispatchCustomEventToMicroApp(this, 'appstate-change', {
@@ -6694,13 +6887,13 @@ class CreateApp {
6694
6887
  // dispatch beforeShow event to base app
6695
6888
  dispatchLifecyclesEvent(container, this.name, lifeCycles.BEFORESHOW);
6696
6889
  this.setKeepAliveState(keepAliveStates.KEEP_ALIVE_SHOW);
6697
- this.container = cloneContainer(container, this.container, false);
6890
+ this.cloneContainer(this.container, oldContainer, false);
6698
6891
  /**
6699
6892
  * TODO:
6700
6893
  * 问题:当路由模式为custom时,keep-alive应用在重新展示,是否需要根据子应用location信息更新浏览器地址?
6701
6894
  * 暂时不这么做吧,因为无法确定二次展示时新旧地址是否相同,是否带有特殊信息
6702
6895
  */
6703
- if (this.routerMode !== ROUTER_MODE_CUSTOM) {
6896
+ if (isRouterModeSearch(this.name)) {
6704
6897
  // called before lifeCyclesEvent
6705
6898
  (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.setRouteInfoForKeepAliveApp();
6706
6899
  }
@@ -6724,6 +6917,34 @@ class CreateApp {
6724
6917
  });
6725
6918
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.ERROR, e);
6726
6919
  }
6920
+ /**
6921
+ * Parse htmlString to DOM
6922
+ * NOTE: iframe sandbox will use DOMParser of iframeWindow, with sandbox will use DOMParser of base app
6923
+ * @param htmlString DOMString
6924
+ * @returns parsed DOM
6925
+ */
6926
+ parseHtmlString(htmlString) {
6927
+ var _a;
6928
+ const DOMParser = ((_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow) ? this.sandBox.proxyWindow.DOMParser
6929
+ : globalEnv.rawWindow.DOMParser;
6930
+ return (new DOMParser()).parseFromString(htmlString, 'text/html').body;
6931
+ }
6932
+ /**
6933
+ * clone origin elements to target
6934
+ * @param origin Cloned element
6935
+ * @param target Accept cloned elements
6936
+ * @param deep deep clone or transfer dom
6937
+ */
6938
+ cloneContainer(target, origin, deep) {
6939
+ // 在基座接受到afterhidden方法后立即执行unmount,彻底destroy应用时,因为unmount时同步执行,所以this.container为null后才执行cloneContainer
6940
+ if (origin && target) {
6941
+ target.innerHTML = '';
6942
+ Array.from(deep ? this.parseHtmlString(origin.innerHTML).childNodes : origin.childNodes).forEach((node) => {
6943
+ target.appendChild(node);
6944
+ });
6945
+ }
6946
+ return target;
6947
+ }
6727
6948
  /**
6728
6949
  * Scene:
6729
6950
  * 1. create app
@@ -6732,12 +6953,7 @@ class CreateApp {
6732
6953
  */
6733
6954
  createSandbox() {
6734
6955
  if (this.useSandbox && !this.sandBox) {
6735
- if (this.iframe) {
6736
- this.sandBox = new IframeSandbox(this.name, this.url);
6737
- }
6738
- else {
6739
- this.sandBox = new WithSandBox(this.name, this.url);
6740
- }
6956
+ this.sandBox = this.iframe ? new IframeSandbox(this.name, this.url) : new WithSandBox(this.name, this.url);
6741
6957
  }
6742
6958
  }
6743
6959
  // set app state
@@ -6902,10 +7118,6 @@ function handleNewNode(child, app) {
6902
7118
  */
6903
7119
  function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
6904
7120
  const hijackParent = getHijackParent(parent, targetChild, app);
6905
- /**
6906
- * If passiveChild is not the child node, insertBefore replaceChild will have a problem, at this time, it will be degraded to appendChild
6907
- * E.g: document.head.insertBefore(targetChild, document.head.childNodes[0])
6908
- */
6909
7121
  if (hijackParent) {
6910
7122
  /**
6911
7123
  * If parentNode is <micro-app-body>, return rawDocument.body
@@ -6944,11 +7156,28 @@ function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild
6944
7156
  });
6945
7157
  }
6946
7158
  }
7159
+ if ((process.env.NODE_ENV !== 'production') &&
7160
+ isIFrameElement(targetChild) &&
7161
+ rawMethod === globalEnv.rawAppendChild) {
7162
+ fixReactHMRConflict(app);
7163
+ }
6947
7164
  /**
6948
7165
  * 1. If passiveChild exists, it must be insertBefore or replaceChild
6949
7166
  * 2. When removeChild, targetChild may not be in microAppHead or head
7167
+ * 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])
7172
+ * ISSUE: https://github.com/micro-zoe/micro-app/issues/1071
6950
7173
  */
6951
7174
  if (passiveChild && !hijackParent.contains(passiveChild)) {
7175
+ if (rawMethod === globalEnv.rawInsertBefore && parent.contains(passiveChild)) {
7176
+ const indexOfParent = Array.from(parent.childNodes).indexOf(passiveChild);
7177
+ if (hijackParent.childNodes[indexOfParent]) {
7178
+ return invokeRawMethod(rawMethod, hijackParent, targetChild, hijackParent.childNodes[indexOfParent]);
7179
+ }
7180
+ }
6952
7181
  return globalEnv.rawAppendChild.call(hijackParent, targetChild);
6953
7182
  }
6954
7183
  else if (rawMethod === globalEnv.rawRemoveChild && !hijackParent.contains(targetChild)) {
@@ -6957,11 +7186,6 @@ function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild
6957
7186
  }
6958
7187
  return targetChild;
6959
7188
  }
6960
- if ((process.env.NODE_ENV !== 'production') &&
6961
- isIFrameElement(targetChild) &&
6962
- rawMethod === globalEnv.rawAppendChild) {
6963
- fixReactHMRConflict(app);
6964
- }
6965
7189
  return invokeRawMethod(rawMethod, hijackParent, targetChild, passiveChild);
6966
7190
  }
6967
7191
  return invokeRawMethod(rawMethod, parent, targetChild, passiveChild);
@@ -7031,6 +7255,11 @@ function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
7031
7255
  currentAppName)) {
7032
7256
  newChild.__MICRO_APP_NAME__ = newChild.__MICRO_APP_NAME__ || currentAppName;
7033
7257
  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
+ }
7034
7263
  if (app === null || app === void 0 ? void 0 : app.container) {
7035
7264
  completePathDynamic(app, newChild);
7036
7265
  return invokePrototypeMethod(app, rawMethod, parent, handleNewNode(newChild, app), passiveChild && getMappingNode(passiveChild));
@@ -7117,30 +7346,34 @@ function patchElementAndDocument() {
7117
7346
  this.__MICRO_APP_NAME__ && (clonedNode.__MICRO_APP_NAME__ = this.__MICRO_APP_NAME__);
7118
7347
  return clonedNode;
7119
7348
  };
7120
- function getQueryTarget(node) {
7349
+ /**
7350
+ * document.body(head).querySelector(querySelectorAll) hijack to microAppBody(microAppHead).querySelector(querySelectorAll)
7351
+ * NOTE:
7352
+ * 1. May cause some problems!
7353
+ * 2. Add config options?
7354
+ */
7355
+ function getQueryTarget(target) {
7121
7356
  const currentAppName = getCurrentAppName();
7122
- if ((node === document.body || node === document.head) && currentAppName) {
7357
+ if ((target === document.body || target === document.head) && currentAppName) {
7123
7358
  const app = appInstanceMap.get(currentAppName);
7124
7359
  if (app === null || app === void 0 ? void 0 : app.container) {
7125
- if (node === document.body) {
7360
+ if (target === document.body) {
7126
7361
  return app.querySelector('micro-app-body');
7127
7362
  }
7128
- else if (node === document.head) {
7363
+ else if (target === document.head) {
7129
7364
  return app.querySelector('micro-app-head');
7130
7365
  }
7131
7366
  }
7132
7367
  }
7133
- return null;
7368
+ return target;
7134
7369
  }
7135
7370
  rawRootElement.prototype.querySelector = function querySelector(selectors) {
7136
7371
  var _a;
7137
- const target = (_a = getQueryTarget(this)) !== null && _a !== void 0 ? _a : this;
7138
- return globalEnv.rawElementQuerySelector.call(target, selectors);
7372
+ return globalEnv.rawElementQuerySelector.call((_a = getQueryTarget(this)) !== null && _a !== void 0 ? _a : this, selectors);
7139
7373
  };
7140
7374
  rawRootElement.prototype.querySelectorAll = function querySelectorAll(selectors) {
7141
7375
  var _a;
7142
- const target = (_a = getQueryTarget(this)) !== null && _a !== void 0 ? _a : this;
7143
- return globalEnv.rawElementQuerySelectorAll.call(target, selectors);
7376
+ return globalEnv.rawElementQuerySelectorAll.call((_a = getQueryTarget(this)) !== null && _a !== void 0 ? _a : this, selectors);
7144
7377
  };
7145
7378
  // rewrite setAttribute, complete resource address
7146
7379
  rawRootElement.prototype.setAttribute = function setAttribute(key, value) {
@@ -7268,14 +7501,15 @@ function patchDocument$2() {
7268
7501
  const element = globalEnv.rawCreateElementNS.call(getBindTarget(this), namespaceURI, name, options);
7269
7502
  return markElement(element);
7270
7503
  };
7271
- rawRootDocument.prototype.createDocumentFragment = function createDocumentFragment() {
7272
- const element = globalEnv.rawCreateDocumentFragment.call(getBindTarget(this));
7273
- return markElement(element);
7274
- };
7504
+ // TODO: 放开
7275
7505
  // rawRootDocument.prototype.createTextNode = function createTextNode (data: string): Text {
7276
7506
  // const element = globalEnv.rawCreateTextNode.call(getBindTarget(this), data)
7277
7507
  // return markElement(element)
7278
7508
  // }
7509
+ rawRootDocument.prototype.createDocumentFragment = function createDocumentFragment() {
7510
+ const element = globalEnv.rawCreateDocumentFragment.call(getBindTarget(this));
7511
+ return markElement(element);
7512
+ };
7279
7513
  rawRootDocument.prototype.createComment = function createComment(data) {
7280
7514
  const element = globalEnv.rawCreateComment.call(getBindTarget(this), data);
7281
7515
  return markElement(element);
@@ -7288,7 +7522,7 @@ function patchDocument$2() {
7288
7522
  if (!currentAppName ||
7289
7523
  !selectors ||
7290
7524
  isUniqueElement(selectors) ||
7291
- // see https://github.com/micro-zoe/micro-app/issues/56
7525
+ // ISSUE: https://github.com/micro-zoe/micro-app/issues/56
7292
7526
  rawDocument !== _this) {
7293
7527
  return globalEnv.rawQuerySelector.call(_this, selectors);
7294
7528
  }
@@ -7438,8 +7672,8 @@ function initGlobalEnv() {
7438
7672
  // Document proto methods
7439
7673
  const rawCreateElement = rawRootDocument.prototype.createElement;
7440
7674
  const rawCreateElementNS = rawRootDocument.prototype.createElementNS;
7441
- const rawCreateDocumentFragment = rawRootDocument.prototype.createDocumentFragment;
7442
7675
  const rawCreateTextNode = rawRootDocument.prototype.createTextNode;
7676
+ const rawCreateDocumentFragment = rawRootDocument.prototype.createDocumentFragment;
7443
7677
  const rawCreateComment = rawRootDocument.prototype.createComment;
7444
7678
  const rawQuerySelector = rawRootDocument.prototype.querySelector;
7445
7679
  const rawQuerySelectorAll = rawRootDocument.prototype.querySelectorAll;
@@ -7723,12 +7957,12 @@ function defineElement(tagName) {
7723
7957
  /**
7724
7958
  * url is different & old app is unmounted or prefetch, create new app to replace old one
7725
7959
  */
7726
- logWarn(`the ${oldApp.isPrefetch ? 'prefetch' : 'unmounted'} app with url: ${oldAppUrl} replaced by a new app with url: ${targetUrl}`, this.appName);
7960
+ logWarn(`the ${oldApp.isPrefetch ? 'prefetch' : 'unmounted'} app with url ${oldAppUrl} replaced by a new app with url ${targetUrl}`, this.appName);
7727
7961
  }
7728
7962
  this.handleCreateApp();
7729
7963
  }
7730
7964
  else {
7731
- logError(`app name conflict, an app named: ${this.appName} with url: ${oldAppUrl} is running`);
7965
+ logError(`app name conflict, an app named ${this.appName} with url ${oldAppUrl} is running`);
7732
7966
  }
7733
7967
  }
7734
7968
  else {
@@ -7972,6 +8206,7 @@ function defineElement(tagName) {
7972
8206
  }
7973
8207
  else {
7974
8208
  // get path from browser URL
8209
+ // TODO: 新版本路由系统要重新兼容ssr
7975
8210
  let targetPath = getNoHashMicroPathFromURL(this.appName, baseUrl);
7976
8211
  const defaultPagePath = this.getDefaultPage();
7977
8212
  if (!targetPath && defaultPagePath) {
@@ -7999,7 +8234,10 @@ function defineElement(tagName) {
7999
8234
  * @returns router-mode
8000
8235
  */
8001
8236
  getMemoryRouterMode() {
8002
- return getRouterMode(this.getAttribute('router-mode'), this);
8237
+ return initRouterMode(this.getAttribute('router-mode'),
8238
+ // is micro-app element set disable-memory-router, like <micro-app disable-memory-router></micro-app>
8239
+ // or <micro-app disable-memory-router='false'></micro-app>
8240
+ this.compatibleProperties('disable-memory-router') && this.compatibleDisableProperties('disable-memory-router'));
8003
8241
  }
8004
8242
  /**
8005
8243
  * rewrite micro-app.setAttribute, process attr data
@@ -8048,6 +8286,18 @@ function defineElement(tagName) {
8048
8286
  }
8049
8287
  return null;
8050
8288
  }
8289
+ /**
8290
+ * get publicPath from a valid address,it can used in micro-app-devtools
8291
+ */
8292
+ get publicPath() {
8293
+ return getEffectivePath(this.appUrl);
8294
+ }
8295
+ /**
8296
+ * get baseRoute from attribute,it can used in micro-app-devtools
8297
+ */
8298
+ get baseRoute() {
8299
+ return this.getBaseRouteCompatible();
8300
+ }
8051
8301
  }
8052
8302
  globalEnv.rawWindow.customElements.define(tagName, MicroAppElement);
8053
8303
  }
@@ -8126,9 +8376,22 @@ function preFetchAction(options) {
8126
8376
  });
8127
8377
  const oldOnload = app.onLoad;
8128
8378
  const oldOnLoadError = app.onLoadError;
8129
- app.onLoad = (html) => {
8379
+ app.onLoad = (onLoadParam) => {
8380
+ if (app.isPrerender) {
8381
+ assign(onLoadParam, {
8382
+ defaultPage: options['default-page'],
8383
+ /**
8384
+ * TODO: 预渲染支持disable-memory-router,默认渲染首页即可,文档中也要保留
8385
+ * 问题:
8386
+ * 1、如何确保子应用进行跳转时不影响到浏览器地址??pure??
8387
+ */
8388
+ routerMode: initRouterMode(options['router-mode']),
8389
+ baseroute: options.baseroute,
8390
+ disablePatchRequest: options['disable-patch-request'],
8391
+ });
8392
+ }
8130
8393
  resolve();
8131
- oldOnload.call(app, html, options['default-page'], options['disable-patch-request'], getRouterMode(options['router-mode']), options.baseroute);
8394
+ oldOnload.call(app, onLoadParam);
8132
8395
  };
8133
8396
  app.onLoadError = (...rests) => {
8134
8397
  resolve();
@@ -8159,7 +8422,7 @@ function getGlobalAssets(assets) {
8159
8422
  // TODO: requestIdleCallback for every file
8160
8423
  function fetchGlobalResources(resources, suffix, sourceHandler) {
8161
8424
  if (isArray(resources)) {
8162
- const effectiveResource = resources.filter((path) => isString(path) && path.includes(`.${suffix}`) && !sourceHandler.hasInfo(path));
8425
+ const effectiveResource = resources.filter((path) => isString(path) && isTargetExtension(path, suffix) && !sourceHandler.hasInfo(path));
8163
8426
  const fetchResourcePromise = effectiveResource.map((path) => fetchSource(path));
8164
8427
  // fetch resource with stream
8165
8428
  promiseStream(fetchResourcePromise, (res) => {