@micro-zoe/micro-app 0.8.5 → 1.0.0-alpha.1

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.5';
1
+ const version = '1.0.0-alpha.1';
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,8 +37,6 @@ 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]';
@@ -41,9 +53,9 @@ function isBoundFunction(target) {
41
53
  function isShadowRoot(target) {
42
54
  return typeof ShadowRoot !== 'undefined' && target instanceof ShadowRoot;
43
55
  }
44
- const rawDefineProperty = Object.defineProperty;
45
- const rawDefineProperties = Object.defineProperties;
46
- const rawHasOwnProperty = Object.prototype.hasOwnProperty;
56
+ function isURL(target) {
57
+ return target instanceof URL;
58
+ }
47
59
  /**
48
60
  * format error log
49
61
  * @param msg message
@@ -80,12 +92,22 @@ function logWarn(msg, appName = null, ...rest) {
80
92
  function defer(fn, ...args) {
81
93
  Promise.resolve().then(fn.bind(null, ...args));
82
94
  }
95
+ /**
96
+ * create URL as MicroLocation
97
+ */
98
+ const createURL = (function () {
99
+ class Location extends URL {
100
+ }
101
+ return (path, base) => {
102
+ return (base ? new Location('' + path, base) : new Location('' + path));
103
+ };
104
+ })();
83
105
  /**
84
106
  * Add address protocol
85
107
  * @param url address
86
108
  */
87
109
  function addProtocol(url) {
88
- return url.startsWith('//') ? `${location.protocol}${url}` : url;
110
+ return url.startsWith('//') ? `${globalThis.location.protocol}${url}` : url;
89
111
  }
90
112
  /**
91
113
  * format URL address
@@ -97,7 +119,7 @@ function formatAppURL(url, appName = null) {
97
119
  if (!isString(url) || !url)
98
120
  return '';
99
121
  try {
100
- const { origin, pathname, search } = new URL(addProtocol(url));
122
+ const { origin, pathname, search } = createURL(addProtocol(url));
101
123
  // If it ends with .html/.node/.php/.net/.etc, don’t need to add /
102
124
  if (/\.(\w+)$/.test(pathname)) {
103
125
  return `${origin}${pathname}${search}`;
@@ -118,6 +140,7 @@ function formatAppURL(url, appName = null) {
118
140
  * 3. event_center -> EventCenterForBaseApp -> all methods
119
141
  * 4. preFetch
120
142
  * 5. plugins
143
+ * 6. router api (push, replace)
121
144
  */
122
145
  function formatAppName(name) {
123
146
  if (!isString(name) || !name)
@@ -129,7 +152,7 @@ function formatAppName(name) {
129
152
  * @param url app.url
130
153
  */
131
154
  function getEffectivePath(url) {
132
- const { origin, pathname } = new URL(url);
155
+ const { origin, pathname } = createURL(url);
133
156
  if (/\.(\w+)$/.test(pathname)) {
134
157
  const fullPath = `${origin}${pathname}`;
135
158
  const pathArr = fullPath.split('/');
@@ -148,7 +171,7 @@ function CompletionPath(path, baseURI) {
148
171
  /^((((ht|f)tps?)|file):)?\/\//.test(path) ||
149
172
  /^(data|blob):/.test(path))
150
173
  return path;
151
- return new URL(path, getEffectivePath(addProtocol(baseURI))).toString();
174
+ return createURL(path, getEffectivePath(addProtocol(baseURI))).toString();
152
175
  }
153
176
  /**
154
177
  * Get the folder where the link resource is located,
@@ -176,24 +199,15 @@ function promiseStream(promiseList, successCb, errorCb, finallyCb) {
176
199
  promiseList.forEach((p, i) => {
177
200
  if (isPromise(p)) {
178
201
  p.then((res) => {
179
- successCb({
180
- data: res,
181
- index: i,
182
- });
202
+ successCb({ data: res, index: i });
183
203
  isFinished();
184
204
  }).catch((err) => {
185
- errorCb({
186
- error: err,
187
- index: i,
188
- });
205
+ errorCb({ error: err, index: i });
189
206
  isFinished();
190
207
  });
191
208
  }
192
209
  else {
193
- successCb({
194
- data: p,
195
- index: i,
196
- });
210
+ successCb({ data: p, index: i });
197
211
  isFinished();
198
212
  }
199
213
  });
@@ -306,6 +320,98 @@ function trim(str) {
306
320
  function isFireFox() {
307
321
  return navigator.userAgent.indexOf('Firefox') > -1;
308
322
  }
323
+ /**
324
+ * Transforms a queryString into object.
325
+ * @param search - search string to parse
326
+ * @returns a query object
327
+ */
328
+ function parseQuery(search) {
329
+ const result = {};
330
+ const queryList = search.split('&');
331
+ // we will not decode the key/value to ensure that the values are consistent when update URL
332
+ for (const queryItem of queryList) {
333
+ const eqPos = queryItem.indexOf('=');
334
+ const key = eqPos < 0 ? queryItem : queryItem.slice(0, eqPos);
335
+ const value = eqPos < 0 ? null : queryItem.slice(eqPos + 1);
336
+ if (key in result) {
337
+ let currentValue = result[key];
338
+ if (!isArray(currentValue)) {
339
+ currentValue = result[key] = [currentValue];
340
+ }
341
+ currentValue.push(value);
342
+ }
343
+ else {
344
+ result[key] = value;
345
+ }
346
+ }
347
+ return result;
348
+ }
349
+ /**
350
+ * Transforms an object to query string
351
+ * @param queryObject - query object to stringify
352
+ * @returns query string without the leading `?`
353
+ */
354
+ function stringifyQuery(queryObject) {
355
+ let result = '';
356
+ for (const key in queryObject) {
357
+ const value = queryObject[key];
358
+ if (isNull(value)) {
359
+ result += (result.length ? '&' : '') + key;
360
+ }
361
+ else {
362
+ const valueList = isArray(value) ? value : [value];
363
+ valueList.forEach(value => {
364
+ if (!isUndefined(value)) {
365
+ result += (result.length ? '&' : '') + key;
366
+ if (!isNull(value))
367
+ result += '=' + value;
368
+ }
369
+ });
370
+ }
371
+ }
372
+ return result;
373
+ }
374
+ /**
375
+ * Register or unregister callback/guard with Set
376
+ */
377
+ function useSetRecord() {
378
+ const handlers = new Set();
379
+ function add(handler) {
380
+ handlers.add(handler);
381
+ return () => {
382
+ if (handlers.has(handler))
383
+ return handlers.delete(handler);
384
+ return false;
385
+ };
386
+ }
387
+ return {
388
+ add,
389
+ list: () => handlers,
390
+ };
391
+ }
392
+ /**
393
+ * record data with Map
394
+ */
395
+ function useMapRecord() {
396
+ const data = new Map();
397
+ function add(key, value) {
398
+ data.set(key, value);
399
+ return () => {
400
+ if (data.has(key))
401
+ return data.delete(key);
402
+ return false;
403
+ };
404
+ }
405
+ return {
406
+ add,
407
+ get: (key) => data.get(key),
408
+ delete: (key) => {
409
+ if (data.has(key))
410
+ return data.delete(key);
411
+ return false;
412
+ }
413
+ };
414
+ }
309
415
 
310
416
  var ObservedAttrName;
311
417
  (function (ObservedAttrName) {
@@ -315,13 +421,13 @@ var ObservedAttrName;
315
421
  // app status
316
422
  var appStates;
317
423
  (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";
424
+ appStates["CREATED"] = "created";
425
+ appStates["LOADING"] = "loading";
426
+ appStates["LOADED"] = "loaded";
427
+ appStates["LOAD_FAILED"] = "load_failed";
428
+ appStates["MOUNTING"] = "mounting";
429
+ appStates["MOUNTED"] = "mounted";
430
+ appStates["UNMOUNT"] = "unmount";
325
431
  })(appStates || (appStates = {}));
326
432
  // lifecycles
327
433
  var lifeCycles;
@@ -339,16 +445,24 @@ var lifeCycles;
339
445
  // keep-alive status
340
446
  var keepAliveStates;
341
447
  (function (keepAliveStates) {
342
- keepAliveStates["KEEP_ALIVE_SHOW"] = "KEEP_ALIVE_SHOW";
343
- keepAliveStates["KEEP_ALIVE_HIDDEN"] = "KEEP_ALIVE_HIDDEN";
448
+ keepAliveStates["KEEP_ALIVE_SHOW"] = "keep_alive_show";
449
+ keepAliveStates["KEEP_ALIVE_HIDDEN"] = "keep_alive_hidden";
344
450
  })(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';
451
+ /**
452
+ * global key must be static key, they can not rewrite
453
+ * e.g.
454
+ * window.Promise = newValue
455
+ * new Promise ==> still get old value, not newValue, because they are cached by top function
456
+ * NOTE:
457
+ * 1. Do not add fetch, XMLHttpRequest, EventSource
458
+ */
459
+ 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
460
 
347
461
  /**
348
462
  * fetch source of html, js, css
349
463
  * @param url source path
350
464
  * @param appName app name
351
- * @param config config of fetch
465
+ * @param config fetch options
352
466
  */
353
467
  function fetchSource(url, appName = null, options = {}) {
354
468
  if (isFunction(microApp.fetch)) {
@@ -900,6 +1014,64 @@ function formatDynamicLink(url, info, app, originLink, replaceStyle) {
900
1014
  });
901
1015
  }
902
1016
 
1017
+ class Adapter {
1018
+ constructor() {
1019
+ // keys that can only assigned to rawWindow
1020
+ this.escapeSetterKeyList = [
1021
+ 'location',
1022
+ ];
1023
+ // keys that can escape to rawWindow
1024
+ this.staticEscapeProperties = [
1025
+ 'System',
1026
+ '__cjsWrapper',
1027
+ ];
1028
+ // keys that scoped in child app
1029
+ this.staticScopeProperties = [
1030
+ 'webpackJsonp',
1031
+ 'webpackHotUpdate',
1032
+ 'Vue',
1033
+ ];
1034
+ this.injectReactHRMProperty();
1035
+ }
1036
+ // TODO: __DEV__ process.env.NODE_ENV !== 'production'
1037
+ // adapter for react
1038
+ injectReactHRMProperty() {
1039
+ if (process.env.NODE_ENV !== 'production') {
1040
+ // react child in non-react env
1041
+ this.staticEscapeProperties.push('__REACT_ERROR_OVERLAY_GLOBAL_HOOK__');
1042
+ // in react parent
1043
+ if (globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__) {
1044
+ this.staticScopeProperties = this.staticScopeProperties.concat([
1045
+ '__REACT_ERROR_OVERLAY_GLOBAL_HOOK__',
1046
+ '__reactRefreshInjected',
1047
+ ]);
1048
+ }
1049
+ }
1050
+ }
1051
+ }
1052
+ // Fix conflict of babel-polyfill@6.x
1053
+ function fixBabelPolyfill6() {
1054
+ if (globalEnv.rawWindow._babelPolyfill)
1055
+ globalEnv.rawWindow._babelPolyfill = false;
1056
+ }
1057
+ /**
1058
+ * Fix error of hot reload when parent&child created by create-react-app in development environment
1059
+ * Issue: https://github.com/micro-zoe/micro-app/issues/382
1060
+ */
1061
+ function fixReactHMRConflict(app) {
1062
+ var _a;
1063
+ if (process.env.NODE_ENV !== 'production') {
1064
+ const rawReactErrorHook = globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__;
1065
+ const childReactErrorHook = (_a = app.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__;
1066
+ if (rawReactErrorHook && childReactErrorHook) {
1067
+ globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__ = childReactErrorHook;
1068
+ defer(() => {
1069
+ globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__ = rawReactErrorHook;
1070
+ });
1071
+ }
1072
+ }
1073
+ }
1074
+
903
1075
  // Record element and map element
904
1076
  const dynamicElementInMicroAppMap = new WeakMap();
905
1077
  /**
@@ -974,51 +1146,55 @@ function handleNewNode(parent, child, app) {
974
1146
  * @param passiveChild second param of insertBefore and replaceChild
975
1147
  */
976
1148
  function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
1149
+ const hijackElement = getHijackElement(parent, app);
977
1150
  /**
978
1151
  * If passiveChild is not the child node, insertBefore replaceChild will have a problem, at this time, it will be degraded to appendChild
979
1152
  * E.g: document.head.insertBefore(targetChild, document.head.childNodes[0])
980
1153
  */
981
- if (parent === document.head) {
982
- const microAppHead = app.container.querySelector('micro-app-head');
1154
+ if (hijackElement) {
983
1155
  /**
984
1156
  * 1. If passiveChild exists, it must be insertBefore or replaceChild
985
1157
  * 2. When removeChild, targetChild may not be in microAppHead or head
986
1158
  */
987
- if (passiveChild && !microAppHead.contains(passiveChild)) {
988
- return globalEnv.rawAppendChild.call(microAppHead, targetChild);
1159
+ if (passiveChild && !hijackElement.contains(passiveChild)) {
1160
+ return globalEnv.rawAppendChild.call(hijackElement, targetChild);
989
1161
  }
990
- else if (rawMethod === globalEnv.rawRemoveChild && !microAppHead.contains(targetChild)) {
1162
+ else if (rawMethod === globalEnv.rawRemoveChild && !hijackElement.contains(targetChild)) {
991
1163
  if (parent.contains(targetChild)) {
992
1164
  return rawMethod.call(parent, targetChild);
993
1165
  }
994
1166
  return targetChild;
995
1167
  }
996
- else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
997
- return rawMethod.call(microAppHead, targetChild);
1168
+ // TODO: __DEV__
1169
+ if (process.env.NODE_ENV !== 'production' &&
1170
+ targetChild instanceof HTMLIFrameElement &&
1171
+ rawMethod === globalEnv.rawAppendChild) {
1172
+ fixReactHMRConflict(app);
998
1173
  }
999
- return rawMethod.call(microAppHead, targetChild, passiveChild);
1174
+ return invokeRawMethod(rawMethod, hijackElement, targetChild, passiveChild);
1000
1175
  }
1001
- else if (parent === document.body) {
1002
- const microAppBody = app.container.querySelector('micro-app-body');
1003
- if (passiveChild && !microAppBody.contains(passiveChild)) {
1004
- return globalEnv.rawAppendChild.call(microAppBody, targetChild);
1005
- }
1006
- else if (rawMethod === globalEnv.rawRemoveChild && !microAppBody.contains(targetChild)) {
1007
- if (parent.contains(targetChild)) {
1008
- return rawMethod.call(parent, targetChild);
1009
- }
1010
- return targetChild;
1011
- }
1012
- else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
1013
- return rawMethod.call(microAppBody, targetChild);
1014
- }
1015
- return rawMethod.call(microAppBody, targetChild, passiveChild);
1176
+ return invokeRawMethod(rawMethod, parent, targetChild, passiveChild);
1177
+ }
1178
+ // head/body map to micro-app-head/micro-app-body
1179
+ function getHijackElement(node, app) {
1180
+ var _a, _b;
1181
+ if (node === document.head) {
1182
+ return (_a = app === null || app === void 0 ? void 0 : app.container) === null || _a === void 0 ? void 0 : _a.querySelector('micro-app-head');
1016
1183
  }
1017
- else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
1184
+ if (node === document.body) {
1185
+ return (_b = app === null || app === void 0 ? void 0 : app.container) === null || _b === void 0 ? void 0 : _b.querySelector('micro-app-body');
1186
+ }
1187
+ return null;
1188
+ }
1189
+ function invokeRawMethod(rawMethod, parent, targetChild, passiveChild) {
1190
+ if (isPendMethod(rawMethod)) {
1018
1191
  return rawMethod.call(parent, targetChild);
1019
1192
  }
1020
1193
  return rawMethod.call(parent, targetChild, passiveChild);
1021
1194
  }
1195
+ function isPendMethod(method) {
1196
+ return method === globalEnv.rawAppend || method === globalEnv.rawPrepend;
1197
+ }
1022
1198
  // Get the map element
1023
1199
  function getMappingNode(node) {
1024
1200
  var _a;
@@ -1106,27 +1282,6 @@ function patchElementPrototypeMethods() {
1106
1282
  this.__MICRO_APP_NAME__ && (clonedNode.__MICRO_APP_NAME__ = this.__MICRO_APP_NAME__);
1107
1283
  return clonedNode;
1108
1284
  };
1109
- // patch getBoundingClientRect
1110
- // TODO: scenes test
1111
- // Element.prototype.getBoundingClientRect = function getBoundingClientRect () {
1112
- // const rawRect: DOMRect = globalEnv.rawGetBoundingClientRect.call(this)
1113
- // if (this.__MICRO_APP_NAME__) {
1114
- // const app = appInstanceMap.get(this.__MICRO_APP_NAME__)
1115
- // if (!app?.container) {
1116
- // return rawRect
1117
- // }
1118
- // const appBody = app.container.querySelector('micro-app-body')
1119
- // const appBodyRect: DOMRect = globalEnv.rawGetBoundingClientRect.call(appBody)
1120
- // const computedRect: DOMRect = new DOMRect(
1121
- // rawRect.x - appBodyRect.x,
1122
- // rawRect.y - appBodyRect.y,
1123
- // rawRect.width,
1124
- // rawRect.height,
1125
- // )
1126
- // return computedRect
1127
- // }
1128
- // return rawRect
1129
- // }
1130
1285
  }
1131
1286
  /**
1132
1287
  * Mark the newly created element in the micro application
@@ -1134,7 +1289,8 @@ function patchElementPrototypeMethods() {
1134
1289
  */
1135
1290
  function markElement(element) {
1136
1291
  const appName = getCurrentAppName();
1137
- appName && (element.__MICRO_APP_NAME__ = appName);
1292
+ if (appName)
1293
+ element.__MICRO_APP_NAME__ = appName;
1138
1294
  return element;
1139
1295
  }
1140
1296
  // methods of document
@@ -1155,27 +1311,29 @@ function patchDocument() {
1155
1311
  };
1156
1312
  // query element👇
1157
1313
  function querySelector(selectors) {
1158
- var _a, _b, _c;
1314
+ var _a, _b, _c, _d;
1159
1315
  const appName = getCurrentAppName();
1160
1316
  if (!appName ||
1317
+ !((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) ||
1161
1318
  !selectors ||
1162
1319
  isUniqueElement(selectors) ||
1163
1320
  // see https://github.com/micro-zoe/micro-app/issues/56
1164
1321
  rawDocument !== this) {
1165
1322
  return globalEnv.rawQuerySelector.call(this, selectors);
1166
1323
  }
1167
- 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;
1324
+ return (_d = (_c = (_b = appInstanceMap.get(appName)) === null || _b === void 0 ? void 0 : _b.container) === null || _c === void 0 ? void 0 : _c.querySelector(selectors)) !== null && _d !== void 0 ? _d : null;
1168
1325
  }
1169
1326
  function querySelectorAll(selectors) {
1170
- var _a, _b, _c;
1327
+ var _a, _b, _c, _d;
1171
1328
  const appName = getCurrentAppName();
1172
1329
  if (!appName ||
1330
+ !((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) ||
1173
1331
  !selectors ||
1174
1332
  isUniqueElement(selectors) ||
1175
1333
  rawDocument !== this) {
1176
1334
  return globalEnv.rawQuerySelectorAll.call(this, selectors);
1177
1335
  }
1178
- 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 : [];
1336
+ return (_d = (_c = (_b = appInstanceMap.get(appName)) === null || _b === void 0 ? void 0 : _b.container) === null || _c === void 0 ? void 0 : _c.querySelectorAll(selectors)) !== null && _d !== void 0 ? _d : [];
1179
1337
  }
1180
1338
  Document.prototype.querySelector = querySelector;
1181
1339
  Document.prototype.querySelectorAll = querySelectorAll;
@@ -1243,10 +1401,9 @@ function patchSetAttribute() {
1243
1401
  if (/^micro-app(-\S+)?/i.test(this.tagName) && key === 'data') {
1244
1402
  if (isPlainObject(value)) {
1245
1403
  const cloneValue = {};
1246
- Object.getOwnPropertyNames(value).forEach((propertyKey) => {
1247
- if (!(isString(propertyKey) && propertyKey.indexOf('__') === 0)) {
1248
- // @ts-ignore
1249
- cloneValue[propertyKey] = value[propertyKey];
1404
+ Object.getOwnPropertyNames(value).forEach((key) => {
1405
+ if (!(isString(key) && key.indexOf('__') === 0)) {
1406
+ cloneValue[key] = value[key];
1250
1407
  }
1251
1408
  });
1252
1409
  this.data = cloneValue;
@@ -1293,7 +1450,6 @@ function releasePatches() {
1293
1450
  Element.prototype.append = globalEnv.rawAppend;
1294
1451
  Element.prototype.prepend = globalEnv.rawPrepend;
1295
1452
  Element.prototype.cloneNode = globalEnv.rawCloneNode;
1296
- // Element.prototype.getBoundingClientRect = globalEnv.rawGetBoundingClientRect
1297
1453
  }
1298
1454
  // Set the style of micro-app-head and micro-app-body
1299
1455
  let hasRejectMicroAppStyle = false;
@@ -1307,27 +1463,6 @@ function rejectMicroAppStyle() {
1307
1463
  }
1308
1464
  }
1309
1465
 
1310
- function unmountNestedApp() {
1311
- releaseUnmountOfNestedApp();
1312
- appInstanceMap.forEach(app => {
1313
- // @ts-ignore
1314
- app.container && getRootContainer(app.container).disconnectedCallback();
1315
- });
1316
- !window.__MICRO_APP_UMD_MODE__ && appInstanceMap.clear();
1317
- }
1318
- // if micro-app run in micro application, delete all next generation application when unmount event received
1319
- function listenUmountOfNestedApp() {
1320
- if (window.__MICRO_APP_ENVIRONMENT__) {
1321
- window.addEventListener('unmount', unmountNestedApp, false);
1322
- }
1323
- }
1324
- // release listener
1325
- function releaseUnmountOfNestedApp() {
1326
- if (window.__MICRO_APP_ENVIRONMENT__) {
1327
- window.removeEventListener('unmount', unmountNestedApp, false);
1328
- }
1329
- }
1330
-
1331
1466
  const globalEnv = {};
1332
1467
  /**
1333
1468
  * Note loop nesting
@@ -1347,7 +1482,6 @@ function initGlobalEnv() {
1347
1482
  const rawAppend = Element.prototype.append;
1348
1483
  const rawPrepend = Element.prototype.prepend;
1349
1484
  const rawCloneNode = Element.prototype.cloneNode;
1350
- // const rawGetBoundingClientRect = Element.prototype.getBoundingClientRect
1351
1485
  const rawCreateElement = Document.prototype.createElement;
1352
1486
  const rawCreateElementNS = Document.prototype.createElementNS;
1353
1487
  const rawCreateDocumentFragment = Document.prototype.createDocumentFragment;
@@ -1381,7 +1515,7 @@ function initGlobalEnv() {
1381
1515
  const rawDocumentRemoveEventListener = rawDocument.removeEventListener;
1382
1516
  // mark current application as base application
1383
1517
  window.__MICRO_APP_BASE_APPLICATION__ = true;
1384
- Object.assign(globalEnv, {
1518
+ assign(globalEnv, {
1385
1519
  // source/patch
1386
1520
  rawSetAttribute,
1387
1521
  rawAppendChild,
@@ -1391,7 +1525,6 @@ function initGlobalEnv() {
1391
1525
  rawAppend,
1392
1526
  rawPrepend,
1393
1527
  rawCloneNode,
1394
- // rawGetBoundingClientRect,
1395
1528
  rawCreateElement,
1396
1529
  rawCreateElementNS,
1397
1530
  rawCreateDocumentFragment,
@@ -1418,8 +1551,6 @@ function initGlobalEnv() {
1418
1551
  });
1419
1552
  // global effect
1420
1553
  rejectMicroAppStyle();
1421
- releaseUnmountOfNestedApp();
1422
- listenUmountOfNestedApp();
1423
1554
  }
1424
1555
  }
1425
1556
 
@@ -2140,6 +2271,28 @@ function rebuildDataCenterSnapshot(microAppEventCenter) {
2140
2271
  }
2141
2272
  }
2142
2273
 
2274
+ function unmountNestedApp() {
2275
+ appInstanceMap.forEach(app => {
2276
+ // @ts-ignore
2277
+ app.container && getRootContainer(app.container).disconnectedCallback();
2278
+ });
2279
+ !window.__MICRO_APP_UMD_MODE__ && appInstanceMap.clear();
2280
+ }
2281
+ // release listener
2282
+ function releaseUnmountOfNestedApp() {
2283
+ if (window.__MICRO_APP_ENVIRONMENT__) {
2284
+ window.removeEventListener('unmount', unmountNestedApp, false);
2285
+ }
2286
+ }
2287
+ // if micro-app run in micro application, delete all next generation application when unmount event received
2288
+ // unmount event will auto release by sandbox
2289
+ function listenUmountOfNestedApp() {
2290
+ if (window.__MICRO_APP_ENVIRONMENT__) {
2291
+ releaseUnmountOfNestedApp();
2292
+ window.addEventListener('unmount', unmountNestedApp, false);
2293
+ }
2294
+ }
2295
+
2143
2296
  /* eslint-disable no-return-assign */
2144
2297
  function isBoundedFunction(value) {
2145
2298
  if (isBoolean(value.__MICRO_APP_IS_BOUND_FUNCTION__))
@@ -2179,6 +2332,21 @@ function bindFunctionToRawWindow(rawWindow, value) {
2179
2332
  return value;
2180
2333
  }
2181
2334
 
2335
+ // this events should be sent to the specified app
2336
+ const formatEventList = ['unmount', 'appstate-change'];
2337
+ /**
2338
+ * Format event name
2339
+ * @param eventName event name
2340
+ * @param appName app name
2341
+ */
2342
+ function formatEventName$1(eventName, appName) {
2343
+ var _a;
2344
+ if (formatEventList.includes(eventName) ||
2345
+ ((eventName === 'popstate' || eventName === 'hashchange') && ((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.useMemoryRouter))) {
2346
+ return `${eventName}-${appName}`;
2347
+ }
2348
+ return eventName;
2349
+ }
2182
2350
  // document.onclick binding list, the binding function of each application is unique
2183
2351
  const documentClickListMap = new Map();
2184
2352
  let hasRewriteDocumentOnClick = false;
@@ -2272,32 +2440,18 @@ function releaseEffectDocumentEvent() {
2272
2440
  document.addEventListener = globalEnv.rawDocumentAddEventListener;
2273
2441
  document.removeEventListener = globalEnv.rawDocumentRemoveEventListener;
2274
2442
  }
2275
- // this events should be sent to the specified app
2276
- const formatEventList = ['unmount', 'appstate-change'];
2277
- /**
2278
- * Format event name
2279
- * @param type event name
2280
- * @param microAppWindow micro window
2281
- */
2282
- function formatEventType(type, microAppWindow) {
2283
- if (formatEventList.includes(type)) {
2284
- return `${type}-${microAppWindow.__MICRO_APP_NAME__}`;
2285
- }
2286
- return type;
2287
- }
2288
2443
  /**
2289
2444
  * Rewrite side-effect events
2290
2445
  * @param microAppWindow micro window
2291
2446
  */
2292
- function effect(microAppWindow) {
2293
- const appName = microAppWindow.__MICRO_APP_NAME__;
2447
+ function effect(appName, microAppWindow) {
2294
2448
  const eventListenerMap = new Map();
2295
2449
  const intervalIdMap = new Map();
2296
2450
  const timeoutIdMap = new Map();
2297
2451
  const { rawWindow, rawDocument, rawWindowAddEventListener, rawWindowRemoveEventListener, rawSetInterval, rawSetTimeout, rawClearInterval, rawClearTimeout, rawDocumentRemoveEventListener, } = globalEnv;
2298
2452
  // listener may be null, e.g test-passive
2299
2453
  microAppWindow.addEventListener = function (type, listener, options) {
2300
- type = formatEventType(type, microAppWindow);
2454
+ type = formatEventName$1(type, appName);
2301
2455
  const listenerList = eventListenerMap.get(type);
2302
2456
  if (listenerList) {
2303
2457
  listenerList.add(listener);
@@ -2309,7 +2463,7 @@ function effect(microAppWindow) {
2309
2463
  rawWindowAddEventListener.call(rawWindow, type, listener, options);
2310
2464
  };
2311
2465
  microAppWindow.removeEventListener = function (type, listener, options) {
2312
- type = formatEventType(type, microAppWindow);
2466
+ type = formatEventName$1(type, appName);
2313
2467
  const listenerList = eventListenerMap.get(type);
2314
2468
  if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
2315
2469
  listenerList.delete(listener);
@@ -2436,125 +2590,1015 @@ function effect(microAppWindow) {
2436
2590
  };
2437
2591
  }
2438
2592
 
2439
- // Variables that can escape to rawWindow
2440
- const staticEscapeProperties = [
2441
- 'System',
2442
- '__cjsWrapper',
2443
- ];
2444
- // Variables that can only assigned to rawWindow
2445
- const escapeSetterKeyList = [
2446
- 'location',
2447
- ];
2448
- const globalPropertyList = ['window', 'self', 'globalThis'];
2449
- class SandBox {
2450
- constructor(appName, url) {
2451
- /**
2452
- * Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
2453
- * https://github.com/micro-zoe/micro-app/issues/234
2454
- */
2455
- this.scopeProperties = ['webpackJsonp', 'Vue'];
2456
- // Properties that can be escape to rawWindow
2457
- this.escapeProperties = [];
2458
- // Properties newly added to microAppWindow
2459
- this.injectedKeys = new Set();
2460
- // Properties escape to rawWindow, cleared when unmount
2461
- this.escapeKeys = new Set();
2462
- // sandbox state
2463
- this.active = false;
2464
- this.microAppWindow = {}; // Proxy target
2465
- // get scopeProperties and escapeProperties from plugins
2466
- this.getSpecialProperties(appName);
2467
- // create proxyWindow with Proxy(microAppWindow)
2468
- this.proxyWindow = this.createProxyWindow(appName);
2469
- // inject global properties
2470
- this.initMicroAppWindow(this.microAppWindow, appName, url);
2471
- // Rewrite global event listener & timeout
2472
- Object.assign(this, effect(this.microAppWindow));
2473
- }
2474
- start(baseRoute) {
2475
- if (!this.active) {
2476
- this.active = true;
2477
- this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseRoute;
2478
- // BUG FIX: bable-polyfill@6.x
2479
- globalEnv.rawWindow._babelPolyfill && (globalEnv.rawWindow._babelPolyfill = false);
2480
- if (++SandBox.activeCount === 1) {
2481
- effectDocumentEvent();
2482
- patchElementPrototypeMethods();
2483
- }
2593
+ // set micro app state to origin state
2594
+ function setMicroState(appName, rawState, microState) {
2595
+ const additionalState = {
2596
+ microAppState: assign({}, rawState === null || rawState === void 0 ? void 0 : rawState.microAppState, {
2597
+ [appName]: microState
2598
+ })
2599
+ };
2600
+ // create new state object
2601
+ return assign({}, rawState, additionalState);
2602
+ }
2603
+ // delete micro app state form origin state
2604
+ function removeMicroState(appName, rawState) {
2605
+ if (isPlainObject(rawState === null || rawState === void 0 ? void 0 : rawState.microAppState)) {
2606
+ if (!isUndefined(rawState.microAppState[appName])) {
2607
+ delete rawState.microAppState[appName];
2484
2608
  }
2485
- }
2486
- stop() {
2487
- if (this.active) {
2488
- this.active = false;
2489
- this.releaseEffect();
2490
- this.microAppWindow.microApp.clearDataListener();
2491
- this.microAppWindow.microApp.clearGlobalDataListener();
2492
- this.injectedKeys.forEach((key) => {
2493
- Reflect.deleteProperty(this.microAppWindow, key);
2494
- });
2495
- this.injectedKeys.clear();
2496
- this.escapeKeys.forEach((key) => {
2497
- Reflect.deleteProperty(globalEnv.rawWindow, key);
2498
- });
2499
- this.escapeKeys.clear();
2500
- if (--SandBox.activeCount === 0) {
2501
- releaseEffectDocumentEvent();
2502
- releasePatches();
2503
- }
2609
+ if (!Object.keys(rawState.microAppState).length) {
2610
+ delete rawState.microAppState;
2504
2611
  }
2505
2612
  }
2506
- // record umd snapshot before the first execution of umdHookMount
2507
- recordUmdSnapshot() {
2508
- this.microAppWindow.__MICRO_APP_UMD_MODE__ = true;
2509
- this.recordUmdEffect();
2510
- recordDataCenterSnapshot(this.microAppWindow.microApp);
2511
- this.recordUmdInjectedValues = new Map();
2512
- this.injectedKeys.forEach((key) => {
2513
- this.recordUmdInjectedValues.set(key, Reflect.get(this.microAppWindow, key));
2514
- });
2613
+ // 生成新的state对象
2614
+ return assign({}, rawState);
2615
+ }
2616
+ // get micro app state form origin state
2617
+ function getMicroState(appName, state) {
2618
+ var _a;
2619
+ return ((_a = state === null || state === void 0 ? void 0 : state.microAppState) === null || _a === void 0 ? void 0 : _a[appName]) || null;
2620
+ }
2621
+ const ENC_AD_RE = /&/g; // %M1
2622
+ const ENC_EQ_RE = /=/g; // %M2
2623
+ const DEC_AD_RE = /%M1/g; // &
2624
+ const DEC_EQ_RE = /%M2/g; // =
2625
+ function encodeMicroPath(path) {
2626
+ return encodeURIComponent(commonDecode(path).replace(ENC_AD_RE, '%M1').replace(ENC_EQ_RE, '%M2'));
2627
+ }
2628
+ function decodeMicroPath(path) {
2629
+ return commonDecode(path).replace(DEC_AD_RE, '&').replace(DEC_EQ_RE, '=');
2630
+ }
2631
+ function commonDecode(path) {
2632
+ try {
2633
+ const decPath = decodeURIComponent(path);
2634
+ if (path === decPath || DEC_AD_RE.test(decPath) || DEC_EQ_RE.test(decPath))
2635
+ return decPath;
2636
+ return commonDecode(decPath);
2515
2637
  }
2516
- // rebuild umd snapshot before remount umd app
2517
- rebuildUmdSnapshot() {
2518
- this.recordUmdInjectedValues.forEach((value, key) => {
2519
- Reflect.set(this.proxyWindow, key, value);
2520
- });
2521
- this.rebuildUmdEffect();
2522
- rebuildDataCenterSnapshot(this.microAppWindow.microApp);
2638
+ catch (_a) {
2639
+ return path;
2523
2640
  }
2524
- /**
2525
- * get scopeProperties and escapeProperties from plugins
2526
- * @param appName app name
2527
- */
2528
- getSpecialProperties(appName) {
2529
- var _a;
2530
- if (!isPlainObject(microApp.plugins))
2531
- return;
2532
- this.commonActionForSpecialProperties(microApp.plugins.global);
2533
- this.commonActionForSpecialProperties((_a = microApp.plugins.modules) === null || _a === void 0 ? void 0 : _a[appName]);
2641
+ }
2642
+ // 格式化query参数key,防止与原有参数的冲突
2643
+ function formatQueryAppName(appName) {
2644
+ return `app-${appName}`;
2645
+ }
2646
+ // 根据浏览器url参数,获取当前子应用的path
2647
+ function getMicroPathFromURL(appName) {
2648
+ var _a, _b;
2649
+ const rawLocation = globalEnv.rawWindow.location;
2650
+ const queryObject = getQueryObjectFromURL(rawLocation.search, rawLocation.hash);
2651
+ 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)]);
2652
+ return isString(microPath) ? decodeMicroPath(microPath) : null;
2653
+ }
2654
+ // 将name=encodeUrl地址插入到浏览器url上
2655
+ function setMicroPathToURL(appName, microLocation) {
2656
+ let { pathname, search, hash } = globalEnv.rawWindow.location;
2657
+ const queryObject = getQueryObjectFromURL(search, hash);
2658
+ const encodedMicroPath = encodeMicroPath(microLocation.pathname +
2659
+ microLocation.search +
2660
+ microLocation.hash);
2661
+ let isAttach2Hash = false; // 基座是否是hash模式,这个其实也不准,只是表示参数加到了hash上
2662
+ // hash存在且search不存在,则认为是hash路由
2663
+ if (hash && !search) {
2664
+ isAttach2Hash = true;
2665
+ if (queryObject.hashQuery) {
2666
+ queryObject.hashQuery[formatQueryAppName(appName)] = encodedMicroPath;
2667
+ }
2668
+ else {
2669
+ queryObject.hashQuery = {
2670
+ [formatQueryAppName(appName)]: encodedMicroPath
2671
+ };
2672
+ }
2673
+ const baseHash = hash.includes('?') ? hash.slice(0, hash.indexOf('?') + 1) : hash + '?';
2674
+ hash = baseHash + stringifyQuery(queryObject.hashQuery);
2534
2675
  }
2535
- // common action for global plugins and module plugins
2536
- commonActionForSpecialProperties(plugins) {
2537
- if (isArray(plugins)) {
2538
- for (const plugin of plugins) {
2539
- if (isPlainObject(plugin)) {
2540
- if (isArray(plugin.scopeProperties)) {
2541
- this.scopeProperties = this.scopeProperties.concat(plugin.scopeProperties);
2542
- }
2543
- if (isArray(plugin.escapeProperties)) {
2544
- this.escapeProperties = this.escapeProperties.concat(plugin.escapeProperties);
2545
- }
2546
- }
2547
- }
2676
+ else {
2677
+ if (queryObject.searchQuery) {
2678
+ queryObject.searchQuery[formatQueryAppName(appName)] = encodedMicroPath;
2679
+ }
2680
+ else {
2681
+ queryObject.searchQuery = {
2682
+ [formatQueryAppName(appName)]: encodedMicroPath
2683
+ };
2548
2684
  }
2685
+ search = '?' + stringifyQuery(queryObject.searchQuery);
2549
2686
  }
2550
- // create proxyWindow with Proxy(microAppWindow)
2551
- createProxyWindow(appName) {
2552
- const rawWindow = globalEnv.rawWindow;
2553
- const descriptorTargetMap = new Map();
2554
- // window.xxx will trigger proxy
2555
- return new Proxy(this.microAppWindow, {
2556
- get: (target, key) => {
2557
- throttleDeferForSetAppName(appName);
2687
+ return {
2688
+ fullPath: pathname + search + hash,
2689
+ isAttach2Hash,
2690
+ };
2691
+ }
2692
+ // 将name=encodeUrl的参数从浏览器url上删除
2693
+ function removeMicroPathFromURL(appName, targetLocation) {
2694
+ var _a, _b, _c, _d;
2695
+ let { pathname, search, hash } = targetLocation || globalEnv.rawWindow.location;
2696
+ const queryObject = getQueryObjectFromURL(search, hash);
2697
+ let isAttach2Hash = false;
2698
+ if ((_a = queryObject.hashQuery) === null || _a === void 0 ? void 0 : _a[formatQueryAppName(appName)]) {
2699
+ isAttach2Hash = true;
2700
+ (_b = queryObject.hashQuery) === null || _b === void 0 ? true : delete _b[formatQueryAppName(appName)];
2701
+ const hashQueryStr = stringifyQuery(queryObject.hashQuery);
2702
+ hash = hash.slice(0, hash.indexOf('?') + Number(Boolean(hashQueryStr))) + hashQueryStr;
2703
+ }
2704
+ else if ((_c = queryObject.searchQuery) === null || _c === void 0 ? void 0 : _c[formatQueryAppName(appName)]) {
2705
+ (_d = queryObject.searchQuery) === null || _d === void 0 ? true : delete _d[formatQueryAppName(appName)];
2706
+ const searchQueryStr = stringifyQuery(queryObject.searchQuery);
2707
+ search = searchQueryStr ? '?' + searchQueryStr : '';
2708
+ }
2709
+ return {
2710
+ fullPath: pathname + search + hash,
2711
+ isAttach2Hash,
2712
+ };
2713
+ }
2714
+ /**
2715
+ * 根据location获取query对象
2716
+ */
2717
+ function getQueryObjectFromURL(search, hash) {
2718
+ const queryObject = {};
2719
+ if (search !== '' && search !== '?') {
2720
+ queryObject.searchQuery = parseQuery(search.slice(1));
2721
+ }
2722
+ if (hash.includes('?')) {
2723
+ queryObject.hashQuery = parseQuery(hash.slice(hash.indexOf('?') + 1));
2724
+ }
2725
+ return queryObject;
2726
+ }
2727
+ /**
2728
+ * get microApp path from browser URL without hash
2729
+ */
2730
+ function getNoHashMicroPathFromURL(appName, baseUrl) {
2731
+ const microPath = getMicroPathFromURL(appName);
2732
+ if (!microPath)
2733
+ return '';
2734
+ const formatLocation = createURL(microPath, baseUrl);
2735
+ return formatLocation.origin + formatLocation.pathname + formatLocation.search;
2736
+ }
2737
+
2738
+ /**
2739
+ * dispatch PopStateEvent & HashChangeEvent to child app
2740
+ * each child app will listen for popstate event when sandbox start
2741
+ * and release it when sandbox stop
2742
+ * @param appName app name
2743
+ * @returns release callback
2744
+ */
2745
+ function addHistoryListener(appName) {
2746
+ const rawWindow = globalEnv.rawWindow;
2747
+ // handle popstate event and distribute to child app
2748
+ const popStateHandler = (e) => {
2749
+ // exclude hidden keep-alive app
2750
+ if (getActiveApps(true).includes(appName) && !e.onlyForBrowser) {
2751
+ const microPath = getMicroPathFromURL(appName);
2752
+ const app = appInstanceMap.get(appName);
2753
+ const proxyWindow = app.sandBox.proxyWindow;
2754
+ let isHashChange = false;
2755
+ // for hashChangeEvent
2756
+ const oldHref = proxyWindow.location.href;
2757
+ // Do not attach micro state to url when microPath is empty
2758
+ if (microPath) {
2759
+ const oldHash = proxyWindow.location.hash;
2760
+ updateMicroLocation(appName, microPath, proxyWindow.location);
2761
+ isHashChange = proxyWindow.location.hash !== oldHash;
2762
+ }
2763
+ // dispatch formatted popStateEvent to child
2764
+ dispatchPopStateEventToMicroApp(appName, proxyWindow, rawWindow.history.state);
2765
+ // dispatch formatted hashChangeEvent to child when hash change
2766
+ if (isHashChange)
2767
+ dispatchHashChangeEventToMicroApp(appName, proxyWindow, oldHref);
2768
+ // clear element scope before trigger event of next app
2769
+ removeDomScope();
2770
+ }
2771
+ };
2772
+ rawWindow.addEventListener('popstate', popStateHandler);
2773
+ return () => {
2774
+ rawWindow.removeEventListener('popstate', popStateHandler);
2775
+ };
2776
+ }
2777
+ /**
2778
+ * dispatch formatted popstate event to microApp
2779
+ * @param appName app name
2780
+ * @param proxyWindow sandbox window
2781
+ * @param eventState history.state
2782
+ */
2783
+ function dispatchPopStateEventToMicroApp(appName, proxyWindow, eventState) {
2784
+ // create PopStateEvent named popstate-appName with sub app state
2785
+ const newPopStateEvent = new PopStateEvent(formatEventName$1('popstate', appName), { state: getMicroState(appName, eventState) });
2786
+ globalEnv.rawWindow.dispatchEvent(newPopStateEvent);
2787
+ // call function window.onpopstate if it exists
2788
+ typeof proxyWindow.onpopstate === 'function' && proxyWindow.onpopstate(newPopStateEvent);
2789
+ }
2790
+ /**
2791
+ * dispatch formatted hashchange event to microApp
2792
+ * @param appName app name
2793
+ * @param proxyWindow sandbox window
2794
+ * @param oldHref old href
2795
+ */
2796
+ function dispatchHashChangeEventToMicroApp(appName, proxyWindow, oldHref) {
2797
+ const newHashChangeEvent = new HashChangeEvent(formatEventName$1('hashchange', appName), {
2798
+ newURL: proxyWindow.location.href,
2799
+ oldURL: oldHref,
2800
+ });
2801
+ globalEnv.rawWindow.dispatchEvent(newHashChangeEvent);
2802
+ // call function window.onhashchange if it exists
2803
+ typeof proxyWindow.onhashchange === 'function' && proxyWindow.onhashchange(newHashChangeEvent);
2804
+ }
2805
+ /**
2806
+ * dispatch native PopStateEvent, simulate location behavior
2807
+ * @param onlyForBrowser only dispatch PopStateEvent to browser
2808
+ */
2809
+ function dispatchNativePopStateEvent(onlyForBrowser) {
2810
+ const event = new PopStateEvent('popstate', { state: null });
2811
+ if (onlyForBrowser)
2812
+ event.onlyForBrowser = true;
2813
+ globalEnv.rawWindow.dispatchEvent(event);
2814
+ }
2815
+ /**
2816
+ * dispatch hashchange event to browser
2817
+ * @param oldHref old href of rawWindow.location
2818
+ */
2819
+ function dispatchNativeHashChangeEvent(oldHref) {
2820
+ const newHashChangeEvent = new HashChangeEvent('hashchange', {
2821
+ newURL: globalEnv.rawWindow.location.href,
2822
+ oldURL: oldHref,
2823
+ });
2824
+ globalEnv.rawWindow.dispatchEvent(newHashChangeEvent);
2825
+ }
2826
+ /**
2827
+ * dispatch popstate & hashchange event to browser
2828
+ * @param onlyForBrowser only dispatch event to browser
2829
+ * @param oldHref old href of rawWindow.location
2830
+ */
2831
+ function dispatchNativeEvent(onlyForBrowser, oldHref) {
2832
+ // clear element scope before dispatch global event
2833
+ removeDomScope();
2834
+ dispatchNativePopStateEvent(onlyForBrowser);
2835
+ if (oldHref) {
2836
+ dispatchNativeHashChangeEvent(oldHref);
2837
+ }
2838
+ }
2839
+
2840
+ /**
2841
+ * create proxyHistory for microApp
2842
+ * MDN https://developer.mozilla.org/en-US/docs/Web/API/History
2843
+ * @param appName app name
2844
+ * @param microLocation microApp location
2845
+ */
2846
+ function createMicroHistory(appName, microLocation) {
2847
+ const rawHistory = globalEnv.rawWindow.history;
2848
+ function getMicroHistoryMethod(methodName) {
2849
+ return function (...rests) {
2850
+ if ((methodName === 'pushState' || methodName === 'replaceState') &&
2851
+ (isString(rests[2]) || isURL(rests[2]))) {
2852
+ const targetLocation = createURL(rests[2], microLocation.href);
2853
+ if (targetLocation.origin === microLocation.origin) {
2854
+ navigateWithNativeEvent(methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rawHistory.state, rests[0]), rests[1]);
2855
+ const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
2856
+ if (targetFullPath !== microLocation.fullPath) {
2857
+ updateMicroLocation(appName, targetFullPath, microLocation);
2858
+ }
2859
+ }
2860
+ else {
2861
+ rawHistory[methodName].apply(rawHistory, rests);
2862
+ }
2863
+ }
2864
+ else {
2865
+ rawHistory[methodName].apply(rawHistory, rests);
2866
+ }
2867
+ };
2868
+ }
2869
+ return new Proxy(rawHistory, {
2870
+ get(target, key) {
2871
+ if (key === 'state') {
2872
+ return getMicroState(appName, rawHistory.state);
2873
+ }
2874
+ else if (isFunction(Reflect.get(target, key))) {
2875
+ return getMicroHistoryMethod(key);
2876
+ }
2877
+ return Reflect.get(target, key);
2878
+ },
2879
+ set(target, key, value) {
2880
+ return Reflect.set(target, key, value);
2881
+ }
2882
+ });
2883
+ }
2884
+ /**
2885
+ * navigate to new path base on native method of history
2886
+ * @param methodName pushState/replaceState
2887
+ * @param fullPath full path
2888
+ * @param state history.state, default is null
2889
+ * @param title history.title, default is ''
2890
+ */
2891
+ function nativeHistoryNavigate(methodName, fullPath, state = null, title = '') {
2892
+ globalEnv.rawWindow.history[methodName](state, title, fullPath);
2893
+ }
2894
+ /**
2895
+ * Navigate to new path, and dispatch native popStateEvent/hashChangeEvent to browser
2896
+ * Use scenes:
2897
+ * 1. mount/unmount through updateBrowserURL with limited popstateEvent
2898
+ * 2. proxyHistory.pushState/replaceState with limited popstateEvent
2899
+ * 3. api microApp.router.push/replace
2900
+ * 4. proxyLocation.hash = xxx
2901
+ * @param methodName pushState/replaceState
2902
+ * @param result result of add/remove microApp path on browser url
2903
+ * @param onlyForBrowser only dispatch event to browser
2904
+ * @param state history.state, not required
2905
+ * @param title history.title, not required
2906
+ */
2907
+ function navigateWithNativeEvent(methodName, result, onlyForBrowser, state, title) {
2908
+ const rawLocation = globalEnv.rawWindow.location;
2909
+ const oldFullPath = rawLocation.pathname + rawLocation.search + rawLocation.hash;
2910
+ const oldHref = result.isAttach2Hash && oldFullPath !== result.fullPath ? rawLocation.href : null;
2911
+ // navigate with native history method
2912
+ nativeHistoryNavigate(methodName, result.fullPath, state, title);
2913
+ if (oldFullPath !== result.fullPath)
2914
+ dispatchNativeEvent(onlyForBrowser, oldHref);
2915
+ }
2916
+ /**
2917
+ * update browser url when mount/unmount/hidden/show
2918
+ * @param result result of add/remove microApp path on browser url
2919
+ * @param state history.state
2920
+ */
2921
+ function updateBrowserURL(result, state) {
2922
+ navigateWithNativeEvent('replaceState', result, true, state);
2923
+ }
2924
+ /**
2925
+ * When path is same, keep the microAppState in history.state
2926
+ * Fix bug of missing microAppState in next.js & angular
2927
+ * @param method history.pushState/replaceState
2928
+ */
2929
+ function patchHistoryState(method) {
2930
+ const rawWindow = globalEnv.rawWindow;
2931
+ return function (...rests) {
2932
+ var _a;
2933
+ if (((_a = rawWindow.history.state) === null || _a === void 0 ? void 0 : _a.microAppState) &&
2934
+ (!isPlainObject(rests[0]) || !rests[0].microAppState) &&
2935
+ (isString(rests[2]) || isURL(rests[2]))) {
2936
+ const currentHref = rawWindow.location.href;
2937
+ const targetLocation = createURL(rests[2], currentHref);
2938
+ if (targetLocation.href === currentHref) {
2939
+ rests[0] = assign({}, rests[0], {
2940
+ microAppState: rawWindow.history.state.microAppState,
2941
+ });
2942
+ }
2943
+ }
2944
+ method.apply(rawWindow.history, rests);
2945
+ };
2946
+ }
2947
+ let isReWriteHistoryState = false;
2948
+ /**
2949
+ * rewrite history.pushState/replaceState
2950
+ * used to fix the problem that the microAppState maybe missing when mainApp navigate to same path
2951
+ * e.g: when nextjs, angular receive popstate event, they will use history.replaceState to update browser url with a new state object
2952
+ */
2953
+ function rewriteHistoryState() {
2954
+ // filter nest app
2955
+ if (!isReWriteHistoryState && !window.__MICRO_APP_ENVIRONMENT__) {
2956
+ isReWriteHistoryState = true;
2957
+ const rawWindow = globalEnv.rawWindow;
2958
+ rawWindow.history.pushState = patchHistoryState(rawWindow.history.pushState);
2959
+ rawWindow.history.replaceState = patchHistoryState(rawWindow.history.replaceState);
2960
+ }
2961
+ }
2962
+
2963
+ function createRouterApi() {
2964
+ /**
2965
+ * common handler for router.push/router.replace method
2966
+ * @param appName app name
2967
+ * @param methodName replaceState/pushState
2968
+ * @param targetLocation target location
2969
+ * @param state to.state
2970
+ */
2971
+ function navigateWithRawHistory(appName, methodName, targetLocation, state) {
2972
+ navigateWithNativeEvent(methodName, setMicroPathToURL(appName, targetLocation), false, setMicroState(appName, globalEnv.rawWindow.history.state, state !== null && state !== void 0 ? state : null));
2973
+ // clear element scope after navigate
2974
+ removeDomScope();
2975
+ }
2976
+ /**
2977
+ * create method of router.push/replace
2978
+ * NOTE:
2979
+ * 1. The same fullPath will be blocked
2980
+ * 2. name & path is required
2981
+ * 3. path is fullPath except for the domain (the domain can be taken, but not valid)
2982
+ * @param replace use router.replace?
2983
+ */
2984
+ function createNavigationMethod(replace) {
2985
+ return function (to) {
2986
+ const appName = formatAppName(to.name);
2987
+ // console.log(3333333, appInstanceMap.get(appName))
2988
+ if (appName && isString(to.path)) {
2989
+ const app = appInstanceMap.get(appName);
2990
+ if (app && !app.sandBox)
2991
+ return logError(`navigation failed, sandBox of app ${appName} is closed`);
2992
+ // active apps, include hidden keep-alive app
2993
+ if (getActiveApps().includes(appName)) {
2994
+ const microLocation = app.sandBox.proxyWindow.location;
2995
+ const targetLocation = createURL(to.path, microLocation.href);
2996
+ // Only get path data, even if the origin is different from microApp
2997
+ const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
2998
+ if (microLocation.fullPath !== targetFullPath || getMicroPathFromURL(appName) !== targetFullPath) {
2999
+ const methodName = (replace && to.replace !== false) || to.replace === true ? 'replaceState' : 'pushState';
3000
+ navigateWithRawHistory(appName, methodName, targetLocation, to.state);
3001
+ }
3002
+ }
3003
+ else {
3004
+ /**
3005
+ * app not exit or unmounted, update browser URL with replaceState
3006
+ * use base app location.origin as baseURL
3007
+ */
3008
+ const rawLocation = globalEnv.rawWindow.location;
3009
+ const targetLocation = createURL(to.path, rawLocation.origin);
3010
+ const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3011
+ if (getMicroPathFromURL(appName) !== targetFullPath) {
3012
+ navigateWithRawHistory(appName, to.replace === false ? 'pushState' : 'replaceState', targetLocation, to.state);
3013
+ }
3014
+ }
3015
+ }
3016
+ else {
3017
+ logError(`navigation failed, name & path are required when use router.${replace ? 'replace' : 'push'}`);
3018
+ }
3019
+ };
3020
+ }
3021
+ // create method of router.go/back/forward
3022
+ function createRawHistoryMethod(methodName) {
3023
+ return function (...rests) {
3024
+ return globalEnv.rawWindow.history[methodName](...rests);
3025
+ };
3026
+ }
3027
+ const beforeGuards = useSetRecord();
3028
+ const afterGuards = useSetRecord();
3029
+ /**
3030
+ * run all of beforeEach/afterEach guards
3031
+ * NOTE:
3032
+ * 1. Modify browser url first, and then run guards,
3033
+ * consistent with the browser forward & back button
3034
+ * 2. Note the element binding
3035
+ * @param appName app name
3036
+ * @param to target location
3037
+ * @param from old location
3038
+ * @param guards guards list
3039
+ */
3040
+ function runGuards(appName, to, from, guards) {
3041
+ // clear element scope before execute function of parent
3042
+ removeDomScope();
3043
+ for (const guard of guards) {
3044
+ if (isFunction(guard)) {
3045
+ guard(appName, to, from);
3046
+ }
3047
+ else if (isPlainObject(guard) && isFunction(guard[appName])) {
3048
+ guard[appName](to, from);
3049
+ }
3050
+ }
3051
+ }
3052
+ /**
3053
+ * global hook for router
3054
+ * update router information base on microLocation
3055
+ * @param appName app name
3056
+ * @param microLocation location of microApp
3057
+ */
3058
+ function executeNavigationGuard(appName, to, from) {
3059
+ router.current.set(appName, to);
3060
+ runGuards(appName, to, from, beforeGuards.list());
3061
+ requestIdleCallback(() => {
3062
+ runGuards(appName, to, from, afterGuards.list());
3063
+ });
3064
+ }
3065
+ function clearRouterWhenUnmount(appName) {
3066
+ router.current.delete(appName);
3067
+ }
3068
+ // defaultPage data
3069
+ const defaultPageRecord = useMapRecord();
3070
+ /**
3071
+ * defaultPage只在子应用初始化时生效,且优先级比浏览器上的子应用路由地址低
3072
+ * @param appName app name
3073
+ * @param path page path
3074
+ */
3075
+ function setDefaultPage(appName, path) {
3076
+ appName = formatAppName(appName);
3077
+ if (!appName)
3078
+ return noopFalse;
3079
+ return defaultPageRecord.add(appName, path);
3080
+ }
3081
+ function removeDefaultPage(appName) {
3082
+ appName = formatAppName(appName);
3083
+ if (!appName)
3084
+ return false;
3085
+ return defaultPageRecord.delete(appName);
3086
+ }
3087
+ // Router API for developer
3088
+ const router = {
3089
+ current: new Map(),
3090
+ encode: encodeMicroPath,
3091
+ decode: decodeMicroPath,
3092
+ push: createNavigationMethod(false),
3093
+ replace: createNavigationMethod(true),
3094
+ go: createRawHistoryMethod('go'),
3095
+ back: createRawHistoryMethod('back'),
3096
+ forward: createRawHistoryMethod('forward'),
3097
+ beforeEach: beforeGuards.add,
3098
+ afterEach: afterGuards.add,
3099
+ // attachToURL: 将指定的子应用路由信息添加到浏览器地址上
3100
+ // attachAllToURL: 将所有正在运行的子应用路由信息添加到浏览器地址上
3101
+ setDefaultPage,
3102
+ removeDefaultPage,
3103
+ getDefaultPage: defaultPageRecord.get,
3104
+ };
3105
+ return {
3106
+ router,
3107
+ executeNavigationGuard,
3108
+ clearRouterWhenUnmount,
3109
+ };
3110
+ }
3111
+ const { router, executeNavigationGuard, clearRouterWhenUnmount, } = createRouterApi();
3112
+
3113
+ const shadowLocationKeys = ['href', 'pathname', 'search', 'hash'];
3114
+ // origin is readonly, so we ignore when updateMicroLocation
3115
+ const locationKeys = [...shadowLocationKeys, 'host', 'hostname', 'port', 'protocol', 'search'];
3116
+ // origin, fullPath is necessary for guardLocation
3117
+ const guardLocationKeys = [...locationKeys, 'origin', 'fullPath'];
3118
+ /**
3119
+ * create guardLocation by microLocation, used for router guard
3120
+ */
3121
+ function createGuardLocation(appName, microLocation) {
3122
+ const guardLocation = assign({ name: appName }, microLocation);
3123
+ // The prototype values on the URL needs to be manually transferred
3124
+ for (const key of guardLocationKeys)
3125
+ guardLocation[key] = microLocation[key];
3126
+ return guardLocation;
3127
+ }
3128
+ // for updateBrowserURLWithLocation when initial
3129
+ function autoTriggerNavigationGuard(appName, microLocation) {
3130
+ executeNavigationGuard(appName, createGuardLocation(appName, microLocation), createGuardLocation(appName, microLocation));
3131
+ }
3132
+ /**
3133
+ * The following scenes will trigger location update:
3134
+ * 1. pushState/replaceState
3135
+ * 2. popStateEvent
3136
+ * 3. query on browser url when init sub app
3137
+ * 4. set defaultPage when when init sub app
3138
+ * NOTE:
3139
+ * 1. update browser URL first, and then update microLocation
3140
+ * 2. the same fullPath will not trigger router guards
3141
+ * @param appName app name
3142
+ * @param path target path
3143
+ * @param base base url
3144
+ * @param microLocation micro app location
3145
+ * @param type auto prevent
3146
+ */
3147
+ function updateMicroLocation(appName, path, microLocation, type) {
3148
+ const newLocation = createURL(path, microLocation.href);
3149
+ // record old values of microLocation to `from`
3150
+ const from = createGuardLocation(appName, microLocation);
3151
+ for (const key of locationKeys) {
3152
+ if (shadowLocationKeys.includes(key)) {
3153
+ // reference of shadowLocation
3154
+ microLocation.shadowLocation[key] = newLocation[key];
3155
+ }
3156
+ else {
3157
+ // @ts-ignore reference of microLocation
3158
+ microLocation[key] = newLocation[key];
3159
+ }
3160
+ }
3161
+ // update latest values of microLocation to `to`
3162
+ const to = createGuardLocation(appName, microLocation);
3163
+ // The hook called only when fullPath changed
3164
+ if (type === 'auto' || (from.fullPath !== to.fullPath && type !== 'prevent')) {
3165
+ executeNavigationGuard(appName, to, from);
3166
+ }
3167
+ }
3168
+ /**
3169
+ * Create location for microApp, each microApp has only one location object, it is a reference type
3170
+ * MDN https://developer.mozilla.org/en-US/docs/Web/API/Location
3171
+ * @param appName app name
3172
+ * @param url app url
3173
+ */
3174
+ function createMicroLocation(appName, url) {
3175
+ const rawWindow = globalEnv.rawWindow;
3176
+ const rawLocation = rawWindow.location;
3177
+ // microLocation is the location of child app, it is globally unique
3178
+ const microLocation = createURL(url);
3179
+ // shadowLocation is the current location information (href, pathname, search, hash)
3180
+ const shadowLocation = {
3181
+ href: microLocation.href,
3182
+ pathname: microLocation.pathname,
3183
+ search: microLocation.search,
3184
+ hash: microLocation.hash,
3185
+ };
3186
+ /**
3187
+ * Common handler for href, assign, replace
3188
+ * It is mainly used to deal with special scenes about hash
3189
+ * @param value target path
3190
+ * @param methodName pushState/replaceState
3191
+ * @returns origin value or formatted value
3192
+ */
3193
+ const commonHandler = (value, methodName) => {
3194
+ const targetLocation = createURL(value, microLocation.href);
3195
+ // Even if the origin is the same, developers still have the possibility of want to jump to a new page
3196
+ if (targetLocation.origin === microLocation.origin) {
3197
+ const setMicroPathResult = setMicroPathToURL(appName, targetLocation);
3198
+ /**
3199
+ * change hash with location.href will not trigger the browser reload
3200
+ * so we use pushState & reload to imitate href behavior
3201
+ * NOTE:
3202
+ * 1. if child app only change hash, it should not trigger browser reload
3203
+ * 2. if address is same and has hash, it should not add route stack
3204
+ */
3205
+ if (targetLocation.pathname === shadowLocation.pathname &&
3206
+ targetLocation.search === shadowLocation.search) {
3207
+ let oldHref = null;
3208
+ if (targetLocation.hash !== shadowLocation.hash) {
3209
+ if (setMicroPathResult.isAttach2Hash)
3210
+ oldHref = rawLocation.href;
3211
+ nativeHistoryNavigate(methodName, setMicroPathResult.fullPath);
3212
+ }
3213
+ if (targetLocation.hash) {
3214
+ dispatchNativeEvent(false, oldHref);
3215
+ }
3216
+ else {
3217
+ rawLocation.reload();
3218
+ }
3219
+ return void 0;
3220
+ /**
3221
+ * when baseApp is hash router, address change of child can not reload browser
3222
+ * so we imitate behavior of browser (reload)
3223
+ */
3224
+ }
3225
+ else if (setMicroPathResult.isAttach2Hash) {
3226
+ nativeHistoryNavigate(methodName, setMicroPathResult.fullPath);
3227
+ rawLocation.reload();
3228
+ return void 0;
3229
+ }
3230
+ value = setMicroPathResult.fullPath;
3231
+ }
3232
+ return value;
3233
+ };
3234
+ /**
3235
+ * create location PropertyDescriptor (href, pathname, search, hash)
3236
+ * @param key property name
3237
+ * @param setter setter of location property
3238
+ */
3239
+ function createPropertyDescriptor(getter, setter) {
3240
+ return {
3241
+ enumerable: true,
3242
+ configurable: true,
3243
+ get: getter,
3244
+ set: setter,
3245
+ };
3246
+ }
3247
+ /**
3248
+ * common handler for location.pathname & location.search
3249
+ * @param targetPath target fullPath
3250
+ * @param key pathname/search
3251
+ */
3252
+ function handleForPathNameAndSearch(targetPath, key) {
3253
+ const targetLocation = createURL(targetPath, url);
3254
+ // When the browser url has a hash value, the same pathname/search will not refresh browser
3255
+ if (targetLocation[key] === shadowLocation[key] && shadowLocation.hash) {
3256
+ // The href has not changed, not need to dispatch hashchange event
3257
+ dispatchNativeEvent(false);
3258
+ }
3259
+ else {
3260
+ /**
3261
+ * When the value is the same, no new route stack will be added
3262
+ * Special scenes such as:
3263
+ * pathname: /path ==> /path#hash, /path ==> /path?query
3264
+ * search: ?query ==> ?query#hash
3265
+ */
3266
+ nativeHistoryNavigate(targetLocation[key] === shadowLocation[key] ? 'replaceState' : 'pushState', setMicroPathToURL(appName, targetLocation).fullPath);
3267
+ rawLocation.reload();
3268
+ }
3269
+ }
3270
+ /**
3271
+ * Special processing for four keys: href, pathname, search and hash
3272
+ * They take values from shadowLocation, and require special operations when assigning values
3273
+ */
3274
+ rawDefineProperties(microLocation, {
3275
+ href: createPropertyDescriptor(() => shadowLocation.href, (value) => {
3276
+ const targetPath = commonHandler(value, 'pushState');
3277
+ if (targetPath)
3278
+ rawLocation.href = targetPath;
3279
+ }),
3280
+ pathname: createPropertyDescriptor(() => shadowLocation.pathname, (value) => {
3281
+ const targetPath = ('/' + value).replace(/^\/+/, '/') + shadowLocation.search + shadowLocation.hash;
3282
+ handleForPathNameAndSearch(targetPath, 'pathname');
3283
+ }),
3284
+ search: createPropertyDescriptor(() => shadowLocation.search, (value) => {
3285
+ const targetPath = shadowLocation.pathname + ('?' + value).replace(/^\?+/, '?') + shadowLocation.hash;
3286
+ handleForPathNameAndSearch(targetPath, 'search');
3287
+ }),
3288
+ hash: createPropertyDescriptor(() => shadowLocation.hash, (value) => {
3289
+ const targetPath = shadowLocation.pathname + shadowLocation.search + ('#' + value).replace(/^#+/, '#');
3290
+ const targetLocation = createURL(targetPath, url);
3291
+ // The same hash will not trigger popStateEvent
3292
+ if (targetLocation.hash !== shadowLocation.hash) {
3293
+ navigateWithNativeEvent('pushState', setMicroPathToURL(appName, targetLocation), false);
3294
+ }
3295
+ }),
3296
+ fullPath: createPropertyDescriptor(() => shadowLocation.pathname + shadowLocation.search + shadowLocation.hash, noop),
3297
+ });
3298
+ const createLocationMethod = (locationMethodName) => {
3299
+ return function (value) {
3300
+ const targetPath = commonHandler(value, locationMethodName === 'assign' ? 'pushState' : 'replaceState');
3301
+ if (targetPath)
3302
+ rawLocation[locationMethodName](targetPath);
3303
+ };
3304
+ };
3305
+ return assign(microLocation, {
3306
+ assign: createLocationMethod('assign'),
3307
+ replace: createLocationMethod('replace'),
3308
+ reload: (forcedReload) => rawLocation.reload(forcedReload),
3309
+ shadowLocation,
3310
+ });
3311
+ }
3312
+
3313
+ /**
3314
+ * The router system has two operations: read and write
3315
+ * Read through location and write through history & location
3316
+ * @param appName app name
3317
+ * @param url app url
3318
+ * @returns MicroRouter
3319
+ */
3320
+ function createMicroRouter(appName, url) {
3321
+ rewriteHistoryState();
3322
+ const microLocation = createMicroLocation(appName, url);
3323
+ return {
3324
+ microLocation,
3325
+ microHistory: createMicroHistory(appName, microLocation),
3326
+ };
3327
+ }
3328
+ // 当沙箱执行start, 或者隐藏的keep-alive应用重新渲染时时才根据浏览器url更新location 或者 将参数更新到url上
3329
+ function initRouteStateWithURL(appName, microLocation, defaultPage) {
3330
+ const microPath = getMicroPathFromURL(appName);
3331
+ if (microPath) {
3332
+ updateMicroLocation(appName, microPath, microLocation, 'auto');
3333
+ }
3334
+ else {
3335
+ updateBrowserURLWithLocation(appName, microLocation, defaultPage);
3336
+ }
3337
+ }
3338
+ /**
3339
+ * initialize browser information according to microLocation
3340
+ * called on sandbox.start or reshow of keep-alive app
3341
+ */
3342
+ function updateBrowserURLWithLocation(appName, microLocation, defaultPage) {
3343
+ // update microLocation with defaultPage
3344
+ if (defaultPage)
3345
+ updateMicroLocation(appName, defaultPage, microLocation, 'prevent');
3346
+ // attach microApp route info to browser URL
3347
+ updateBrowserURL(setMicroPathToURL(appName, microLocation), setMicroState(appName, globalEnv.rawWindow.history.state, null));
3348
+ // trigger guards after change browser URL
3349
+ autoTriggerNavigationGuard(appName, microLocation);
3350
+ }
3351
+ /**
3352
+ * In any case, microPath & microState will be removed from browser, but location will be initialized only when keep-router-state is false
3353
+ * @param appName app name
3354
+ * @param url app url
3355
+ * @param microLocation location of microApp
3356
+ * @param keepRouteState keep-router-state is only used to control whether to clear the location of microApp
3357
+ */
3358
+ function clearRouteStateFromURL(appName, url, microLocation, keepRouteState) {
3359
+ if (!keepRouteState) {
3360
+ const { pathname, search, hash } = createURL(url);
3361
+ updateMicroLocation(appName, pathname + search + hash, microLocation, 'prevent');
3362
+ }
3363
+ removeStateAndPathFromBrowser(appName);
3364
+ clearRouterWhenUnmount(appName);
3365
+ }
3366
+ /**
3367
+ * remove microState from history.state and remove microPath from browserURL
3368
+ * called on sandbox.stop or hidden of keep-alive app
3369
+ */
3370
+ function removeStateAndPathFromBrowser(appName) {
3371
+ updateBrowserURL(removeMicroPathFromURL(appName), removeMicroState(appName, globalEnv.rawWindow.history.state));
3372
+ }
3373
+
3374
+ /**
3375
+ * https://developer.mozilla.org/en-US/docs/Web/API/fetch
3376
+ * Promise<Response> fetch(input[, init])
3377
+ * input: string/Request
3378
+ * init?: object
3379
+ * @param url app url
3380
+ * @param target proxy target
3381
+ */
3382
+ function createMicroFetch(url, target) {
3383
+ if (!isUndefined(target) && !isFunction(target))
3384
+ return target;
3385
+ const rawFetch = target || globalEnv.rawWindow.fetch;
3386
+ return function microFetch(input, init, ...rests) {
3387
+ if (isString(input) || isURL(input)) {
3388
+ input = createURL(input, url).toString();
3389
+ }
3390
+ return rawFetch.call(globalEnv.rawWindow, input, init, ...rests);
3391
+ };
3392
+ }
3393
+ /**
3394
+ * https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
3395
+ * https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
3396
+ * @param url app url
3397
+ * @param target proxy target
3398
+ */
3399
+ function createMicroXMLHttpRequest(url, target) {
3400
+ if (!isUndefined(target) && !isFunction(target))
3401
+ return target;
3402
+ const rawXMLHttpRequest = target || globalEnv.rawWindow.XMLHttpRequest;
3403
+ return class MicroXMLHttpRequest extends rawXMLHttpRequest {
3404
+ open(method, reqUrl, ...rests) {
3405
+ if ((isString(reqUrl) && !/^f(ile|tp):\/\//.test(reqUrl)) || isURL(reqUrl)) {
3406
+ reqUrl = createURL(reqUrl, url).toString();
3407
+ }
3408
+ super.open(method, reqUrl, ...rests);
3409
+ }
3410
+ };
3411
+ }
3412
+ function useMicroEventSource() {
3413
+ let eventSourceMap;
3414
+ /**
3415
+ * https://developer.mozilla.org/en-US/docs/Web/API/EventSource
3416
+ * pc = new EventSource(url[, configuration])
3417
+ * url: string/Request
3418
+ * configuration?: object
3419
+ * @param url app url
3420
+ * @param target proxy target
3421
+ */
3422
+ function createMicroEventSource(appName, url, target) {
3423
+ if (!isUndefined(target) && !isFunction(target))
3424
+ return target;
3425
+ const rawEventSource = target || globalEnv.rawWindow.EventSource;
3426
+ return class MicroEventSource extends rawEventSource {
3427
+ constructor(eventSourceUrl, eventSourceInitDict, ...rests) {
3428
+ if (isString(eventSourceUrl) || isURL(eventSourceUrl)) {
3429
+ eventSourceUrl = createURL(eventSourceUrl, url).toString();
3430
+ }
3431
+ super(eventSourceUrl, eventSourceInitDict, ...rests);
3432
+ if (eventSourceMap) {
3433
+ const eventSourceList = eventSourceMap.get(appName);
3434
+ if (eventSourceList) {
3435
+ eventSourceList.add(this);
3436
+ }
3437
+ else {
3438
+ eventSourceMap.set(appName, new Set([this]));
3439
+ }
3440
+ }
3441
+ else {
3442
+ eventSourceMap = new Map([[appName, new Set([this])]]);
3443
+ }
3444
+ }
3445
+ close() {
3446
+ var _a;
3447
+ super.close();
3448
+ (_a = eventSourceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.delete(this);
3449
+ }
3450
+ };
3451
+ }
3452
+ function clearMicroEventSource(appName) {
3453
+ const eventSourceList = eventSourceMap === null || eventSourceMap === void 0 ? void 0 : eventSourceMap.get(appName);
3454
+ if (eventSourceList === null || eventSourceList === void 0 ? void 0 : eventSourceList.size) {
3455
+ eventSourceList.forEach(item => {
3456
+ item.close();
3457
+ });
3458
+ eventSourceList.clear();
3459
+ }
3460
+ }
3461
+ return {
3462
+ createMicroEventSource,
3463
+ clearMicroEventSource,
3464
+ };
3465
+ }
3466
+
3467
+ const { createMicroEventSource, clearMicroEventSource } = useMicroEventSource();
3468
+ const globalPropertyList = ['window', 'self', 'globalThis'];
3469
+ class SandBox {
3470
+ constructor(appName, url, useMemoryRouter = true) {
3471
+ /**
3472
+ * Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
3473
+ * Fix https://github.com/micro-zoe/micro-app/issues/234
3474
+ */
3475
+ this.scopeProperties = [];
3476
+ // Properties that can be escape to rawWindow
3477
+ this.escapeProperties = [];
3478
+ // Properties newly added to microAppWindow
3479
+ this.injectedKeys = new Set();
3480
+ // Properties escape to rawWindow, cleared when unmount
3481
+ this.escapeKeys = new Set();
3482
+ // sandbox state
3483
+ this.active = false;
3484
+ this.microAppWindow = {}; // Proxy target
3485
+ this.adapter = new Adapter();
3486
+ // get scopeProperties and escapeProperties from plugins
3487
+ this.getSpecialProperties(appName);
3488
+ // create proxyWindow with Proxy(microAppWindow)
3489
+ this.proxyWindow = this.createProxyWindow(appName);
3490
+ // Rewrite global event listener & timeout
3491
+ assign(this, effect(appName, this.microAppWindow));
3492
+ // inject global properties
3493
+ this.initStaticGlobalKeys(this.microAppWindow, appName, url, useMemoryRouter);
3494
+ }
3495
+ start(baseRoute, useMemoryRouter = true, defaultPage = '') {
3496
+ if (!this.active) {
3497
+ this.active = true;
3498
+ if (useMemoryRouter) {
3499
+ this.initRouteState(defaultPage);
3500
+ // unique listener of popstate event for sub app
3501
+ this.removeHistoryListener = addHistoryListener(this.proxyWindow.__MICRO_APP_NAME__);
3502
+ }
3503
+ else {
3504
+ this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseRoute;
3505
+ }
3506
+ // prevent the key deleted during sandBox.stop after rewrite
3507
+ this.initGlobalKeysWhenStart(this.microAppWindow, this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.__MICRO_APP_URL__);
3508
+ if (++SandBox.activeCount === 1) {
3509
+ effectDocumentEvent();
3510
+ patchElementPrototypeMethods();
3511
+ listenUmountOfNestedApp();
3512
+ }
3513
+ fixBabelPolyfill6();
3514
+ }
3515
+ }
3516
+ stop(keepRouteState, clearEventSource) {
3517
+ if (this.active) {
3518
+ this.releaseEffect();
3519
+ this.microAppWindow.microApp.clearDataListener();
3520
+ this.microAppWindow.microApp.clearGlobalDataListener();
3521
+ if (this.removeHistoryListener) {
3522
+ this.clearRouteState(keepRouteState);
3523
+ // release listener of popstate
3524
+ this.removeHistoryListener();
3525
+ }
3526
+ if (clearEventSource) {
3527
+ clearMicroEventSource(this.proxyWindow.__MICRO_APP_NAME__);
3528
+ }
3529
+ /**
3530
+ * NOTE:
3531
+ * 1. injectedKeys and escapeKeys must be placed at the back
3532
+ * 2. if key in initial microAppWindow, and then rewrite, this key will be delete from microAppWindow when stop, and lost when restart
3533
+ */
3534
+ this.injectedKeys.forEach((key) => {
3535
+ Reflect.deleteProperty(this.microAppWindow, key);
3536
+ });
3537
+ this.injectedKeys.clear();
3538
+ this.escapeKeys.forEach((key) => {
3539
+ Reflect.deleteProperty(globalEnv.rawWindow, key);
3540
+ });
3541
+ this.escapeKeys.clear();
3542
+ if (--SandBox.activeCount === 0) {
3543
+ releaseEffectDocumentEvent();
3544
+ releasePatches();
3545
+ }
3546
+ this.active = false;
3547
+ }
3548
+ }
3549
+ // record umd snapshot before the first execution of umdHookMount
3550
+ recordUmdSnapshot() {
3551
+ this.microAppWindow.__MICRO_APP_UMD_MODE__ = true;
3552
+ this.recordUmdEffect();
3553
+ recordDataCenterSnapshot(this.microAppWindow.microApp);
3554
+ this.recordUmdInjectedValues = new Map();
3555
+ this.injectedKeys.forEach((key) => {
3556
+ this.recordUmdInjectedValues.set(key, Reflect.get(this.microAppWindow, key));
3557
+ });
3558
+ }
3559
+ // rebuild umd snapshot before remount umd app
3560
+ rebuildUmdSnapshot() {
3561
+ this.recordUmdInjectedValues.forEach((value, key) => {
3562
+ Reflect.set(this.proxyWindow, key, value);
3563
+ });
3564
+ this.rebuildUmdEffect();
3565
+ rebuildDataCenterSnapshot(this.microAppWindow.microApp);
3566
+ }
3567
+ /**
3568
+ * get scopeProperties and escapeProperties from plugins & adapter
3569
+ * @param appName app name
3570
+ */
3571
+ getSpecialProperties(appName) {
3572
+ var _a;
3573
+ this.scopeProperties = this.scopeProperties.concat(this.adapter.staticScopeProperties);
3574
+ if (isPlainObject(microApp.plugins)) {
3575
+ this.commonActionForSpecialProperties(microApp.plugins.global);
3576
+ this.commonActionForSpecialProperties((_a = microApp.plugins.modules) === null || _a === void 0 ? void 0 : _a[appName]);
3577
+ }
3578
+ }
3579
+ // common action for global plugins and module plugins
3580
+ commonActionForSpecialProperties(plugins) {
3581
+ if (isArray(plugins)) {
3582
+ for (const plugin of plugins) {
3583
+ if (isPlainObject(plugin)) {
3584
+ if (isArray(plugin.scopeProperties)) {
3585
+ this.scopeProperties = this.scopeProperties.concat(plugin.scopeProperties);
3586
+ }
3587
+ if (isArray(plugin.escapeProperties)) {
3588
+ this.escapeProperties = this.escapeProperties.concat(plugin.escapeProperties);
3589
+ }
3590
+ }
3591
+ }
3592
+ }
3593
+ }
3594
+ // create proxyWindow with Proxy(microAppWindow)
3595
+ createProxyWindow(appName) {
3596
+ const rawWindow = globalEnv.rawWindow;
3597
+ const descriptorTargetMap = new Map();
3598
+ // window.xxx will trigger proxy
3599
+ return new Proxy(this.microAppWindow, {
3600
+ get: (target, key) => {
3601
+ throttleDeferForSetAppName(appName);
2558
3602
  if (Reflect.has(target, key) ||
2559
3603
  (isString(key) && /^__MICRO_APP_/.test(key)) ||
2560
3604
  this.scopeProperties.includes(key))
@@ -2564,7 +3608,7 @@ class SandBox {
2564
3608
  },
2565
3609
  set: (target, key, value) => {
2566
3610
  if (this.active) {
2567
- if (escapeSetterKeyList.includes(key)) {
3611
+ if (this.adapter.escapeSetterKeyList.includes(key)) {
2568
3612
  Reflect.set(rawWindow, key, value);
2569
3613
  }
2570
3614
  else if (
@@ -2588,7 +3632,8 @@ class SandBox {
2588
3632
  this.injectedKeys.add(key);
2589
3633
  }
2590
3634
  if ((this.escapeProperties.includes(key) ||
2591
- (staticEscapeProperties.includes(key) && !Reflect.has(rawWindow, key))) &&
3635
+ (this.adapter.staticEscapeProperties.includes(key) &&
3636
+ !Reflect.has(rawWindow, key))) &&
2592
3637
  !this.scopeProperties.includes(key)) {
2593
3638
  Reflect.set(rawWindow, key, value);
2594
3639
  this.escapeKeys.add(key);
@@ -2644,21 +3689,35 @@ class SandBox {
2644
3689
  * @param microAppWindow micro window
2645
3690
  * @param appName app name
2646
3691
  * @param url app url
3692
+ * @param useMemoryRouter whether use memory router
2647
3693
  */
2648
- initMicroAppWindow(microAppWindow, appName, url) {
3694
+ initStaticGlobalKeys(microAppWindow, appName, url, useMemoryRouter) {
2649
3695
  microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
2650
3696
  microAppWindow.__MICRO_APP_NAME__ = appName;
3697
+ microAppWindow.__MICRO_APP_URL__ = url;
2651
3698
  microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
2652
3699
  microAppWindow.__MICRO_APP_WINDOW__ = microAppWindow;
2653
- microAppWindow.microApp = Object.assign(new EventCenterForMicroApp(appName), {
3700
+ microAppWindow.rawWindow = globalEnv.rawWindow;
3701
+ microAppWindow.rawDocument = globalEnv.rawDocument;
3702
+ microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
2654
3703
  removeDomScope,
2655
3704
  pureCreateElement,
3705
+ router,
2656
3706
  });
2657
- microAppWindow.rawWindow = globalEnv.rawWindow;
2658
- microAppWindow.rawDocument = globalEnv.rawDocument;
2659
- microAppWindow.hasOwnProperty = (key) => rawHasOwnProperty.call(microAppWindow, key) || rawHasOwnProperty.call(globalEnv.rawWindow, key);
2660
3707
  this.setMappingPropertiesWithRawDescriptor(microAppWindow);
3708
+ if (useMemoryRouter)
3709
+ this.setMicroAppRouter(microAppWindow, appName, url);
3710
+ }
3711
+ /**
3712
+ * init global properties of microAppWindow when exec sandBox.start
3713
+ * @param microAppWindow micro window
3714
+ * @param appName app name
3715
+ * @param url app url
3716
+ */
3717
+ initGlobalKeysWhenStart(microAppWindow, appName, url) {
3718
+ microAppWindow.hasOwnProperty = (key) => rawHasOwnProperty.call(microAppWindow, key) || rawHasOwnProperty.call(globalEnv.rawWindow, key);
2661
3719
  this.setHijackProperties(microAppWindow, appName);
3720
+ this.patchHijackRequest(microAppWindow, appName, url);
2662
3721
  }
2663
3722
  // properties associated with the native window
2664
3723
  setMappingPropertiesWithRawDescriptor(microAppWindow) {
@@ -2692,14 +3751,16 @@ class SandBox {
2692
3751
  let modifiedEval, modifiedImage;
2693
3752
  rawDefineProperties(microAppWindow, {
2694
3753
  document: {
3754
+ configurable: true,
3755
+ enumerable: true,
2695
3756
  get() {
2696
3757
  throttleDeferForSetAppName(appName);
2697
3758
  return globalEnv.rawDocument;
2698
3759
  },
2699
- configurable: false,
2700
- enumerable: true,
2701
3760
  },
2702
3761
  eval: {
3762
+ configurable: true,
3763
+ enumerable: false,
2703
3764
  get() {
2704
3765
  throttleDeferForSetAppName(appName);
2705
3766
  return modifiedEval || eval;
@@ -2707,10 +3768,10 @@ class SandBox {
2707
3768
  set: (value) => {
2708
3769
  modifiedEval = value;
2709
3770
  },
2710
- configurable: true,
2711
- enumerable: false,
2712
3771
  },
2713
3772
  Image: {
3773
+ configurable: true,
3774
+ enumerable: false,
2714
3775
  get() {
2715
3776
  throttleDeferForSetAppName(appName);
2716
3777
  return modifiedImage || globalEnv.ImageProxy;
@@ -2718,11 +3779,82 @@ class SandBox {
2718
3779
  set: (value) => {
2719
3780
  modifiedImage = value;
2720
3781
  },
3782
+ },
3783
+ });
3784
+ }
3785
+ // rewrite fetch, XMLHttpRequest, EventSource
3786
+ patchHijackRequest(microAppWindow, appName, url) {
3787
+ let microFetch = createMicroFetch(url);
3788
+ let microXMLHttpRequest = createMicroXMLHttpRequest(url);
3789
+ let microEventSource = createMicroEventSource(appName, url);
3790
+ rawDefineProperties(microAppWindow, {
3791
+ fetch: {
2721
3792
  configurable: true,
2722
- enumerable: false,
3793
+ enumerable: true,
3794
+ get() {
3795
+ return microFetch;
3796
+ },
3797
+ set(value) {
3798
+ microFetch = createMicroFetch(url, value);
3799
+ },
3800
+ },
3801
+ XMLHttpRequest: {
3802
+ configurable: true,
3803
+ enumerable: true,
3804
+ get() {
3805
+ return microXMLHttpRequest;
3806
+ },
3807
+ set(value) {
3808
+ microXMLHttpRequest = createMicroXMLHttpRequest(url, value);
3809
+ },
3810
+ },
3811
+ EventSource: {
3812
+ configurable: true,
3813
+ enumerable: true,
3814
+ get() {
3815
+ return microEventSource;
3816
+ },
3817
+ set(value) {
3818
+ microEventSource = createMicroEventSource(appName, url, value);
3819
+ },
3820
+ },
3821
+ });
3822
+ }
3823
+ // set location & history for memory router
3824
+ setMicroAppRouter(microAppWindow, appName, url) {
3825
+ const { microLocation, microHistory } = createMicroRouter(appName, url);
3826
+ rawDefineProperties(microAppWindow, {
3827
+ location: {
3828
+ configurable: false,
3829
+ enumerable: true,
3830
+ get() {
3831
+ return microLocation;
3832
+ },
3833
+ set: (value) => {
3834
+ globalEnv.rawWindow.location = value;
3835
+ },
3836
+ },
3837
+ history: {
3838
+ configurable: true,
3839
+ enumerable: true,
3840
+ get() {
3841
+ return microHistory;
3842
+ },
2723
3843
  },
2724
3844
  });
2725
3845
  }
3846
+ initRouteState(defaultPage) {
3847
+ initRouteStateWithURL(this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.location, defaultPage);
3848
+ }
3849
+ clearRouteState(keepRouteState) {
3850
+ clearRouteStateFromURL(this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.__MICRO_APP_URL__, this.proxyWindow.location, keepRouteState);
3851
+ }
3852
+ setRouteInfoForKeepAliveApp() {
3853
+ updateBrowserURLWithLocation(this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.location);
3854
+ }
3855
+ removeRouteInfoForKeepAliveApp() {
3856
+ removeStateAndPathFromBrowser(this.proxyWindow.__MICRO_APP_NAME__);
3857
+ }
2726
3858
  }
2727
3859
  SandBox.activeCount = 0; // number of active sandbox
2728
3860
 
@@ -2756,7 +3888,7 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
2756
3888
  element = getRootContainer(element);
2757
3889
  // clear dom scope before dispatch lifeCycles event to base app, especially mounted & unmount
2758
3890
  removeDomScope();
2759
- const detail = Object.assign({
3891
+ const detail = assign({
2760
3892
  name: appName,
2761
3893
  container: element,
2762
3894
  }, error && {
@@ -2781,7 +3913,7 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
2781
3913
  * @param detail event detail
2782
3914
  */
2783
3915
  function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
2784
- const event = new CustomEvent(`${eventName}-${appName}`, {
3916
+ const event = new CustomEvent(formatEventName$1(eventName, appName), {
2785
3917
  detail,
2786
3918
  });
2787
3919
  window.dispatchEvent(event);
@@ -2790,8 +3922,8 @@ function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
2790
3922
  // micro app instances
2791
3923
  const appInstanceMap = new Map();
2792
3924
  class CreateApp {
2793
- constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, baseroute, }) {
2794
- this.state = appStates.NOT_LOADED;
3925
+ constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, useMemoryRouter, baseroute, keepRouteState, defaultPage, }) {
3926
+ this.state = appStates.CREATED;
2795
3927
  this.keepAliveState = null;
2796
3928
  this.keepAliveContainer = null;
2797
3929
  this.loadSourceLevel = 0;
@@ -2802,27 +3934,29 @@ class CreateApp {
2802
3934
  this.isPrefetch = false;
2803
3935
  this.prefetchResolve = null;
2804
3936
  this.container = null;
2805
- this.baseroute = '';
2806
3937
  this.sandBox = null;
2807
3938
  this.container = container !== null && container !== void 0 ? container : null;
2808
3939
  this.inline = inline !== null && inline !== void 0 ? inline : false;
2809
3940
  this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : '';
3941
+ this.keepRouteState = keepRouteState !== null && keepRouteState !== void 0 ? keepRouteState : false;
2810
3942
  this.ssrUrl = ssrUrl !== null && ssrUrl !== void 0 ? ssrUrl : '';
2811
3943
  // optional during init👆
2812
3944
  this.name = name;
2813
3945
  this.url = url;
2814
3946
  this.useSandbox = useSandbox;
2815
3947
  this.scopecss = this.useSandbox && scopecss;
3948
+ this.useMemoryRouter = this.useSandbox && useMemoryRouter;
3949
+ this.defaultPage = defaultPage !== null && defaultPage !== void 0 ? defaultPage : '';
2816
3950
  this.source = {
2817
3951
  links: new Map(),
2818
3952
  scripts: new Map(),
2819
3953
  };
2820
3954
  this.loadSourceCode();
2821
- this.useSandbox && (this.sandBox = new SandBox(name, url));
3955
+ this.useSandbox && (this.sandBox = new SandBox(name, url, this.useMemoryRouter));
2822
3956
  }
2823
3957
  // Load resources
2824
3958
  loadSourceCode() {
2825
- this.state = appStates.LOADING_SOURCE_CODE;
3959
+ this.state = appStates.LOADING;
2826
3960
  extractHtml(this);
2827
3961
  }
2828
3962
  /**
@@ -2837,7 +3971,7 @@ class CreateApp {
2837
3971
  this.prefetchResolve = null;
2838
3972
  }
2839
3973
  else if (appStates.UNMOUNT !== this.state) {
2840
- this.state = appStates.LOAD_SOURCE_FINISHED;
3974
+ this.state = appStates.LOADED;
2841
3975
  this.mount();
2842
3976
  }
2843
3977
  }
@@ -2854,7 +3988,7 @@ class CreateApp {
2854
3988
  }
2855
3989
  if (appStates.UNMOUNT !== this.state) {
2856
3990
  this.onerror(e);
2857
- this.state = appStates.LOAD_SOURCE_ERROR;
3991
+ this.state = appStates.LOAD_FAILED;
2858
3992
  }
2859
3993
  }
2860
3994
  /**
@@ -2862,22 +3996,26 @@ class CreateApp {
2862
3996
  * @param container app container
2863
3997
  * @param inline js runs in inline mode
2864
3998
  * @param baseroute route prefix, default is ''
3999
+ * @param keepRouteState keep route state when unmount, default is false
2865
4000
  */
2866
- mount(container, inline, baseroute) {
4001
+ mount(container, inline, baseroute, keepRouteState, defaultPage) {
2867
4002
  var _a, _b, _c;
2868
- if (isBoolean(inline) && inline !== this.inline) {
4003
+ if (isBoolean(inline))
2869
4004
  this.inline = inline;
2870
- }
4005
+ // keepRouteState effective on unmount
4006
+ if (isBoolean(keepRouteState))
4007
+ this.keepRouteState = keepRouteState;
2871
4008
  this.container = (_a = this.container) !== null && _a !== void 0 ? _a : container;
2872
4009
  this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : this.baseroute;
4010
+ this.defaultPage = defaultPage !== null && defaultPage !== void 0 ? defaultPage : this.defaultPage;
2873
4011
  if (this.loadSourceLevel !== 2) {
2874
- this.state = appStates.LOADING_SOURCE_CODE;
4012
+ this.state = appStates.LOADING;
2875
4013
  return;
2876
4014
  }
2877
4015
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
2878
4016
  this.state = appStates.MOUNTING;
2879
4017
  cloneContainer(this.source.html, this.container, !this.umdMode);
2880
- (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.start(this.baseroute);
4018
+ (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.start(this.baseroute, this.useMemoryRouter, this.defaultPage);
2881
4019
  let umdHookMountResult; // result of mount function
2882
4020
  if (!this.umdMode) {
2883
4021
  let hasDispatchMountedEvent = false;
@@ -2942,11 +4080,12 @@ class CreateApp {
2942
4080
  }
2943
4081
  /**
2944
4082
  * unmount app
4083
+ * NOTE: Do not add any params on account of unmountApp
2945
4084
  * @param destroy completely destroy, delete cache resources
2946
4085
  * @param unmountcb callback of unmount
2947
4086
  */
2948
4087
  unmount(destroy, unmountcb) {
2949
- if (this.state === appStates.LOAD_SOURCE_ERROR) {
4088
+ if (this.state === appStates.LOAD_FAILED) {
2950
4089
  destroy = true;
2951
4090
  }
2952
4091
  this.state = appStates.UNMOUNT;
@@ -2999,8 +4138,13 @@ class CreateApp {
2999
4138
  else if (this.umdMode && this.container.childElementCount) {
3000
4139
  cloneContainer(this.container, this.source.html, false);
3001
4140
  }
3002
- // this.container maybe contains micro-app element, stop sandbox should exec after cloneContainer
3003
- (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.stop();
4141
+ /**
4142
+ * this.container maybe contains micro-app element, stop sandbox should exec after cloneContainer
4143
+ * NOTE:
4144
+ * 1. if destroy is true, clear route state
4145
+ * 2. umd mode and keep-alive will not clear EventSource
4146
+ */
4147
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.stop(this.keepRouteState && !destroy, !this.umdMode || destroy);
3004
4148
  if (!getActiveApps().length) {
3005
4149
  releasePatchSetAttribute();
3006
4150
  }
@@ -3019,34 +4163,40 @@ class CreateApp {
3019
4163
  }
3020
4164
  // hidden app when disconnectedCallback called with keep-alive
3021
4165
  hiddenKeepAliveApp() {
4166
+ var _a;
3022
4167
  const oldContainer = this.container;
3023
4168
  cloneContainer(this.container, this.keepAliveContainer ? this.keepAliveContainer : (this.keepAliveContainer = document.createElement('div')), false);
3024
4169
  this.container = this.keepAliveContainer;
3025
4170
  this.keepAliveState = keepAliveStates.KEEP_ALIVE_HIDDEN;
3026
4171
  // event should dispatch before clone node
3027
- // dispatch afterhidden event to micro-app
4172
+ // dispatch afterHidden event to micro-app
3028
4173
  dispatchCustomEventToMicroApp('appstate-change', this.name, {
3029
4174
  appState: 'afterhidden',
3030
4175
  });
3031
- // dispatch afterhidden event to base app
4176
+ // dispatch afterHidden event to base app
3032
4177
  dispatchLifecyclesEvent(oldContainer, this.name, lifeCycles.AFTERHIDDEN);
4178
+ // called after lifeCyclesEvent
4179
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.removeRouteInfoForKeepAliveApp();
3033
4180
  }
3034
4181
  // show app when connectedCallback called with keep-alive
3035
4182
  showKeepAliveApp(container) {
3036
- // dispatch beforeshow event to micro-app
4183
+ var _a;
4184
+ // dispatch beforeShow event to micro-app
3037
4185
  dispatchCustomEventToMicroApp('appstate-change', this.name, {
3038
4186
  appState: 'beforeshow',
3039
4187
  });
3040
- // dispatch beforeshow event to base app
4188
+ // dispatch beforeShow event to base app
3041
4189
  dispatchLifecyclesEvent(container, this.name, lifeCycles.BEFORESHOW);
3042
4190
  cloneContainer(this.container, container, false);
3043
4191
  this.container = container;
3044
4192
  this.keepAliveState = keepAliveStates.KEEP_ALIVE_SHOW;
3045
- // dispatch aftershow event to micro-app
4193
+ // called before lifeCyclesEvent
4194
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.setRouteInfoForKeepAliveApp();
4195
+ // dispatch afterShow event to micro-app
3046
4196
  dispatchCustomEventToMicroApp('appstate-change', this.name, {
3047
4197
  appState: 'aftershow',
3048
4198
  });
3049
- // dispatch aftershow event to base app
4199
+ // dispatch afterShow event to base app
3050
4200
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.AFTERSHOW);
3051
4201
  }
3052
4202
  /**
@@ -3154,12 +4304,17 @@ function defineElement(tagName) {
3154
4304
  }
3155
4305
  disconnectedCallback() {
3156
4306
  this.hasConnected = false;
3157
- // keep-alive
3158
- if (this.getKeepAliveModeResult()) {
3159
- this.handleHiddenKeepAliveApp();
3160
- }
3161
- else {
3162
- this.handleUnmount(this.getDestroyCompatibleResult());
4307
+ const app = appInstanceMap.get(this.appName);
4308
+ if (app &&
4309
+ app.getAppState() !== appStates.UNMOUNT &&
4310
+ app.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN) {
4311
+ // keep-alive
4312
+ if (this.getKeepAliveModeResult()) {
4313
+ this.handleHiddenKeepAliveApp();
4314
+ }
4315
+ else {
4316
+ this.handleUnmount(this.getDestroyCompatibleResult());
4317
+ }
3163
4318
  }
3164
4319
  }
3165
4320
  attributeChangedCallback(attr, _oldVal, newVal) {
@@ -3207,12 +4362,7 @@ function defineElement(tagName) {
3207
4362
  if (this.getDisposeResult('shadowDOM') && !this.shadowRoot && isFunction(this.attachShadow)) {
3208
4363
  this.attachShadow({ mode: 'open' });
3209
4364
  }
3210
- if (this.getDisposeResult('ssr')) {
3211
- this.ssrUrl = CompletionPath(globalEnv.rawWindow.location.pathname, this.appUrl);
3212
- }
3213
- else if (this.ssrUrl) {
3214
- this.ssrUrl = '';
3215
- }
4365
+ this.updateSsrUrl(this.appUrl);
3216
4366
  if (appInstanceMap.has(this.appName)) {
3217
4367
  const app = appInstanceMap.get(this.appName);
3218
4368
  const existAppUrl = app.ssrUrl || app.url;
@@ -3246,15 +4396,9 @@ function defineElement(tagName) {
3246
4396
  actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp) {
3247
4397
  var _a;
3248
4398
  /**
3249
- * change ssrUrl in ssr mode
3250
4399
  * do not add judgment of formatAttrUrl === this.appUrl
3251
4400
  */
3252
- if (this.getDisposeResult('ssr')) {
3253
- this.ssrUrl = CompletionPath(globalEnv.rawWindow.location.pathname, formatAttrUrl);
3254
- }
3255
- else if (this.ssrUrl) {
3256
- this.ssrUrl = '';
3257
- }
4401
+ this.updateSsrUrl(formatAttrUrl);
3258
4402
  this.appName = formatAttrName;
3259
4403
  this.appUrl = formatAttrUrl;
3260
4404
  ((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this).innerHTML = '';
@@ -3314,14 +4458,14 @@ function defineElement(tagName) {
3314
4458
  app.isPrefetch = false;
3315
4459
  defer(() => {
3316
4460
  var _a;
3317
- return app.mount((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this, this.getDisposeResult('inline'), this.getBaseRouteCompatible());
4461
+ return app.mount((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this, this.getDisposeResult('inline'), this.getBaseRouteCompatible(), this.getDisposeResult('keep-router-state'), this.getDefaultPageValue());
3318
4462
  });
3319
4463
  }
3320
4464
  // create app instance
3321
4465
  handleCreateApp() {
3322
4466
  var _a;
3323
4467
  /**
3324
- * actions for destory old app
4468
+ * actions for destroy old app
3325
4469
  * fix of unmounted umd app with disableSandbox
3326
4470
  */
3327
4471
  if (appInstanceMap.has(this.appName)) {
@@ -3335,7 +4479,10 @@ function defineElement(tagName) {
3335
4479
  inline: this.getDisposeResult('inline'),
3336
4480
  scopecss: !(this.getDisposeResult('disableScopecss') || this.getDisposeResult('shadowDOM')),
3337
4481
  useSandbox: !this.getDisposeResult('disableSandbox'),
4482
+ useMemoryRouter: !this.getDisposeResult('disable-memory-router'),
3338
4483
  baseroute: this.getBaseRouteCompatible(),
4484
+ keepRouteState: this.getDisposeResult('keep-router-state'),
4485
+ defaultPage: this.getDefaultPageValue(),
3339
4486
  });
3340
4487
  appInstanceMap.set(this.appName, instance);
3341
4488
  }
@@ -3343,11 +4490,12 @@ function defineElement(tagName) {
3343
4490
  * unmount app
3344
4491
  * @param destroy delete cache resources when unmount
3345
4492
  */
3346
- handleUnmount(destroy, unmountcb) {
4493
+ handleUnmount(destroy, unmountCb) {
3347
4494
  const app = appInstanceMap.get(this.appName);
3348
4495
  if (app &&
3349
- app.getAppState() !== appStates.UNMOUNT)
3350
- app.unmount(destroy, unmountcb);
4496
+ app.getAppState() !== appStates.UNMOUNT) {
4497
+ app.unmount(destroy, unmountCb);
4498
+ }
3351
4499
  }
3352
4500
  // hidden app when disconnectedCallback called with keep-alive
3353
4501
  handleHiddenKeepAliveApp() {
@@ -3410,6 +4558,37 @@ function defineElement(tagName) {
3410
4558
  getKeepAliveModeResult() {
3411
4559
  return this.getDisposeResult('keep-alive') && !this.getDestroyCompatibleResult();
3412
4560
  }
4561
+ /**
4562
+ * change ssrUrl in ssr mode
4563
+ */
4564
+ updateSsrUrl(baseUrl) {
4565
+ if (this.getDisposeResult('ssr')) {
4566
+ if (this.getDisposeResult('disable-memory-router')) {
4567
+ const rawLocation = globalEnv.rawWindow.location;
4568
+ this.ssrUrl = CompletionPath(rawLocation.pathname + rawLocation.search, baseUrl);
4569
+ }
4570
+ else {
4571
+ // get path from browser URL
4572
+ let targetPath = getNoHashMicroPathFromURL(this.appName, baseUrl);
4573
+ const defaultPagePath = this.getDefaultPageValue();
4574
+ if (!targetPath && defaultPagePath) {
4575
+ const targetLocation = createURL(defaultPagePath, baseUrl);
4576
+ targetPath = targetLocation.origin + targetLocation.pathname + targetLocation.search;
4577
+ }
4578
+ this.ssrUrl = targetPath;
4579
+ }
4580
+ }
4581
+ else if (this.ssrUrl) {
4582
+ this.ssrUrl = '';
4583
+ }
4584
+ }
4585
+ /**
4586
+ * get config of default page
4587
+ */
4588
+ getDefaultPageValue() {
4589
+ var _a, _b, _c;
4590
+ 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 : '';
4591
+ }
3413
4592
  /**
3414
4593
  * Data from the base application
3415
4594
  */
@@ -3444,12 +4623,13 @@ function defineElement(tagName) {
3444
4623
  * url: string,
3445
4624
  * disableScopecss?: boolean,
3446
4625
  * disableSandbox?: boolean,
4626
+ * disableMemoryRouter?: boolean,
3447
4627
  * },
3448
4628
  * ...
3449
4629
  * ])
3450
4630
  * Note:
3451
4631
  * 1: preFetch is asynchronous and is performed only when the browser is idle
3452
- * 2: disableScopecss, disableSandbox must be same with micro-app element, if conflict, the one who executes first shall prevail
4632
+ * 2: disableScopecss, disableSandbox, disableMemoryRouter must be same with micro-app element, if conflict, the one who executes first shall prevail
3453
4633
  * @param apps micro apps
3454
4634
  */
3455
4635
  function preFetch(apps) {
@@ -3467,7 +4647,7 @@ function preFetch(apps) {
3467
4647
  function preFetchInSerial(prefetchApp) {
3468
4648
  return new Promise((resolve) => {
3469
4649
  requestIdleCallback(() => {
3470
- var _a, _b;
4650
+ var _a, _b, _c;
3471
4651
  if (isPlainObject(prefetchApp) && navigator.onLine) {
3472
4652
  prefetchApp.name = formatAppName(prefetchApp.name);
3473
4653
  prefetchApp.url = formatAppURL(prefetchApp.url, prefetchApp.name);
@@ -3477,6 +4657,7 @@ function preFetchInSerial(prefetchApp) {
3477
4657
  url: prefetchApp.url,
3478
4658
  scopecss: !((_a = prefetchApp.disableScopecss) !== null && _a !== void 0 ? _a : microApp.disableScopecss),
3479
4659
  useSandbox: !((_b = prefetchApp.disableSandbox) !== null && _b !== void 0 ? _b : microApp.disableSandbox),
4660
+ useMemoryRouter: !((_c = prefetchApp.disableMemoryRouter) !== null && _c !== void 0 ? _c : microApp.disableMemoryRouter),
3480
4661
  });
3481
4662
  app.isPrefetch = true;
3482
4663
  app.prefetchResolve = resolve;
@@ -3545,7 +4726,7 @@ function getAllApps() {
3545
4726
  /**
3546
4727
  * unmount app by appName
3547
4728
  * @param appName
3548
- * @param options unmountAppParams
4729
+ * @param options unmountAppOptions
3549
4730
  * @returns Promise<void>
3550
4731
  */
3551
4732
  function unmountApp(appName, options) {
@@ -3619,6 +4800,7 @@ class MicroApp extends EventCenterForBaseApp {
3619
4800
  super(...arguments);
3620
4801
  this.tagName = 'micro-app';
3621
4802
  this.preFetch = preFetch;
4803
+ this.router = router;
3622
4804
  }
3623
4805
  start(options) {
3624
4806
  if (!isBrowser || !window.customElements) {
@@ -3648,6 +4830,7 @@ class MicroApp extends EventCenterForBaseApp {
3648
4830
  this.inline = options.inline;
3649
4831
  this.disableScopecss = options.disableScopecss;
3650
4832
  this.disableSandbox = options.disableSandbox;
4833
+ this.disableMemoryRouter = options.disableMemoryRouter;
3651
4834
  this.ssr = options.ssr;
3652
4835
  isFunction(options.fetch) && (this.fetch = options.fetch);
3653
4836
  isPlainObject(options.lifeCycles) && (this.lifeCycles = options.lifeCycles);