@micro-zoe/micro-app 0.8.7 → 1.0.0-alpha.2

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 = '0.8.7';
1
+ const version = '1.0.0-alpha.2';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -7,10 +7,24 @@ const globalThis = (typeof global !== 'undefined')
7
7
  : ((typeof window !== 'undefined')
8
8
  ? window
9
9
  : ((typeof self !== 'undefined') ? self : Function('return this')()));
10
+ const noop = () => { };
11
+ const noopFalse = () => false;
12
+ // Array.isArray
13
+ const isArray = Array.isArray;
14
+ // Object.assign
15
+ const assign = Object.assign;
16
+ // Object prototype methods
17
+ const rawDefineProperty = Object.defineProperty;
18
+ const rawDefineProperties = Object.defineProperties;
19
+ const rawHasOwnProperty = Object.prototype.hasOwnProperty;
10
20
  // is Undefined
11
21
  function isUndefined(target) {
12
22
  return target === undefined;
13
23
  }
24
+ // is Null
25
+ function isNull(target) {
26
+ return target === null;
27
+ }
14
28
  // is String
15
29
  function isString(target) {
16
30
  return typeof target === 'string';
@@ -23,12 +37,14 @@ function isBoolean(target) {
23
37
  function isFunction(target) {
24
38
  return typeof target === 'function';
25
39
  }
26
- // is Array
27
- const isArray = Array.isArray;
28
40
  // is PlainObject
29
41
  function isPlainObject(target) {
30
42
  return toString.call(target) === '[object Object]';
31
43
  }
44
+ // is Object
45
+ function isObject(target) {
46
+ return typeof target === 'object';
47
+ }
32
48
  // is Promise
33
49
  function isPromise(target) {
34
50
  return toString.call(target) === '[object Promise]';
@@ -37,13 +53,25 @@ function isPromise(target) {
37
53
  function isBoundFunction(target) {
38
54
  return isFunction(target) && target.name.indexOf('bound ') === 0 && !target.hasOwnProperty('prototype');
39
55
  }
56
+ // is constructor function
57
+ function isConstructor(target) {
58
+ var _a;
59
+ if (isFunction(target)) {
60
+ const targetStr = target.toString();
61
+ return (((_a = target.prototype) === null || _a === void 0 ? void 0 : _a.constructor) === target &&
62
+ Object.getOwnPropertyNames(target.prototype).length > 1) ||
63
+ /^function\s+[A-Z]/.test(targetStr) ||
64
+ /^class\s+/.test(targetStr);
65
+ }
66
+ return false;
67
+ }
40
68
  // is ShadowRoot
41
69
  function isShadowRoot(target) {
42
70
  return typeof ShadowRoot !== 'undefined' && target instanceof ShadowRoot;
43
71
  }
44
- const rawDefineProperty = Object.defineProperty;
45
- const rawDefineProperties = Object.defineProperties;
46
- const rawHasOwnProperty = Object.prototype.hasOwnProperty;
72
+ function isURL(target) {
73
+ return target instanceof URL;
74
+ }
47
75
  /**
48
76
  * format error log
49
77
  * @param msg message
@@ -80,12 +108,22 @@ function logWarn(msg, appName = null, ...rest) {
80
108
  function defer(fn, ...args) {
81
109
  Promise.resolve().then(fn.bind(null, ...args));
82
110
  }
111
+ /**
112
+ * create URL as MicroLocation
113
+ */
114
+ const createURL = (function () {
115
+ class Location extends URL {
116
+ }
117
+ return (path, base) => {
118
+ return (base ? new Location('' + path, base) : new Location('' + path));
119
+ };
120
+ })();
83
121
  /**
84
122
  * Add address protocol
85
123
  * @param url address
86
124
  */
87
125
  function addProtocol(url) {
88
- return url.startsWith('//') ? `${location.protocol}${url}` : url;
126
+ return url.startsWith('//') ? `${globalThis.location.protocol}${url}` : url;
89
127
  }
90
128
  /**
91
129
  * format URL address
@@ -97,7 +135,7 @@ function formatAppURL(url, appName = null) {
97
135
  if (!isString(url) || !url)
98
136
  return '';
99
137
  try {
100
- const { origin, pathname, search } = new URL(addProtocol(url));
138
+ const { origin, pathname, search } = createURL(addProtocol(url));
101
139
  // If it ends with .html/.node/.php/.net/.etc, don’t need to add /
102
140
  if (/\.(\w+)$/.test(pathname)) {
103
141
  return `${origin}${pathname}${search}`;
@@ -118,6 +156,7 @@ function formatAppURL(url, appName = null) {
118
156
  * 3. event_center -> EventCenterForBaseApp -> all methods
119
157
  * 4. preFetch
120
158
  * 5. plugins
159
+ * 6. router api (push, replace)
121
160
  */
122
161
  function formatAppName(name) {
123
162
  if (!isString(name) || !name)
@@ -129,7 +168,7 @@ function formatAppName(name) {
129
168
  * @param url app.url
130
169
  */
131
170
  function getEffectivePath(url) {
132
- const { origin, pathname } = new URL(url);
171
+ const { origin, pathname } = createURL(url);
133
172
  if (/\.(\w+)$/.test(pathname)) {
134
173
  const fullPath = `${origin}${pathname}`;
135
174
  const pathArr = fullPath.split('/');
@@ -148,7 +187,7 @@ function CompletionPath(path, baseURI) {
148
187
  /^((((ht|f)tps?)|file):)?\/\//.test(path) ||
149
188
  /^(data|blob):/.test(path))
150
189
  return path;
151
- return new URL(path, getEffectivePath(addProtocol(baseURI))).toString();
190
+ return createURL(path, getEffectivePath(addProtocol(baseURI))).toString();
152
191
  }
153
192
  /**
154
193
  * Get the folder where the link resource is located,
@@ -176,24 +215,15 @@ function promiseStream(promiseList, successCb, errorCb, finallyCb) {
176
215
  promiseList.forEach((p, i) => {
177
216
  if (isPromise(p)) {
178
217
  p.then((res) => {
179
- successCb({
180
- data: res,
181
- index: i,
182
- });
218
+ successCb({ data: res, index: i });
183
219
  isFinished();
184
220
  }).catch((err) => {
185
- errorCb({
186
- error: err,
187
- index: i,
188
- });
221
+ errorCb({ error: err, index: i });
189
222
  isFinished();
190
223
  });
191
224
  }
192
225
  else {
193
- successCb({
194
- data: p,
195
- index: i,
196
- });
226
+ successCb({ data: p, index: i });
197
227
  isFinished();
198
228
  }
199
229
  });
@@ -256,6 +286,7 @@ function pureCreateElement(tagName, options) {
256
286
  const element = document.createElement(tagName, options);
257
287
  if (element.__MICRO_APP_NAME__)
258
288
  delete element.__MICRO_APP_NAME__;
289
+ element.__PURE_ELEMENT__ = true;
259
290
  return element;
260
291
  }
261
292
  /**
@@ -306,6 +337,98 @@ function trim(str) {
306
337
  function isFireFox() {
307
338
  return navigator.userAgent.indexOf('Firefox') > -1;
308
339
  }
340
+ /**
341
+ * Transforms a queryString into object.
342
+ * @param search - search string to parse
343
+ * @returns a query object
344
+ */
345
+ function parseQuery(search) {
346
+ const result = {};
347
+ const queryList = search.split('&');
348
+ // we will not decode the key/value to ensure that the values are consistent when update URL
349
+ for (const queryItem of queryList) {
350
+ const eqPos = queryItem.indexOf('=');
351
+ const key = eqPos < 0 ? queryItem : queryItem.slice(0, eqPos);
352
+ const value = eqPos < 0 ? null : queryItem.slice(eqPos + 1);
353
+ if (key in result) {
354
+ let currentValue = result[key];
355
+ if (!isArray(currentValue)) {
356
+ currentValue = result[key] = [currentValue];
357
+ }
358
+ currentValue.push(value);
359
+ }
360
+ else {
361
+ result[key] = value;
362
+ }
363
+ }
364
+ return result;
365
+ }
366
+ /**
367
+ * Transforms an object to query string
368
+ * @param queryObject - query object to stringify
369
+ * @returns query string without the leading `?`
370
+ */
371
+ function stringifyQuery(queryObject) {
372
+ let result = '';
373
+ for (const key in queryObject) {
374
+ const value = queryObject[key];
375
+ if (isNull(value)) {
376
+ result += (result.length ? '&' : '') + key;
377
+ }
378
+ else {
379
+ const valueList = isArray(value) ? value : [value];
380
+ valueList.forEach(value => {
381
+ if (!isUndefined(value)) {
382
+ result += (result.length ? '&' : '') + key;
383
+ if (!isNull(value))
384
+ result += '=' + value;
385
+ }
386
+ });
387
+ }
388
+ }
389
+ return result;
390
+ }
391
+ /**
392
+ * Register or unregister callback/guard with Set
393
+ */
394
+ function useSetRecord() {
395
+ const handlers = new Set();
396
+ function add(handler) {
397
+ handlers.add(handler);
398
+ return () => {
399
+ if (handlers.has(handler))
400
+ return handlers.delete(handler);
401
+ return false;
402
+ };
403
+ }
404
+ return {
405
+ add,
406
+ list: () => handlers,
407
+ };
408
+ }
409
+ /**
410
+ * record data with Map
411
+ */
412
+ function useMapRecord() {
413
+ const data = new Map();
414
+ function add(key, value) {
415
+ data.set(key, value);
416
+ return () => {
417
+ if (data.has(key))
418
+ return data.delete(key);
419
+ return false;
420
+ };
421
+ }
422
+ return {
423
+ add,
424
+ get: (key) => data.get(key),
425
+ delete: (key) => {
426
+ if (data.has(key))
427
+ return data.delete(key);
428
+ return false;
429
+ }
430
+ };
431
+ }
309
432
 
310
433
  var ObservedAttrName;
311
434
  (function (ObservedAttrName) {
@@ -315,13 +438,13 @@ var ObservedAttrName;
315
438
  // app status
316
439
  var appStates;
317
440
  (function (appStates) {
318
- appStates["NOT_LOADED"] = "NOT_LOADED";
319
- appStates["LOADING_SOURCE_CODE"] = "LOADING_SOURCE_CODE";
320
- appStates["LOAD_SOURCE_FINISHED"] = "LOAD_SOURCE_FINISHED";
321
- appStates["LOAD_SOURCE_ERROR"] = "LOAD_SOURCE_ERROR";
322
- appStates["MOUNTING"] = "MOUNTING";
323
- appStates["MOUNTED"] = "MOUNTED";
324
- appStates["UNMOUNT"] = "UNMOUNT";
441
+ appStates["CREATED"] = "created";
442
+ appStates["LOADING"] = "loading";
443
+ appStates["LOADED"] = "loaded";
444
+ appStates["LOAD_FAILED"] = "load_failed";
445
+ appStates["MOUNTING"] = "mounting";
446
+ appStates["MOUNTED"] = "mounted";
447
+ appStates["UNMOUNT"] = "unmount";
325
448
  })(appStates || (appStates = {}));
326
449
  // lifecycles
327
450
  var lifeCycles;
@@ -339,86 +462,42 @@ var lifeCycles;
339
462
  // keep-alive status
340
463
  var keepAliveStates;
341
464
  (function (keepAliveStates) {
342
- keepAliveStates["KEEP_ALIVE_SHOW"] = "KEEP_ALIVE_SHOW";
343
- keepAliveStates["KEEP_ALIVE_HIDDEN"] = "KEEP_ALIVE_HIDDEN";
465
+ keepAliveStates["KEEP_ALIVE_SHOW"] = "keep_alive_show";
466
+ keepAliveStates["KEEP_ALIVE_HIDDEN"] = "keep_alive_hidden";
344
467
  })(keepAliveStates || (keepAliveStates = {}));
345
- const globalKeyToBeCached = 'window,self,globalThis,Array,Object,String,Boolean,Math,Number,Symbol,Date,Promise,Function,Proxy,WeakMap,WeakSet,Set,Map,Reflect,Element,Node,Document,RegExp,Error,TypeError,JSON,isNaN,parseFloat,parseInt,performance,console,decodeURI,encodeURI,decodeURIComponent,encodeURIComponent,location,navigator,undefined';
468
+ /**
469
+ * global key must be static key, they can not rewrite
470
+ * e.g.
471
+ * window.Promise = newValue
472
+ * new Promise ==> still get old value, not newValue, because they are cached by top function
473
+ * NOTE:
474
+ * 1. Do not add fetch, XMLHttpRequest, EventSource
475
+ */
476
+ const globalKeyToBeCached = 'window,self,globalThis,Array,Object,String,Boolean,Math,Number,Symbol,Date,Function,Proxy,WeakMap,WeakSet,Set,Map,Reflect,Element,Node,Document,RegExp,Error,TypeError,JSON,isNaN,parseFloat,parseInt,performance,console,decodeURI,encodeURI,decodeURIComponent,encodeURIComponent,navigator,undefined,location,history';
346
477
 
347
478
  /**
348
479
  * fetch source of html, js, css
349
480
  * @param url source path
350
481
  * @param appName app name
351
- * @param config config of fetch
482
+ * @param config fetch options
352
483
  */
353
484
  function fetchSource(url, appName = null, options = {}) {
485
+ /**
486
+ * When child navigate to new async page, click event will scope dom to child and then fetch new source
487
+ * this may cause error when fetch rewrite by baseApp
488
+ * e.g.
489
+ * baseApp: <script crossorigin src="https://sgm-static.jd.com/sgm-2.8.0.js" name="SGMH5" sid="6f88a6e4ba4b4ae5acef2ec22c075085" appKey="jdb-adminb2b-pc"></script>
490
+ */
491
+ removeDomScope();
354
492
  if (isFunction(microApp.fetch)) {
355
493
  return microApp.fetch(url, options, appName);
356
494
  }
357
- return fetch(url, options).then((res) => {
495
+ // Don’t use globalEnv.rawWindow.fetch, will cause sgm-2.8.0.js throw error in nest app
496
+ return window.fetch(url, options).then((res) => {
358
497
  return res.text();
359
498
  });
360
499
  }
361
500
 
362
- class HTMLLoader {
363
- static getInstance() {
364
- if (!this.instance) {
365
- this.instance = new HTMLLoader();
366
- }
367
- return this.instance;
368
- }
369
- /**
370
- * run logic of load and format html
371
- * @param successCb success callback
372
- * @param errorCb error callback, type: (err: Error, meetFetchErr: boolean) => void
373
- */
374
- run(app, successCb) {
375
- const appName = app.name;
376
- const htmlUrl = app.ssrUrl || app.url;
377
- fetchSource(htmlUrl, appName, { cache: 'no-cache' }).then((htmlStr) => {
378
- if (!htmlStr) {
379
- const msg = 'html is empty, please check in detail';
380
- app.onerror(new Error(msg));
381
- return logError(msg, appName);
382
- }
383
- htmlStr = this.formatHTML(htmlUrl, htmlStr, appName);
384
- successCb(htmlStr, app);
385
- }).catch((e) => {
386
- logError(`Failed to fetch data from ${app.url}, micro-app stop rendering`, appName, e);
387
- app.onLoadError(e);
388
- });
389
- }
390
- formatHTML(htmlUrl, htmlStr, appName) {
391
- return this.processHtml(htmlUrl, htmlStr, appName, microApp.plugins)
392
- .replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
393
- return match
394
- .replace(/<head/i, '<micro-app-head')
395
- .replace(/<\/head>/i, '</micro-app-head>');
396
- })
397
- .replace(/<body[^>]*>[\s\S]*?<\/body>/i, (match) => {
398
- return match
399
- .replace(/<body/i, '<micro-app-body')
400
- .replace(/<\/body>/i, '</micro-app-body>');
401
- });
402
- }
403
- processHtml(url, code, appName, plugins) {
404
- var _a;
405
- if (!plugins)
406
- return code;
407
- const mergedPlugins = [];
408
- plugins.global && mergedPlugins.push(...plugins.global);
409
- ((_a = plugins.modules) === null || _a === void 0 ? void 0 : _a[appName]) && mergedPlugins.push(...plugins.modules[appName]);
410
- if (mergedPlugins.length > 0) {
411
- return mergedPlugins.reduce((preCode, plugin) => {
412
- if (isPlainObject(plugin) && isFunction(plugin.processHtml)) {
413
- return plugin.processHtml(preCode, url, plugin.options);
414
- }
415
- return preCode;
416
- }, code);
417
- }
418
- return code;
419
- }
420
- }
421
-
422
501
  // common reg
423
502
  const rootSelectorREG = /(^|\s+)(html|:root)(?=[\s>~[.#:]+|$)/;
424
503
  const bodySelectorREG = /(^|\s+)((html[\s>~]+body)|body)(?=[\s>~[.#:]+|$)/;
@@ -497,7 +576,7 @@ class CSSParser {
497
576
  return true;
498
577
  }
499
578
  formatSelector(skip) {
500
- const m = this.commonMatch(/^([^{]+)/, skip);
579
+ const m = this.commonMatch(/^[^{]+/, skip);
501
580
  if (!m)
502
581
  return false;
503
582
  return m[0].replace(/(^|,[\n\s]*)([^,]+)/g, (_, separator, selector) => {
@@ -577,7 +656,7 @@ class CSSParser {
577
656
  keyframesRule() {
578
657
  if (!this.commonMatch(/^@([-\w]+)?keyframes\s*/))
579
658
  return false;
580
- if (!this.commonMatch(/^([-\w]+)\s*/))
659
+ if (!this.commonMatch(/^[^{]+/))
581
660
  return parseError('@keyframes missing name', this.linkPath);
582
661
  this.matchComments();
583
662
  if (!this.matchOpenBrace())
@@ -729,7 +808,7 @@ class CSSParser {
729
808
  }
730
809
  // splice string
731
810
  recordResult(strFragment) {
732
- // Firefox is slow when string contain special characters, see https://github.com/micro-zoe/micro-app/issues/256
811
+ // Firefox performance degradation when string contain special characters, see https://github.com/micro-zoe/micro-app/issues/256
733
812
  if (isFireFox()) {
734
813
  this.result += encodeURIComponent(strFragment);
735
814
  }
@@ -960,6 +1039,85 @@ function formatDynamicLink(url, info, app, originLink, replaceStyle) {
960
1039
  });
961
1040
  }
962
1041
 
1042
+ class Adapter {
1043
+ constructor() {
1044
+ // keys that can only assigned to rawWindow
1045
+ this.escapeSetterKeyList = [
1046
+ 'location',
1047
+ ];
1048
+ // keys that can escape to rawWindow
1049
+ this.staticEscapeProperties = [
1050
+ 'System',
1051
+ '__cjsWrapper',
1052
+ ];
1053
+ // keys that scoped in child app
1054
+ this.staticScopeProperties = [
1055
+ 'webpackJsonp',
1056
+ 'webpackHotUpdate',
1057
+ 'Vue',
1058
+ ];
1059
+ this.injectReactHRMProperty();
1060
+ }
1061
+ // TODO: __DEV__ process.env.NODE_ENV !== 'production'
1062
+ // adapter for react
1063
+ injectReactHRMProperty() {
1064
+ if (process.env.NODE_ENV !== 'production') {
1065
+ // react child in non-react env
1066
+ this.staticEscapeProperties.push('__REACT_ERROR_OVERLAY_GLOBAL_HOOK__');
1067
+ // in react parent
1068
+ if (globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__) {
1069
+ this.staticScopeProperties = this.staticScopeProperties.concat([
1070
+ '__REACT_ERROR_OVERLAY_GLOBAL_HOOK__',
1071
+ '__reactRefreshInjected',
1072
+ ]);
1073
+ }
1074
+ }
1075
+ }
1076
+ }
1077
+ // Fix conflict of babel-polyfill@6.x
1078
+ function fixBabelPolyfill6() {
1079
+ if (globalEnv.rawWindow._babelPolyfill)
1080
+ globalEnv.rawWindow._babelPolyfill = false;
1081
+ }
1082
+ /**
1083
+ * Fix error of hot reload when parent&child created by create-react-app in development environment
1084
+ * Issue: https://github.com/micro-zoe/micro-app/issues/382
1085
+ */
1086
+ function fixReactHMRConflict(app) {
1087
+ var _a;
1088
+ if (process.env.NODE_ENV !== 'production') {
1089
+ const rawReactErrorHook = globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__;
1090
+ const childReactErrorHook = (_a = app.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__;
1091
+ if (rawReactErrorHook && childReactErrorHook) {
1092
+ globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__ = childReactErrorHook;
1093
+ defer(() => {
1094
+ globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__ = rawReactErrorHook;
1095
+ });
1096
+ }
1097
+ }
1098
+ }
1099
+ /**
1100
+ * reDefine parentNode of html
1101
+ * Scenes:
1102
+ * 1. element-ui popover.js
1103
+ * if (html.parentNode === document) ...
1104
+ */
1105
+ function throttleDeferForParentNode(proxyDocument) {
1106
+ const html = globalEnv.rawDocument.firstElementChild;
1107
+ if (html && html.parentNode !== proxyDocument) {
1108
+ setRootParentNode(html, proxyDocument);
1109
+ defer(() => {
1110
+ setRootParentNode(html, globalEnv.rawDocument);
1111
+ });
1112
+ }
1113
+ }
1114
+ function setRootParentNode(root, value) {
1115
+ Object.defineProperty(root, 'parentNode', {
1116
+ value,
1117
+ configurable: true,
1118
+ });
1119
+ }
1120
+
963
1121
  // Record element and map element
964
1122
  const dynamicElementInMicroAppMap = new WeakMap();
965
1123
  /**
@@ -981,12 +1139,12 @@ function handleNewNode(parent, child, app) {
981
1139
  return child;
982
1140
  }
983
1141
  else if (child instanceof HTMLLinkElement) {
984
- if (child.hasAttribute('exclude') || checkExcludeUrl(child.getAttribute('href'), app.name)) {
1142
+ if (child.hasAttribute('exclude')) {
985
1143
  const linkReplaceComment = document.createComment('link element with exclude attribute ignored by micro-app');
986
1144
  dynamicElementInMicroAppMap.set(child, linkReplaceComment);
987
1145
  return linkReplaceComment;
988
1146
  }
989
- else if (child.hasAttribute('ignore') || checkIgnoreUrl(child.getAttribute('href'), app.name)) {
1147
+ else if (child.hasAttribute('ignore')) {
990
1148
  return child;
991
1149
  }
992
1150
  const { url, info, replaceComment } = extractLinkFromHtml(child, parent, app, true);
@@ -1034,39 +1192,37 @@ function handleNewNode(parent, child, app) {
1034
1192
  * @param passiveChild second param of insertBefore and replaceChild
1035
1193
  */
1036
1194
  function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
1037
- const container = getContainer(parent, app);
1195
+ const hijackElement = getHijackElement(parent, app);
1038
1196
  /**
1039
1197
  * If passiveChild is not the child node, insertBefore replaceChild will have a problem, at this time, it will be degraded to appendChild
1040
1198
  * E.g: document.head.insertBefore(targetChild, document.head.childNodes[0])
1041
1199
  */
1042
- if (container) {
1200
+ if (hijackElement) {
1043
1201
  /**
1044
1202
  * 1. If passiveChild exists, it must be insertBefore or replaceChild
1045
1203
  * 2. When removeChild, targetChild may not be in microAppHead or head
1046
1204
  */
1047
- if (passiveChild && !container.contains(passiveChild)) {
1048
- return globalEnv.rawAppendChild.call(container, targetChild);
1205
+ if (passiveChild && !hijackElement.contains(passiveChild)) {
1206
+ return globalEnv.rawAppendChild.call(hijackElement, targetChild);
1049
1207
  }
1050
- else if (rawMethod === globalEnv.rawRemoveChild && !container.contains(targetChild)) {
1208
+ else if (rawMethod === globalEnv.rawRemoveChild && !hijackElement.contains(targetChild)) {
1051
1209
  if (parent.contains(targetChild)) {
1052
1210
  return rawMethod.call(parent, targetChild);
1053
1211
  }
1054
1212
  return targetChild;
1055
1213
  }
1056
- return invokeRawMethod(rawMethod, container, targetChild, passiveChild);
1214
+ // TODO: __DEV__
1215
+ if (process.env.NODE_ENV !== 'production' &&
1216
+ targetChild instanceof HTMLIFrameElement &&
1217
+ rawMethod === globalEnv.rawAppendChild) {
1218
+ fixReactHMRConflict(app);
1219
+ }
1220
+ return invokeRawMethod(rawMethod, hijackElement, targetChild, passiveChild);
1057
1221
  }
1058
1222
  return invokeRawMethod(rawMethod, parent, targetChild, passiveChild);
1059
1223
  }
1060
- function invokeRawMethod(rawMethod, parent, targetChild, passiveChild) {
1061
- if (isPendMethod(rawMethod)) {
1062
- return rawMethod.call(parent, targetChild);
1063
- }
1064
- return rawMethod.call(parent, targetChild, passiveChild);
1065
- }
1066
- function isPendMethod(method) {
1067
- return method === globalEnv.rawAppend || method === globalEnv.rawPrepend;
1068
- }
1069
- function getContainer(node, app) {
1224
+ // head/body map to micro-app-head/micro-app-body
1225
+ function getHijackElement(node, app) {
1070
1226
  var _a, _b;
1071
1227
  if (node === document.head) {
1072
1228
  return (_a = app === null || app === void 0 ? void 0 : app.container) === null || _a === void 0 ? void 0 : _a.querySelector('micro-app-head');
@@ -1076,6 +1232,15 @@ function getContainer(node, app) {
1076
1232
  }
1077
1233
  return null;
1078
1234
  }
1235
+ function invokeRawMethod(rawMethod, parent, targetChild, passiveChild) {
1236
+ if (isPendMethod(rawMethod)) {
1237
+ return rawMethod.call(parent, targetChild);
1238
+ }
1239
+ return rawMethod.call(parent, targetChild, passiveChild);
1240
+ }
1241
+ function isPendMethod(method) {
1242
+ return method === globalEnv.rawAppend || method === globalEnv.rawPrepend;
1243
+ }
1079
1244
  // Get the map element
1080
1245
  function getMappingNode(node) {
1081
1246
  var _a;
@@ -1089,20 +1254,35 @@ function getMappingNode(node) {
1089
1254
  * @param rawMethod method
1090
1255
  */
1091
1256
  function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
1092
- if (newChild === null || newChild === void 0 ? void 0 : newChild.__MICRO_APP_NAME__) {
1257
+ const currentAppName = getCurrentAppName();
1258
+ if (newChild instanceof Node &&
1259
+ (newChild.__MICRO_APP_NAME__ ||
1260
+ (currentAppName && !newChild.__PURE_ELEMENT__))) {
1261
+ newChild.__MICRO_APP_NAME__ = newChild.__MICRO_APP_NAME__ || currentAppName;
1093
1262
  const app = appInstanceMap.get(newChild.__MICRO_APP_NAME__);
1094
1263
  if (app === null || app === void 0 ? void 0 : app.container) {
1264
+ if (newChild instanceof Element) {
1265
+ if (/^(img|script)$/i.test(newChild.tagName)) {
1266
+ if (newChild.hasAttribute('src')) {
1267
+ globalEnv.rawSetAttribute.call(newChild, 'src', CompletionPath(newChild.getAttribute('src'), app.url));
1268
+ }
1269
+ if (newChild.hasAttribute('srcset')) {
1270
+ globalEnv.rawSetAttribute.call(newChild, 'srcset', CompletionPath(newChild.getAttribute('srcset'), app.url));
1271
+ }
1272
+ }
1273
+ else if (/^link$/i.test(newChild.tagName) && newChild.hasAttribute('href')) {
1274
+ globalEnv.rawSetAttribute.call(newChild, 'href', CompletionPath(newChild.getAttribute('href'), app.url));
1275
+ }
1276
+ }
1095
1277
  return invokePrototypeMethod(app, rawMethod, parent, handleNewNode(parent, newChild, app), passiveChild && getMappingNode(passiveChild));
1096
1278
  }
1097
1279
  else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
1098
1280
  return rawMethod.call(parent, newChild);
1099
1281
  }
1100
- return rawMethod.call(parent, newChild, passiveChild);
1101
1282
  }
1102
1283
  else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
1103
- const appName = getCurrentAppName();
1104
- if (!(newChild instanceof Node) && appName) {
1105
- const app = appInstanceMap.get(appName);
1284
+ if (!(newChild instanceof Node) && currentAppName) {
1285
+ const app = appInstanceMap.get(currentAppName);
1106
1286
  if (app === null || app === void 0 ? void 0 : app.container) {
1107
1287
  if (parent === document.head) {
1108
1288
  return rawMethod.call(app.container.querySelector('micro-app-head'), newChild);
@@ -1148,12 +1328,18 @@ function patchElementPrototypeMethods() {
1148
1328
  };
1149
1329
  // prototype methods of delete element👇
1150
1330
  Element.prototype.removeChild = function removeChild(oldChild) {
1331
+ var _a;
1151
1332
  if (oldChild === null || oldChild === void 0 ? void 0 : oldChild.__MICRO_APP_NAME__) {
1152
1333
  const app = appInstanceMap.get(oldChild.__MICRO_APP_NAME__);
1153
1334
  if (app === null || app === void 0 ? void 0 : app.container) {
1154
1335
  return invokePrototypeMethod(app, globalEnv.rawRemoveChild, this, getMappingNode(oldChild));
1155
1336
  }
1156
- return globalEnv.rawRemoveChild.call(this, oldChild);
1337
+ try {
1338
+ return globalEnv.rawRemoveChild.call(this, oldChild);
1339
+ }
1340
+ catch (_b) {
1341
+ return (_a = oldChild === null || oldChild === void 0 ? void 0 : oldChild.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(oldChild);
1342
+ }
1157
1343
  }
1158
1344
  return globalEnv.rawRemoveChild.call(this, oldChild);
1159
1345
  };
@@ -1163,36 +1349,15 @@ function patchElementPrototypeMethods() {
1163
1349
  this.__MICRO_APP_NAME__ && (clonedNode.__MICRO_APP_NAME__ = this.__MICRO_APP_NAME__);
1164
1350
  return clonedNode;
1165
1351
  };
1166
- // patch getBoundingClientRect
1167
- // TODO: scenes test
1168
- // Element.prototype.getBoundingClientRect = function getBoundingClientRect () {
1169
- // const rawRect: DOMRect = globalEnv.rawGetBoundingClientRect.call(this)
1170
- // if (this.__MICRO_APP_NAME__) {
1171
- // const app = appInstanceMap.get(this.__MICRO_APP_NAME__)
1172
- // if (!app?.container) {
1173
- // return rawRect
1174
- // }
1175
- // const appBody = app.container.querySelector('micro-app-body')
1176
- // const appBodyRect: DOMRect = globalEnv.rawGetBoundingClientRect.call(appBody)
1177
- // const computedRect: DOMRect = new DOMRect(
1178
- // rawRect.x - appBodyRect.x,
1179
- // rawRect.y - appBodyRect.y,
1180
- // rawRect.width,
1181
- // rawRect.height,
1182
- // )
1183
- // return computedRect
1184
- // }
1185
- // return rawRect
1186
- // }
1187
1352
  }
1188
1353
  /**
1189
1354
  * Mark the newly created element in the micro application
1190
1355
  * @param element new element
1191
1356
  */
1192
1357
  function markElement(element) {
1193
- const appName = getCurrentAppName();
1194
- if (appName)
1195
- element.__MICRO_APP_NAME__ = appName;
1358
+ const currentAppName = getCurrentAppName();
1359
+ if (currentAppName)
1360
+ element.__MICRO_APP_NAME__ = currentAppName;
1196
1361
  return element;
1197
1362
  }
1198
1363
  // methods of document
@@ -1213,27 +1378,29 @@ function patchDocument() {
1213
1378
  };
1214
1379
  // query element👇
1215
1380
  function querySelector(selectors) {
1216
- var _a, _b, _c;
1217
- const appName = getCurrentAppName();
1218
- if (!appName ||
1381
+ var _a, _b, _c, _d;
1382
+ const currentAppName = getCurrentAppName();
1383
+ if (!currentAppName ||
1384
+ !((_a = appInstanceMap.get(currentAppName)) === null || _a === void 0 ? void 0 : _a.container) ||
1219
1385
  !selectors ||
1220
1386
  isUniqueElement(selectors) ||
1221
1387
  // see https://github.com/micro-zoe/micro-app/issues/56
1222
1388
  rawDocument !== this) {
1223
1389
  return globalEnv.rawQuerySelector.call(this, selectors);
1224
1390
  }
1225
- return (_c = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) === null || _b === void 0 ? void 0 : _b.querySelector(selectors)) !== null && _c !== void 0 ? _c : null;
1391
+ return (_d = (_c = (_b = appInstanceMap.get(currentAppName)) === null || _b === void 0 ? void 0 : _b.container) === null || _c === void 0 ? void 0 : _c.querySelector(selectors)) !== null && _d !== void 0 ? _d : null;
1226
1392
  }
1227
1393
  function querySelectorAll(selectors) {
1228
- var _a, _b, _c;
1229
- const appName = getCurrentAppName();
1230
- if (!appName ||
1394
+ var _a, _b, _c, _d;
1395
+ const currentAppName = getCurrentAppName();
1396
+ if (!currentAppName ||
1397
+ !((_a = appInstanceMap.get(currentAppName)) === null || _a === void 0 ? void 0 : _a.container) ||
1231
1398
  !selectors ||
1232
1399
  isUniqueElement(selectors) ||
1233
1400
  rawDocument !== this) {
1234
1401
  return globalEnv.rawQuerySelectorAll.call(this, selectors);
1235
1402
  }
1236
- return (_c = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) === null || _b === void 0 ? void 0 : _b.querySelectorAll(selectors)) !== null && _c !== void 0 ? _c : [];
1403
+ return (_d = (_c = (_b = appInstanceMap.get(currentAppName)) === null || _b === void 0 ? void 0 : _b.container) === null || _c === void 0 ? void 0 : _c.querySelectorAll(selectors)) !== null && _d !== void 0 ? _d : [];
1237
1404
  }
1238
1405
  Document.prototype.querySelector = querySelector;
1239
1406
  Document.prototype.querySelectorAll = querySelectorAll;
@@ -1261,11 +1428,11 @@ function patchDocument() {
1261
1428
  };
1262
1429
  Document.prototype.getElementsByTagName = function getElementsByTagName(key) {
1263
1430
  var _a;
1264
- const appName = getCurrentAppName();
1265
- if (!appName ||
1431
+ const currentAppName = getCurrentAppName();
1432
+ if (!currentAppName ||
1266
1433
  isUniqueElement(key) ||
1267
1434
  isInvalidQuerySelectorKey(key) ||
1268
- (!((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.inline) && /^script$/i.test(key))) {
1435
+ (!((_a = appInstanceMap.get(currentAppName)) === null || _a === void 0 ? void 0 : _a.inline) && /^script$/i.test(key))) {
1269
1436
  return globalEnv.rawGetElementsByTagName.call(this, key);
1270
1437
  }
1271
1438
  try {
@@ -1301,10 +1468,9 @@ function patchSetAttribute() {
1301
1468
  if (/^micro-app(-\S+)?/i.test(this.tagName) && key === 'data') {
1302
1469
  if (isPlainObject(value)) {
1303
1470
  const cloneValue = {};
1304
- Object.getOwnPropertyNames(value).forEach((propertyKey) => {
1305
- if (!(isString(propertyKey) && propertyKey.indexOf('__') === 0)) {
1306
- // @ts-ignore
1307
- cloneValue[propertyKey] = value[propertyKey];
1471
+ Object.getOwnPropertyNames(value).forEach((ownKey) => {
1472
+ if (!(isString(ownKey) && ownKey.indexOf('__') === 0)) {
1473
+ cloneValue[ownKey] = value[ownKey];
1308
1474
  }
1309
1475
  });
1310
1476
  this.data = cloneValue;
@@ -1313,14 +1479,15 @@ function patchSetAttribute() {
1313
1479
  logWarn('property data must be an object', this.getAttribute('name'));
1314
1480
  }
1315
1481
  }
1316
- else if ((((key === 'src' || key === 'srcset') && /^(img|script)$/i.test(this.tagName)) ||
1317
- (key === 'href' && /^link$/i.test(this.tagName))) &&
1318
- this.__MICRO_APP_NAME__ &&
1319
- appInstanceMap.has(this.__MICRO_APP_NAME__)) {
1320
- const app = appInstanceMap.get(this.__MICRO_APP_NAME__);
1321
- globalEnv.rawSetAttribute.call(this, key, CompletionPath(value, app.url));
1322
- }
1323
1482
  else {
1483
+ const appName = this.__MICRO_APP_NAME__ || getCurrentAppName();
1484
+ if (appName &&
1485
+ appInstanceMap.has(appName) &&
1486
+ (((key === 'src' || key === 'srcset') && /^(img|script)$/i.test(this.tagName)) ||
1487
+ (key === 'href' && /^link$/i.test(this.tagName)))) {
1488
+ const app = appInstanceMap.get(appName);
1489
+ value = CompletionPath(value, app.url);
1490
+ }
1324
1491
  globalEnv.rawSetAttribute.call(this, key, value);
1325
1492
  }
1326
1493
  };
@@ -1351,7 +1518,6 @@ function releasePatches() {
1351
1518
  Element.prototype.append = globalEnv.rawAppend;
1352
1519
  Element.prototype.prepend = globalEnv.rawPrepend;
1353
1520
  Element.prototype.cloneNode = globalEnv.rawCloneNode;
1354
- // Element.prototype.getBoundingClientRect = globalEnv.rawGetBoundingClientRect
1355
1521
  }
1356
1522
  // Set the style of micro-app-head and micro-app-body
1357
1523
  let hasRejectMicroAppStyle = false;
@@ -1365,53 +1531,6 @@ function rejectMicroAppStyle() {
1365
1531
  }
1366
1532
  }
1367
1533
 
1368
- // 管理 app 的单例
1369
- class AppManager {
1370
- constructor() {
1371
- // Todo: appInstanceMap 由 AppManager 来创建,不再由 create_app 管理
1372
- this.appInstanceMap = appInstanceMap;
1373
- }
1374
- static getInstance() {
1375
- if (!this.instance) {
1376
- this.instance = new AppManager();
1377
- }
1378
- return this.instance;
1379
- }
1380
- get(appName) {
1381
- return this.appInstanceMap.get(appName);
1382
- }
1383
- set(appName, app) {
1384
- this.appInstanceMap.set(appName, app);
1385
- }
1386
- getAll() {
1387
- return Array.from(this.appInstanceMap.values());
1388
- }
1389
- clear() {
1390
- this.appInstanceMap.clear();
1391
- }
1392
- }
1393
-
1394
- function unmountNestedApp() {
1395
- releaseUnmountOfNestedApp();
1396
- AppManager.getInstance().getAll().forEach(app => {
1397
- // @ts-ignore
1398
- app.container && getRootContainer(app.container).disconnectedCallback();
1399
- });
1400
- !window.__MICRO_APP_UMD_MODE__ && AppManager.getInstance().clear();
1401
- }
1402
- // if micro-app run in micro application, delete all next generation application when unmount event received
1403
- function listenUmountOfNestedApp() {
1404
- if (window.__MICRO_APP_ENVIRONMENT__) {
1405
- window.addEventListener('unmount', unmountNestedApp, false);
1406
- }
1407
- }
1408
- // release listener
1409
- function releaseUnmountOfNestedApp() {
1410
- if (window.__MICRO_APP_ENVIRONMENT__) {
1411
- window.removeEventListener('unmount', unmountNestedApp, false);
1412
- }
1413
- }
1414
-
1415
1534
  const globalEnv = {};
1416
1535
  /**
1417
1536
  * Note loop nesting
@@ -1431,7 +1550,6 @@ function initGlobalEnv() {
1431
1550
  const rawAppend = Element.prototype.append;
1432
1551
  const rawPrepend = Element.prototype.prepend;
1433
1552
  const rawCloneNode = Element.prototype.cloneNode;
1434
- // const rawGetBoundingClientRect = Element.prototype.getBoundingClientRect
1435
1553
  const rawCreateElement = Document.prototype.createElement;
1436
1554
  const rawCreateElementNS = Document.prototype.createElementNS;
1437
1555
  const rawCreateDocumentFragment = Document.prototype.createDocumentFragment;
@@ -1465,7 +1583,7 @@ function initGlobalEnv() {
1465
1583
  const rawDocumentRemoveEventListener = rawDocument.removeEventListener;
1466
1584
  // mark current application as base application
1467
1585
  window.__MICRO_APP_BASE_APPLICATION__ = true;
1468
- Object.assign(globalEnv, {
1586
+ assign(globalEnv, {
1469
1587
  // source/patch
1470
1588
  rawSetAttribute,
1471
1589
  rawAppendChild,
@@ -1475,7 +1593,6 @@ function initGlobalEnv() {
1475
1593
  rawAppend,
1476
1594
  rawPrepend,
1477
1595
  rawCloneNode,
1478
- // rawGetBoundingClientRect,
1479
1596
  rawCreateElement,
1480
1597
  rawCreateElementNS,
1481
1598
  rawCreateDocumentFragment,
@@ -1502,8 +1619,6 @@ function initGlobalEnv() {
1502
1619
  });
1503
1620
  // global effect
1504
1621
  rejectMicroAppStyle();
1505
- releaseUnmountOfNestedApp();
1506
- listenUmountOfNestedApp();
1507
1622
  }
1508
1623
  }
1509
1624
 
@@ -1519,14 +1634,11 @@ const globalScripts = new Map();
1519
1634
  function extractScriptElement(script, parent, app, isDynamic = false) {
1520
1635
  let replaceComment = null;
1521
1636
  let src = script.getAttribute('src');
1522
- if (src) {
1523
- src = CompletionPath(src, app.url);
1524
- }
1525
- if (script.hasAttribute('exclude') || checkExcludeUrl(src, app.name)) {
1637
+ if (script.hasAttribute('exclude')) {
1526
1638
  replaceComment = document.createComment('script element with exclude attribute removed by micro-app');
1527
1639
  }
1528
1640
  else if ((script.type && !['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module'].includes(script.type)) ||
1529
- script.hasAttribute('ignore') || checkIgnoreUrl(src, app.name)) {
1641
+ script.hasAttribute('ignore')) {
1530
1642
  return null;
1531
1643
  }
1532
1644
  else if ((globalEnv.supportModuleScript && script.noModule) ||
@@ -1534,6 +1646,7 @@ function extractScriptElement(script, parent, app, isDynamic = false) {
1534
1646
  replaceComment = document.createComment(`${script.noModule ? 'noModule' : 'module'} script ignored by micro-app`);
1535
1647
  }
1536
1648
  else if (src) { // remote script
1649
+ src = CompletionPath(src, app.url);
1537
1650
  const info = {
1538
1651
  code: '',
1539
1652
  isExternal: true,
@@ -1583,46 +1696,6 @@ function extractScriptElement(script, parent, app, isDynamic = false) {
1583
1696
  return parent.replaceChild(replaceComment, script);
1584
1697
  }
1585
1698
  }
1586
- /**
1587
- * get assets plugins
1588
- * @param appName app name
1589
- */
1590
- function getAssetsPlugins(appName) {
1591
- var _a, _b, _c;
1592
- const globalPlugins = ((_a = microApp.plugins) === null || _a === void 0 ? void 0 : _a.global) || [];
1593
- const modulePlugins = ((_c = (_b = microApp.plugins) === null || _b === void 0 ? void 0 : _b.modules) === null || _c === void 0 ? void 0 : _c[appName]) || [];
1594
- return [...globalPlugins, ...modulePlugins];
1595
- }
1596
- /**
1597
- * whether the url needs to be excluded
1598
- * @param url css or js link
1599
- * @param plugins microApp plugins
1600
- */
1601
- function checkExcludeUrl(url, appName) {
1602
- if (!url)
1603
- return false;
1604
- const plugins = getAssetsPlugins(appName) || [];
1605
- return plugins.some(plugin => {
1606
- if (!plugin.excludeChecker)
1607
- return false;
1608
- return plugin.excludeChecker(url);
1609
- });
1610
- }
1611
- /**
1612
- * whether the url needs to be ignore
1613
- * @param url css or js link
1614
- * @param plugins microApp plugins
1615
- */
1616
- function checkIgnoreUrl(url, appName) {
1617
- if (!url)
1618
- return false;
1619
- const plugins = getAssetsPlugins(appName) || [];
1620
- return plugins.some(plugin => {
1621
- if (!plugin.ignoreChecker)
1622
- return false;
1623
- return plugin.ignoreChecker(url);
1624
- });
1625
- }
1626
1699
  /**
1627
1700
  * Get remote resources of script
1628
1701
  * @param wrapElement htmlDom
@@ -1899,10 +1972,10 @@ function flatChildren(parent, app, microAppHead) {
1899
1972
  });
1900
1973
  for (const dom of children) {
1901
1974
  if (dom instanceof HTMLLinkElement) {
1902
- if (dom.hasAttribute('exclude') || checkExcludeUrl(dom.getAttribute('href'), app.name)) {
1975
+ if (dom.hasAttribute('exclude')) {
1903
1976
  parent.replaceChild(document.createComment('link element with exclude attribute ignored by micro-app'), dom);
1904
1977
  }
1905
- else if (!(dom.hasAttribute('ignore') || checkIgnoreUrl(dom.getAttribute('href'), app.name))) {
1978
+ else if (!dom.hasAttribute('ignore')) {
1906
1979
  extractLinkFromHtml(dom, parent, app);
1907
1980
  }
1908
1981
  else if (dom.hasAttribute('href')) {
@@ -1956,6 +2029,34 @@ function extractSourceDom(htmlStr, app) {
1956
2029
  app.onLoad(wrapElement);
1957
2030
  }
1958
2031
  }
2032
+ /**
2033
+ * Get and format html
2034
+ * @param app app
2035
+ */
2036
+ function extractHtml(app) {
2037
+ fetchSource(app.ssrUrl || app.url, app.name, { cache: 'no-cache' }).then((htmlStr) => {
2038
+ if (!htmlStr) {
2039
+ const msg = 'html is empty, please check in detail';
2040
+ app.onerror(new Error(msg));
2041
+ return logError(msg, app.name);
2042
+ }
2043
+ htmlStr = htmlStr
2044
+ .replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
2045
+ return match
2046
+ .replace(/<head/i, '<micro-app-head')
2047
+ .replace(/<\/head>/i, '</micro-app-head>');
2048
+ })
2049
+ .replace(/<body[^>]*>[\s\S]*?<\/body>/i, (match) => {
2050
+ return match
2051
+ .replace(/<body/i, '<micro-app-body')
2052
+ .replace(/<\/body>/i, '</micro-app-body>');
2053
+ });
2054
+ extractSourceDom(htmlStr, app);
2055
+ }).catch((e) => {
2056
+ logError(`Failed to fetch data from ${app.url}, micro-app stop rendering`, app.name, e);
2057
+ app.onLoadError(e);
2058
+ });
2059
+ }
1959
2060
 
1960
2061
  class EventCenter {
1961
2062
  constructor() {
@@ -2238,45 +2339,77 @@ function rebuildDataCenterSnapshot(microAppEventCenter) {
2238
2339
  }
2239
2340
  }
2240
2341
 
2342
+ function unmountNestedApp() {
2343
+ appInstanceMap.forEach(app => {
2344
+ // @ts-ignore
2345
+ app.container && getRootContainer(app.container).disconnectedCallback();
2346
+ });
2347
+ !window.__MICRO_APP_UMD_MODE__ && appInstanceMap.clear();
2348
+ }
2349
+ // release listener
2350
+ function releaseUnmountOfNestedApp() {
2351
+ if (window.__MICRO_APP_ENVIRONMENT__) {
2352
+ window.removeEventListener('unmount', unmountNestedApp, false);
2353
+ }
2354
+ }
2355
+ // if micro-app run in micro application, delete all next generation application when unmount event received
2356
+ // unmount event will auto release by sandbox
2357
+ function initEnvOfNestedApp() {
2358
+ if (window.__MICRO_APP_ENVIRONMENT__) {
2359
+ releaseUnmountOfNestedApp();
2360
+ window.addEventListener('unmount', unmountNestedApp, false);
2361
+ }
2362
+ }
2363
+
2241
2364
  /* eslint-disable no-return-assign */
2242
2365
  function isBoundedFunction(value) {
2243
2366
  if (isBoolean(value.__MICRO_APP_IS_BOUND_FUNCTION__))
2244
2367
  return value.__MICRO_APP_IS_BOUND_FUNCTION__;
2245
2368
  return value.__MICRO_APP_IS_BOUND_FUNCTION__ = isBoundFunction(value);
2246
2369
  }
2247
- function isConstructor(value) {
2248
- var _a;
2370
+ function isConstructorFunction(value) {
2249
2371
  if (isBoolean(value.__MICRO_APP_IS_CONSTRUCTOR__))
2250
2372
  return value.__MICRO_APP_IS_CONSTRUCTOR__;
2251
- const valueStr = value.toString();
2252
- const result = (((_a = value.prototype) === null || _a === void 0 ? void 0 : _a.constructor) === value &&
2253
- Object.getOwnPropertyNames(value.prototype).length > 1) ||
2254
- /^function\s+[A-Z]/.test(valueStr) ||
2255
- /^class\s+/.test(valueStr);
2256
- return value.__MICRO_APP_IS_CONSTRUCTOR__ = result;
2373
+ return value.__MICRO_APP_IS_CONSTRUCTOR__ = isConstructor(value);
2257
2374
  }
2258
2375
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
2259
- function bindFunctionToRawWindow(rawWindow, value) {
2260
- if (value.__MICRO_APP_BOUND_WINDOW_FUNCTION__)
2261
- return value.__MICRO_APP_BOUND_WINDOW_FUNCTION__;
2262
- if (!isConstructor(value) && !isBoundedFunction(value)) {
2263
- const bindRawWindowValue = value.bind(rawWindow);
2376
+ function bindFunctionToRawObject(rawObject, value, key = 'WINDOW') {
2377
+ const cacheKey = `__MICRO_APP_BOUND_${key}_FUNCTION__`;
2378
+ if (value[cacheKey])
2379
+ return value[cacheKey];
2380
+ if (!isConstructorFunction(value) && !isBoundedFunction(value)) {
2381
+ const bindRawObjectValue = value.bind(rawObject);
2264
2382
  for (const key in value) {
2265
- bindRawWindowValue[key] = value[key];
2383
+ bindRawObjectValue[key] = value[key];
2266
2384
  }
2267
2385
  if (value.hasOwnProperty('prototype')) {
2268
- rawDefineProperty(bindRawWindowValue, 'prototype', {
2386
+ rawDefineProperty(bindRawObjectValue, 'prototype', {
2269
2387
  value: value.prototype,
2270
2388
  configurable: true,
2271
2389
  enumerable: false,
2272
2390
  writable: true,
2273
2391
  });
2274
2392
  }
2275
- return value.__MICRO_APP_BOUND_WINDOW_FUNCTION__ = bindRawWindowValue;
2393
+ return value[cacheKey] = bindRawObjectValue;
2276
2394
  }
2277
2395
  return value;
2278
2396
  }
2279
2397
 
2398
+ // this events should be sent to the specified app
2399
+ const formatEventList = ['unmount', 'appstate-change'];
2400
+ /**
2401
+ * Format event name
2402
+ * @param eventName event name
2403
+ * @param appName app name
2404
+ */
2405
+ function formatEventName$1(eventName, appName) {
2406
+ var _a;
2407
+ if (formatEventList.includes(eventName) ||
2408
+ ((eventName === 'popstate' || eventName === 'hashchange') && ((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.useMemoryRouter))) {
2409
+ return `${eventName}-${appName}`;
2410
+ }
2411
+ return eventName;
2412
+ }
2280
2413
  // document.onclick binding list, the binding function of each application is unique
2281
2414
  const documentClickListMap = new Map();
2282
2415
  let hasRewriteDocumentOnClick = false;
@@ -2370,32 +2503,18 @@ function releaseEffectDocumentEvent() {
2370
2503
  document.addEventListener = globalEnv.rawDocumentAddEventListener;
2371
2504
  document.removeEventListener = globalEnv.rawDocumentRemoveEventListener;
2372
2505
  }
2373
- // this events should be sent to the specified app
2374
- const formatEventList = ['unmount', 'appstate-change'];
2375
- /**
2376
- * Format event name
2377
- * @param type event name
2378
- * @param microAppWindow micro window
2379
- */
2380
- function formatEventType(type, microAppWindow) {
2381
- if (formatEventList.includes(type)) {
2382
- return `${type}-${microAppWindow.__MICRO_APP_NAME__}`;
2383
- }
2384
- return type;
2385
- }
2386
2506
  /**
2387
2507
  * Rewrite side-effect events
2388
2508
  * @param microAppWindow micro window
2389
2509
  */
2390
- function effect(microAppWindow) {
2391
- const appName = microAppWindow.__MICRO_APP_NAME__;
2510
+ function effect(appName, microAppWindow) {
2392
2511
  const eventListenerMap = new Map();
2393
2512
  const intervalIdMap = new Map();
2394
2513
  const timeoutIdMap = new Map();
2395
2514
  const { rawWindow, rawDocument, rawWindowAddEventListener, rawWindowRemoveEventListener, rawSetInterval, rawSetTimeout, rawClearInterval, rawClearTimeout, rawDocumentRemoveEventListener, } = globalEnv;
2396
2515
  // listener may be null, e.g test-passive
2397
2516
  microAppWindow.addEventListener = function (type, listener, options) {
2398
- type = formatEventType(type, microAppWindow);
2517
+ type = formatEventName$1(type, appName);
2399
2518
  const listenerList = eventListenerMap.get(type);
2400
2519
  if (listenerList) {
2401
2520
  listenerList.add(listener);
@@ -2407,7 +2526,7 @@ function effect(microAppWindow) {
2407
2526
  rawWindowAddEventListener.call(rawWindow, type, listener, options);
2408
2527
  };
2409
2528
  microAppWindow.removeEventListener = function (type, listener, options) {
2410
- type = formatEventType(type, microAppWindow);
2529
+ type = formatEventName$1(type, appName);
2411
2530
  const listenerList = eventListenerMap.get(type);
2412
2531
  if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
2413
2532
  listenerList.delete(listener);
@@ -2534,59 +2653,1015 @@ function effect(microAppWindow) {
2534
2653
  };
2535
2654
  }
2536
2655
 
2537
- // Variables that can escape to rawWindow
2538
- const staticEscapeProperties = [
2539
- 'System',
2540
- '__cjsWrapper',
2541
- ];
2542
- // Variables that can only assigned to rawWindow
2543
- const escapeSetterKeyList = [
2544
- 'location',
2545
- ];
2546
- const globalPropertyList = ['window', 'self', 'globalThis'];
2547
- class SandBox {
2548
- constructor(appName, url) {
2549
- /**
2550
- * Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
2551
- * https://github.com/micro-zoe/micro-app/issues/234
2552
- */
2553
- this.scopeProperties = ['webpackJsonp', 'Vue'];
2554
- // Properties that can be escape to rawWindow
2555
- this.escapeProperties = [];
2556
- // Properties newly added to microAppWindow
2557
- this.injectedKeys = new Set();
2558
- // Properties escape to rawWindow, cleared when unmount
2559
- this.escapeKeys = new Set();
2656
+ // set micro app state to origin state
2657
+ function setMicroState(appName, rawState, microState) {
2658
+ const additionalState = {
2659
+ microAppState: assign({}, rawState === null || rawState === void 0 ? void 0 : rawState.microAppState, {
2660
+ [appName]: microState
2661
+ })
2662
+ };
2663
+ // create new state object
2664
+ return assign({}, rawState, additionalState);
2665
+ }
2666
+ // delete micro app state form origin state
2667
+ function removeMicroState(appName, rawState) {
2668
+ if (isPlainObject(rawState === null || rawState === void 0 ? void 0 : rawState.microAppState)) {
2669
+ if (!isUndefined(rawState.microAppState[appName])) {
2670
+ delete rawState.microAppState[appName];
2671
+ }
2672
+ if (!Object.keys(rawState.microAppState).length) {
2673
+ delete rawState.microAppState;
2674
+ }
2675
+ }
2676
+ // 生成新的state对象
2677
+ return assign({}, rawState);
2678
+ }
2679
+ // get micro app state form origin state
2680
+ function getMicroState(appName, state) {
2681
+ var _a;
2682
+ return ((_a = state === null || state === void 0 ? void 0 : state.microAppState) === null || _a === void 0 ? void 0 : _a[appName]) || null;
2683
+ }
2684
+ const ENC_AD_RE = /&/g; // %M1
2685
+ const ENC_EQ_RE = /=/g; // %M2
2686
+ const DEC_AD_RE = /%M1/g; // &
2687
+ const DEC_EQ_RE = /%M2/g; // =
2688
+ function encodeMicroPath(path) {
2689
+ return encodeURIComponent(commonDecode(path).replace(ENC_AD_RE, '%M1').replace(ENC_EQ_RE, '%M2'));
2690
+ }
2691
+ function decodeMicroPath(path) {
2692
+ return commonDecode(path).replace(DEC_AD_RE, '&').replace(DEC_EQ_RE, '=');
2693
+ }
2694
+ function commonDecode(path) {
2695
+ try {
2696
+ const decPath = decodeURIComponent(path);
2697
+ if (path === decPath || DEC_AD_RE.test(decPath) || DEC_EQ_RE.test(decPath))
2698
+ return decPath;
2699
+ return commonDecode(decPath);
2700
+ }
2701
+ catch (_a) {
2702
+ return path;
2703
+ }
2704
+ }
2705
+ // 格式化query参数key,防止与原有参数的冲突
2706
+ function formatQueryAppName(appName) {
2707
+ return `app-${appName}`;
2708
+ }
2709
+ // 根据浏览器url参数,获取当前子应用的path
2710
+ function getMicroPathFromURL(appName) {
2711
+ var _a, _b;
2712
+ const rawLocation = globalEnv.rawWindow.location;
2713
+ const queryObject = getQueryObjectFromURL(rawLocation.search, rawLocation.hash);
2714
+ 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)]);
2715
+ return isString(microPath) ? decodeMicroPath(microPath) : null;
2716
+ }
2717
+ // 将name=encodeUrl地址插入到浏览器url上
2718
+ function setMicroPathToURL(appName, microLocation) {
2719
+ let { pathname, search, hash } = globalEnv.rawWindow.location;
2720
+ const queryObject = getQueryObjectFromURL(search, hash);
2721
+ const encodedMicroPath = encodeMicroPath(microLocation.pathname +
2722
+ microLocation.search +
2723
+ microLocation.hash);
2724
+ let isAttach2Hash = false; // 基座是否是hash模式,这个其实也不准,只是表示参数加到了hash上
2725
+ // hash存在且search不存在,则认为是hash路由
2726
+ if (hash && !search) {
2727
+ isAttach2Hash = true;
2728
+ if (queryObject.hashQuery) {
2729
+ queryObject.hashQuery[formatQueryAppName(appName)] = encodedMicroPath;
2730
+ }
2731
+ else {
2732
+ queryObject.hashQuery = {
2733
+ [formatQueryAppName(appName)]: encodedMicroPath
2734
+ };
2735
+ }
2736
+ const baseHash = hash.includes('?') ? hash.slice(0, hash.indexOf('?') + 1) : hash + '?';
2737
+ hash = baseHash + stringifyQuery(queryObject.hashQuery);
2738
+ }
2739
+ else {
2740
+ if (queryObject.searchQuery) {
2741
+ queryObject.searchQuery[formatQueryAppName(appName)] = encodedMicroPath;
2742
+ }
2743
+ else {
2744
+ queryObject.searchQuery = {
2745
+ [formatQueryAppName(appName)]: encodedMicroPath
2746
+ };
2747
+ }
2748
+ search = '?' + stringifyQuery(queryObject.searchQuery);
2749
+ }
2750
+ return {
2751
+ fullPath: pathname + search + hash,
2752
+ isAttach2Hash,
2753
+ };
2754
+ }
2755
+ // 将name=encodeUrl的参数从浏览器url上删除
2756
+ function removeMicroPathFromURL(appName, targetLocation) {
2757
+ var _a, _b, _c, _d;
2758
+ let { pathname, search, hash } = targetLocation || globalEnv.rawWindow.location;
2759
+ const queryObject = getQueryObjectFromURL(search, hash);
2760
+ let isAttach2Hash = false;
2761
+ if ((_a = queryObject.hashQuery) === null || _a === void 0 ? void 0 : _a[formatQueryAppName(appName)]) {
2762
+ isAttach2Hash = true;
2763
+ (_b = queryObject.hashQuery) === null || _b === void 0 ? true : delete _b[formatQueryAppName(appName)];
2764
+ const hashQueryStr = stringifyQuery(queryObject.hashQuery);
2765
+ hash = hash.slice(0, hash.indexOf('?') + Number(Boolean(hashQueryStr))) + hashQueryStr;
2766
+ }
2767
+ else if ((_c = queryObject.searchQuery) === null || _c === void 0 ? void 0 : _c[formatQueryAppName(appName)]) {
2768
+ (_d = queryObject.searchQuery) === null || _d === void 0 ? true : delete _d[formatQueryAppName(appName)];
2769
+ const searchQueryStr = stringifyQuery(queryObject.searchQuery);
2770
+ search = searchQueryStr ? '?' + searchQueryStr : '';
2771
+ }
2772
+ return {
2773
+ fullPath: pathname + search + hash,
2774
+ isAttach2Hash,
2775
+ };
2776
+ }
2777
+ /**
2778
+ * 根据location获取query对象
2779
+ */
2780
+ function getQueryObjectFromURL(search, hash) {
2781
+ const queryObject = {};
2782
+ if (search !== '' && search !== '?') {
2783
+ queryObject.searchQuery = parseQuery(search.slice(1));
2784
+ }
2785
+ if (hash.includes('?')) {
2786
+ queryObject.hashQuery = parseQuery(hash.slice(hash.indexOf('?') + 1));
2787
+ }
2788
+ return queryObject;
2789
+ }
2790
+ /**
2791
+ * get microApp path from browser URL without hash
2792
+ */
2793
+ function getNoHashMicroPathFromURL(appName, baseUrl) {
2794
+ const microPath = getMicroPathFromURL(appName);
2795
+ if (!microPath)
2796
+ return '';
2797
+ const formatLocation = createURL(microPath, baseUrl);
2798
+ return formatLocation.origin + formatLocation.pathname + formatLocation.search;
2799
+ }
2800
+
2801
+ /**
2802
+ * dispatch PopStateEvent & HashChangeEvent to child app
2803
+ * each child app will listen for popstate event when sandbox start
2804
+ * and release it when sandbox stop
2805
+ * @param appName app name
2806
+ * @returns release callback
2807
+ */
2808
+ function addHistoryListener(appName) {
2809
+ const rawWindow = globalEnv.rawWindow;
2810
+ // handle popstate event and distribute to child app
2811
+ const popStateHandler = (e) => {
2812
+ /**
2813
+ * 1. unmount app & hidden keep-alive app will not receive popstate event
2814
+ * 2. filter out onlyForBrowser
2815
+ */
2816
+ if (getActiveApps(true).includes(appName) && !e.onlyForBrowser) {
2817
+ const microPath = getMicroPathFromURL(appName);
2818
+ const app = appInstanceMap.get(appName);
2819
+ const proxyWindow = app.sandBox.proxyWindow;
2820
+ let isHashChange = false;
2821
+ // for hashChangeEvent
2822
+ const oldHref = proxyWindow.location.href;
2823
+ // Do not attach micro state to url when microPath is empty
2824
+ if (microPath) {
2825
+ const oldHash = proxyWindow.location.hash;
2826
+ updateMicroLocation(appName, microPath, proxyWindow.location);
2827
+ isHashChange = proxyWindow.location.hash !== oldHash;
2828
+ }
2829
+ // dispatch formatted popStateEvent to child
2830
+ dispatchPopStateEventToMicroApp(appName, proxyWindow, rawWindow.history.state);
2831
+ // dispatch formatted hashChangeEvent to child when hash change
2832
+ if (isHashChange)
2833
+ dispatchHashChangeEventToMicroApp(appName, proxyWindow, oldHref);
2834
+ // clear element scope before trigger event of next app
2835
+ removeDomScope();
2836
+ }
2837
+ };
2838
+ rawWindow.addEventListener('popstate', popStateHandler);
2839
+ return () => {
2840
+ rawWindow.removeEventListener('popstate', popStateHandler);
2841
+ };
2842
+ }
2843
+ /**
2844
+ * dispatch formatted popstate event to microApp
2845
+ * @param appName app name
2846
+ * @param proxyWindow sandbox window
2847
+ * @param eventState history.state
2848
+ */
2849
+ function dispatchPopStateEventToMicroApp(appName, proxyWindow, eventState) {
2850
+ // create PopStateEvent named popstate-appName with sub app state
2851
+ const newPopStateEvent = new PopStateEvent(formatEventName$1('popstate', appName), { state: getMicroState(appName, eventState) });
2852
+ globalEnv.rawWindow.dispatchEvent(newPopStateEvent);
2853
+ // call function window.onpopstate if it exists
2854
+ typeof proxyWindow.onpopstate === 'function' && proxyWindow.onpopstate(newPopStateEvent);
2855
+ }
2856
+ /**
2857
+ * dispatch formatted hashchange event to microApp
2858
+ * @param appName app name
2859
+ * @param proxyWindow sandbox window
2860
+ * @param oldHref old href
2861
+ */
2862
+ function dispatchHashChangeEventToMicroApp(appName, proxyWindow, oldHref) {
2863
+ const newHashChangeEvent = new HashChangeEvent(formatEventName$1('hashchange', appName), {
2864
+ newURL: proxyWindow.location.href,
2865
+ oldURL: oldHref,
2866
+ });
2867
+ globalEnv.rawWindow.dispatchEvent(newHashChangeEvent);
2868
+ // call function window.onhashchange if it exists
2869
+ typeof proxyWindow.onhashchange === 'function' && proxyWindow.onhashchange(newHashChangeEvent);
2870
+ }
2871
+ /**
2872
+ * dispatch native PopStateEvent, simulate location behavior
2873
+ * @param onlyForBrowser only dispatch PopStateEvent to browser
2874
+ */
2875
+ function dispatchNativePopStateEvent(onlyForBrowser) {
2876
+ const event = new PopStateEvent('popstate', { state: null });
2877
+ if (onlyForBrowser)
2878
+ event.onlyForBrowser = true;
2879
+ globalEnv.rawWindow.dispatchEvent(event);
2880
+ }
2881
+ /**
2882
+ * dispatch hashchange event to browser
2883
+ * @param oldHref old href of rawWindow.location
2884
+ */
2885
+ function dispatchNativeHashChangeEvent(oldHref) {
2886
+ const newHashChangeEvent = new HashChangeEvent('hashchange', {
2887
+ newURL: globalEnv.rawWindow.location.href,
2888
+ oldURL: oldHref,
2889
+ });
2890
+ globalEnv.rawWindow.dispatchEvent(newHashChangeEvent);
2891
+ }
2892
+ /**
2893
+ * dispatch popstate & hashchange event to browser
2894
+ * @param onlyForBrowser only dispatch event to browser
2895
+ * @param oldHref old href of rawWindow.location
2896
+ */
2897
+ function dispatchNativeEvent(onlyForBrowser, oldHref) {
2898
+ // clear element scope before dispatch global event
2899
+ removeDomScope();
2900
+ dispatchNativePopStateEvent(onlyForBrowser);
2901
+ if (oldHref) {
2902
+ dispatchNativeHashChangeEvent(oldHref);
2903
+ }
2904
+ }
2905
+
2906
+ /**
2907
+ * create proxyHistory for microApp
2908
+ * MDN https://developer.mozilla.org/en-US/docs/Web/API/History
2909
+ * @param appName app name
2910
+ * @param microLocation microApp location
2911
+ */
2912
+ function createMicroHistory(appName, microLocation) {
2913
+ const rawHistory = globalEnv.rawWindow.history;
2914
+ function getMicroHistoryMethod(methodName) {
2915
+ return function (...rests) {
2916
+ if (isString(rests[2]) || isURL(rests[2])) {
2917
+ const targetLocation = createURL(rests[2], microLocation.href);
2918
+ if (targetLocation.origin === microLocation.origin) {
2919
+ navigateWithNativeEvent(methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rawHistory.state, rests[0]), rests[1]);
2920
+ const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
2921
+ if (targetFullPath !== microLocation.fullPath) {
2922
+ updateMicroLocation(appName, targetFullPath, microLocation);
2923
+ }
2924
+ }
2925
+ else {
2926
+ rawHistory[methodName].apply(rawHistory, rests);
2927
+ }
2928
+ }
2929
+ else {
2930
+ rawHistory[methodName].apply(rawHistory, rests);
2931
+ }
2932
+ };
2933
+ }
2934
+ const pushState = getMicroHistoryMethod('pushState');
2935
+ const replaceState = getMicroHistoryMethod('replaceState');
2936
+ return new Proxy(rawHistory, {
2937
+ get(target, key) {
2938
+ if (key === 'state') {
2939
+ return getMicroState(appName, rawHistory.state);
2940
+ }
2941
+ else if (key === 'pushState') {
2942
+ return pushState;
2943
+ }
2944
+ else if (key === 'replaceState') {
2945
+ return replaceState;
2946
+ }
2947
+ const rawValue = Reflect.get(target, key);
2948
+ return isFunction(rawValue) ? bindFunctionToRawObject(target, rawValue, 'HISTORY') : rawValue;
2949
+ }
2950
+ });
2951
+ }
2952
+ /**
2953
+ * navigate to new path base on native method of history
2954
+ * @param methodName pushState/replaceState
2955
+ * @param fullPath full path
2956
+ * @param state history.state, default is null
2957
+ * @param title history.title, default is ''
2958
+ */
2959
+ function nativeHistoryNavigate(methodName, fullPath, state = null, title = '') {
2960
+ globalEnv.rawWindow.history[methodName](state, title, fullPath);
2961
+ }
2962
+ /**
2963
+ * Navigate to new path, and dispatch native popStateEvent/hashChangeEvent to browser
2964
+ * Use scenes:
2965
+ * 1. mount/unmount through attachRouteToBrowserURL with limited popstateEvent
2966
+ * 2. proxyHistory.pushState/replaceState with limited popstateEvent
2967
+ * 3. api microApp.router.push/replace
2968
+ * 4. proxyLocation.hash = xxx
2969
+ * @param methodName pushState/replaceState
2970
+ * @param result result of add/remove microApp path on browser url
2971
+ * @param onlyForBrowser only dispatch event to browser
2972
+ * @param state history.state, not required
2973
+ * @param title history.title, not required
2974
+ */
2975
+ function navigateWithNativeEvent(methodName, result, onlyForBrowser, state, title) {
2976
+ const rawLocation = globalEnv.rawWindow.location;
2977
+ const oldFullPath = rawLocation.pathname + rawLocation.search + rawLocation.hash;
2978
+ const oldHref = result.isAttach2Hash && oldFullPath !== result.fullPath ? rawLocation.href : null;
2979
+ // navigate with native history method
2980
+ nativeHistoryNavigate(methodName, result.fullPath, state, title);
2981
+ if (oldFullPath !== result.fullPath)
2982
+ dispatchNativeEvent(onlyForBrowser, oldHref);
2983
+ }
2984
+ /**
2985
+ * update browser url when mount/unmount/hidden/show/attachToURL/attachAllToURL
2986
+ * just attach microRoute info to browser, dispatch event to base app(exclude child)
2987
+ * @param result result of add/remove microApp path on browser url
2988
+ * @param state history.state
2989
+ */
2990
+ function attachRouteToBrowserURL(result, state) {
2991
+ navigateWithNativeEvent('replaceState', result, true, state);
2992
+ }
2993
+ /**
2994
+ * When path is same, keep the microAppState in history.state
2995
+ * Fix bug of missing microAppState in next.js & angular
2996
+ * @param method history.pushState/replaceState
2997
+ */
2998
+ function patchHistoryState(method) {
2999
+ const rawWindow = globalEnv.rawWindow;
3000
+ return function (...rests) {
3001
+ var _a;
3002
+ if (((_a = rawWindow.history.state) === null || _a === void 0 ? void 0 : _a.microAppState) &&
3003
+ (!isPlainObject(rests[0]) || !rests[0].microAppState) &&
3004
+ (isString(rests[2]) || isURL(rests[2]))) {
3005
+ const currentHref = rawWindow.location.href;
3006
+ const targetLocation = createURL(rests[2], currentHref);
3007
+ if (targetLocation.href === currentHref) {
3008
+ rests[0] = assign({}, rests[0], {
3009
+ microAppState: rawWindow.history.state.microAppState,
3010
+ });
3011
+ }
3012
+ }
3013
+ method.apply(rawWindow.history, rests);
3014
+ };
3015
+ }
3016
+ let isReWriteHistoryState = false;
3017
+ /**
3018
+ * rewrite history.pushState/replaceState
3019
+ * used to fix the problem that the microAppState maybe missing when mainApp navigate to same path
3020
+ * e.g: when nextjs, angular receive popstate event, they will use history.replaceState to update browser url with a new state object
3021
+ */
3022
+ function rewriteHistoryState() {
3023
+ // filter nest app
3024
+ if (!isReWriteHistoryState && !window.__MICRO_APP_ENVIRONMENT__) {
3025
+ isReWriteHistoryState = true;
3026
+ const rawWindow = globalEnv.rawWindow;
3027
+ rawWindow.history.pushState = patchHistoryState(rawWindow.history.pushState);
3028
+ rawWindow.history.replaceState = patchHistoryState(rawWindow.history.replaceState);
3029
+ }
3030
+ }
3031
+
3032
+ function createRouterApi() {
3033
+ /**
3034
+ * common handler for router.push/router.replace method
3035
+ * @param appName app name
3036
+ * @param methodName replaceState/pushState
3037
+ * @param targetLocation target location
3038
+ * @param state to.state
3039
+ */
3040
+ function navigateWithRawHistory(appName, methodName, targetLocation, state) {
3041
+ navigateWithNativeEvent(methodName, setMicroPathToURL(appName, targetLocation), false, setMicroState(appName, globalEnv.rawWindow.history.state, state !== null && state !== void 0 ? state : null));
3042
+ // clear element scope after navigate
3043
+ removeDomScope();
3044
+ }
3045
+ /**
3046
+ * create method of router.push/replace
3047
+ * NOTE:
3048
+ * 1. The same fullPath will be blocked
3049
+ * 2. name & path is required
3050
+ * 3. path is fullPath except for the domain (the domain can be taken, but not valid)
3051
+ * @param replace use router.replace?
3052
+ */
3053
+ function createNavigationMethod(replace) {
3054
+ return function (to) {
3055
+ const appName = formatAppName(to.name);
3056
+ if (appName && isString(to.path)) {
3057
+ const app = appInstanceMap.get(appName);
3058
+ if (app && (!app.sandBox || !app.useMemoryRouter)) {
3059
+ return logError(`navigation failed, memory router of app ${appName} is closed`);
3060
+ }
3061
+ // active apps, include hidden keep-alive app
3062
+ if (getActiveApps().includes(appName)) {
3063
+ const microLocation = app.sandBox.proxyWindow.location;
3064
+ const targetLocation = createURL(to.path, microLocation.href);
3065
+ // Only get path data, even if the origin is different from microApp
3066
+ const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3067
+ if (microLocation.fullPath !== targetFullPath || getMicroPathFromURL(appName) !== targetFullPath) {
3068
+ const methodName = (replace && to.replace !== false) || to.replace === true ? 'replaceState' : 'pushState';
3069
+ navigateWithRawHistory(appName, methodName, targetLocation, to.state);
3070
+ }
3071
+ }
3072
+ else {
3073
+ /**
3074
+ * app not exit or unmounted, update browser URL with replaceState
3075
+ * use base app location.origin as baseURL
3076
+ */
3077
+ const rawLocation = globalEnv.rawWindow.location;
3078
+ const targetLocation = createURL(to.path, rawLocation.origin);
3079
+ const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3080
+ if (getMicroPathFromURL(appName) !== targetFullPath) {
3081
+ navigateWithRawHistory(appName, to.replace === false ? 'pushState' : 'replaceState', targetLocation, to.state);
3082
+ }
3083
+ }
3084
+ }
3085
+ else {
3086
+ logError(`navigation failed, name & path are required when use router.${replace ? 'replace' : 'push'}`);
3087
+ }
3088
+ };
3089
+ }
3090
+ // create method of router.go/back/forward
3091
+ function createRawHistoryMethod(methodName) {
3092
+ return function (...rests) {
3093
+ return globalEnv.rawWindow.history[methodName](...rests);
3094
+ };
3095
+ }
3096
+ const beforeGuards = useSetRecord();
3097
+ const afterGuards = useSetRecord();
3098
+ /**
3099
+ * run all of beforeEach/afterEach guards
3100
+ * NOTE:
3101
+ * 1. Modify browser url first, and then run guards,
3102
+ * consistent with the browser forward & back button
3103
+ * 2. Note the element binding
3104
+ * @param appName app name
3105
+ * @param to target location
3106
+ * @param from old location
3107
+ * @param guards guards list
3108
+ */
3109
+ function runGuards(appName, to, from, guards) {
3110
+ // clear element scope before execute function of parent
3111
+ removeDomScope();
3112
+ for (const guard of guards) {
3113
+ if (isFunction(guard)) {
3114
+ guard(appName, to, from);
3115
+ }
3116
+ else if (isPlainObject(guard) && isFunction(guard[appName])) {
3117
+ guard[appName](to, from);
3118
+ }
3119
+ }
3120
+ }
3121
+ /**
3122
+ * global hook for router
3123
+ * update router information base on microLocation
3124
+ * @param appName app name
3125
+ * @param microLocation location of microApp
3126
+ */
3127
+ function executeNavigationGuard(appName, to, from) {
3128
+ router.current.set(appName, to);
3129
+ runGuards(appName, to, from, beforeGuards.list());
3130
+ requestIdleCallback(() => {
3131
+ runGuards(appName, to, from, afterGuards.list());
3132
+ });
3133
+ }
3134
+ function clearRouterWhenUnmount(appName) {
3135
+ router.current.delete(appName);
3136
+ }
3137
+ /**
3138
+ * NOTE:
3139
+ * 1. sandbox not open
3140
+ * 2. useMemoryRouter is false
3141
+ */
3142
+ function commonHandlerForAttachToURL(appName) {
3143
+ const app = appInstanceMap.get(appName);
3144
+ if (app.sandBox && app.useMemoryRouter) {
3145
+ attachRouteToBrowserURL(setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, globalEnv.rawWindow.history.state, null));
3146
+ }
3147
+ }
3148
+ /**
3149
+ * Attach specified active app router info to browser url
3150
+ * @param appName app name
3151
+ */
3152
+ function attachToURL(appName) {
3153
+ appName = formatAppName(appName);
3154
+ if (appName && getActiveApps().includes(appName)) {
3155
+ commonHandlerForAttachToURL(appName);
3156
+ }
3157
+ }
3158
+ /**
3159
+ * Attach all active app router info to browser url
3160
+ */
3161
+ function attachAllToURL() {
3162
+ getActiveApps().forEach(appName => commonHandlerForAttachToURL(appName));
3163
+ }
3164
+ function createDefaultPageApi() {
3165
+ // defaultPage data
3166
+ const defaultPageRecord = useMapRecord();
3167
+ /**
3168
+ * defaultPage only effect when mount, and has lower priority than query on browser url
3169
+ * @param appName app name
3170
+ * @param path page path
3171
+ */
3172
+ function setDefaultPage(appName, path) {
3173
+ appName = formatAppName(appName);
3174
+ if (!appName || !path) {
3175
+ if (process.env.NODE_ENV !== 'production') {
3176
+ if (!appName) {
3177
+ logWarn(`setDefaultPage: invalid appName "${appName}"`);
3178
+ }
3179
+ else {
3180
+ logWarn('setDefaultPage: path is required');
3181
+ }
3182
+ }
3183
+ return noopFalse;
3184
+ }
3185
+ return defaultPageRecord.add(appName, path);
3186
+ }
3187
+ function removeDefaultPage(appName) {
3188
+ appName = formatAppName(appName);
3189
+ if (!appName)
3190
+ return false;
3191
+ return defaultPageRecord.delete(appName);
3192
+ }
3193
+ return {
3194
+ setDefaultPage,
3195
+ removeDefaultPage,
3196
+ getDefaultPage: defaultPageRecord.get,
3197
+ };
3198
+ }
3199
+ function createBaseRouterApi() {
3200
+ /**
3201
+ * Record base app router, let child app control base app navigation
3202
+ */
3203
+ let baseRouterProxy = null;
3204
+ function setBaseAppRouter(baseRouter) {
3205
+ if (isObject(baseRouter)) {
3206
+ baseRouterProxy = new Proxy(baseRouter, {
3207
+ get(target, key) {
3208
+ removeDomScope();
3209
+ const rawValue = Reflect.get(target, key);
3210
+ return isFunction(rawValue) ? bindFunctionToRawObject(target, rawValue, 'BASEROUTER') : rawValue;
3211
+ }
3212
+ });
3213
+ }
3214
+ else if (process.env.NODE_ENV !== 'production') {
3215
+ logWarn('setBaseAppRouter: Invalid base router');
3216
+ }
3217
+ }
3218
+ return {
3219
+ setBaseAppRouter,
3220
+ getBaseAppRouter: () => baseRouterProxy,
3221
+ };
3222
+ }
3223
+ // Router API for developer
3224
+ const router = Object.assign(Object.assign({ current: new Map(), encode: encodeMicroPath, decode: decodeMicroPath, push: createNavigationMethod(false), replace: createNavigationMethod(true), go: createRawHistoryMethod('go'), back: createRawHistoryMethod('back'), forward: createRawHistoryMethod('forward'), beforeEach: beforeGuards.add, afterEach: afterGuards.add, attachToURL,
3225
+ attachAllToURL }, createDefaultPageApi()), createBaseRouterApi());
3226
+ return {
3227
+ router,
3228
+ executeNavigationGuard,
3229
+ clearRouterWhenUnmount,
3230
+ };
3231
+ }
3232
+ const { router, executeNavigationGuard, clearRouterWhenUnmount, } = createRouterApi();
3233
+
3234
+ const shadowLocationKeys = ['href', 'pathname', 'search', 'hash'];
3235
+ // origin is readonly, so we ignore when updateMicroLocation
3236
+ const locationKeys = [...shadowLocationKeys, 'host', 'hostname', 'port', 'protocol', 'search'];
3237
+ // origin, fullPath is necessary for guardLocation
3238
+ const guardLocationKeys = [...locationKeys, 'origin', 'fullPath'];
3239
+ /**
3240
+ * Create location for microApp, each microApp has only one location object, it is a reference type
3241
+ * MDN https://developer.mozilla.org/en-US/docs/Web/API/Location
3242
+ * @param appName app name
3243
+ * @param url app url
3244
+ */
3245
+ function createMicroLocation(appName, url) {
3246
+ const rawWindow = globalEnv.rawWindow;
3247
+ const rawLocation = rawWindow.location;
3248
+ // microLocation is the location of child app, it is globally unique
3249
+ const microLocation = createURL(url);
3250
+ // shadowLocation is the current location information (href, pathname, search, hash)
3251
+ const shadowLocation = {
3252
+ href: microLocation.href,
3253
+ pathname: microLocation.pathname,
3254
+ search: microLocation.search,
3255
+ hash: microLocation.hash,
3256
+ };
3257
+ /**
3258
+ * Common handler for href, assign, replace
3259
+ * It is mainly used to deal with special scenes about hash
3260
+ * @param value target path
3261
+ * @param methodName pushState/replaceState
3262
+ * @returns origin value or formatted value
3263
+ */
3264
+ const commonHandler = (value, methodName) => {
3265
+ const targetLocation = createURL(value, microLocation.href);
3266
+ // Even if the origin is the same, developers still have the possibility of want to jump to a new page
3267
+ if (targetLocation.origin === microLocation.origin) {
3268
+ const setMicroPathResult = setMicroPathToURL(appName, targetLocation);
3269
+ /**
3270
+ * change hash with location.href will not trigger the browser reload
3271
+ * so we use pushState & reload to imitate href behavior
3272
+ * NOTE:
3273
+ * 1. if child app only change hash, it should not trigger browser reload
3274
+ * 2. if address is same and has hash, it should not add route stack
3275
+ */
3276
+ if (targetLocation.pathname === shadowLocation.pathname &&
3277
+ targetLocation.search === shadowLocation.search) {
3278
+ let oldHref = null;
3279
+ if (targetLocation.hash !== shadowLocation.hash) {
3280
+ if (setMicroPathResult.isAttach2Hash)
3281
+ oldHref = rawLocation.href;
3282
+ nativeHistoryNavigate(methodName, setMicroPathResult.fullPath);
3283
+ }
3284
+ if (targetLocation.hash) {
3285
+ dispatchNativeEvent(false, oldHref);
3286
+ }
3287
+ else {
3288
+ rawLocation.reload();
3289
+ }
3290
+ return void 0;
3291
+ /**
3292
+ * when baseApp is hash router, address change of child can not reload browser
3293
+ * so we imitate behavior of browser (reload)
3294
+ */
3295
+ }
3296
+ else if (setMicroPathResult.isAttach2Hash) {
3297
+ nativeHistoryNavigate(methodName, setMicroPathResult.fullPath);
3298
+ rawLocation.reload();
3299
+ return void 0;
3300
+ }
3301
+ value = setMicroPathResult.fullPath;
3302
+ }
3303
+ return value;
3304
+ };
3305
+ /**
3306
+ * create location PropertyDescriptor (href, pathname, search, hash)
3307
+ * @param key property name
3308
+ * @param setter setter of location property
3309
+ */
3310
+ function createPropertyDescriptor(getter, setter) {
3311
+ return {
3312
+ enumerable: true,
3313
+ configurable: true,
3314
+ get: getter,
3315
+ set: setter,
3316
+ };
3317
+ }
3318
+ /**
3319
+ * common handler for location.pathname & location.search
3320
+ * @param targetPath target fullPath
3321
+ * @param key pathname/search
3322
+ */
3323
+ function handleForPathNameAndSearch(targetPath, key) {
3324
+ const targetLocation = createURL(targetPath, url);
3325
+ // When the browser url has a hash value, the same pathname/search will not refresh browser
3326
+ if (targetLocation[key] === shadowLocation[key] && shadowLocation.hash) {
3327
+ // The href has not changed, not need to dispatch hashchange event
3328
+ dispatchNativeEvent(false);
3329
+ }
3330
+ else {
3331
+ /**
3332
+ * When the value is the same, no new route stack will be added
3333
+ * Special scenes such as:
3334
+ * pathname: /path ==> /path#hash, /path ==> /path?query
3335
+ * search: ?query ==> ?query#hash
3336
+ */
3337
+ nativeHistoryNavigate(targetLocation[key] === shadowLocation[key] ? 'replaceState' : 'pushState', setMicroPathToURL(appName, targetLocation).fullPath);
3338
+ rawLocation.reload();
3339
+ }
3340
+ }
3341
+ /**
3342
+ * Special processing for four keys: href, pathname, search and hash
3343
+ * They take values from shadowLocation, and require special operations when assigning values
3344
+ */
3345
+ rawDefineProperties(microLocation, {
3346
+ href: createPropertyDescriptor(() => shadowLocation.href, (value) => {
3347
+ const targetPath = commonHandler(value, 'pushState');
3348
+ if (targetPath)
3349
+ rawLocation.href = targetPath;
3350
+ }),
3351
+ pathname: createPropertyDescriptor(() => shadowLocation.pathname, (value) => {
3352
+ const targetPath = ('/' + value).replace(/^\/+/, '/') + shadowLocation.search + shadowLocation.hash;
3353
+ handleForPathNameAndSearch(targetPath, 'pathname');
3354
+ }),
3355
+ search: createPropertyDescriptor(() => shadowLocation.search, (value) => {
3356
+ const targetPath = shadowLocation.pathname + ('?' + value).replace(/^\?+/, '?') + shadowLocation.hash;
3357
+ handleForPathNameAndSearch(targetPath, 'search');
3358
+ }),
3359
+ hash: createPropertyDescriptor(() => shadowLocation.hash, (value) => {
3360
+ const targetPath = shadowLocation.pathname + shadowLocation.search + ('#' + value).replace(/^#+/, '#');
3361
+ const targetLocation = createURL(targetPath, url);
3362
+ // The same hash will not trigger popStateEvent
3363
+ if (targetLocation.hash !== shadowLocation.hash) {
3364
+ navigateWithNativeEvent('pushState', setMicroPathToURL(appName, targetLocation), false);
3365
+ }
3366
+ }),
3367
+ fullPath: createPropertyDescriptor(() => shadowLocation.pathname + shadowLocation.search + shadowLocation.hash, noop),
3368
+ });
3369
+ const createLocationMethod = (locationMethodName) => {
3370
+ return function (value) {
3371
+ const targetPath = commonHandler(value, locationMethodName === 'assign' ? 'pushState' : 'replaceState');
3372
+ if (targetPath)
3373
+ rawLocation[locationMethodName](targetPath);
3374
+ };
3375
+ };
3376
+ return assign(microLocation, {
3377
+ assign: createLocationMethod('assign'),
3378
+ replace: createLocationMethod('replace'),
3379
+ reload: (forcedReload) => rawLocation.reload(forcedReload),
3380
+ shadowLocation,
3381
+ });
3382
+ }
3383
+ /**
3384
+ * create guardLocation by microLocation, used for router guard
3385
+ */
3386
+ function createGuardLocation(appName, microLocation) {
3387
+ const guardLocation = assign({ name: appName }, microLocation);
3388
+ // The prototype values on the URL needs to be manually transferred
3389
+ for (const key of guardLocationKeys)
3390
+ guardLocation[key] = microLocation[key];
3391
+ return guardLocation;
3392
+ }
3393
+ // for updateBrowserURLWithLocation when initial
3394
+ function autoTriggerNavigationGuard(appName, microLocation) {
3395
+ executeNavigationGuard(appName, createGuardLocation(appName, microLocation), createGuardLocation(appName, microLocation));
3396
+ }
3397
+ /**
3398
+ * The following scenes will trigger location update:
3399
+ * 1. pushState/replaceState
3400
+ * 2. popStateEvent
3401
+ * 3. query on browser url when init sub app
3402
+ * 4. set defaultPage when when init sub app
3403
+ * NOTE:
3404
+ * 1. update browser URL first, and then update microLocation
3405
+ * 2. the same fullPath will not trigger router guards
3406
+ * @param appName app name
3407
+ * @param path target path
3408
+ * @param base base url
3409
+ * @param microLocation micro app location
3410
+ * @param type auto prevent
3411
+ */
3412
+ function updateMicroLocation(appName, path, microLocation, type) {
3413
+ const newLocation = createURL(path, microLocation.href);
3414
+ // record old values of microLocation to `from`
3415
+ const from = createGuardLocation(appName, microLocation);
3416
+ for (const key of locationKeys) {
3417
+ if (shadowLocationKeys.includes(key)) {
3418
+ // reference of shadowLocation
3419
+ microLocation.shadowLocation[key] = newLocation[key];
3420
+ }
3421
+ else {
3422
+ // @ts-ignore reference of microLocation
3423
+ microLocation[key] = newLocation[key];
3424
+ }
3425
+ }
3426
+ // update latest values of microLocation to `to`
3427
+ const to = createGuardLocation(appName, microLocation);
3428
+ // The hook called only when fullPath changed
3429
+ if (type === 'auto' || (from.fullPath !== to.fullPath && type !== 'prevent')) {
3430
+ executeNavigationGuard(appName, to, from);
3431
+ }
3432
+ }
3433
+
3434
+ /**
3435
+ * The router system has two operations: read and write
3436
+ * Read through location and write through history & location
3437
+ * @param appName app name
3438
+ * @param url app url
3439
+ * @returns MicroRouter
3440
+ */
3441
+ function createMicroRouter(appName, url) {
3442
+ rewriteHistoryState();
3443
+ const microLocation = createMicroLocation(appName, url);
3444
+ return {
3445
+ microLocation,
3446
+ microHistory: createMicroHistory(appName, microLocation),
3447
+ };
3448
+ }
3449
+ // 当沙箱执行start, 或者隐藏的keep-alive应用重新渲染时时才根据浏览器url更新location 或者 将参数更新到url上
3450
+ function initRouteStateWithURL(appName, microLocation, defaultPage) {
3451
+ const microPath = getMicroPathFromURL(appName);
3452
+ if (microPath) {
3453
+ updateMicroLocation(appName, microPath, microLocation, 'auto');
3454
+ }
3455
+ else {
3456
+ updateBrowserURLWithLocation(appName, microLocation, defaultPage);
3457
+ }
3458
+ }
3459
+ /**
3460
+ * initialize browser information according to microLocation
3461
+ * called on sandbox.start or reshow of keep-alive app
3462
+ */
3463
+ function updateBrowserURLWithLocation(appName, microLocation, defaultPage) {
3464
+ // update microLocation with defaultPage
3465
+ if (defaultPage)
3466
+ updateMicroLocation(appName, defaultPage, microLocation, 'prevent');
3467
+ // attach microApp route info to browser URL
3468
+ attachRouteToBrowserURL(setMicroPathToURL(appName, microLocation), setMicroState(appName, globalEnv.rawWindow.history.state, null));
3469
+ // trigger guards after change browser URL
3470
+ autoTriggerNavigationGuard(appName, microLocation);
3471
+ }
3472
+ /**
3473
+ * In any case, microPath & microState will be removed from browser, but location will be initialized only when keep-router-state is false
3474
+ * @param appName app name
3475
+ * @param url app url
3476
+ * @param microLocation location of microApp
3477
+ * @param keepRouteState keep-router-state is only used to control whether to clear the location of microApp
3478
+ */
3479
+ function clearRouteStateFromURL(appName, url, microLocation, keepRouteState) {
3480
+ if (!keepRouteState) {
3481
+ const { pathname, search, hash } = createURL(url);
3482
+ updateMicroLocation(appName, pathname + search + hash, microLocation, 'prevent');
3483
+ }
3484
+ removeStateAndPathFromBrowser(appName);
3485
+ clearRouterWhenUnmount(appName);
3486
+ }
3487
+ /**
3488
+ * remove microState from history.state and remove microPath from browserURL
3489
+ * called on sandbox.stop or hidden of keep-alive app
3490
+ */
3491
+ function removeStateAndPathFromBrowser(appName) {
3492
+ attachRouteToBrowserURL(removeMicroPathFromURL(appName), removeMicroState(appName, globalEnv.rawWindow.history.state));
3493
+ }
3494
+
3495
+ /**
3496
+ * https://developer.mozilla.org/en-US/docs/Web/API/fetch
3497
+ * Promise<Response> fetch(input[, init])
3498
+ * input: string/Request
3499
+ * init?: object
3500
+ * @param url app url
3501
+ * @param target proxy target
3502
+ */
3503
+ function createMicroFetch(url, target) {
3504
+ const rawFetch = !isUndefined(target) ? target : globalEnv.rawWindow.fetch;
3505
+ if (!isFunction(rawFetch))
3506
+ return rawFetch;
3507
+ return function microFetch(input, init, ...rests) {
3508
+ if (isString(input) || isURL(input)) {
3509
+ input = createURL(input, url).toString();
3510
+ }
3511
+ /**
3512
+ * When fetch rewrite by baseApp, domScope still active when exec rawWindow.fetch
3513
+ * If baseApp operate dom in fetch, it will cause error
3514
+ * The same for XMLHttpRequest, EventSource
3515
+ * e.g.
3516
+ * baseApp: <script crossorigin src="https://sgm-static.jd.com/sgm-2.8.0.js" name="SGMH5" sid="6f88a6e4ba4b4ae5acef2ec22c075085" appKey="jdb-adminb2b-pc"></script>
3517
+ */
3518
+ removeDomScope();
3519
+ return rawFetch.call(globalEnv.rawWindow, input, init, ...rests);
3520
+ };
3521
+ }
3522
+ /**
3523
+ * https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
3524
+ * https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
3525
+ * @param url app url
3526
+ * @param target proxy target
3527
+ */
3528
+ function createMicroXMLHttpRequest(url, target) {
3529
+ const rawXMLHttpRequest = !isUndefined(target) ? target : globalEnv.rawWindow.XMLHttpRequest;
3530
+ if (!isConstructor(rawXMLHttpRequest))
3531
+ return rawXMLHttpRequest;
3532
+ return class MicroXMLHttpRequest extends rawXMLHttpRequest {
3533
+ open(method, reqUrl, ...rests) {
3534
+ if ((isString(reqUrl) && !/^f(ile|tp):\/\//.test(reqUrl)) || isURL(reqUrl)) {
3535
+ reqUrl = createURL(reqUrl, url).toString();
3536
+ }
3537
+ removeDomScope();
3538
+ super.open(method, reqUrl, ...rests);
3539
+ }
3540
+ };
3541
+ }
3542
+ function useMicroEventSource() {
3543
+ let eventSourceMap;
3544
+ /**
3545
+ * https://developer.mozilla.org/en-US/docs/Web/API/EventSource
3546
+ * pc = new EventSource(url[, configuration])
3547
+ * url: string/Request
3548
+ * configuration?: object
3549
+ * @param url app url
3550
+ * @param target proxy target
3551
+ */
3552
+ function createMicroEventSource(appName, url, target) {
3553
+ const rawEventSource = !isUndefined(target) ? target : globalEnv.rawWindow.EventSource;
3554
+ if (!isConstructor(rawEventSource))
3555
+ return rawEventSource;
3556
+ return class MicroEventSource extends rawEventSource {
3557
+ constructor(eventSourceUrl, eventSourceInitDict, ...rests) {
3558
+ if (isString(eventSourceUrl) || isURL(eventSourceUrl)) {
3559
+ eventSourceUrl = createURL(eventSourceUrl, url).toString();
3560
+ }
3561
+ removeDomScope();
3562
+ super(eventSourceUrl, eventSourceInitDict, ...rests);
3563
+ if (eventSourceMap) {
3564
+ const eventSourceList = eventSourceMap.get(appName);
3565
+ if (eventSourceList) {
3566
+ eventSourceList.add(this);
3567
+ }
3568
+ else {
3569
+ eventSourceMap.set(appName, new Set([this]));
3570
+ }
3571
+ }
3572
+ else {
3573
+ eventSourceMap = new Map([[appName, new Set([this])]]);
3574
+ }
3575
+ }
3576
+ close() {
3577
+ var _a;
3578
+ super.close();
3579
+ (_a = eventSourceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.delete(this);
3580
+ }
3581
+ };
3582
+ }
3583
+ function clearMicroEventSource(appName) {
3584
+ const eventSourceList = eventSourceMap === null || eventSourceMap === void 0 ? void 0 : eventSourceMap.get(appName);
3585
+ if (eventSourceList === null || eventSourceList === void 0 ? void 0 : eventSourceList.size) {
3586
+ eventSourceList.forEach(item => {
3587
+ item.close();
3588
+ });
3589
+ eventSourceList.clear();
3590
+ }
3591
+ }
3592
+ return {
3593
+ createMicroEventSource,
3594
+ clearMicroEventSource,
3595
+ };
3596
+ }
3597
+
3598
+ const { createMicroEventSource, clearMicroEventSource } = useMicroEventSource();
3599
+ const globalPropertyList = ['window', 'self', 'globalThis'];
3600
+ class SandBox {
3601
+ constructor(appName, url, useMemoryRouter = true) {
3602
+ /**
3603
+ * Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
3604
+ * Fix https://github.com/micro-zoe/micro-app/issues/234
3605
+ */
3606
+ this.scopeProperties = [];
3607
+ // Properties that can be escape to rawWindow
3608
+ this.escapeProperties = [];
3609
+ // Properties newly added to microAppWindow
3610
+ this.injectedKeys = new Set();
3611
+ // Properties escape to rawWindow, cleared when unmount
3612
+ this.escapeKeys = new Set();
2560
3613
  // sandbox state
2561
3614
  this.active = false;
2562
3615
  this.microAppWindow = {}; // Proxy target
3616
+ this.adapter = new Adapter();
2563
3617
  // get scopeProperties and escapeProperties from plugins
2564
3618
  this.getSpecialProperties(appName);
2565
3619
  // create proxyWindow with Proxy(microAppWindow)
2566
3620
  this.proxyWindow = this.createProxyWindow(appName);
2567
- // inject global properties
2568
- this.initMicroAppWindow(this.microAppWindow, appName, url);
2569
3621
  // Rewrite global event listener & timeout
2570
- Object.assign(this, effect(this.microAppWindow));
3622
+ assign(this, effect(appName, this.microAppWindow));
3623
+ // inject global properties
3624
+ this.initStaticGlobalKeys(this.microAppWindow, appName, url, useMemoryRouter);
2571
3625
  }
2572
- start(baseRoute) {
3626
+ start(baseRoute, useMemoryRouter = true, defaultPage = '') {
2573
3627
  if (!this.active) {
2574
3628
  this.active = true;
2575
- this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseRoute;
2576
- // BUG FIX: bable-polyfill@6.x
2577
- globalEnv.rawWindow._babelPolyfill && (globalEnv.rawWindow._babelPolyfill = false);
3629
+ if (useMemoryRouter) {
3630
+ this.initRouteState(defaultPage);
3631
+ // unique listener of popstate event for sub app
3632
+ this.removeHistoryListener = addHistoryListener(this.proxyWindow.__MICRO_APP_NAME__);
3633
+ }
3634
+ else {
3635
+ this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseRoute;
3636
+ }
3637
+ // prevent the key deleted during sandBox.stop after rewrite
3638
+ this.initGlobalKeysWhenStart(this.microAppWindow, this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.__MICRO_APP_URL__);
2578
3639
  if (++SandBox.activeCount === 1) {
2579
3640
  effectDocumentEvent();
2580
3641
  patchElementPrototypeMethods();
3642
+ initEnvOfNestedApp();
2581
3643
  }
3644
+ fixBabelPolyfill6();
2582
3645
  }
2583
3646
  }
2584
- stop() {
3647
+ stop(keepRouteState, clearEventSource) {
2585
3648
  if (this.active) {
2586
- this.active = false;
2587
3649
  this.releaseEffect();
2588
3650
  this.microAppWindow.microApp.clearDataListener();
2589
3651
  this.microAppWindow.microApp.clearGlobalDataListener();
3652
+ if (this.removeHistoryListener) {
3653
+ this.clearRouteState(keepRouteState);
3654
+ // release listener of popstate
3655
+ this.removeHistoryListener();
3656
+ }
3657
+ if (clearEventSource) {
3658
+ clearMicroEventSource(this.proxyWindow.__MICRO_APP_NAME__);
3659
+ }
3660
+ /**
3661
+ * NOTE:
3662
+ * 1. injectedKeys and escapeKeys must be placed at the back
3663
+ * 2. if key in initial microAppWindow, and then rewrite, this key will be delete from microAppWindow when stop, and lost when restart
3664
+ */
2590
3665
  this.injectedKeys.forEach((key) => {
2591
3666
  Reflect.deleteProperty(this.microAppWindow, key);
2592
3667
  });
@@ -2599,6 +3674,7 @@ class SandBox {
2599
3674
  releaseEffectDocumentEvent();
2600
3675
  releasePatches();
2601
3676
  }
3677
+ this.active = false;
2602
3678
  }
2603
3679
  }
2604
3680
  // record umd snapshot before the first execution of umdHookMount
@@ -2620,15 +3696,16 @@ class SandBox {
2620
3696
  rebuildDataCenterSnapshot(this.microAppWindow.microApp);
2621
3697
  }
2622
3698
  /**
2623
- * get scopeProperties and escapeProperties from plugins
3699
+ * get scopeProperties and escapeProperties from plugins & adapter
2624
3700
  * @param appName app name
2625
3701
  */
2626
3702
  getSpecialProperties(appName) {
2627
3703
  var _a;
2628
- if (!isPlainObject(microApp.plugins))
2629
- return;
2630
- this.commonActionForSpecialProperties(microApp.plugins.global);
2631
- this.commonActionForSpecialProperties((_a = microApp.plugins.modules) === null || _a === void 0 ? void 0 : _a[appName]);
3704
+ this.scopeProperties = this.scopeProperties.concat(this.adapter.staticScopeProperties);
3705
+ if (isPlainObject(microApp.plugins)) {
3706
+ this.commonActionForSpecialProperties(microApp.plugins.global);
3707
+ this.commonActionForSpecialProperties((_a = microApp.plugins.modules) === null || _a === void 0 ? void 0 : _a[appName]);
3708
+ }
2632
3709
  }
2633
3710
  // common action for global plugins and module plugins
2634
3711
  commonActionForSpecialProperties(plugins) {
@@ -2658,11 +3735,11 @@ class SandBox {
2658
3735
  this.scopeProperties.includes(key))
2659
3736
  return Reflect.get(target, key);
2660
3737
  const rawValue = Reflect.get(rawWindow, key);
2661
- return isFunction(rawValue) ? bindFunctionToRawWindow(rawWindow, rawValue) : rawValue;
3738
+ return isFunction(rawValue) ? bindFunctionToRawObject(rawWindow, rawValue) : rawValue;
2662
3739
  },
2663
3740
  set: (target, key, value) => {
2664
3741
  if (this.active) {
2665
- if (escapeSetterKeyList.includes(key)) {
3742
+ if (this.adapter.escapeSetterKeyList.includes(key)) {
2666
3743
  Reflect.set(rawWindow, key, value);
2667
3744
  }
2668
3745
  else if (
@@ -2686,7 +3763,8 @@ class SandBox {
2686
3763
  this.injectedKeys.add(key);
2687
3764
  }
2688
3765
  if ((this.escapeProperties.includes(key) ||
2689
- (staticEscapeProperties.includes(key) && !Reflect.has(rawWindow, key))) &&
3766
+ (this.adapter.staticEscapeProperties.includes(key) &&
3767
+ !Reflect.has(rawWindow, key))) &&
2690
3768
  !this.scopeProperties.includes(key)) {
2691
3769
  Reflect.set(rawWindow, key, value);
2692
3770
  this.escapeKeys.add(key);
@@ -2742,21 +3820,37 @@ class SandBox {
2742
3820
  * @param microAppWindow micro window
2743
3821
  * @param appName app name
2744
3822
  * @param url app url
3823
+ * @param useMemoryRouter whether use memory router
2745
3824
  */
2746
- initMicroAppWindow(microAppWindow, appName, url) {
3825
+ initStaticGlobalKeys(microAppWindow, appName, url, useMemoryRouter) {
2747
3826
  microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
2748
3827
  microAppWindow.__MICRO_APP_NAME__ = appName;
3828
+ microAppWindow.__MICRO_APP_URL__ = url;
2749
3829
  microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
2750
3830
  microAppWindow.__MICRO_APP_WINDOW__ = microAppWindow;
2751
- microAppWindow.microApp = Object.assign(new EventCenterForMicroApp(appName), {
3831
+ microAppWindow.rawWindow = globalEnv.rawWindow;
3832
+ microAppWindow.rawDocument = globalEnv.rawDocument;
3833
+ microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
2752
3834
  removeDomScope,
2753
3835
  pureCreateElement,
3836
+ router,
2754
3837
  });
2755
- microAppWindow.rawWindow = globalEnv.rawWindow;
2756
- microAppWindow.rawDocument = globalEnv.rawDocument;
2757
- microAppWindow.hasOwnProperty = (key) => rawHasOwnProperty.call(microAppWindow, key) || rawHasOwnProperty.call(globalEnv.rawWindow, key);
3838
+ this.setProxyDocument(microAppWindow, appName);
2758
3839
  this.setMappingPropertiesWithRawDescriptor(microAppWindow);
2759
- this.setHijackProperties(microAppWindow, appName);
3840
+ if (useMemoryRouter)
3841
+ this.setMicroAppRouter(microAppWindow, appName, url);
3842
+ }
3843
+ setProxyDocument(microAppWindow, appName) {
3844
+ const proxyDocument = this.createProxyDocument(appName);
3845
+ rawDefineProperty(microAppWindow, 'document', {
3846
+ configurable: false,
3847
+ enumerable: true,
3848
+ get() {
3849
+ throttleDeferForSetAppName(appName);
3850
+ // return globalEnv.rawDocument
3851
+ return proxyDocument;
3852
+ },
3853
+ });
2760
3854
  }
2761
3855
  // properties associated with the native window
2762
3856
  setMappingPropertiesWithRawDescriptor(microAppWindow) {
@@ -2785,19 +3879,24 @@ class SandBox {
2785
3879
  };
2786
3880
  return descriptor;
2787
3881
  }
3882
+ /**
3883
+ * init global properties of microAppWindow when exec sandBox.start
3884
+ * @param microAppWindow micro window
3885
+ * @param appName app name
3886
+ * @param url app url
3887
+ */
3888
+ initGlobalKeysWhenStart(microAppWindow, appName, url) {
3889
+ microAppWindow.hasOwnProperty = (key) => rawHasOwnProperty.call(microAppWindow, key) || rawHasOwnProperty.call(globalEnv.rawWindow, key);
3890
+ this.setHijackProperty(microAppWindow, appName);
3891
+ this.patchRequestApi(microAppWindow, appName, url);
3892
+ }
2788
3893
  // set hijack Properties to microAppWindow
2789
- setHijackProperties(microAppWindow, appName) {
3894
+ setHijackProperty(microAppWindow, appName) {
2790
3895
  let modifiedEval, modifiedImage;
2791
3896
  rawDefineProperties(microAppWindow, {
2792
- document: {
2793
- get() {
2794
- throttleDeferForSetAppName(appName);
2795
- return globalEnv.rawDocument;
2796
- },
2797
- configurable: false,
2798
- enumerable: true,
2799
- },
2800
3897
  eval: {
3898
+ configurable: true,
3899
+ enumerable: false,
2801
3900
  get() {
2802
3901
  throttleDeferForSetAppName(appName);
2803
3902
  return modifiedEval || eval;
@@ -2805,10 +3904,10 @@ class SandBox {
2805
3904
  set: (value) => {
2806
3905
  modifiedEval = value;
2807
3906
  },
2808
- configurable: true,
2809
- enumerable: false,
2810
3907
  },
2811
3908
  Image: {
3909
+ configurable: true,
3910
+ enumerable: false,
2812
3911
  get() {
2813
3912
  throttleDeferForSetAppName(appName);
2814
3913
  return modifiedImage || globalEnv.ImageProxy;
@@ -2816,10 +3915,99 @@ class SandBox {
2816
3915
  set: (value) => {
2817
3916
  modifiedImage = value;
2818
3917
  },
3918
+ },
3919
+ });
3920
+ }
3921
+ // rewrite fetch, XMLHttpRequest, EventSource
3922
+ patchRequestApi(microAppWindow, appName, url) {
3923
+ let microFetch = createMicroFetch(url);
3924
+ let microXMLHttpRequest = createMicroXMLHttpRequest(url);
3925
+ let microEventSource = createMicroEventSource(appName, url);
3926
+ rawDefineProperties(microAppWindow, {
3927
+ fetch: {
2819
3928
  configurable: true,
2820
- enumerable: false,
3929
+ enumerable: true,
3930
+ get() {
3931
+ return microFetch;
3932
+ },
3933
+ set(value) {
3934
+ microFetch = createMicroFetch(url, value);
3935
+ },
3936
+ },
3937
+ XMLHttpRequest: {
3938
+ configurable: true,
3939
+ enumerable: true,
3940
+ get() {
3941
+ return microXMLHttpRequest;
3942
+ },
3943
+ set(value) {
3944
+ microXMLHttpRequest = createMicroXMLHttpRequest(url, value);
3945
+ },
3946
+ },
3947
+ EventSource: {
3948
+ configurable: true,
3949
+ enumerable: true,
3950
+ get() {
3951
+ return microEventSource;
3952
+ },
3953
+ set(value) {
3954
+ microEventSource = createMicroEventSource(appName, url, value);
3955
+ },
3956
+ },
3957
+ });
3958
+ }
3959
+ // set location & history for memory router
3960
+ setMicroAppRouter(microAppWindow, appName, url) {
3961
+ const { microLocation, microHistory } = createMicroRouter(appName, url);
3962
+ rawDefineProperties(microAppWindow, {
3963
+ location: {
3964
+ configurable: false,
3965
+ enumerable: true,
3966
+ get() {
3967
+ return microLocation;
3968
+ },
3969
+ set: (value) => {
3970
+ globalEnv.rawWindow.location = value;
3971
+ },
3972
+ },
3973
+ history: {
3974
+ configurable: true,
3975
+ enumerable: true,
3976
+ get() {
3977
+ return microHistory;
3978
+ },
3979
+ },
3980
+ });
3981
+ }
3982
+ initRouteState(defaultPage) {
3983
+ initRouteStateWithURL(this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.location, defaultPage);
3984
+ }
3985
+ clearRouteState(keepRouteState) {
3986
+ clearRouteStateFromURL(this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.__MICRO_APP_URL__, this.proxyWindow.location, keepRouteState);
3987
+ }
3988
+ setRouteInfoForKeepAliveApp() {
3989
+ updateBrowserURLWithLocation(this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.location);
3990
+ }
3991
+ removeRouteInfoForKeepAliveApp() {
3992
+ removeStateAndPathFromBrowser(this.proxyWindow.__MICRO_APP_NAME__);
3993
+ }
3994
+ createProxyDocument(appName) {
3995
+ const createElement = function (tagName, options) {
3996
+ const element = globalEnv.rawCreateElement.call(globalEnv.rawDocument, tagName, options);
3997
+ element.__MICRO_APP_NAME__ = appName;
3998
+ return element;
3999
+ };
4000
+ const proxyDocument = new Proxy(globalEnv.rawDocument, {
4001
+ get(target, key) {
4002
+ throttleDeferForSetAppName(appName);
4003
+ throttleDeferForParentNode(proxyDocument);
4004
+ if (key === 'createElement')
4005
+ return createElement;
4006
+ const rawValue = Reflect.get(target, key);
4007
+ return isFunction(rawValue) ? bindFunctionToRawObject(target, rawValue, 'DOCUMENT') : rawValue;
2821
4008
  },
2822
4009
  });
4010
+ return proxyDocument;
2823
4011
  }
2824
4012
  }
2825
4013
  SandBox.activeCount = 0; // number of active sandbox
@@ -2854,7 +4042,7 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
2854
4042
  element = getRootContainer(element);
2855
4043
  // clear dom scope before dispatch lifeCycles event to base app, especially mounted & unmount
2856
4044
  removeDomScope();
2857
- const detail = Object.assign({
4045
+ const detail = assign({
2858
4046
  name: appName,
2859
4047
  container: element,
2860
4048
  }, error && {
@@ -2879,7 +4067,7 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
2879
4067
  * @param detail event detail
2880
4068
  */
2881
4069
  function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
2882
- const event = new CustomEvent(`${eventName}-${appName}`, {
4070
+ const event = new CustomEvent(formatEventName$1(eventName, appName), {
2883
4071
  detail,
2884
4072
  });
2885
4073
  window.dispatchEvent(event);
@@ -2888,8 +4076,8 @@ function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
2888
4076
  // micro app instances
2889
4077
  const appInstanceMap = new Map();
2890
4078
  class CreateApp {
2891
- constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, baseroute, }) {
2892
- this.state = appStates.NOT_LOADED;
4079
+ constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, useMemoryRouter, baseroute, keepRouteState, defaultPage, }) {
4080
+ this.state = appStates.CREATED;
2893
4081
  this.keepAliveState = null;
2894
4082
  this.keepAliveContainer = null;
2895
4083
  this.loadSourceLevel = 0;
@@ -2900,28 +4088,30 @@ class CreateApp {
2900
4088
  this.isPrefetch = false;
2901
4089
  this.prefetchResolve = null;
2902
4090
  this.container = null;
2903
- this.baseroute = '';
2904
4091
  this.sandBox = null;
2905
4092
  this.container = container !== null && container !== void 0 ? container : null;
2906
4093
  this.inline = inline !== null && inline !== void 0 ? inline : false;
2907
4094
  this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : '';
4095
+ this.keepRouteState = keepRouteState !== null && keepRouteState !== void 0 ? keepRouteState : false;
2908
4096
  this.ssrUrl = ssrUrl !== null && ssrUrl !== void 0 ? ssrUrl : '';
2909
4097
  // optional during init👆
2910
4098
  this.name = name;
2911
4099
  this.url = url;
2912
4100
  this.useSandbox = useSandbox;
2913
4101
  this.scopecss = this.useSandbox && scopecss;
4102
+ this.useMemoryRouter = this.useSandbox && useMemoryRouter;
4103
+ this.defaultPage = defaultPage !== null && defaultPage !== void 0 ? defaultPage : '';
2914
4104
  this.source = {
2915
4105
  links: new Map(),
2916
4106
  scripts: new Map(),
2917
4107
  };
2918
4108
  this.loadSourceCode();
2919
- this.useSandbox && (this.sandBox = new SandBox(name, url));
4109
+ this.useSandbox && (this.sandBox = new SandBox(name, url, this.useMemoryRouter));
2920
4110
  }
2921
4111
  // Load resources
2922
4112
  loadSourceCode() {
2923
- this.state = appStates.LOADING_SOURCE_CODE;
2924
- HTMLLoader.getInstance().run(this, extractSourceDom);
4113
+ this.state = appStates.LOADING;
4114
+ extractHtml(this);
2925
4115
  }
2926
4116
  /**
2927
4117
  * When resource is loaded, mount app if it is not prefetch or unmount
@@ -2935,7 +4125,7 @@ class CreateApp {
2935
4125
  this.prefetchResolve = null;
2936
4126
  }
2937
4127
  else if (appStates.UNMOUNT !== this.state) {
2938
- this.state = appStates.LOAD_SOURCE_FINISHED;
4128
+ this.state = appStates.LOADED;
2939
4129
  this.mount();
2940
4130
  }
2941
4131
  }
@@ -2952,7 +4142,7 @@ class CreateApp {
2952
4142
  }
2953
4143
  if (appStates.UNMOUNT !== this.state) {
2954
4144
  this.onerror(e);
2955
- this.state = appStates.LOAD_SOURCE_ERROR;
4145
+ this.state = appStates.LOAD_FAILED;
2956
4146
  }
2957
4147
  }
2958
4148
  /**
@@ -2960,22 +4150,26 @@ class CreateApp {
2960
4150
  * @param container app container
2961
4151
  * @param inline js runs in inline mode
2962
4152
  * @param baseroute route prefix, default is ''
4153
+ * @param keepRouteState keep route state when unmount, default is false
2963
4154
  */
2964
- mount(container, inline, baseroute) {
4155
+ mount(container, inline, baseroute, keepRouteState, defaultPage) {
2965
4156
  var _a, _b, _c;
2966
- if (isBoolean(inline) && inline !== this.inline) {
4157
+ if (isBoolean(inline))
2967
4158
  this.inline = inline;
2968
- }
4159
+ // keepRouteState effective on unmount
4160
+ if (isBoolean(keepRouteState))
4161
+ this.keepRouteState = keepRouteState;
2969
4162
  this.container = (_a = this.container) !== null && _a !== void 0 ? _a : container;
2970
4163
  this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : this.baseroute;
4164
+ this.defaultPage = defaultPage !== null && defaultPage !== void 0 ? defaultPage : this.defaultPage;
2971
4165
  if (this.loadSourceLevel !== 2) {
2972
- this.state = appStates.LOADING_SOURCE_CODE;
4166
+ this.state = appStates.LOADING;
2973
4167
  return;
2974
4168
  }
2975
4169
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
2976
4170
  this.state = appStates.MOUNTING;
2977
4171
  cloneContainer(this.source.html, this.container, !this.umdMode);
2978
- (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.start(this.baseroute);
4172
+ (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.start(this.baseroute, this.useMemoryRouter, this.defaultPage);
2979
4173
  let umdHookMountResult; // result of mount function
2980
4174
  if (!this.umdMode) {
2981
4175
  let hasDispatchMountedEvent = false;
@@ -3040,11 +4234,12 @@ class CreateApp {
3040
4234
  }
3041
4235
  /**
3042
4236
  * unmount app
4237
+ * NOTE: Do not add any params on account of unmountApp
3043
4238
  * @param destroy completely destroy, delete cache resources
3044
4239
  * @param unmountcb callback of unmount
3045
4240
  */
3046
4241
  unmount(destroy, unmountcb) {
3047
- if (this.state === appStates.LOAD_SOURCE_ERROR) {
4242
+ if (this.state === appStates.LOAD_FAILED) {
3048
4243
  destroy = true;
3049
4244
  }
3050
4245
  this.state = appStates.UNMOUNT;
@@ -3097,8 +4292,13 @@ class CreateApp {
3097
4292
  else if (this.umdMode && this.container.childElementCount) {
3098
4293
  cloneContainer(this.container, this.source.html, false);
3099
4294
  }
3100
- // this.container maybe contains micro-app element, stop sandbox should exec after cloneContainer
3101
- (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.stop();
4295
+ /**
4296
+ * this.container maybe contains micro-app element, stop sandbox should exec after cloneContainer
4297
+ * NOTE:
4298
+ * 1. if destroy is true, clear route state
4299
+ * 2. umd mode and keep-alive will not clear EventSource
4300
+ */
4301
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.stop(this.keepRouteState && !destroy, !this.umdMode || destroy);
3102
4302
  if (!getActiveApps().length) {
3103
4303
  releasePatchSetAttribute();
3104
4304
  }
@@ -3117,34 +4317,40 @@ class CreateApp {
3117
4317
  }
3118
4318
  // hidden app when disconnectedCallback called with keep-alive
3119
4319
  hiddenKeepAliveApp() {
4320
+ var _a;
3120
4321
  const oldContainer = this.container;
3121
4322
  cloneContainer(this.container, this.keepAliveContainer ? this.keepAliveContainer : (this.keepAliveContainer = document.createElement('div')), false);
3122
4323
  this.container = this.keepAliveContainer;
3123
4324
  this.keepAliveState = keepAliveStates.KEEP_ALIVE_HIDDEN;
3124
4325
  // event should dispatch before clone node
3125
- // dispatch afterhidden event to micro-app
4326
+ // dispatch afterHidden event to micro-app
3126
4327
  dispatchCustomEventToMicroApp('appstate-change', this.name, {
3127
4328
  appState: 'afterhidden',
3128
4329
  });
3129
- // dispatch afterhidden event to base app
4330
+ // dispatch afterHidden event to base app
3130
4331
  dispatchLifecyclesEvent(oldContainer, this.name, lifeCycles.AFTERHIDDEN);
4332
+ // called after lifeCyclesEvent
4333
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.removeRouteInfoForKeepAliveApp();
3131
4334
  }
3132
4335
  // show app when connectedCallback called with keep-alive
3133
4336
  showKeepAliveApp(container) {
3134
- // dispatch beforeshow event to micro-app
4337
+ var _a;
4338
+ // dispatch beforeShow event to micro-app
3135
4339
  dispatchCustomEventToMicroApp('appstate-change', this.name, {
3136
4340
  appState: 'beforeshow',
3137
4341
  });
3138
- // dispatch beforeshow event to base app
4342
+ // dispatch beforeShow event to base app
3139
4343
  dispatchLifecyclesEvent(container, this.name, lifeCycles.BEFORESHOW);
3140
4344
  cloneContainer(this.container, container, false);
3141
4345
  this.container = container;
3142
4346
  this.keepAliveState = keepAliveStates.KEEP_ALIVE_SHOW;
3143
- // dispatch aftershow event to micro-app
4347
+ // called before lifeCyclesEvent
4348
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.setRouteInfoForKeepAliveApp();
4349
+ // dispatch afterShow event to micro-app
3144
4350
  dispatchCustomEventToMicroApp('appstate-change', this.name, {
3145
4351
  appState: 'aftershow',
3146
4352
  });
3147
- // dispatch aftershow event to base app
4353
+ // dispatch afterShow event to base app
3148
4354
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.AFTERSHOW);
3149
4355
  }
3150
4356
  /**
@@ -3252,12 +4458,17 @@ function defineElement(tagName) {
3252
4458
  }
3253
4459
  disconnectedCallback() {
3254
4460
  this.hasConnected = false;
3255
- // keep-alive
3256
- if (this.getKeepAliveModeResult()) {
3257
- this.handleHiddenKeepAliveApp();
3258
- }
3259
- else {
3260
- this.handleUnmount(this.getDestroyCompatibleResult());
4461
+ const app = appInstanceMap.get(this.appName);
4462
+ if (app &&
4463
+ app.getAppState() !== appStates.UNMOUNT &&
4464
+ app.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN) {
4465
+ // keep-alive
4466
+ if (this.getKeepAliveModeResult()) {
4467
+ this.handleHiddenKeepAliveApp();
4468
+ }
4469
+ else {
4470
+ this.handleUnmount(this.getDestroyCompatibleResult());
4471
+ }
3261
4472
  }
3262
4473
  }
3263
4474
  attributeChangedCallback(attr, _oldVal, newVal) {
@@ -3305,12 +4516,7 @@ function defineElement(tagName) {
3305
4516
  if (this.getDisposeResult('shadowDOM') && !this.shadowRoot && isFunction(this.attachShadow)) {
3306
4517
  this.attachShadow({ mode: 'open' });
3307
4518
  }
3308
- if (this.getDisposeResult('ssr')) {
3309
- this.ssrUrl = CompletionPath(globalEnv.rawWindow.location.pathname, this.appUrl);
3310
- }
3311
- else if (this.ssrUrl) {
3312
- this.ssrUrl = '';
3313
- }
4519
+ this.updateSsrUrl(this.appUrl);
3314
4520
  if (appInstanceMap.has(this.appName)) {
3315
4521
  const app = appInstanceMap.get(this.appName);
3316
4522
  const existAppUrl = app.ssrUrl || app.url;
@@ -3344,15 +4550,9 @@ function defineElement(tagName) {
3344
4550
  actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp) {
3345
4551
  var _a;
3346
4552
  /**
3347
- * change ssrUrl in ssr mode
3348
4553
  * do not add judgment of formatAttrUrl === this.appUrl
3349
4554
  */
3350
- if (this.getDisposeResult('ssr')) {
3351
- this.ssrUrl = CompletionPath(globalEnv.rawWindow.location.pathname, formatAttrUrl);
3352
- }
3353
- else if (this.ssrUrl) {
3354
- this.ssrUrl = '';
3355
- }
4555
+ this.updateSsrUrl(formatAttrUrl);
3356
4556
  this.appName = formatAttrName;
3357
4557
  this.appUrl = formatAttrUrl;
3358
4558
  ((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this).innerHTML = '';
@@ -3412,14 +4612,14 @@ function defineElement(tagName) {
3412
4612
  app.isPrefetch = false;
3413
4613
  defer(() => {
3414
4614
  var _a;
3415
- return app.mount((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this, this.getDisposeResult('inline'), this.getBaseRouteCompatible());
4615
+ return app.mount((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this, this.getDisposeResult('inline'), this.getBaseRouteCompatible(), this.getDisposeResult('keep-router-state'), this.getDefaultPageValue());
3416
4616
  });
3417
4617
  }
3418
4618
  // create app instance
3419
4619
  handleCreateApp() {
3420
4620
  var _a;
3421
4621
  /**
3422
- * actions for destory old app
4622
+ * actions for destroy old app
3423
4623
  * fix of unmounted umd app with disableSandbox
3424
4624
  */
3425
4625
  if (appInstanceMap.has(this.appName)) {
@@ -3433,7 +4633,10 @@ function defineElement(tagName) {
3433
4633
  inline: this.getDisposeResult('inline'),
3434
4634
  scopecss: !(this.getDisposeResult('disableScopecss') || this.getDisposeResult('shadowDOM')),
3435
4635
  useSandbox: !this.getDisposeResult('disableSandbox'),
4636
+ useMemoryRouter: !this.getDisposeResult('disable-memory-router'),
3436
4637
  baseroute: this.getBaseRouteCompatible(),
4638
+ keepRouteState: this.getDisposeResult('keep-router-state'),
4639
+ defaultPage: this.getDefaultPageValue(),
3437
4640
  });
3438
4641
  appInstanceMap.set(this.appName, instance);
3439
4642
  }
@@ -3441,11 +4644,12 @@ function defineElement(tagName) {
3441
4644
  * unmount app
3442
4645
  * @param destroy delete cache resources when unmount
3443
4646
  */
3444
- handleUnmount(destroy, unmountcb) {
4647
+ handleUnmount(destroy, unmountCb) {
3445
4648
  const app = appInstanceMap.get(this.appName);
3446
4649
  if (app &&
3447
- app.getAppState() !== appStates.UNMOUNT)
3448
- app.unmount(destroy, unmountcb);
4650
+ app.getAppState() !== appStates.UNMOUNT) {
4651
+ app.unmount(destroy, unmountCb);
4652
+ }
3449
4653
  }
3450
4654
  // hidden app when disconnectedCallback called with keep-alive
3451
4655
  handleHiddenKeepAliveApp() {
@@ -3508,6 +4712,37 @@ function defineElement(tagName) {
3508
4712
  getKeepAliveModeResult() {
3509
4713
  return this.getDisposeResult('keep-alive') && !this.getDestroyCompatibleResult();
3510
4714
  }
4715
+ /**
4716
+ * change ssrUrl in ssr mode
4717
+ */
4718
+ updateSsrUrl(baseUrl) {
4719
+ if (this.getDisposeResult('ssr')) {
4720
+ if (this.getDisposeResult('disable-memory-router') || this.getDisposeResult('disableSandbox')) {
4721
+ const rawLocation = globalEnv.rawWindow.location;
4722
+ this.ssrUrl = CompletionPath(rawLocation.pathname + rawLocation.search, baseUrl);
4723
+ }
4724
+ else {
4725
+ // get path from browser URL
4726
+ let targetPath = getNoHashMicroPathFromURL(this.appName, baseUrl);
4727
+ const defaultPagePath = this.getDefaultPageValue();
4728
+ if (!targetPath && defaultPagePath) {
4729
+ const targetLocation = createURL(defaultPagePath, baseUrl);
4730
+ targetPath = targetLocation.origin + targetLocation.pathname + targetLocation.search;
4731
+ }
4732
+ this.ssrUrl = targetPath;
4733
+ }
4734
+ }
4735
+ else if (this.ssrUrl) {
4736
+ this.ssrUrl = '';
4737
+ }
4738
+ }
4739
+ /**
4740
+ * get config of default page
4741
+ */
4742
+ getDefaultPageValue() {
4743
+ var _a, _b, _c;
4744
+ return (_c = (_b = (_a = router.getDefaultPage(this.appName)) !== null && _a !== void 0 ? _a : this.getAttribute('default-page')) !== null && _b !== void 0 ? _b : this.getAttribute('defaultPage')) !== null && _c !== void 0 ? _c : '';
4745
+ }
3511
4746
  /**
3512
4747
  * Data from the base application
3513
4748
  */
@@ -3542,12 +4777,13 @@ function defineElement(tagName) {
3542
4777
  * url: string,
3543
4778
  * disableScopecss?: boolean,
3544
4779
  * disableSandbox?: boolean,
4780
+ * disableMemoryRouter?: boolean,
3545
4781
  * },
3546
4782
  * ...
3547
4783
  * ])
3548
4784
  * Note:
3549
4785
  * 1: preFetch is asynchronous and is performed only when the browser is idle
3550
- * 2: disableScopecss, disableSandbox must be same with micro-app element, if conflict, the one who executes first shall prevail
4786
+ * 2: disableScopecss, disableSandbox, disableMemoryRouter must be same with micro-app element, if conflict, the one who executes first shall prevail
3551
4787
  * @param apps micro apps
3552
4788
  */
3553
4789
  function preFetch(apps) {
@@ -3565,7 +4801,7 @@ function preFetch(apps) {
3565
4801
  function preFetchInSerial(prefetchApp) {
3566
4802
  return new Promise((resolve) => {
3567
4803
  requestIdleCallback(() => {
3568
- var _a, _b;
4804
+ var _a, _b, _c;
3569
4805
  if (isPlainObject(prefetchApp) && navigator.onLine) {
3570
4806
  prefetchApp.name = formatAppName(prefetchApp.name);
3571
4807
  prefetchApp.url = formatAppURL(prefetchApp.url, prefetchApp.name);
@@ -3575,6 +4811,7 @@ function preFetchInSerial(prefetchApp) {
3575
4811
  url: prefetchApp.url,
3576
4812
  scopecss: !((_a = prefetchApp.disableScopecss) !== null && _a !== void 0 ? _a : microApp.disableScopecss),
3577
4813
  useSandbox: !((_b = prefetchApp.disableSandbox) !== null && _b !== void 0 ? _b : microApp.disableSandbox),
4814
+ useMemoryRouter: !((_c = prefetchApp.disableMemoryRouter) !== null && _c !== void 0 ? _c : microApp.disableMemoryRouter),
3578
4815
  });
3579
4816
  app.isPrefetch = true;
3580
4817
  app.prefetchResolve = resolve;
@@ -3643,7 +4880,7 @@ function getAllApps() {
3643
4880
  /**
3644
4881
  * unmount app by appName
3645
4882
  * @param appName
3646
- * @param options unmountAppParams
4883
+ * @param options unmountAppOptions
3647
4884
  * @returns Promise<void>
3648
4885
  */
3649
4886
  function unmountApp(appName, options) {
@@ -3717,6 +4954,7 @@ class MicroApp extends EventCenterForBaseApp {
3717
4954
  super(...arguments);
3718
4955
  this.tagName = 'micro-app';
3719
4956
  this.preFetch = preFetch;
4957
+ this.router = router;
3720
4958
  }
3721
4959
  start(options) {
3722
4960
  if (!isBrowser || !window.customElements) {
@@ -3746,6 +4984,7 @@ class MicroApp extends EventCenterForBaseApp {
3746
4984
  this.inline = options.inline;
3747
4985
  this.disableScopecss = options.disableScopecss;
3748
4986
  this.disableSandbox = options.disableSandbox;
4987
+ this.disableMemoryRouter = options.disableMemoryRouter;
3749
4988
  this.ssr = options.ssr;
3750
4989
  isFunction(options.fetch) && (this.fetch = options.fetch);
3751
4990
  isPlainObject(options.lifeCycles) && (this.lifeCycles = options.lifeCycles);