@micro-zoe/micro-app 0.8.6 → 1.0.0-alpha.0

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.6';
1
+ const version = '1.0.0-alpha.0';
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,6 +92,16 @@ 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
@@ -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,
@@ -306,6 +329,98 @@ function trim(str) {
306
329
  function isFireFox() {
307
330
  return navigator.userAgent.indexOf('Firefox') > -1;
308
331
  }
332
+ /**
333
+ * Transforms a queryString into object.
334
+ * @param search - search string to parse
335
+ * @returns a query object
336
+ */
337
+ function parseQuery(search) {
338
+ const result = {};
339
+ const queryList = search.split('&');
340
+ // we will not decode the key/value to ensure that the values are consistent when update URL
341
+ for (const queryItem of queryList) {
342
+ const eqPos = queryItem.indexOf('=');
343
+ const key = eqPos < 0 ? queryItem : queryItem.slice(0, eqPos);
344
+ const value = eqPos < 0 ? null : queryItem.slice(eqPos + 1);
345
+ if (key in result) {
346
+ let currentValue = result[key];
347
+ if (!isArray(currentValue)) {
348
+ currentValue = result[key] = [currentValue];
349
+ }
350
+ currentValue.push(value);
351
+ }
352
+ else {
353
+ result[key] = value;
354
+ }
355
+ }
356
+ return result;
357
+ }
358
+ /**
359
+ * Transforms an object to query string
360
+ * @param queryObject - query object to stringify
361
+ * @returns query string without the leading `?`
362
+ */
363
+ function stringifyQuery(queryObject) {
364
+ let result = '';
365
+ for (const key in queryObject) {
366
+ const value = queryObject[key];
367
+ if (isNull(value)) {
368
+ result += (result.length ? '&' : '') + key;
369
+ }
370
+ else {
371
+ const valueList = isArray(value) ? value : [value];
372
+ valueList.forEach(value => {
373
+ if (!isUndefined(value)) {
374
+ result += (result.length ? '&' : '') + key;
375
+ if (!isNull(value))
376
+ result += '=' + value;
377
+ }
378
+ });
379
+ }
380
+ }
381
+ return result;
382
+ }
383
+ /**
384
+ * Register or unregister callback/guard with Set
385
+ */
386
+ function useSetRecord() {
387
+ const handlers = new Set();
388
+ function add(handler) {
389
+ handlers.add(handler);
390
+ return () => {
391
+ if (handlers.has(handler))
392
+ return handlers.delete(handler);
393
+ return false;
394
+ };
395
+ }
396
+ return {
397
+ add,
398
+ list: () => handlers,
399
+ };
400
+ }
401
+ /**
402
+ * record data with Map
403
+ */
404
+ function useMapRecord() {
405
+ const data = new Map();
406
+ function add(key, value) {
407
+ data.set(key, value);
408
+ return () => {
409
+ if (data.has(key))
410
+ return data.delete(key);
411
+ return false;
412
+ };
413
+ }
414
+ return {
415
+ add,
416
+ get: (key) => data.get(key),
417
+ delete: (key) => {
418
+ if (data.has(key))
419
+ return data.delete(key);
420
+ return false;
421
+ }
422
+ };
423
+ }
309
424
 
310
425
  var ObservedAttrName;
311
426
  (function (ObservedAttrName) {
@@ -315,13 +430,13 @@ var ObservedAttrName;
315
430
  // app status
316
431
  var appStates;
317
432
  (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";
433
+ appStates["CREATED"] = "created";
434
+ appStates["LOADING"] = "loading";
435
+ appStates["LOADED"] = "loaded";
436
+ appStates["LOAD_FAILED"] = "load_failed";
437
+ appStates["MOUNTING"] = "mounting";
438
+ appStates["MOUNTED"] = "mounted";
439
+ appStates["UNMOUNT"] = "unmount";
325
440
  })(appStates || (appStates = {}));
326
441
  // lifecycles
327
442
  var lifeCycles;
@@ -339,16 +454,16 @@ var lifeCycles;
339
454
  // keep-alive status
340
455
  var keepAliveStates;
341
456
  (function (keepAliveStates) {
342
- keepAliveStates["KEEP_ALIVE_SHOW"] = "KEEP_ALIVE_SHOW";
343
- keepAliveStates["KEEP_ALIVE_HIDDEN"] = "KEEP_ALIVE_HIDDEN";
457
+ keepAliveStates["KEEP_ALIVE_SHOW"] = "keep_alive_show";
458
+ keepAliveStates["KEEP_ALIVE_HIDDEN"] = "keep_alive_hidden";
344
459
  })(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';
460
+ 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,navigator,undefined,location,history,EventSource,fetch,XMLHttpRequest';
346
461
 
347
462
  /**
348
463
  * fetch source of html, js, css
349
464
  * @param url source path
350
465
  * @param appName app name
351
- * @param config config of fetch
466
+ * @param config fetch options
352
467
  */
353
468
  function fetchSource(url, appName = null, options = {}) {
354
469
  if (isFunction(microApp.fetch)) {
@@ -359,66 +474,6 @@ function fetchSource(url, appName = null, options = {}) {
359
474
  });
360
475
  }
361
476
 
362
- class HTMLLoader {
363
- static getInstance() {
364
- if (!this.instance) {
365
- this.instance = new HTMLLoader();
366
- }
367
- return this.instance;
368
- }
369
- /**
370
- * run logic of load and format html
371
- * @param successCb success callback
372
- * @param errorCb error callback, type: (err: Error, meetFetchErr: boolean) => void
373
- */
374
- run(app, successCb) {
375
- const appName = app.name;
376
- const htmlUrl = app.ssrUrl || app.url;
377
- fetchSource(htmlUrl, appName, { cache: 'no-cache' }).then((htmlStr) => {
378
- if (!htmlStr) {
379
- const msg = 'html is empty, please check in detail';
380
- app.onerror(new Error(msg));
381
- return logError(msg, appName);
382
- }
383
- htmlStr = this.formatHTML(htmlUrl, htmlStr, appName);
384
- successCb(htmlStr, app);
385
- }).catch((e) => {
386
- logError(`Failed to fetch data from ${app.url}, micro-app stop rendering`, appName, e);
387
- app.onLoadError(e);
388
- });
389
- }
390
- formatHTML(htmlUrl, htmlStr, appName) {
391
- return this.processHtml(htmlUrl, htmlStr, appName, microApp.plugins)
392
- .replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
393
- return match
394
- .replace(/<head/i, '<micro-app-head')
395
- .replace(/<\/head>/i, '</micro-app-head>');
396
- })
397
- .replace(/<body[^>]*>[\s\S]*?<\/body>/i, (match) => {
398
- return match
399
- .replace(/<body/i, '<micro-app-body')
400
- .replace(/<\/body>/i, '</micro-app-body>');
401
- });
402
- }
403
- processHtml(url, code, appName, plugins) {
404
- var _a;
405
- if (!plugins)
406
- return code;
407
- const mergedPlugins = [];
408
- plugins.global && mergedPlugins.push(...plugins.global);
409
- ((_a = plugins.modules) === null || _a === void 0 ? void 0 : _a[appName]) && mergedPlugins.push(...plugins.modules[appName]);
410
- if (mergedPlugins.length > 0) {
411
- return mergedPlugins.reduce((preCode, plugin) => {
412
- if (isPlainObject(plugin) && isFunction(plugin.processHtml)) {
413
- return plugin.processHtml(preCode, url, plugin.options);
414
- }
415
- return preCode;
416
- }, code);
417
- }
418
- return code;
419
- }
420
- }
421
-
422
477
  // common reg
423
478
  const rootSelectorREG = /(^|\s+)(html|:root)(?=[\s>~[.#:]+|$)/;
424
479
  const bodySelectorREG = /(^|\s+)((html[\s>~]+body)|body)(?=[\s>~[.#:]+|$)/;
@@ -981,12 +1036,12 @@ function handleNewNode(parent, child, app) {
981
1036
  return child;
982
1037
  }
983
1038
  else if (child instanceof HTMLLinkElement) {
984
- if (child.hasAttribute('exclude') || checkExcludeUrl(child.getAttribute('href'), app.name)) {
1039
+ if (child.hasAttribute('exclude')) {
985
1040
  const linkReplaceComment = document.createComment('link element with exclude attribute ignored by micro-app');
986
1041
  dynamicElementInMicroAppMap.set(child, linkReplaceComment);
987
1042
  return linkReplaceComment;
988
1043
  }
989
- else if (child.hasAttribute('ignore') || checkIgnoreUrl(child.getAttribute('href'), app.name)) {
1044
+ else if (child.hasAttribute('ignore')) {
990
1045
  return child;
991
1046
  }
992
1047
  const { url, info, replaceComment } = extractLinkFromHtml(child, parent, app, true);
@@ -1034,39 +1089,31 @@ function handleNewNode(parent, child, app) {
1034
1089
  * @param passiveChild second param of insertBefore and replaceChild
1035
1090
  */
1036
1091
  function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
1037
- const container = getContainer(parent, app);
1092
+ const hijackElement = getHijackElement(parent, app);
1038
1093
  /**
1039
1094
  * If passiveChild is not the child node, insertBefore replaceChild will have a problem, at this time, it will be degraded to appendChild
1040
1095
  * E.g: document.head.insertBefore(targetChild, document.head.childNodes[0])
1041
1096
  */
1042
- if (container) {
1097
+ if (hijackElement) {
1043
1098
  /**
1044
1099
  * 1. If passiveChild exists, it must be insertBefore or replaceChild
1045
1100
  * 2. When removeChild, targetChild may not be in microAppHead or head
1046
1101
  */
1047
- if (passiveChild && !container.contains(passiveChild)) {
1048
- return globalEnv.rawAppendChild.call(container, targetChild);
1102
+ if (passiveChild && !hijackElement.contains(passiveChild)) {
1103
+ return globalEnv.rawAppendChild.call(hijackElement, targetChild);
1049
1104
  }
1050
- else if (rawMethod === globalEnv.rawRemoveChild && !container.contains(targetChild)) {
1105
+ else if (rawMethod === globalEnv.rawRemoveChild && !hijackElement.contains(targetChild)) {
1051
1106
  if (parent.contains(targetChild)) {
1052
1107
  return rawMethod.call(parent, targetChild);
1053
1108
  }
1054
1109
  return targetChild;
1055
1110
  }
1056
- return invokeRawMethod(rawMethod, container, targetChild, passiveChild);
1111
+ return invokeRawMethod(rawMethod, hijackElement, targetChild, passiveChild);
1057
1112
  }
1058
1113
  return invokeRawMethod(rawMethod, parent, targetChild, passiveChild);
1059
1114
  }
1060
- function invokeRawMethod(rawMethod, parent, targetChild, passiveChild) {
1061
- if (isPendMethod(rawMethod)) {
1062
- return rawMethod.call(parent, targetChild);
1063
- }
1064
- return rawMethod.call(parent, targetChild, passiveChild);
1065
- }
1066
- function isPendMethod(method) {
1067
- return method === globalEnv.rawAppend || method === globalEnv.rawPrepend;
1068
- }
1069
- function getContainer(node, app) {
1115
+ // head/body map to micro-app-head/micro-app-body
1116
+ function getHijackElement(node, app) {
1070
1117
  var _a, _b;
1071
1118
  if (node === document.head) {
1072
1119
  return (_a = app === null || app === void 0 ? void 0 : app.container) === null || _a === void 0 ? void 0 : _a.querySelector('micro-app-head');
@@ -1076,6 +1123,15 @@ function getContainer(node, app) {
1076
1123
  }
1077
1124
  return null;
1078
1125
  }
1126
+ function invokeRawMethod(rawMethod, parent, targetChild, passiveChild) {
1127
+ if (isPendMethod(rawMethod)) {
1128
+ return rawMethod.call(parent, targetChild);
1129
+ }
1130
+ return rawMethod.call(parent, targetChild, passiveChild);
1131
+ }
1132
+ function isPendMethod(method) {
1133
+ return method === globalEnv.rawAppend || method === globalEnv.rawPrepend;
1134
+ }
1079
1135
  // Get the map element
1080
1136
  function getMappingNode(node) {
1081
1137
  var _a;
@@ -1163,27 +1219,6 @@ function patchElementPrototypeMethods() {
1163
1219
  this.__MICRO_APP_NAME__ && (clonedNode.__MICRO_APP_NAME__ = this.__MICRO_APP_NAME__);
1164
1220
  return clonedNode;
1165
1221
  };
1166
- // patch getBoundingClientRect
1167
- // TODO: scenes test
1168
- // Element.prototype.getBoundingClientRect = function getBoundingClientRect () {
1169
- // const rawRect: DOMRect = globalEnv.rawGetBoundingClientRect.call(this)
1170
- // if (this.__MICRO_APP_NAME__) {
1171
- // const app = appInstanceMap.get(this.__MICRO_APP_NAME__)
1172
- // if (!app?.container) {
1173
- // return rawRect
1174
- // }
1175
- // const appBody = app.container.querySelector('micro-app-body')
1176
- // const appBodyRect: DOMRect = globalEnv.rawGetBoundingClientRect.call(appBody)
1177
- // const computedRect: DOMRect = new DOMRect(
1178
- // rawRect.x - appBodyRect.x,
1179
- // rawRect.y - appBodyRect.y,
1180
- // rawRect.width,
1181
- // rawRect.height,
1182
- // )
1183
- // return computedRect
1184
- // }
1185
- // return rawRect
1186
- // }
1187
1222
  }
1188
1223
  /**
1189
1224
  * Mark the newly created element in the micro application
@@ -1213,27 +1248,29 @@ function patchDocument() {
1213
1248
  };
1214
1249
  // query element👇
1215
1250
  function querySelector(selectors) {
1216
- var _a, _b, _c;
1251
+ var _a, _b, _c, _d;
1217
1252
  const appName = getCurrentAppName();
1218
1253
  if (!appName ||
1254
+ !((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) ||
1219
1255
  !selectors ||
1220
1256
  isUniqueElement(selectors) ||
1221
1257
  // see https://github.com/micro-zoe/micro-app/issues/56
1222
1258
  rawDocument !== this) {
1223
1259
  return globalEnv.rawQuerySelector.call(this, selectors);
1224
1260
  }
1225
- return (_c = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) === null || _b === void 0 ? void 0 : _b.querySelector(selectors)) !== null && _c !== void 0 ? _c : null;
1261
+ 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;
1226
1262
  }
1227
1263
  function querySelectorAll(selectors) {
1228
- var _a, _b, _c;
1264
+ var _a, _b, _c, _d;
1229
1265
  const appName = getCurrentAppName();
1230
1266
  if (!appName ||
1267
+ !((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) ||
1231
1268
  !selectors ||
1232
1269
  isUniqueElement(selectors) ||
1233
1270
  rawDocument !== this) {
1234
1271
  return globalEnv.rawQuerySelectorAll.call(this, selectors);
1235
1272
  }
1236
- return (_c = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) === null || _b === void 0 ? void 0 : _b.querySelectorAll(selectors)) !== null && _c !== void 0 ? _c : [];
1273
+ 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 : [];
1237
1274
  }
1238
1275
  Document.prototype.querySelector = querySelector;
1239
1276
  Document.prototype.querySelectorAll = querySelectorAll;
@@ -1301,10 +1338,9 @@ function patchSetAttribute() {
1301
1338
  if (/^micro-app(-\S+)?/i.test(this.tagName) && key === 'data') {
1302
1339
  if (isPlainObject(value)) {
1303
1340
  const cloneValue = {};
1304
- Object.getOwnPropertyNames(value).forEach((propertyKey) => {
1305
- if (!(isString(propertyKey) && propertyKey.indexOf('__') === 0)) {
1306
- // @ts-ignore
1307
- cloneValue[propertyKey] = value[propertyKey];
1341
+ Object.getOwnPropertyNames(value).forEach((key) => {
1342
+ if (!(isString(key) && key.indexOf('__') === 0)) {
1343
+ cloneValue[key] = value[key];
1308
1344
  }
1309
1345
  });
1310
1346
  this.data = cloneValue;
@@ -1351,7 +1387,6 @@ function releasePatches() {
1351
1387
  Element.prototype.append = globalEnv.rawAppend;
1352
1388
  Element.prototype.prepend = globalEnv.rawPrepend;
1353
1389
  Element.prototype.cloneNode = globalEnv.rawCloneNode;
1354
- // Element.prototype.getBoundingClientRect = globalEnv.rawGetBoundingClientRect
1355
1390
  }
1356
1391
  // Set the style of micro-app-head and micro-app-body
1357
1392
  let hasRejectMicroAppStyle = false;
@@ -1365,53 +1400,6 @@ function rejectMicroAppStyle() {
1365
1400
  }
1366
1401
  }
1367
1402
 
1368
- // 管理 app 的单例
1369
- class AppManager {
1370
- constructor() {
1371
- // Todo: appInstanceMap 由 AppManager 来创建,不再由 create_app 管理
1372
- this.appInstanceMap = appInstanceMap;
1373
- }
1374
- static getInstance() {
1375
- if (!this.instance) {
1376
- this.instance = new AppManager();
1377
- }
1378
- return this.instance;
1379
- }
1380
- get(appName) {
1381
- return this.appInstanceMap.get(appName);
1382
- }
1383
- set(appName, app) {
1384
- this.appInstanceMap.set(appName, app);
1385
- }
1386
- getAll() {
1387
- return Array.from(this.appInstanceMap.values());
1388
- }
1389
- clear() {
1390
- this.appInstanceMap.clear();
1391
- }
1392
- }
1393
-
1394
- function unmountNestedApp() {
1395
- releaseUnmountOfNestedApp();
1396
- AppManager.getInstance().getAll().forEach(app => {
1397
- // @ts-ignore
1398
- app.container && getRootContainer(app.container).disconnectedCallback();
1399
- });
1400
- !window.__MICRO_APP_UMD_MODE__ && AppManager.getInstance().clear();
1401
- }
1402
- // if micro-app run in micro application, delete all next generation application when unmount event received
1403
- function listenUmountOfNestedApp() {
1404
- if (window.__MICRO_APP_ENVIRONMENT__) {
1405
- window.addEventListener('unmount', unmountNestedApp, false);
1406
- }
1407
- }
1408
- // release listener
1409
- function releaseUnmountOfNestedApp() {
1410
- if (window.__MICRO_APP_ENVIRONMENT__) {
1411
- window.removeEventListener('unmount', unmountNestedApp, false);
1412
- }
1413
- }
1414
-
1415
1403
  const globalEnv = {};
1416
1404
  /**
1417
1405
  * Note loop nesting
@@ -1431,7 +1419,6 @@ function initGlobalEnv() {
1431
1419
  const rawAppend = Element.prototype.append;
1432
1420
  const rawPrepend = Element.prototype.prepend;
1433
1421
  const rawCloneNode = Element.prototype.cloneNode;
1434
- // const rawGetBoundingClientRect = Element.prototype.getBoundingClientRect
1435
1422
  const rawCreateElement = Document.prototype.createElement;
1436
1423
  const rawCreateElementNS = Document.prototype.createElementNS;
1437
1424
  const rawCreateDocumentFragment = Document.prototype.createDocumentFragment;
@@ -1465,7 +1452,7 @@ function initGlobalEnv() {
1465
1452
  const rawDocumentRemoveEventListener = rawDocument.removeEventListener;
1466
1453
  // mark current application as base application
1467
1454
  window.__MICRO_APP_BASE_APPLICATION__ = true;
1468
- Object.assign(globalEnv, {
1455
+ assign(globalEnv, {
1469
1456
  // source/patch
1470
1457
  rawSetAttribute,
1471
1458
  rawAppendChild,
@@ -1475,7 +1462,6 @@ function initGlobalEnv() {
1475
1462
  rawAppend,
1476
1463
  rawPrepend,
1477
1464
  rawCloneNode,
1478
- // rawGetBoundingClientRect,
1479
1465
  rawCreateElement,
1480
1466
  rawCreateElementNS,
1481
1467
  rawCreateDocumentFragment,
@@ -1502,8 +1488,6 @@ function initGlobalEnv() {
1502
1488
  });
1503
1489
  // global effect
1504
1490
  rejectMicroAppStyle();
1505
- releaseUnmountOfNestedApp();
1506
- listenUmountOfNestedApp();
1507
1491
  }
1508
1492
  }
1509
1493
 
@@ -1519,14 +1503,11 @@ const globalScripts = new Map();
1519
1503
  function extractScriptElement(script, parent, app, isDynamic = false) {
1520
1504
  let replaceComment = null;
1521
1505
  let src = script.getAttribute('src');
1522
- if (src) {
1523
- src = CompletionPath(src, app.url);
1524
- }
1525
- if (script.hasAttribute('exclude') || checkExcludeUrl(src, app.name)) {
1506
+ if (script.hasAttribute('exclude')) {
1526
1507
  replaceComment = document.createComment('script element with exclude attribute removed by micro-app');
1527
1508
  }
1528
1509
  else if ((script.type && !['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module'].includes(script.type)) ||
1529
- script.hasAttribute('ignore') || checkIgnoreUrl(src, app.name)) {
1510
+ script.hasAttribute('ignore')) {
1530
1511
  return null;
1531
1512
  }
1532
1513
  else if ((globalEnv.supportModuleScript && script.noModule) ||
@@ -1534,6 +1515,7 @@ function extractScriptElement(script, parent, app, isDynamic = false) {
1534
1515
  replaceComment = document.createComment(`${script.noModule ? 'noModule' : 'module'} script ignored by micro-app`);
1535
1516
  }
1536
1517
  else if (src) { // remote script
1518
+ src = CompletionPath(src, app.url);
1537
1519
  const info = {
1538
1520
  code: '',
1539
1521
  isExternal: true,
@@ -1583,46 +1565,6 @@ function extractScriptElement(script, parent, app, isDynamic = false) {
1583
1565
  return parent.replaceChild(replaceComment, script);
1584
1566
  }
1585
1567
  }
1586
- /**
1587
- * get assets plugins
1588
- * @param appName app name
1589
- */
1590
- function getAssetsPlugins(appName) {
1591
- var _a, _b, _c;
1592
- const globalPlugins = ((_a = microApp.plugins) === null || _a === void 0 ? void 0 : _a.global) || [];
1593
- const modulePlugins = ((_c = (_b = microApp.plugins) === null || _b === void 0 ? void 0 : _b.modules) === null || _c === void 0 ? void 0 : _c[appName]) || [];
1594
- return [...globalPlugins, ...modulePlugins];
1595
- }
1596
- /**
1597
- * whether the url needs to be excluded
1598
- * @param url css or js link
1599
- * @param plugins microApp plugins
1600
- */
1601
- function checkExcludeUrl(url, appName) {
1602
- if (!url)
1603
- return false;
1604
- const plugins = getAssetsPlugins(appName) || [];
1605
- return plugins.some(plugin => {
1606
- if (!plugin.excludeChecker)
1607
- return false;
1608
- return plugin.excludeChecker(url);
1609
- });
1610
- }
1611
- /**
1612
- * whether the url needs to be ignore
1613
- * @param url css or js link
1614
- * @param plugins microApp plugins
1615
- */
1616
- function checkIgnoreUrl(url, appName) {
1617
- if (!url)
1618
- return false;
1619
- const plugins = getAssetsPlugins(appName) || [];
1620
- return plugins.some(plugin => {
1621
- if (!plugin.ignoreChecker)
1622
- return false;
1623
- return plugin.ignoreChecker(url);
1624
- });
1625
- }
1626
1568
  /**
1627
1569
  * Get remote resources of script
1628
1570
  * @param wrapElement htmlDom
@@ -1899,10 +1841,10 @@ function flatChildren(parent, app, microAppHead) {
1899
1841
  });
1900
1842
  for (const dom of children) {
1901
1843
  if (dom instanceof HTMLLinkElement) {
1902
- if (dom.hasAttribute('exclude') || checkExcludeUrl(dom.getAttribute('href'), app.name)) {
1844
+ if (dom.hasAttribute('exclude')) {
1903
1845
  parent.replaceChild(document.createComment('link element with exclude attribute ignored by micro-app'), dom);
1904
1846
  }
1905
- else if (!(dom.hasAttribute('ignore') || checkIgnoreUrl(dom.getAttribute('href'), app.name))) {
1847
+ else if (!dom.hasAttribute('ignore')) {
1906
1848
  extractLinkFromHtml(dom, parent, app);
1907
1849
  }
1908
1850
  else if (dom.hasAttribute('href')) {
@@ -1956,6 +1898,34 @@ function extractSourceDom(htmlStr, app) {
1956
1898
  app.onLoad(wrapElement);
1957
1899
  }
1958
1900
  }
1901
+ /**
1902
+ * Get and format html
1903
+ * @param app app
1904
+ */
1905
+ function extractHtml(app) {
1906
+ fetchSource(app.ssrUrl || app.url, app.name, { cache: 'no-cache' }).then((htmlStr) => {
1907
+ if (!htmlStr) {
1908
+ const msg = 'html is empty, please check in detail';
1909
+ app.onerror(new Error(msg));
1910
+ return logError(msg, app.name);
1911
+ }
1912
+ htmlStr = htmlStr
1913
+ .replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
1914
+ return match
1915
+ .replace(/<head/i, '<micro-app-head')
1916
+ .replace(/<\/head>/i, '</micro-app-head>');
1917
+ })
1918
+ .replace(/<body[^>]*>[\s\S]*?<\/body>/i, (match) => {
1919
+ return match
1920
+ .replace(/<body/i, '<micro-app-body')
1921
+ .replace(/<\/body>/i, '</micro-app-body>');
1922
+ });
1923
+ extractSourceDom(htmlStr, app);
1924
+ }).catch((e) => {
1925
+ logError(`Failed to fetch data from ${app.url}, micro-app stop rendering`, app.name, e);
1926
+ app.onLoadError(e);
1927
+ });
1928
+ }
1959
1929
 
1960
1930
  class EventCenter {
1961
1931
  constructor() {
@@ -2238,6 +2208,28 @@ function rebuildDataCenterSnapshot(microAppEventCenter) {
2238
2208
  }
2239
2209
  }
2240
2210
 
2211
+ function unmountNestedApp() {
2212
+ appInstanceMap.forEach(app => {
2213
+ // @ts-ignore
2214
+ app.container && getRootContainer(app.container).disconnectedCallback();
2215
+ });
2216
+ !window.__MICRO_APP_UMD_MODE__ && appInstanceMap.clear();
2217
+ }
2218
+ // release listener
2219
+ function releaseUnmountOfNestedApp() {
2220
+ if (window.__MICRO_APP_ENVIRONMENT__) {
2221
+ window.removeEventListener('unmount', unmountNestedApp, false);
2222
+ }
2223
+ }
2224
+ // if micro-app run in micro application, delete all next generation application when unmount event received
2225
+ // unmount event will auto release by sandbox
2226
+ function listenUmountOfNestedApp() {
2227
+ if (window.__MICRO_APP_ENVIRONMENT__) {
2228
+ releaseUnmountOfNestedApp();
2229
+ window.addEventListener('unmount', unmountNestedApp, false);
2230
+ }
2231
+ }
2232
+
2241
2233
  /* eslint-disable no-return-assign */
2242
2234
  function isBoundedFunction(value) {
2243
2235
  if (isBoolean(value.__MICRO_APP_IS_BOUND_FUNCTION__))
@@ -2277,6 +2269,21 @@ function bindFunctionToRawWindow(rawWindow, value) {
2277
2269
  return value;
2278
2270
  }
2279
2271
 
2272
+ // this events should be sent to the specified app
2273
+ const formatEventList = ['unmount', 'appstate-change'];
2274
+ /**
2275
+ * Format event name
2276
+ * @param eventName event name
2277
+ * @param appName app name
2278
+ */
2279
+ function formatEventName$1(eventName, appName) {
2280
+ var _a;
2281
+ if (formatEventList.includes(eventName) ||
2282
+ ((eventName === 'popstate' || eventName === 'hashchange') && ((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.useMemoryRouter))) {
2283
+ return `${eventName}-${appName}`;
2284
+ }
2285
+ return eventName;
2286
+ }
2280
2287
  // document.onclick binding list, the binding function of each application is unique
2281
2288
  const documentClickListMap = new Map();
2282
2289
  let hasRewriteDocumentOnClick = false;
@@ -2370,19 +2377,6 @@ function releaseEffectDocumentEvent() {
2370
2377
  document.addEventListener = globalEnv.rawDocumentAddEventListener;
2371
2378
  document.removeEventListener = globalEnv.rawDocumentRemoveEventListener;
2372
2379
  }
2373
- // this events should be sent to the specified app
2374
- const formatEventList = ['unmount', 'appstate-change'];
2375
- /**
2376
- * Format event name
2377
- * @param type event name
2378
- * @param microAppWindow micro window
2379
- */
2380
- function formatEventType(type, microAppWindow) {
2381
- if (formatEventList.includes(type)) {
2382
- return `${type}-${microAppWindow.__MICRO_APP_NAME__}`;
2383
- }
2384
- return type;
2385
- }
2386
2380
  /**
2387
2381
  * Rewrite side-effect events
2388
2382
  * @param microAppWindow micro window
@@ -2395,7 +2389,7 @@ function effect(microAppWindow) {
2395
2389
  const { rawWindow, rawDocument, rawWindowAddEventListener, rawWindowRemoveEventListener, rawSetInterval, rawSetTimeout, rawClearInterval, rawClearTimeout, rawDocumentRemoveEventListener, } = globalEnv;
2396
2390
  // listener may be null, e.g test-passive
2397
2391
  microAppWindow.addEventListener = function (type, listener, options) {
2398
- type = formatEventType(type, microAppWindow);
2392
+ type = formatEventName$1(type, appName);
2399
2393
  const listenerList = eventListenerMap.get(type);
2400
2394
  if (listenerList) {
2401
2395
  listenerList.add(listener);
@@ -2407,7 +2401,7 @@ function effect(microAppWindow) {
2407
2401
  rawWindowAddEventListener.call(rawWindow, type, listener, options);
2408
2402
  };
2409
2403
  microAppWindow.removeEventListener = function (type, listener, options) {
2410
- type = formatEventType(type, microAppWindow);
2404
+ type = formatEventName$1(type, appName);
2411
2405
  const listenerList = eventListenerMap.get(type);
2412
2406
  if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
2413
2407
  listenerList.delete(listener);
@@ -2534,71 +2528,866 @@ function effect(microAppWindow) {
2534
2528
  };
2535
2529
  }
2536
2530
 
2537
- // Variables that can escape to rawWindow
2538
- const staticEscapeProperties = [
2539
- 'System',
2540
- '__cjsWrapper',
2541
- ];
2542
- // Variables that can only assigned to rawWindow
2543
- const escapeSetterKeyList = [
2544
- 'location',
2545
- ];
2546
- const globalPropertyList = ['window', 'self', 'globalThis'];
2547
- class SandBox {
2548
- constructor(appName, url) {
2549
- /**
2550
- * Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
2551
- * https://github.com/micro-zoe/micro-app/issues/234
2552
- */
2553
- this.scopeProperties = ['webpackJsonp', 'Vue'];
2554
- // Properties that can be escape to rawWindow
2555
- this.escapeProperties = [];
2556
- // Properties newly added to microAppWindow
2557
- this.injectedKeys = new Set();
2558
- // Properties escape to rawWindow, cleared when unmount
2559
- this.escapeKeys = new Set();
2560
- // sandbox state
2561
- this.active = false;
2562
- this.microAppWindow = {}; // Proxy target
2563
- // get scopeProperties and escapeProperties from plugins
2564
- this.getSpecialProperties(appName);
2565
- // create proxyWindow with Proxy(microAppWindow)
2566
- this.proxyWindow = this.createProxyWindow(appName);
2567
- // inject global properties
2568
- this.initMicroAppWindow(this.microAppWindow, appName, url);
2569
- // Rewrite global event listener & timeout
2570
- Object.assign(this, effect(this.microAppWindow));
2571
- }
2572
- start(baseRoute) {
2573
- if (!this.active) {
2574
- this.active = true;
2575
- this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseRoute;
2576
- // BUG FIX: bable-polyfill@6.x
2577
- globalEnv.rawWindow._babelPolyfill && (globalEnv.rawWindow._babelPolyfill = false);
2578
- if (++SandBox.activeCount === 1) {
2579
- effectDocumentEvent();
2580
- patchElementPrototypeMethods();
2581
- }
2531
+ // set micro app state to origin state
2532
+ function setMicroState(appName, rawState, microState) {
2533
+ const additionalState = {
2534
+ microAppState: assign({}, rawState === null || rawState === void 0 ? void 0 : rawState.microAppState, {
2535
+ [appName]: microState
2536
+ })
2537
+ };
2538
+ // create new state object
2539
+ return assign({}, rawState, additionalState);
2540
+ }
2541
+ // delete micro app state form origin state
2542
+ function removeMicroState(appName, rawState) {
2543
+ if (isPlainObject(rawState === null || rawState === void 0 ? void 0 : rawState.microAppState)) {
2544
+ if (!isUndefined(rawState.microAppState[appName])) {
2545
+ delete rawState.microAppState[appName];
2546
+ }
2547
+ if (!Object.keys(rawState.microAppState).length) {
2548
+ delete rawState.microAppState;
2582
2549
  }
2583
2550
  }
2584
- stop() {
2585
- if (this.active) {
2586
- this.active = false;
2587
- this.releaseEffect();
2588
- this.microAppWindow.microApp.clearDataListener();
2589
- this.microAppWindow.microApp.clearGlobalDataListener();
2590
- this.injectedKeys.forEach((key) => {
2591
- Reflect.deleteProperty(this.microAppWindow, key);
2592
- });
2593
- this.injectedKeys.clear();
2594
- this.escapeKeys.forEach((key) => {
2595
- Reflect.deleteProperty(globalEnv.rawWindow, key);
2596
- });
2597
- this.escapeKeys.clear();
2598
- if (--SandBox.activeCount === 0) {
2551
+ // 生成新的state对象
2552
+ return assign({}, rawState);
2553
+ }
2554
+ // get micro app state form origin state
2555
+ function getMicroState(appName, state) {
2556
+ var _a;
2557
+ return ((_a = state === null || state === void 0 ? void 0 : state.microAppState) === null || _a === void 0 ? void 0 : _a[appName]) || null;
2558
+ }
2559
+ const ENC_AD_RE = /&/g; // %M1
2560
+ const ENC_EQ_RE = /=/g; // %M2
2561
+ const DEC_AD_RE = /%M1/g; // &
2562
+ const DEC_EQ_RE = /%M2/g; // =
2563
+ function encodeMicroPath(path) {
2564
+ return encodeURIComponent(commonDecode(path).replace(ENC_AD_RE, '%M1').replace(ENC_EQ_RE, '%M2'));
2565
+ }
2566
+ function decodeMicroPath(path) {
2567
+ return commonDecode(path).replace(DEC_AD_RE, '&').replace(DEC_EQ_RE, '=');
2568
+ }
2569
+ function commonDecode(path) {
2570
+ try {
2571
+ const decPath = decodeURIComponent(path);
2572
+ if (path === decPath || DEC_AD_RE.test(decPath) || DEC_EQ_RE.test(decPath))
2573
+ return decPath;
2574
+ return commonDecode(decPath);
2575
+ }
2576
+ catch (_a) {
2577
+ return path;
2578
+ }
2579
+ }
2580
+ // 格式化query参数key,防止与原有参数的冲突
2581
+ function formatQueryAppName(appName) {
2582
+ return `app-${appName}`;
2583
+ }
2584
+ // 根据浏览器url参数,获取当前子应用的path
2585
+ function getMicroPathFromURL(appName) {
2586
+ var _a, _b;
2587
+ const rawLocation = globalEnv.rawWindow.location;
2588
+ const queryObject = getQueryObjectFromURL(rawLocation.search, rawLocation.hash);
2589
+ 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)]);
2590
+ return isString(microPath) ? decodeMicroPath(microPath) : null;
2591
+ }
2592
+ // 将name=encodeUrl地址插入到浏览器url上
2593
+ function setMicroPathToURL(appName, microLocation) {
2594
+ let { pathname, search, hash } = globalEnv.rawWindow.location;
2595
+ const queryObject = getQueryObjectFromURL(search, hash);
2596
+ const encodedMicroPath = encodeMicroPath(microLocation.pathname +
2597
+ microLocation.search +
2598
+ microLocation.hash);
2599
+ let isAttach2Hash = false; // 基座是否是hash模式,这个其实也不准,只是表示参数加到了hash上
2600
+ // hash存在且search不存在,则认为是hash路由
2601
+ if (hash && !search) {
2602
+ isAttach2Hash = true;
2603
+ if (queryObject.hashQuery) {
2604
+ queryObject.hashQuery[formatQueryAppName(appName)] = encodedMicroPath;
2605
+ }
2606
+ else {
2607
+ queryObject.hashQuery = {
2608
+ [formatQueryAppName(appName)]: encodedMicroPath
2609
+ };
2610
+ }
2611
+ const baseHash = hash.includes('?') ? hash.slice(0, hash.indexOf('?') + 1) : hash + '?';
2612
+ hash = baseHash + stringifyQuery(queryObject.hashQuery);
2613
+ }
2614
+ else {
2615
+ if (queryObject.searchQuery) {
2616
+ queryObject.searchQuery[formatQueryAppName(appName)] = encodedMicroPath;
2617
+ }
2618
+ else {
2619
+ queryObject.searchQuery = {
2620
+ [formatQueryAppName(appName)]: encodedMicroPath
2621
+ };
2622
+ }
2623
+ search = '?' + stringifyQuery(queryObject.searchQuery);
2624
+ }
2625
+ return {
2626
+ fullPath: pathname + search + hash,
2627
+ isAttach2Hash,
2628
+ };
2629
+ }
2630
+ // 将name=encodeUrl的参数从浏览器url上删除
2631
+ function removeMicroPathFromURL(appName, targetLocation) {
2632
+ var _a, _b, _c, _d;
2633
+ let { pathname, search, hash } = targetLocation || globalEnv.rawWindow.location;
2634
+ const queryObject = getQueryObjectFromURL(search, hash);
2635
+ let isAttach2Hash = false;
2636
+ if ((_a = queryObject.hashQuery) === null || _a === void 0 ? void 0 : _a[formatQueryAppName(appName)]) {
2637
+ isAttach2Hash = true;
2638
+ (_b = queryObject.hashQuery) === null || _b === void 0 ? true : delete _b[formatQueryAppName(appName)];
2639
+ const hashQueryStr = stringifyQuery(queryObject.hashQuery);
2640
+ hash = hash.slice(0, hash.indexOf('?') + Number(Boolean(hashQueryStr))) + hashQueryStr;
2641
+ }
2642
+ else if ((_c = queryObject.searchQuery) === null || _c === void 0 ? void 0 : _c[formatQueryAppName(appName)]) {
2643
+ (_d = queryObject.searchQuery) === null || _d === void 0 ? true : delete _d[formatQueryAppName(appName)];
2644
+ const searchQueryStr = stringifyQuery(queryObject.searchQuery);
2645
+ search = searchQueryStr ? '?' + searchQueryStr : '';
2646
+ }
2647
+ return {
2648
+ fullPath: pathname + search + hash,
2649
+ isAttach2Hash,
2650
+ };
2651
+ }
2652
+ /**
2653
+ * 根据location获取query对象
2654
+ */
2655
+ function getQueryObjectFromURL(search, hash) {
2656
+ const queryObject = {};
2657
+ if (search !== '' && search !== '?') {
2658
+ queryObject.searchQuery = parseQuery(search.slice(1));
2659
+ }
2660
+ if (hash.includes('?')) {
2661
+ queryObject.hashQuery = parseQuery(hash.slice(hash.indexOf('?') + 1));
2662
+ }
2663
+ return queryObject;
2664
+ }
2665
+ /**
2666
+ * get microApp path from browser URL without hash
2667
+ */
2668
+ function getNoHashMicroPathFromURL(appName, baseUrl) {
2669
+ const microPath = getMicroPathFromURL(appName);
2670
+ if (!microPath)
2671
+ return '';
2672
+ const formatLocation = createURL(microPath, baseUrl);
2673
+ return formatLocation.origin + formatLocation.pathname + formatLocation.search;
2674
+ }
2675
+
2676
+ /**
2677
+ * dispatch PopStateEvent & HashChangeEvent to child app
2678
+ * each child app will listen for popstate event when sandbox start
2679
+ * and release it when sandbox stop
2680
+ * @param appName app name
2681
+ * @returns release callback
2682
+ */
2683
+ function addHistoryListener(appName) {
2684
+ const rawWindow = globalEnv.rawWindow;
2685
+ // handle popstate event and distribute to child app
2686
+ const popStateHandler = (e) => {
2687
+ // exclude hidden keep-alive app
2688
+ if (getActiveApps(true).includes(appName) && !e.onlyForBrowser) {
2689
+ const microPath = getMicroPathFromURL(appName);
2690
+ const app = appInstanceMap.get(appName);
2691
+ const proxyWindow = app.sandBox.proxyWindow;
2692
+ let isHashChange = false;
2693
+ // for hashChangeEvent
2694
+ const oldHref = proxyWindow.location.href;
2695
+ // Do not attach micro state to url when microPath is empty
2696
+ if (microPath) {
2697
+ const oldHash = proxyWindow.location.hash;
2698
+ updateMicroLocation(appName, microPath, proxyWindow.location);
2699
+ isHashChange = proxyWindow.location.hash !== oldHash;
2700
+ }
2701
+ // dispatch formatted popStateEvent to child
2702
+ dispatchPopStateEventToMicroApp(appName, proxyWindow, rawWindow.history.state);
2703
+ // dispatch formatted hashChangeEvent to child when hash change
2704
+ if (isHashChange)
2705
+ dispatchHashChangeEventToMicroApp(appName, proxyWindow, oldHref);
2706
+ // clear element scope before trigger event of next app
2707
+ removeDomScope();
2708
+ }
2709
+ };
2710
+ rawWindow.addEventListener('popstate', popStateHandler);
2711
+ return () => {
2712
+ rawWindow.removeEventListener('popstate', popStateHandler);
2713
+ };
2714
+ }
2715
+ /**
2716
+ * dispatch formatted popstate event to microApp
2717
+ * @param appName app name
2718
+ * @param proxyWindow sandbox window
2719
+ * @param eventState history.state
2720
+ */
2721
+ function dispatchPopStateEventToMicroApp(appName, proxyWindow, eventState) {
2722
+ // create PopStateEvent named popstate-appName with sub app state
2723
+ const newPopStateEvent = new PopStateEvent(formatEventName$1('popstate', appName), { state: getMicroState(appName, eventState) });
2724
+ globalEnv.rawWindow.dispatchEvent(newPopStateEvent);
2725
+ // call function window.onpopstate if it exists
2726
+ typeof proxyWindow.onpopstate === 'function' && proxyWindow.onpopstate(newPopStateEvent);
2727
+ }
2728
+ /**
2729
+ * dispatch formatted hashchange event to microApp
2730
+ * @param appName app name
2731
+ * @param proxyWindow sandbox window
2732
+ * @param oldHref old href
2733
+ */
2734
+ function dispatchHashChangeEventToMicroApp(appName, proxyWindow, oldHref) {
2735
+ const newHashChangeEvent = new HashChangeEvent(formatEventName$1('hashchange', appName), {
2736
+ newURL: proxyWindow.location.href,
2737
+ oldURL: oldHref,
2738
+ });
2739
+ globalEnv.rawWindow.dispatchEvent(newHashChangeEvent);
2740
+ // call function window.onhashchange if it exists
2741
+ typeof proxyWindow.onhashchange === 'function' && proxyWindow.onhashchange(newHashChangeEvent);
2742
+ }
2743
+ /**
2744
+ * dispatch native PopStateEvent, simulate location behavior
2745
+ * @param onlyForBrowser only dispatch PopStateEvent to browser
2746
+ */
2747
+ function dispatchNativePopStateEvent(onlyForBrowser) {
2748
+ const event = new PopStateEvent('popstate', { state: null });
2749
+ if (onlyForBrowser)
2750
+ event.onlyForBrowser = true;
2751
+ globalEnv.rawWindow.dispatchEvent(event);
2752
+ }
2753
+ /**
2754
+ * dispatch hashchange event to browser
2755
+ * @param oldHref old href of rawWindow.location
2756
+ */
2757
+ function dispatchNativeHashChangeEvent(oldHref) {
2758
+ const newHashChangeEvent = new HashChangeEvent('hashchange', {
2759
+ newURL: globalEnv.rawWindow.location.href,
2760
+ oldURL: oldHref,
2761
+ });
2762
+ globalEnv.rawWindow.dispatchEvent(newHashChangeEvent);
2763
+ }
2764
+ /**
2765
+ * dispatch popstate & hashchange event to browser
2766
+ * @param onlyForBrowser only dispatch event to browser
2767
+ * @param oldHref old href of rawWindow.location
2768
+ */
2769
+ function dispatchNativeEvent(onlyForBrowser, oldHref) {
2770
+ // clear element scope before dispatch global event
2771
+ removeDomScope();
2772
+ dispatchNativePopStateEvent(onlyForBrowser);
2773
+ if (oldHref) {
2774
+ dispatchNativeHashChangeEvent(oldHref);
2775
+ }
2776
+ }
2777
+
2778
+ /**
2779
+ * create proxyHistory for microApp
2780
+ * MDN https://developer.mozilla.org/en-US/docs/Web/API/History
2781
+ * @param appName app name
2782
+ * @param microLocation microApp location
2783
+ */
2784
+ function createMicroHistory(appName, microLocation) {
2785
+ const rawHistory = globalEnv.rawWindow.history;
2786
+ function getMicroHistoryMethod(methodName) {
2787
+ return function (...rests) {
2788
+ if ((methodName === 'pushState' || methodName === 'replaceState') &&
2789
+ (isString(rests[2]) || isURL(rests[2]))) {
2790
+ const targetLocation = createURL(rests[2], microLocation.href);
2791
+ if (targetLocation.origin === microLocation.origin) {
2792
+ navigateWithNativeEvent(methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rawHistory.state, rests[0]), rests[1]);
2793
+ const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
2794
+ if (targetFullPath !== microLocation.fullPath) {
2795
+ updateMicroLocation(appName, targetFullPath, microLocation);
2796
+ }
2797
+ }
2798
+ else {
2799
+ rawHistory[methodName].apply(rawHistory, rests);
2800
+ }
2801
+ }
2802
+ else {
2803
+ rawHistory[methodName].apply(rawHistory, rests);
2804
+ }
2805
+ };
2806
+ }
2807
+ return new Proxy(rawHistory, {
2808
+ get(target, key) {
2809
+ if (key === 'state') {
2810
+ return getMicroState(appName, rawHistory.state);
2811
+ }
2812
+ else if (isFunction(Reflect.get(target, key))) {
2813
+ return getMicroHistoryMethod(key);
2814
+ }
2815
+ return Reflect.get(target, key);
2816
+ },
2817
+ set(target, key, value) {
2818
+ return Reflect.set(target, key, value);
2819
+ }
2820
+ });
2821
+ }
2822
+ /**
2823
+ * navigate to new path base on native method of history
2824
+ * @param methodName pushState/replaceState
2825
+ * @param fullPath full path
2826
+ * @param state history.state, default is null
2827
+ * @param title history.title, default is ''
2828
+ */
2829
+ function nativeHistoryNavigate(methodName, fullPath, state = null, title = '') {
2830
+ globalEnv.rawWindow.history[methodName](state, title, fullPath);
2831
+ }
2832
+ /**
2833
+ * Navigate to new path, and dispatch native popStateEvent/hashChangeEvent to browser
2834
+ * Use scenes:
2835
+ * 1. mount/unmount through updateBrowserURL with limited popstateEvent
2836
+ * 2. proxyHistory.pushState/replaceState with limited popstateEvent
2837
+ * 3. api microApp.router.push/replace
2838
+ * 4. proxyLocation.hash = xxx
2839
+ * @param methodName pushState/replaceState
2840
+ * @param result result of add/remove microApp path on browser url
2841
+ * @param onlyForBrowser only dispatch event to browser
2842
+ * @param state history.state, not required
2843
+ * @param title history.title, not required
2844
+ */
2845
+ function navigateWithNativeEvent(methodName, result, onlyForBrowser, state, title) {
2846
+ const rawLocation = globalEnv.rawWindow.location;
2847
+ const oldFullPath = rawLocation.pathname + rawLocation.search + rawLocation.hash;
2848
+ const oldHref = result.isAttach2Hash && oldFullPath !== result.fullPath ? rawLocation.href : null;
2849
+ // navigate with native history method
2850
+ nativeHistoryNavigate(methodName, result.fullPath, state, title);
2851
+ if (oldFullPath !== result.fullPath)
2852
+ dispatchNativeEvent(onlyForBrowser, oldHref);
2853
+ }
2854
+ /**
2855
+ * update browser url when mount/unmount/hidden/show
2856
+ * @param result result of add/remove microApp path on browser url
2857
+ * @param state history.state
2858
+ */
2859
+ function updateBrowserURL(result, state) {
2860
+ navigateWithNativeEvent('replaceState', result, true, state);
2861
+ }
2862
+ /**
2863
+ * When path is same, keep the microAppState in history.state
2864
+ * Fix bug of missing microAppState in next.js & angular
2865
+ * @param method history.pushState/replaceState
2866
+ */
2867
+ function patchHistoryState(method) {
2868
+ const rawWindow = globalEnv.rawWindow;
2869
+ return function (...rests) {
2870
+ var _a;
2871
+ if (((_a = rawWindow.history.state) === null || _a === void 0 ? void 0 : _a.microAppState) &&
2872
+ (!isPlainObject(rests[0]) || !rests[0].microAppState) &&
2873
+ (isString(rests[2]) || isURL(rests[2]))) {
2874
+ const currentHref = rawWindow.location.href;
2875
+ const targetLocation = createURL(rests[2], currentHref);
2876
+ if (targetLocation.href === currentHref) {
2877
+ rests[0] = assign({}, rests[0], {
2878
+ microAppState: rawWindow.history.state.microAppState,
2879
+ });
2880
+ }
2881
+ }
2882
+ method.apply(rawWindow.history, rests);
2883
+ };
2884
+ }
2885
+ let isReWriteHistoryState = false;
2886
+ /**
2887
+ * rewrite history.pushState/replaceState
2888
+ * used to fix the problem that the microAppState maybe missing when mainApp navigate to same path
2889
+ * e.g: when nextjs, angular receive popstate event, they will use history.replaceState to update browser url with a new state object
2890
+ */
2891
+ function rewriteHistoryState() {
2892
+ // filter nest app
2893
+ if (!isReWriteHistoryState && !window.__MICRO_APP_ENVIRONMENT__) {
2894
+ isReWriteHistoryState = true;
2895
+ const rawWindow = globalEnv.rawWindow;
2896
+ rawWindow.history.pushState = patchHistoryState(rawWindow.history.pushState);
2897
+ rawWindow.history.replaceState = patchHistoryState(rawWindow.history.replaceState);
2898
+ }
2899
+ }
2900
+
2901
+ function createRouterApi() {
2902
+ /**
2903
+ * common handler for router.push/router.replace method
2904
+ * @param appName app name
2905
+ * @param methodName replaceState/pushState
2906
+ * @param targetLocation target location
2907
+ * @param state to.state
2908
+ */
2909
+ function navigateWithRawHistory(appName, methodName, targetLocation, state) {
2910
+ navigateWithNativeEvent(methodName, setMicroPathToURL(appName, targetLocation), false, setMicroState(appName, globalEnv.rawWindow.history.state, state !== null && state !== void 0 ? state : null));
2911
+ // clear element scope after navigate
2912
+ removeDomScope();
2913
+ }
2914
+ /**
2915
+ * create method of router.push/replace
2916
+ * NOTE:
2917
+ * 1. The same fullPath will be blocked
2918
+ * 2. name & path is required
2919
+ * 3. path is fullPath except for the domain (the domain can be taken, but not valid)
2920
+ * @param replace use router.replace?
2921
+ */
2922
+ function createNavigationMethod(replace) {
2923
+ return function (to) {
2924
+ const appName = formatAppName(to.name);
2925
+ // console.log(3333333, appInstanceMap.get(appName))
2926
+ if (appName && isString(to.path)) {
2927
+ const app = appInstanceMap.get(appName);
2928
+ if (app && !app.sandBox)
2929
+ return logError(`navigation failed, sandBox of app ${appName} is closed`);
2930
+ // active apps, include hidden keep-alive app
2931
+ if (getActiveApps().includes(appName)) {
2932
+ const microLocation = app.sandBox.proxyWindow.location;
2933
+ const targetLocation = createURL(to.path, microLocation.href);
2934
+ // Only get path data, even if the origin is different from microApp
2935
+ const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
2936
+ if (microLocation.fullPath !== targetFullPath || getMicroPathFromURL(appName) !== targetFullPath) {
2937
+ const methodName = (replace && to.replace !== false) || to.replace === true ? 'replaceState' : 'pushState';
2938
+ navigateWithRawHistory(appName, methodName, targetLocation, to.state);
2939
+ }
2940
+ }
2941
+ else {
2942
+ /**
2943
+ * app not exit or unmounted, update browser URL with replaceState
2944
+ * use base app location.origin as baseURL
2945
+ */
2946
+ const rawLocation = globalEnv.rawWindow.location;
2947
+ const targetLocation = createURL(to.path, rawLocation.origin);
2948
+ const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
2949
+ if (getMicroPathFromURL(appName) !== targetFullPath) {
2950
+ navigateWithRawHistory(appName, to.replace === false ? 'pushState' : 'replaceState', targetLocation, to.state);
2951
+ }
2952
+ }
2953
+ }
2954
+ else {
2955
+ logError(`navigation failed, name & path are required when use router.${replace ? 'replace' : 'push'}`);
2956
+ }
2957
+ };
2958
+ }
2959
+ // create method of router.go/back/forward
2960
+ function createRawHistoryMethod(methodName) {
2961
+ return function (...rests) {
2962
+ return globalEnv.rawWindow.history[methodName](...rests);
2963
+ };
2964
+ }
2965
+ const beforeGuards = useSetRecord();
2966
+ const afterGuards = useSetRecord();
2967
+ /**
2968
+ * run all of beforeEach/afterEach guards
2969
+ * NOTE:
2970
+ * 1. Modify browser url first, and then run guards,
2971
+ * consistent with the browser forward & back button
2972
+ * 2. Note the element binding
2973
+ * @param appName app name
2974
+ * @param to target location
2975
+ * @param from old location
2976
+ * @param guards guards list
2977
+ */
2978
+ function runGuards(appName, to, from, guards) {
2979
+ // clear element scope before execute function of parent
2980
+ removeDomScope();
2981
+ for (const guard of guards) {
2982
+ if (isFunction(guard)) {
2983
+ guard(appName, to, from);
2984
+ }
2985
+ else if (isPlainObject(guard) && isFunction(guard[appName])) {
2986
+ guard[appName](to, from);
2987
+ }
2988
+ }
2989
+ }
2990
+ /**
2991
+ * global hook for router
2992
+ * update router information base on microLocation
2993
+ * @param appName app name
2994
+ * @param microLocation location of microApp
2995
+ */
2996
+ function executeNavigationGuard(appName, to, from) {
2997
+ router.current.set(appName, to);
2998
+ runGuards(appName, to, from, beforeGuards.list());
2999
+ requestIdleCallback(() => {
3000
+ runGuards(appName, to, from, afterGuards.list());
3001
+ });
3002
+ }
3003
+ function clearRouterWhenUnmount(appName) {
3004
+ router.current.delete(appName);
3005
+ }
3006
+ // defaultPage data
3007
+ const defaultPageRecord = useMapRecord();
3008
+ /**
3009
+ * defaultPage只在子应用初始化时生效,且优先级比浏览器上的子应用路由地址低
3010
+ * @param appName app name
3011
+ * @param path page path
3012
+ */
3013
+ function setDefaultPage(appName, path) {
3014
+ appName = formatAppName(appName);
3015
+ if (!appName)
3016
+ return noopFalse;
3017
+ return defaultPageRecord.add(appName, path);
3018
+ }
3019
+ function removeDefaultPage(appName) {
3020
+ appName = formatAppName(appName);
3021
+ if (!appName)
3022
+ return false;
3023
+ return defaultPageRecord.delete(appName);
3024
+ }
3025
+ // Router API for developer
3026
+ const router = {
3027
+ current: new Map(),
3028
+ encode: encodeMicroPath,
3029
+ decode: decodeMicroPath,
3030
+ push: createNavigationMethod(false),
3031
+ replace: createNavigationMethod(true),
3032
+ go: createRawHistoryMethod('go'),
3033
+ back: createRawHistoryMethod('back'),
3034
+ forward: createRawHistoryMethod('forward'),
3035
+ beforeEach: beforeGuards.add,
3036
+ afterEach: afterGuards.add,
3037
+ // attachToURL: 将指定的子应用路由信息添加到浏览器地址上
3038
+ // attachAllToURL: 将所有正在运行的子应用路由信息添加到浏览器地址上
3039
+ setDefaultPage,
3040
+ removeDefaultPage,
3041
+ getDefaultPage: defaultPageRecord.get,
3042
+ };
3043
+ return {
3044
+ router,
3045
+ executeNavigationGuard,
3046
+ clearRouterWhenUnmount,
3047
+ };
3048
+ }
3049
+ const { router, executeNavigationGuard, clearRouterWhenUnmount, } = createRouterApi();
3050
+
3051
+ const shadowLocationKeys = ['href', 'pathname', 'search', 'hash'];
3052
+ // origin is readonly, so we ignore when updateMicroLocation
3053
+ const locationKeys = [...shadowLocationKeys, 'host', 'hostname', 'port', 'protocol', 'search'];
3054
+ // origin, fullPath is necessary for guardLocation
3055
+ const guardLocationKeys = [...locationKeys, 'origin', 'fullPath'];
3056
+ /**
3057
+ * create guardLocation by microLocation, used for router guard
3058
+ */
3059
+ function createGuardLocation(appName, microLocation) {
3060
+ const guardLocation = assign({ name: appName }, microLocation);
3061
+ // The prototype values on the URL needs to be manually transferred
3062
+ for (const key of guardLocationKeys)
3063
+ guardLocation[key] = microLocation[key];
3064
+ return guardLocation;
3065
+ }
3066
+ // for updateBrowserURLWithLocation when initial
3067
+ function autoTriggerNavigationGuard(appName, microLocation) {
3068
+ executeNavigationGuard(appName, createGuardLocation(appName, microLocation), createGuardLocation(appName, microLocation));
3069
+ }
3070
+ /**
3071
+ * The following scenes will trigger location update:
3072
+ * 1. pushState/replaceState
3073
+ * 2. popStateEvent
3074
+ * 3. query on browser url when init sub app
3075
+ * 4. set defaultPage when when init sub app
3076
+ * NOTE:
3077
+ * 1. update browser URL first, and then update microLocation
3078
+ * 2. the same fullPath will not trigger router guards
3079
+ * @param appName app name
3080
+ * @param path target path
3081
+ * @param base base url
3082
+ * @param microLocation micro app location
3083
+ * @param type auto prevent
3084
+ */
3085
+ function updateMicroLocation(appName, path, microLocation, type) {
3086
+ const newLocation = createURL(path, microLocation.href);
3087
+ // record old values of microLocation to `from`
3088
+ const from = createGuardLocation(appName, microLocation);
3089
+ for (const key of locationKeys) {
3090
+ if (shadowLocationKeys.includes(key)) {
3091
+ // reference of shadowLocation
3092
+ microLocation.shadowLocation[key] = newLocation[key];
3093
+ }
3094
+ else {
3095
+ // @ts-ignore reference of microLocation
3096
+ microLocation[key] = newLocation[key];
3097
+ }
3098
+ }
3099
+ // update latest values of microLocation to `to`
3100
+ const to = createGuardLocation(appName, microLocation);
3101
+ // The hook called only when fullPath changed
3102
+ if (type === 'auto' || (from.fullPath !== to.fullPath && type !== 'prevent')) {
3103
+ executeNavigationGuard(appName, to, from);
3104
+ }
3105
+ }
3106
+ /**
3107
+ * Create location for microApp, each microApp has only one location object, it is a reference type
3108
+ * MDN https://developer.mozilla.org/en-US/docs/Web/API/Location
3109
+ * @param appName app name
3110
+ * @param url app url
3111
+ */
3112
+ function createMicroLocation(appName, url) {
3113
+ const rawWindow = globalEnv.rawWindow;
3114
+ const rawLocation = rawWindow.location;
3115
+ // microLocation is the location of child app, it is globally unique
3116
+ const microLocation = createURL(url);
3117
+ // shadowLocation is the current location information (href, pathname, search, hash)
3118
+ const shadowLocation = {
3119
+ href: microLocation.href,
3120
+ pathname: microLocation.pathname,
3121
+ search: microLocation.search,
3122
+ hash: microLocation.hash,
3123
+ };
3124
+ /**
3125
+ * Common handler for href, assign, replace
3126
+ * It is mainly used to deal with special scenes about hash
3127
+ * @param value target path
3128
+ * @param methodName pushState/replaceState
3129
+ * @returns origin value or formatted value
3130
+ */
3131
+ const commonHandler = (value, methodName) => {
3132
+ const targetLocation = createURL(value, microLocation.href);
3133
+ // Even if the origin is the same, developers still have the possibility of want to jump to a new page
3134
+ if (targetLocation.origin === microLocation.origin) {
3135
+ const setMicroPathResult = setMicroPathToURL(appName, targetLocation);
3136
+ /**
3137
+ * change hash with location.href will not trigger the browser reload
3138
+ * so we use pushState & reload to imitate href behavior
3139
+ * NOTE:
3140
+ * 1. if child app only change hash, it should not trigger browser reload
3141
+ * 2. if address is same and has hash, it should not add route stack
3142
+ */
3143
+ if (targetLocation.pathname === shadowLocation.pathname &&
3144
+ targetLocation.search === shadowLocation.search) {
3145
+ let oldHref = null;
3146
+ if (targetLocation.hash !== shadowLocation.hash) {
3147
+ if (setMicroPathResult.isAttach2Hash)
3148
+ oldHref = rawLocation.href;
3149
+ nativeHistoryNavigate(methodName, setMicroPathResult.fullPath);
3150
+ }
3151
+ if (targetLocation.hash) {
3152
+ dispatchNativeEvent(false, oldHref);
3153
+ }
3154
+ else {
3155
+ rawLocation.reload();
3156
+ }
3157
+ return void 0;
3158
+ /**
3159
+ * when baseApp is hash router, address change of child can not reload browser
3160
+ * so we imitate behavior of browser (reload)
3161
+ */
3162
+ }
3163
+ else if (setMicroPathResult.isAttach2Hash) {
3164
+ nativeHistoryNavigate(methodName, setMicroPathResult.fullPath);
3165
+ rawLocation.reload();
3166
+ return void 0;
3167
+ }
3168
+ value = setMicroPathResult.fullPath;
3169
+ }
3170
+ return value;
3171
+ };
3172
+ /**
3173
+ * create location PropertyDescriptor (href, pathname, search, hash)
3174
+ * @param key property name
3175
+ * @param setter setter of location property
3176
+ */
3177
+ function createPropertyDescriptor(getter, setter) {
3178
+ return {
3179
+ enumerable: true,
3180
+ configurable: true,
3181
+ get: getter,
3182
+ set: setter,
3183
+ };
3184
+ }
3185
+ /**
3186
+ * common handler for location.pathname & location.search
3187
+ * @param targetPath target fullPath
3188
+ * @param key pathname/search
3189
+ */
3190
+ function handleForPathNameAndSearch(targetPath, key) {
3191
+ const targetLocation = createURL(targetPath, url);
3192
+ // When the browser url has a hash value, the same pathname/search will not refresh browser
3193
+ if (targetLocation[key] === shadowLocation[key] && shadowLocation.hash) {
3194
+ // The href has not changed, not need to dispatch hashchange event
3195
+ dispatchNativeEvent(false);
3196
+ }
3197
+ else {
3198
+ /**
3199
+ * When the value is the same, no new route stack will be added
3200
+ * Special scenes such as:
3201
+ * pathname: /path ==> /path#hash, /path ==> /path?query
3202
+ * search: ?query ==> ?query#hash
3203
+ */
3204
+ nativeHistoryNavigate(targetLocation[key] === shadowLocation[key] ? 'replaceState' : 'pushState', setMicroPathToURL(appName, targetLocation).fullPath);
3205
+ rawLocation.reload();
3206
+ }
3207
+ }
3208
+ /**
3209
+ * Special processing for four keys: href, pathname, search and hash
3210
+ * They take values from shadowLocation, and require special operations when assigning values
3211
+ */
3212
+ rawDefineProperties(microLocation, {
3213
+ href: createPropertyDescriptor(() => shadowLocation.href, (value) => {
3214
+ const targetPath = commonHandler(value, 'pushState');
3215
+ if (targetPath)
3216
+ rawLocation.href = targetPath;
3217
+ }),
3218
+ pathname: createPropertyDescriptor(() => shadowLocation.pathname, (value) => {
3219
+ const targetPath = ('/' + value).replace(/^\/+/, '/') + shadowLocation.search + shadowLocation.hash;
3220
+ handleForPathNameAndSearch(targetPath, 'pathname');
3221
+ }),
3222
+ search: createPropertyDescriptor(() => shadowLocation.search, (value) => {
3223
+ const targetPath = shadowLocation.pathname + ('?' + value).replace(/^\?+/, '?') + shadowLocation.hash;
3224
+ handleForPathNameAndSearch(targetPath, 'search');
3225
+ }),
3226
+ hash: createPropertyDescriptor(() => shadowLocation.hash, (value) => {
3227
+ const targetPath = shadowLocation.pathname + shadowLocation.search + ('#' + value).replace(/^#+/, '#');
3228
+ const targetLocation = createURL(targetPath, url);
3229
+ // The same hash will not trigger popStateEvent
3230
+ if (targetLocation.hash !== shadowLocation.hash) {
3231
+ navigateWithNativeEvent('pushState', setMicroPathToURL(appName, targetLocation), false);
3232
+ }
3233
+ }),
3234
+ fullPath: createPropertyDescriptor(() => shadowLocation.pathname + shadowLocation.search + shadowLocation.hash, noop),
3235
+ });
3236
+ const createLocationMethod = (locationMethodName) => {
3237
+ return function (value) {
3238
+ const targetPath = commonHandler(value, locationMethodName === 'assign' ? 'pushState' : 'replaceState');
3239
+ if (targetPath)
3240
+ rawLocation[locationMethodName](targetPath);
3241
+ };
3242
+ };
3243
+ return assign(microLocation, {
3244
+ assign: createLocationMethod('assign'),
3245
+ replace: createLocationMethod('replace'),
3246
+ reload: (forcedReload) => rawLocation.reload(forcedReload),
3247
+ shadowLocation,
3248
+ });
3249
+ }
3250
+
3251
+ /**
3252
+ * The router system has two operations: read and write
3253
+ * Read through location and write through history & location
3254
+ * @param appName app name
3255
+ * @param url app url
3256
+ * @returns MicroRouter
3257
+ */
3258
+ function createMicroRouter(appName, url) {
3259
+ rewriteHistoryState();
3260
+ const microLocation = createMicroLocation(appName, url);
3261
+ return {
3262
+ microLocation,
3263
+ microHistory: createMicroHistory(appName, microLocation),
3264
+ };
3265
+ }
3266
+ // 当沙箱执行start, 或者隐藏的keep-alive应用重新渲染时时才根据浏览器url更新location 或者 将参数更新到url上
3267
+ function initRouteStateWithURL(appName, microLocation, defaultPage) {
3268
+ const microPath = getMicroPathFromURL(appName);
3269
+ if (microPath) {
3270
+ updateMicroLocation(appName, microPath, microLocation, 'auto');
3271
+ }
3272
+ else {
3273
+ updateBrowserURLWithLocation(appName, microLocation, defaultPage);
3274
+ }
3275
+ }
3276
+ /**
3277
+ * initialize browser information according to microLocation
3278
+ * called on sandbox.start or reshow of keep-alive app
3279
+ */
3280
+ function updateBrowserURLWithLocation(appName, microLocation, defaultPage) {
3281
+ // update microLocation with defaultPage
3282
+ if (defaultPage)
3283
+ updateMicroLocation(appName, defaultPage, microLocation, 'prevent');
3284
+ // attach microApp route info to browser URL
3285
+ updateBrowserURL(setMicroPathToURL(appName, microLocation), setMicroState(appName, globalEnv.rawWindow.history.state, null));
3286
+ // trigger guards after change browser URL
3287
+ autoTriggerNavigationGuard(appName, microLocation);
3288
+ }
3289
+ /**
3290
+ * In any case, microPath & microState will be removed from browser, but location will be initialized only when keep-router-state is false
3291
+ * @param appName app name
3292
+ * @param url app url
3293
+ * @param microLocation location of microApp
3294
+ * @param keepRouteState keep-router-state is only used to control whether to clear the location of microApp
3295
+ */
3296
+ function clearRouteStateFromURL(appName, url, microLocation, keepRouteState) {
3297
+ if (!keepRouteState) {
3298
+ const { pathname, search, hash } = createURL(url);
3299
+ updateMicroLocation(appName, pathname + search + hash, microLocation, 'prevent');
3300
+ }
3301
+ removeStateAndPathFromBrowser(appName);
3302
+ clearRouterWhenUnmount(appName);
3303
+ }
3304
+ /**
3305
+ * remove microState from history.state and remove microPath from browserURL
3306
+ * called on sandbox.stop or hidden of keep-alive app
3307
+ */
3308
+ function removeStateAndPathFromBrowser(appName) {
3309
+ updateBrowserURL(removeMicroPathFromURL(appName), removeMicroState(appName, globalEnv.rawWindow.history.state));
3310
+ }
3311
+
3312
+ // Variables that can escape to rawWindow
3313
+ const staticEscapeProperties = [
3314
+ '__REACT_ERROR_OVERLAY_GLOBAL_HOOK__',
3315
+ 'System',
3316
+ '__cjsWrapper',
3317
+ ];
3318
+ // Variables that can only assigned to rawWindow
3319
+ const escapeSetterKeyList = [
3320
+ 'location',
3321
+ ];
3322
+ const globalPropertyList = ['window', 'self', 'globalThis'];
3323
+ class SandBox {
3324
+ constructor(appName, url, useMemoryRouter = true) {
3325
+ /**
3326
+ * Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
3327
+ * https://github.com/micro-zoe/micro-app/issues/234
3328
+ */
3329
+ this.scopeProperties = ['webpackJsonp', 'Vue'];
3330
+ // Properties that can be escape to rawWindow
3331
+ this.escapeProperties = [];
3332
+ // Properties newly added to microAppWindow
3333
+ this.injectedKeys = new Set();
3334
+ // Properties escape to rawWindow, cleared when unmount
3335
+ this.escapeKeys = new Set();
3336
+ // sandbox state
3337
+ this.active = false;
3338
+ this.microAppWindow = {}; // Proxy target
3339
+ // get scopeProperties and escapeProperties from plugins
3340
+ this.getSpecialProperties(appName);
3341
+ // create proxyWindow with Proxy(microAppWindow)
3342
+ this.proxyWindow = this.createProxyWindow(appName);
3343
+ // inject global properties
3344
+ this.initMicroAppWindow(this.microAppWindow, appName, url, useMemoryRouter);
3345
+ // Rewrite global event listener & timeout
3346
+ assign(this, effect(this.microAppWindow));
3347
+ }
3348
+ start(baseRoute, useMemoryRouter = true, defaultPage = '') {
3349
+ if (!this.active) {
3350
+ this.active = true;
3351
+ if (useMemoryRouter) {
3352
+ this.initRouteState(defaultPage);
3353
+ // unique listener of popstate event for sub app
3354
+ this.removeHistoryListener = addHistoryListener(this.proxyWindow.__MICRO_APP_NAME__);
3355
+ }
3356
+ else {
3357
+ this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseRoute;
3358
+ }
3359
+ if (++SandBox.activeCount === 1) {
3360
+ effectDocumentEvent();
3361
+ patchElementPrototypeMethods();
3362
+ listenUmountOfNestedApp();
3363
+ }
3364
+ // BUG FIX: bable-polyfill@6.x
3365
+ globalEnv.rawWindow._babelPolyfill && (globalEnv.rawWindow._babelPolyfill = false);
3366
+ }
3367
+ }
3368
+ stop(keepRouteState = false) {
3369
+ if (this.active) {
3370
+ this.releaseEffect();
3371
+ this.microAppWindow.microApp.clearDataListener();
3372
+ this.microAppWindow.microApp.clearGlobalDataListener();
3373
+ this.injectedKeys.forEach((key) => {
3374
+ Reflect.deleteProperty(this.microAppWindow, key);
3375
+ });
3376
+ this.injectedKeys.clear();
3377
+ this.escapeKeys.forEach((key) => {
3378
+ Reflect.deleteProperty(globalEnv.rawWindow, key);
3379
+ });
3380
+ this.escapeKeys.clear();
3381
+ if (this.removeHistoryListener) {
3382
+ this.clearRouteState(keepRouteState);
3383
+ // release listener of popstate
3384
+ this.removeHistoryListener();
3385
+ }
3386
+ if (--SandBox.activeCount === 0) {
2599
3387
  releaseEffectDocumentEvent();
2600
3388
  releasePatches();
2601
3389
  }
3390
+ this.active = false;
2602
3391
  }
2603
3392
  }
2604
3393
  // record umd snapshot before the first execution of umdHookMount
@@ -2743,20 +3532,25 @@ class SandBox {
2743
3532
  * @param appName app name
2744
3533
  * @param url app url
2745
3534
  */
2746
- initMicroAppWindow(microAppWindow, appName, url) {
3535
+ initMicroAppWindow(microAppWindow, appName, url, useMemoryRouter) {
2747
3536
  microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
2748
3537
  microAppWindow.__MICRO_APP_NAME__ = appName;
3538
+ microAppWindow.__MICRO_APP_URL__ = url;
2749
3539
  microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
2750
3540
  microAppWindow.__MICRO_APP_WINDOW__ = microAppWindow;
2751
- microAppWindow.microApp = Object.assign(new EventCenterForMicroApp(appName), {
3541
+ microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
2752
3542
  removeDomScope,
2753
3543
  pureCreateElement,
3544
+ router,
2754
3545
  });
2755
3546
  microAppWindow.rawWindow = globalEnv.rawWindow;
2756
3547
  microAppWindow.rawDocument = globalEnv.rawDocument;
2757
3548
  microAppWindow.hasOwnProperty = (key) => rawHasOwnProperty.call(microAppWindow, key) || rawHasOwnProperty.call(globalEnv.rawWindow, key);
2758
3549
  this.setMappingPropertiesWithRawDescriptor(microAppWindow);
2759
3550
  this.setHijackProperties(microAppWindow, appName);
3551
+ // this.patchHijackRequest(microAppWindow, appName, url)
3552
+ if (useMemoryRouter)
3553
+ this.setRouterApi(microAppWindow, appName, url);
2760
3554
  }
2761
3555
  // properties associated with the native window
2762
3556
  setMappingPropertiesWithRawDescriptor(microAppWindow) {
@@ -2790,14 +3584,16 @@ class SandBox {
2790
3584
  let modifiedEval, modifiedImage;
2791
3585
  rawDefineProperties(microAppWindow, {
2792
3586
  document: {
3587
+ configurable: false,
3588
+ enumerable: true,
2793
3589
  get() {
2794
3590
  throttleDeferForSetAppName(appName);
2795
3591
  return globalEnv.rawDocument;
2796
3592
  },
2797
- configurable: false,
2798
- enumerable: true,
2799
3593
  },
2800
3594
  eval: {
3595
+ configurable: true,
3596
+ enumerable: false,
2801
3597
  get() {
2802
3598
  throttleDeferForSetAppName(appName);
2803
3599
  return modifiedEval || eval;
@@ -2805,10 +3601,10 @@ class SandBox {
2805
3601
  set: (value) => {
2806
3602
  modifiedEval = value;
2807
3603
  },
2808
- configurable: true,
2809
- enumerable: false,
2810
3604
  },
2811
3605
  Image: {
3606
+ configurable: true,
3607
+ enumerable: false,
2812
3608
  get() {
2813
3609
  throttleDeferForSetAppName(appName);
2814
3610
  return modifiedImage || globalEnv.ImageProxy;
@@ -2816,11 +3612,101 @@ class SandBox {
2816
3612
  set: (value) => {
2817
3613
  modifiedImage = value;
2818
3614
  },
3615
+ },
3616
+ });
3617
+ }
3618
+ // private patchHijackRequest (microAppWindow: microAppWindowType, appName: string, url: string) {
3619
+ // let modifiedImage: unknown
3620
+ // function EventSource (...rests: any[]) {
3621
+ // console.log(appName + ' EventSource', rests)
3622
+ // if (typeof rests[0] === 'string') {
3623
+ // rests[0] = (new URL(rests[0], url)).toString()
3624
+ // }
3625
+ // return new globalEnv.rawWindow.EventSource(...rests)
3626
+ // }
3627
+ // function patchFetch (...rests: any[]) {
3628
+ // console.log(appName + ' fetch', rests)
3629
+ // if (typeof rests[0] === 'string') {
3630
+ // rests[0] = (new URL(rests[0], url)).toString()
3631
+ // }
3632
+ // return globalEnv.rawWindow.fetch(...rests)
3633
+ // }
3634
+ // const rawXMLHttpRequest = globalEnv.rawWindow.XMLHttpRequest
3635
+ // class XMLHttpRequest extends rawXMLHttpRequest {
3636
+ // open (method: string, reqUrl: string) {
3637
+ // console.log(appName + ' XMLHttpRequest', method, reqUrl)
3638
+ // reqUrl = (new URL(reqUrl, url)).toString()
3639
+ // super.open(method, reqUrl)
3640
+ // }
3641
+ // }
3642
+ // rawDefineProperties(microAppWindow, {
3643
+ // EventSource: {
3644
+ // configurable: true,
3645
+ // enumerable: true,
3646
+ // get () {
3647
+ // return EventSource
3648
+ // },
3649
+ // set: (value) => {
3650
+ // modifiedImage = value
3651
+ // },
3652
+ // },
3653
+ // fetch: {
3654
+ // configurable: true,
3655
+ // enumerable: true,
3656
+ // get () {
3657
+ // return patchFetch
3658
+ // },
3659
+ // set: (value) => {
3660
+ // modifiedImage = value
3661
+ // },
3662
+ // },
3663
+ // XMLHttpRequest: {
3664
+ // configurable: true,
3665
+ // enumerable: true,
3666
+ // get () {
3667
+ // return XMLHttpRequest
3668
+ // },
3669
+ // set: (value) => {
3670
+ // modifiedImage = value
3671
+ // },
3672
+ // },
3673
+ // })
3674
+ // }
3675
+ // set location & history for memory router
3676
+ setRouterApi(microAppWindow, appName, url) {
3677
+ const { microLocation, microHistory } = createMicroRouter(appName, url);
3678
+ rawDefineProperties(microAppWindow, {
3679
+ location: {
3680
+ configurable: false,
3681
+ enumerable: true,
3682
+ get() {
3683
+ return microLocation;
3684
+ },
3685
+ set: (value) => {
3686
+ globalEnv.rawWindow.location = value;
3687
+ },
3688
+ },
3689
+ history: {
2819
3690
  configurable: true,
2820
- enumerable: false,
3691
+ enumerable: true,
3692
+ get() {
3693
+ return microHistory;
3694
+ },
2821
3695
  },
2822
3696
  });
2823
3697
  }
3698
+ initRouteState(defaultPage) {
3699
+ initRouteStateWithURL(this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.location, defaultPage);
3700
+ }
3701
+ clearRouteState(keepRouteState) {
3702
+ clearRouteStateFromURL(this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.__MICRO_APP_URL__, this.proxyWindow.location, keepRouteState);
3703
+ }
3704
+ setRouteInfoForKeepAliveApp() {
3705
+ updateBrowserURLWithLocation(this.proxyWindow.__MICRO_APP_NAME__, this.proxyWindow.location);
3706
+ }
3707
+ removeRouteInfoForKeepAliveApp() {
3708
+ removeStateAndPathFromBrowser(this.proxyWindow.__MICRO_APP_NAME__);
3709
+ }
2824
3710
  }
2825
3711
  SandBox.activeCount = 0; // number of active sandbox
2826
3712
 
@@ -2854,7 +3740,7 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
2854
3740
  element = getRootContainer(element);
2855
3741
  // clear dom scope before dispatch lifeCycles event to base app, especially mounted & unmount
2856
3742
  removeDomScope();
2857
- const detail = Object.assign({
3743
+ const detail = assign({
2858
3744
  name: appName,
2859
3745
  container: element,
2860
3746
  }, error && {
@@ -2879,7 +3765,7 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
2879
3765
  * @param detail event detail
2880
3766
  */
2881
3767
  function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
2882
- const event = new CustomEvent(`${eventName}-${appName}`, {
3768
+ const event = new CustomEvent(formatEventName$1(eventName, appName), {
2883
3769
  detail,
2884
3770
  });
2885
3771
  window.dispatchEvent(event);
@@ -2888,8 +3774,8 @@ function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
2888
3774
  // micro app instances
2889
3775
  const appInstanceMap = new Map();
2890
3776
  class CreateApp {
2891
- constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, baseroute, }) {
2892
- this.state = appStates.NOT_LOADED;
3777
+ constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, useMemoryRouter, baseroute, keepRouteState, defaultPage, }) {
3778
+ this.state = appStates.CREATED;
2893
3779
  this.keepAliveState = null;
2894
3780
  this.keepAliveContainer = null;
2895
3781
  this.loadSourceLevel = 0;
@@ -2900,28 +3786,30 @@ class CreateApp {
2900
3786
  this.isPrefetch = false;
2901
3787
  this.prefetchResolve = null;
2902
3788
  this.container = null;
2903
- this.baseroute = '';
2904
3789
  this.sandBox = null;
2905
3790
  this.container = container !== null && container !== void 0 ? container : null;
2906
3791
  this.inline = inline !== null && inline !== void 0 ? inline : false;
2907
3792
  this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : '';
3793
+ this.keepRouteState = keepRouteState !== null && keepRouteState !== void 0 ? keepRouteState : false;
2908
3794
  this.ssrUrl = ssrUrl !== null && ssrUrl !== void 0 ? ssrUrl : '';
2909
3795
  // optional during init👆
2910
3796
  this.name = name;
2911
3797
  this.url = url;
2912
3798
  this.useSandbox = useSandbox;
2913
3799
  this.scopecss = this.useSandbox && scopecss;
3800
+ this.useMemoryRouter = this.useSandbox && useMemoryRouter;
3801
+ this.defaultPage = defaultPage !== null && defaultPage !== void 0 ? defaultPage : '';
2914
3802
  this.source = {
2915
3803
  links: new Map(),
2916
3804
  scripts: new Map(),
2917
3805
  };
2918
3806
  this.loadSourceCode();
2919
- this.useSandbox && (this.sandBox = new SandBox(name, url));
3807
+ this.useSandbox && (this.sandBox = new SandBox(name, url, this.useMemoryRouter));
2920
3808
  }
2921
3809
  // Load resources
2922
3810
  loadSourceCode() {
2923
- this.state = appStates.LOADING_SOURCE_CODE;
2924
- HTMLLoader.getInstance().run(this, extractSourceDom);
3811
+ this.state = appStates.LOADING;
3812
+ extractHtml(this);
2925
3813
  }
2926
3814
  /**
2927
3815
  * When resource is loaded, mount app if it is not prefetch or unmount
@@ -2935,7 +3823,7 @@ class CreateApp {
2935
3823
  this.prefetchResolve = null;
2936
3824
  }
2937
3825
  else if (appStates.UNMOUNT !== this.state) {
2938
- this.state = appStates.LOAD_SOURCE_FINISHED;
3826
+ this.state = appStates.LOADED;
2939
3827
  this.mount();
2940
3828
  }
2941
3829
  }
@@ -2952,7 +3840,7 @@ class CreateApp {
2952
3840
  }
2953
3841
  if (appStates.UNMOUNT !== this.state) {
2954
3842
  this.onerror(e);
2955
- this.state = appStates.LOAD_SOURCE_ERROR;
3843
+ this.state = appStates.LOAD_FAILED;
2956
3844
  }
2957
3845
  }
2958
3846
  /**
@@ -2960,22 +3848,26 @@ class CreateApp {
2960
3848
  * @param container app container
2961
3849
  * @param inline js runs in inline mode
2962
3850
  * @param baseroute route prefix, default is ''
3851
+ * @param keepRouteState keep route state when unmount, default is false
2963
3852
  */
2964
- mount(container, inline, baseroute) {
3853
+ mount(container, inline, baseroute, keepRouteState, defaultPage) {
2965
3854
  var _a, _b, _c;
2966
- if (isBoolean(inline) && inline !== this.inline) {
3855
+ if (isBoolean(inline))
2967
3856
  this.inline = inline;
2968
- }
3857
+ // keepRouteState effective on unmount
3858
+ if (isBoolean(keepRouteState))
3859
+ this.keepRouteState = keepRouteState;
2969
3860
  this.container = (_a = this.container) !== null && _a !== void 0 ? _a : container;
2970
3861
  this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : this.baseroute;
3862
+ this.defaultPage = defaultPage !== null && defaultPage !== void 0 ? defaultPage : this.defaultPage;
2971
3863
  if (this.loadSourceLevel !== 2) {
2972
- this.state = appStates.LOADING_SOURCE_CODE;
3864
+ this.state = appStates.LOADING;
2973
3865
  return;
2974
3866
  }
2975
3867
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
2976
3868
  this.state = appStates.MOUNTING;
2977
3869
  cloneContainer(this.source.html, this.container, !this.umdMode);
2978
- (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.start(this.baseroute);
3870
+ (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.start(this.baseroute, this.useMemoryRouter, this.defaultPage);
2979
3871
  let umdHookMountResult; // result of mount function
2980
3872
  if (!this.umdMode) {
2981
3873
  let hasDispatchMountedEvent = false;
@@ -3040,11 +3932,12 @@ class CreateApp {
3040
3932
  }
3041
3933
  /**
3042
3934
  * unmount app
3935
+ * NOTE: Do not add any params on account of unmountApp
3043
3936
  * @param destroy completely destroy, delete cache resources
3044
3937
  * @param unmountcb callback of unmount
3045
3938
  */
3046
3939
  unmount(destroy, unmountcb) {
3047
- if (this.state === appStates.LOAD_SOURCE_ERROR) {
3940
+ if (this.state === appStates.LOAD_FAILED) {
3048
3941
  destroy = true;
3049
3942
  }
3050
3943
  this.state = appStates.UNMOUNT;
@@ -3098,7 +3991,7 @@ class CreateApp {
3098
3991
  cloneContainer(this.container, this.source.html, false);
3099
3992
  }
3100
3993
  // this.container maybe contains micro-app element, stop sandbox should exec after cloneContainer
3101
- (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.stop();
3994
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.stop(this.keepRouteState && !destroy);
3102
3995
  if (!getActiveApps().length) {
3103
3996
  releasePatchSetAttribute();
3104
3997
  }
@@ -3117,34 +4010,40 @@ class CreateApp {
3117
4010
  }
3118
4011
  // hidden app when disconnectedCallback called with keep-alive
3119
4012
  hiddenKeepAliveApp() {
4013
+ var _a;
3120
4014
  const oldContainer = this.container;
3121
4015
  cloneContainer(this.container, this.keepAliveContainer ? this.keepAliveContainer : (this.keepAliveContainer = document.createElement('div')), false);
3122
4016
  this.container = this.keepAliveContainer;
3123
4017
  this.keepAliveState = keepAliveStates.KEEP_ALIVE_HIDDEN;
3124
4018
  // event should dispatch before clone node
3125
- // dispatch afterhidden event to micro-app
4019
+ // dispatch afterHidden event to micro-app
3126
4020
  dispatchCustomEventToMicroApp('appstate-change', this.name, {
3127
4021
  appState: 'afterhidden',
3128
4022
  });
3129
- // dispatch afterhidden event to base app
4023
+ // dispatch afterHidden event to base app
3130
4024
  dispatchLifecyclesEvent(oldContainer, this.name, lifeCycles.AFTERHIDDEN);
4025
+ // called after lifeCyclesEvent
4026
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.removeRouteInfoForKeepAliveApp();
3131
4027
  }
3132
4028
  // show app when connectedCallback called with keep-alive
3133
4029
  showKeepAliveApp(container) {
3134
- // dispatch beforeshow event to micro-app
4030
+ var _a;
4031
+ // dispatch beforeShow event to micro-app
3135
4032
  dispatchCustomEventToMicroApp('appstate-change', this.name, {
3136
4033
  appState: 'beforeshow',
3137
4034
  });
3138
- // dispatch beforeshow event to base app
4035
+ // dispatch beforeShow event to base app
3139
4036
  dispatchLifecyclesEvent(container, this.name, lifeCycles.BEFORESHOW);
3140
4037
  cloneContainer(this.container, container, false);
3141
4038
  this.container = container;
3142
4039
  this.keepAliveState = keepAliveStates.KEEP_ALIVE_SHOW;
3143
- // dispatch aftershow event to micro-app
4040
+ // called before lifeCyclesEvent
4041
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.setRouteInfoForKeepAliveApp();
4042
+ // dispatch afterShow event to micro-app
3144
4043
  dispatchCustomEventToMicroApp('appstate-change', this.name, {
3145
4044
  appState: 'aftershow',
3146
4045
  });
3147
- // dispatch aftershow event to base app
4046
+ // dispatch afterShow event to base app
3148
4047
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.AFTERSHOW);
3149
4048
  }
3150
4049
  /**
@@ -3252,12 +4151,17 @@ function defineElement(tagName) {
3252
4151
  }
3253
4152
  disconnectedCallback() {
3254
4153
  this.hasConnected = false;
3255
- // keep-alive
3256
- if (this.getKeepAliveModeResult()) {
3257
- this.handleHiddenKeepAliveApp();
3258
- }
3259
- else {
3260
- this.handleUnmount(this.getDestroyCompatibleResult());
4154
+ const app = appInstanceMap.get(this.appName);
4155
+ if (app &&
4156
+ app.getAppState() !== appStates.UNMOUNT &&
4157
+ app.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN) {
4158
+ // keep-alive
4159
+ if (this.getKeepAliveModeResult()) {
4160
+ this.handleHiddenKeepAliveApp();
4161
+ }
4162
+ else {
4163
+ this.handleUnmount(this.getDestroyCompatibleResult());
4164
+ }
3261
4165
  }
3262
4166
  }
3263
4167
  attributeChangedCallback(attr, _oldVal, newVal) {
@@ -3305,12 +4209,7 @@ function defineElement(tagName) {
3305
4209
  if (this.getDisposeResult('shadowDOM') && !this.shadowRoot && isFunction(this.attachShadow)) {
3306
4210
  this.attachShadow({ mode: 'open' });
3307
4211
  }
3308
- if (this.getDisposeResult('ssr')) {
3309
- this.ssrUrl = CompletionPath(globalEnv.rawWindow.location.pathname, this.appUrl);
3310
- }
3311
- else if (this.ssrUrl) {
3312
- this.ssrUrl = '';
3313
- }
4212
+ this.updateSsrUrl(this.appUrl);
3314
4213
  if (appInstanceMap.has(this.appName)) {
3315
4214
  const app = appInstanceMap.get(this.appName);
3316
4215
  const existAppUrl = app.ssrUrl || app.url;
@@ -3344,15 +4243,9 @@ function defineElement(tagName) {
3344
4243
  actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp) {
3345
4244
  var _a;
3346
4245
  /**
3347
- * change ssrUrl in ssr mode
3348
4246
  * do not add judgment of formatAttrUrl === this.appUrl
3349
4247
  */
3350
- if (this.getDisposeResult('ssr')) {
3351
- this.ssrUrl = CompletionPath(globalEnv.rawWindow.location.pathname, formatAttrUrl);
3352
- }
3353
- else if (this.ssrUrl) {
3354
- this.ssrUrl = '';
3355
- }
4248
+ this.updateSsrUrl(formatAttrUrl);
3356
4249
  this.appName = formatAttrName;
3357
4250
  this.appUrl = formatAttrUrl;
3358
4251
  ((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this).innerHTML = '';
@@ -3412,14 +4305,14 @@ function defineElement(tagName) {
3412
4305
  app.isPrefetch = false;
3413
4306
  defer(() => {
3414
4307
  var _a;
3415
- return app.mount((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this, this.getDisposeResult('inline'), this.getBaseRouteCompatible());
4308
+ return app.mount((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this, this.getDisposeResult('inline'), this.getBaseRouteCompatible(), this.getDisposeResult('keep-router-state'), this.getDefaultPageValue());
3416
4309
  });
3417
4310
  }
3418
4311
  // create app instance
3419
4312
  handleCreateApp() {
3420
4313
  var _a;
3421
4314
  /**
3422
- * actions for destory old app
4315
+ * actions for destroy old app
3423
4316
  * fix of unmounted umd app with disableSandbox
3424
4317
  */
3425
4318
  if (appInstanceMap.has(this.appName)) {
@@ -3433,7 +4326,10 @@ function defineElement(tagName) {
3433
4326
  inline: this.getDisposeResult('inline'),
3434
4327
  scopecss: !(this.getDisposeResult('disableScopecss') || this.getDisposeResult('shadowDOM')),
3435
4328
  useSandbox: !this.getDisposeResult('disableSandbox'),
4329
+ useMemoryRouter: !this.getDisposeResult('disable-memory-router'),
3436
4330
  baseroute: this.getBaseRouteCompatible(),
4331
+ keepRouteState: this.getDisposeResult('keep-router-state'),
4332
+ defaultPage: this.getDefaultPageValue(),
3437
4333
  });
3438
4334
  appInstanceMap.set(this.appName, instance);
3439
4335
  }
@@ -3441,11 +4337,12 @@ function defineElement(tagName) {
3441
4337
  * unmount app
3442
4338
  * @param destroy delete cache resources when unmount
3443
4339
  */
3444
- handleUnmount(destroy, unmountcb) {
4340
+ handleUnmount(destroy, unmountCb) {
3445
4341
  const app = appInstanceMap.get(this.appName);
3446
4342
  if (app &&
3447
- app.getAppState() !== appStates.UNMOUNT)
3448
- app.unmount(destroy, unmountcb);
4343
+ app.getAppState() !== appStates.UNMOUNT) {
4344
+ app.unmount(destroy, unmountCb);
4345
+ }
3449
4346
  }
3450
4347
  // hidden app when disconnectedCallback called with keep-alive
3451
4348
  handleHiddenKeepAliveApp() {
@@ -3508,6 +4405,37 @@ function defineElement(tagName) {
3508
4405
  getKeepAliveModeResult() {
3509
4406
  return this.getDisposeResult('keep-alive') && !this.getDestroyCompatibleResult();
3510
4407
  }
4408
+ /**
4409
+ * change ssrUrl in ssr mode
4410
+ */
4411
+ updateSsrUrl(baseUrl) {
4412
+ if (this.getDisposeResult('ssr')) {
4413
+ if (this.getDisposeResult('disable-memory-router')) {
4414
+ const rawLocation = globalEnv.rawWindow.location;
4415
+ this.ssrUrl = CompletionPath(rawLocation.pathname + rawLocation.search, baseUrl);
4416
+ }
4417
+ else {
4418
+ // get path from browser URL
4419
+ let targetPath = getNoHashMicroPathFromURL(this.appName, baseUrl);
4420
+ const defaultPagePath = this.getDefaultPageValue();
4421
+ if (!targetPath && defaultPagePath) {
4422
+ const targetLocation = createURL(defaultPagePath, baseUrl);
4423
+ targetPath = targetLocation.origin + targetLocation.pathname + targetLocation.search;
4424
+ }
4425
+ this.ssrUrl = targetPath;
4426
+ }
4427
+ }
4428
+ else if (this.ssrUrl) {
4429
+ this.ssrUrl = '';
4430
+ }
4431
+ }
4432
+ /**
4433
+ * get config of default page
4434
+ */
4435
+ getDefaultPageValue() {
4436
+ var _a, _b, _c;
4437
+ 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 : '';
4438
+ }
3511
4439
  /**
3512
4440
  * Data from the base application
3513
4441
  */
@@ -3542,12 +4470,13 @@ function defineElement(tagName) {
3542
4470
  * url: string,
3543
4471
  * disableScopecss?: boolean,
3544
4472
  * disableSandbox?: boolean,
4473
+ * disableMemoryRouter?: boolean,
3545
4474
  * },
3546
4475
  * ...
3547
4476
  * ])
3548
4477
  * Note:
3549
4478
  * 1: preFetch is asynchronous and is performed only when the browser is idle
3550
- * 2: disableScopecss, disableSandbox must be same with micro-app element, if conflict, the one who executes first shall prevail
4479
+ * 2: disableScopecss, disableSandbox, disableMemoryRouter must be same with micro-app element, if conflict, the one who executes first shall prevail
3551
4480
  * @param apps micro apps
3552
4481
  */
3553
4482
  function preFetch(apps) {
@@ -3565,7 +4494,7 @@ function preFetch(apps) {
3565
4494
  function preFetchInSerial(prefetchApp) {
3566
4495
  return new Promise((resolve) => {
3567
4496
  requestIdleCallback(() => {
3568
- var _a, _b;
4497
+ var _a, _b, _c;
3569
4498
  if (isPlainObject(prefetchApp) && navigator.onLine) {
3570
4499
  prefetchApp.name = formatAppName(prefetchApp.name);
3571
4500
  prefetchApp.url = formatAppURL(prefetchApp.url, prefetchApp.name);
@@ -3575,6 +4504,7 @@ function preFetchInSerial(prefetchApp) {
3575
4504
  url: prefetchApp.url,
3576
4505
  scopecss: !((_a = prefetchApp.disableScopecss) !== null && _a !== void 0 ? _a : microApp.disableScopecss),
3577
4506
  useSandbox: !((_b = prefetchApp.disableSandbox) !== null && _b !== void 0 ? _b : microApp.disableSandbox),
4507
+ useMemoryRouter: !((_c = prefetchApp.disableMemoryRouter) !== null && _c !== void 0 ? _c : microApp.disableMemoryRouter),
3578
4508
  });
3579
4509
  app.isPrefetch = true;
3580
4510
  app.prefetchResolve = resolve;
@@ -3643,7 +4573,7 @@ function getAllApps() {
3643
4573
  /**
3644
4574
  * unmount app by appName
3645
4575
  * @param appName
3646
- * @param options unmountAppParams
4576
+ * @param options unmountAppOptions
3647
4577
  * @returns Promise<void>
3648
4578
  */
3649
4579
  function unmountApp(appName, options) {
@@ -3717,6 +4647,7 @@ class MicroApp extends EventCenterForBaseApp {
3717
4647
  super(...arguments);
3718
4648
  this.tagName = 'micro-app';
3719
4649
  this.preFetch = preFetch;
4650
+ this.router = router;
3720
4651
  }
3721
4652
  start(options) {
3722
4653
  if (!isBrowser || !window.customElements) {
@@ -3746,6 +4677,7 @@ class MicroApp extends EventCenterForBaseApp {
3746
4677
  this.inline = options.inline;
3747
4678
  this.disableScopecss = options.disableScopecss;
3748
4679
  this.disableSandbox = options.disableSandbox;
4680
+ this.disableMemoryRouter = options.disableMemoryRouter;
3749
4681
  this.ssr = options.ssr;
3750
4682
  isFunction(options.fetch) && (this.fetch = options.fetch);
3751
4683
  isPlainObject(options.lifeCycles) && (this.lifeCycles = options.lifeCycles);