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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- const version = '1.0.0-alpha.1';
1
+ const version = '1.0.0-alpha.2';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -41,6 +41,10 @@ function isFunction(target) {
41
41
  function isPlainObject(target) {
42
42
  return toString.call(target) === '[object Object]';
43
43
  }
44
+ // is Object
45
+ function isObject(target) {
46
+ return typeof target === 'object';
47
+ }
44
48
  // is Promise
45
49
  function isPromise(target) {
46
50
  return toString.call(target) === '[object Promise]';
@@ -49,6 +53,18 @@ function isPromise(target) {
49
53
  function isBoundFunction(target) {
50
54
  return isFunction(target) && target.name.indexOf('bound ') === 0 && !target.hasOwnProperty('prototype');
51
55
  }
56
+ // is constructor function
57
+ function isConstructor(target) {
58
+ var _a;
59
+ if (isFunction(target)) {
60
+ const targetStr = target.toString();
61
+ return (((_a = target.prototype) === null || _a === void 0 ? void 0 : _a.constructor) === target &&
62
+ Object.getOwnPropertyNames(target.prototype).length > 1) ||
63
+ /^function\s+[A-Z]/.test(targetStr) ||
64
+ /^class\s+/.test(targetStr);
65
+ }
66
+ return false;
67
+ }
52
68
  // is ShadowRoot
53
69
  function isShadowRoot(target) {
54
70
  return typeof ShadowRoot !== 'undefined' && target instanceof ShadowRoot;
@@ -270,6 +286,7 @@ function pureCreateElement(tagName, options) {
270
286
  const element = document.createElement(tagName, options);
271
287
  if (element.__MICRO_APP_NAME__)
272
288
  delete element.__MICRO_APP_NAME__;
289
+ element.__PURE_ELEMENT__ = true;
273
290
  return element;
274
291
  }
275
292
  /**
@@ -465,10 +482,18 @@ const globalKeyToBeCached = 'window,self,globalThis,Array,Object,String,Boolean,
465
482
  * @param config fetch options
466
483
  */
467
484
  function fetchSource(url, appName = null, options = {}) {
485
+ /**
486
+ * When child navigate to new async page, click event will scope dom to child and then fetch new source
487
+ * this may cause error when fetch rewrite by baseApp
488
+ * e.g.
489
+ * baseApp: <script crossorigin src="https://sgm-static.jd.com/sgm-2.8.0.js" name="SGMH5" sid="6f88a6e4ba4b4ae5acef2ec22c075085" appKey="jdb-adminb2b-pc"></script>
490
+ */
491
+ removeDomScope();
468
492
  if (isFunction(microApp.fetch)) {
469
493
  return microApp.fetch(url, options, appName);
470
494
  }
471
- return fetch(url, options).then((res) => {
495
+ // Don’t use globalEnv.rawWindow.fetch, will cause sgm-2.8.0.js throw error in nest app
496
+ return window.fetch(url, options).then((res) => {
472
497
  return res.text();
473
498
  });
474
499
  }
@@ -551,7 +576,7 @@ class CSSParser {
551
576
  return true;
552
577
  }
553
578
  formatSelector(skip) {
554
- const m = this.commonMatch(/^([^{]+)/, skip);
579
+ const m = this.commonMatch(/^[^{]+/, skip);
555
580
  if (!m)
556
581
  return false;
557
582
  return m[0].replace(/(^|,[\n\s]*)([^,]+)/g, (_, separator, selector) => {
@@ -631,7 +656,7 @@ class CSSParser {
631
656
  keyframesRule() {
632
657
  if (!this.commonMatch(/^@([-\w]+)?keyframes\s*/))
633
658
  return false;
634
- if (!this.commonMatch(/^([-\w]+)\s*/))
659
+ if (!this.commonMatch(/^[^{]+/))
635
660
  return parseError('@keyframes missing name', this.linkPath);
636
661
  this.matchComments();
637
662
  if (!this.matchOpenBrace())
@@ -783,7 +808,7 @@ class CSSParser {
783
808
  }
784
809
  // splice string
785
810
  recordResult(strFragment) {
786
- // Firefox is slow when string contain special characters, see https://github.com/micro-zoe/micro-app/issues/256
811
+ // Firefox performance degradation when string contain special characters, see https://github.com/micro-zoe/micro-app/issues/256
787
812
  if (isFireFox()) {
788
813
  this.result += encodeURIComponent(strFragment);
789
814
  }
@@ -1071,6 +1096,27 @@ function fixReactHMRConflict(app) {
1071
1096
  }
1072
1097
  }
1073
1098
  }
1099
+ /**
1100
+ * reDefine parentNode of html
1101
+ * Scenes:
1102
+ * 1. element-ui popover.js
1103
+ * if (html.parentNode === document) ...
1104
+ */
1105
+ function throttleDeferForParentNode(proxyDocument) {
1106
+ const html = globalEnv.rawDocument.firstElementChild;
1107
+ if (html && html.parentNode !== proxyDocument) {
1108
+ setRootParentNode(html, proxyDocument);
1109
+ defer(() => {
1110
+ setRootParentNode(html, globalEnv.rawDocument);
1111
+ });
1112
+ }
1113
+ }
1114
+ function setRootParentNode(root, value) {
1115
+ Object.defineProperty(root, 'parentNode', {
1116
+ value,
1117
+ configurable: true,
1118
+ });
1119
+ }
1074
1120
 
1075
1121
  // Record element and map element
1076
1122
  const dynamicElementInMicroAppMap = new WeakMap();
@@ -1208,20 +1254,35 @@ function getMappingNode(node) {
1208
1254
  * @param rawMethod method
1209
1255
  */
1210
1256
  function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
1211
- if (newChild === null || newChild === void 0 ? void 0 : newChild.__MICRO_APP_NAME__) {
1257
+ const currentAppName = getCurrentAppName();
1258
+ if (newChild instanceof Node &&
1259
+ (newChild.__MICRO_APP_NAME__ ||
1260
+ (currentAppName && !newChild.__PURE_ELEMENT__))) {
1261
+ newChild.__MICRO_APP_NAME__ = newChild.__MICRO_APP_NAME__ || currentAppName;
1212
1262
  const app = appInstanceMap.get(newChild.__MICRO_APP_NAME__);
1213
1263
  if (app === null || app === void 0 ? void 0 : app.container) {
1264
+ if (newChild instanceof Element) {
1265
+ if (/^(img|script)$/i.test(newChild.tagName)) {
1266
+ if (newChild.hasAttribute('src')) {
1267
+ globalEnv.rawSetAttribute.call(newChild, 'src', CompletionPath(newChild.getAttribute('src'), app.url));
1268
+ }
1269
+ if (newChild.hasAttribute('srcset')) {
1270
+ globalEnv.rawSetAttribute.call(newChild, 'srcset', CompletionPath(newChild.getAttribute('srcset'), app.url));
1271
+ }
1272
+ }
1273
+ else if (/^link$/i.test(newChild.tagName) && newChild.hasAttribute('href')) {
1274
+ globalEnv.rawSetAttribute.call(newChild, 'href', CompletionPath(newChild.getAttribute('href'), app.url));
1275
+ }
1276
+ }
1214
1277
  return invokePrototypeMethod(app, rawMethod, parent, handleNewNode(parent, newChild, app), passiveChild && getMappingNode(passiveChild));
1215
1278
  }
1216
1279
  else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
1217
1280
  return rawMethod.call(parent, newChild);
1218
1281
  }
1219
- return rawMethod.call(parent, newChild, passiveChild);
1220
1282
  }
1221
1283
  else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
1222
- const appName = getCurrentAppName();
1223
- if (!(newChild instanceof Node) && appName) {
1224
- const app = appInstanceMap.get(appName);
1284
+ if (!(newChild instanceof Node) && currentAppName) {
1285
+ const app = appInstanceMap.get(currentAppName);
1225
1286
  if (app === null || app === void 0 ? void 0 : app.container) {
1226
1287
  if (parent === document.head) {
1227
1288
  return rawMethod.call(app.container.querySelector('micro-app-head'), newChild);
@@ -1267,12 +1328,18 @@ function patchElementPrototypeMethods() {
1267
1328
  };
1268
1329
  // prototype methods of delete element👇
1269
1330
  Element.prototype.removeChild = function removeChild(oldChild) {
1331
+ var _a;
1270
1332
  if (oldChild === null || oldChild === void 0 ? void 0 : oldChild.__MICRO_APP_NAME__) {
1271
1333
  const app = appInstanceMap.get(oldChild.__MICRO_APP_NAME__);
1272
1334
  if (app === null || app === void 0 ? void 0 : app.container) {
1273
1335
  return invokePrototypeMethod(app, globalEnv.rawRemoveChild, this, getMappingNode(oldChild));
1274
1336
  }
1275
- return globalEnv.rawRemoveChild.call(this, oldChild);
1337
+ try {
1338
+ return globalEnv.rawRemoveChild.call(this, oldChild);
1339
+ }
1340
+ catch (_b) {
1341
+ return (_a = oldChild === null || oldChild === void 0 ? void 0 : oldChild.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(oldChild);
1342
+ }
1276
1343
  }
1277
1344
  return globalEnv.rawRemoveChild.call(this, oldChild);
1278
1345
  };
@@ -1288,9 +1355,9 @@ function patchElementPrototypeMethods() {
1288
1355
  * @param element new element
1289
1356
  */
1290
1357
  function markElement(element) {
1291
- const appName = getCurrentAppName();
1292
- if (appName)
1293
- element.__MICRO_APP_NAME__ = appName;
1358
+ const currentAppName = getCurrentAppName();
1359
+ if (currentAppName)
1360
+ element.__MICRO_APP_NAME__ = currentAppName;
1294
1361
  return element;
1295
1362
  }
1296
1363
  // methods of document
@@ -1312,28 +1379,28 @@ function patchDocument() {
1312
1379
  // query element👇
1313
1380
  function querySelector(selectors) {
1314
1381
  var _a, _b, _c, _d;
1315
- const appName = getCurrentAppName();
1316
- if (!appName ||
1317
- !((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) ||
1382
+ const currentAppName = getCurrentAppName();
1383
+ if (!currentAppName ||
1384
+ !((_a = appInstanceMap.get(currentAppName)) === null || _a === void 0 ? void 0 : _a.container) ||
1318
1385
  !selectors ||
1319
1386
  isUniqueElement(selectors) ||
1320
1387
  // see https://github.com/micro-zoe/micro-app/issues/56
1321
1388
  rawDocument !== this) {
1322
1389
  return globalEnv.rawQuerySelector.call(this, selectors);
1323
1390
  }
1324
- return (_d = (_c = (_b = appInstanceMap.get(appName)) === null || _b === void 0 ? void 0 : _b.container) === null || _c === void 0 ? void 0 : _c.querySelector(selectors)) !== null && _d !== void 0 ? _d : null;
1391
+ return (_d = (_c = (_b = appInstanceMap.get(currentAppName)) === null || _b === void 0 ? void 0 : _b.container) === null || _c === void 0 ? void 0 : _c.querySelector(selectors)) !== null && _d !== void 0 ? _d : null;
1325
1392
  }
1326
1393
  function querySelectorAll(selectors) {
1327
1394
  var _a, _b, _c, _d;
1328
- const appName = getCurrentAppName();
1329
- if (!appName ||
1330
- !((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) ||
1395
+ const currentAppName = getCurrentAppName();
1396
+ if (!currentAppName ||
1397
+ !((_a = appInstanceMap.get(currentAppName)) === null || _a === void 0 ? void 0 : _a.container) ||
1331
1398
  !selectors ||
1332
1399
  isUniqueElement(selectors) ||
1333
1400
  rawDocument !== this) {
1334
1401
  return globalEnv.rawQuerySelectorAll.call(this, selectors);
1335
1402
  }
1336
- return (_d = (_c = (_b = appInstanceMap.get(appName)) === null || _b === void 0 ? void 0 : _b.container) === null || _c === void 0 ? void 0 : _c.querySelectorAll(selectors)) !== null && _d !== void 0 ? _d : [];
1403
+ return (_d = (_c = (_b = appInstanceMap.get(currentAppName)) === null || _b === void 0 ? void 0 : _b.container) === null || _c === void 0 ? void 0 : _c.querySelectorAll(selectors)) !== null && _d !== void 0 ? _d : [];
1337
1404
  }
1338
1405
  Document.prototype.querySelector = querySelector;
1339
1406
  Document.prototype.querySelectorAll = querySelectorAll;
@@ -1361,11 +1428,11 @@ function patchDocument() {
1361
1428
  };
1362
1429
  Document.prototype.getElementsByTagName = function getElementsByTagName(key) {
1363
1430
  var _a;
1364
- const appName = getCurrentAppName();
1365
- if (!appName ||
1431
+ const currentAppName = getCurrentAppName();
1432
+ if (!currentAppName ||
1366
1433
  isUniqueElement(key) ||
1367
1434
  isInvalidQuerySelectorKey(key) ||
1368
- (!((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.inline) && /^script$/i.test(key))) {
1435
+ (!((_a = appInstanceMap.get(currentAppName)) === null || _a === void 0 ? void 0 : _a.inline) && /^script$/i.test(key))) {
1369
1436
  return globalEnv.rawGetElementsByTagName.call(this, key);
1370
1437
  }
1371
1438
  try {
@@ -1401,9 +1468,9 @@ function patchSetAttribute() {
1401
1468
  if (/^micro-app(-\S+)?/i.test(this.tagName) && key === 'data') {
1402
1469
  if (isPlainObject(value)) {
1403
1470
  const cloneValue = {};
1404
- Object.getOwnPropertyNames(value).forEach((key) => {
1405
- if (!(isString(key) && key.indexOf('__') === 0)) {
1406
- cloneValue[key] = value[key];
1471
+ Object.getOwnPropertyNames(value).forEach((ownKey) => {
1472
+ if (!(isString(ownKey) && ownKey.indexOf('__') === 0)) {
1473
+ cloneValue[ownKey] = value[ownKey];
1407
1474
  }
1408
1475
  });
1409
1476
  this.data = cloneValue;
@@ -1412,14 +1479,15 @@ function patchSetAttribute() {
1412
1479
  logWarn('property data must be an object', this.getAttribute('name'));
1413
1480
  }
1414
1481
  }
1415
- else if ((((key === 'src' || key === 'srcset') && /^(img|script)$/i.test(this.tagName)) ||
1416
- (key === 'href' && /^link$/i.test(this.tagName))) &&
1417
- this.__MICRO_APP_NAME__ &&
1418
- appInstanceMap.has(this.__MICRO_APP_NAME__)) {
1419
- const app = appInstanceMap.get(this.__MICRO_APP_NAME__);
1420
- globalEnv.rawSetAttribute.call(this, key, CompletionPath(value, app.url));
1421
- }
1422
1482
  else {
1483
+ const appName = this.__MICRO_APP_NAME__ || getCurrentAppName();
1484
+ if (appName &&
1485
+ appInstanceMap.has(appName) &&
1486
+ (((key === 'src' || key === 'srcset') && /^(img|script)$/i.test(this.tagName)) ||
1487
+ (key === 'href' && /^link$/i.test(this.tagName)))) {
1488
+ const app = appInstanceMap.get(appName);
1489
+ value = CompletionPath(value, app.url);
1490
+ }
1423
1491
  globalEnv.rawSetAttribute.call(this, key, value);
1424
1492
  }
1425
1493
  };
@@ -2286,7 +2354,7 @@ function releaseUnmountOfNestedApp() {
2286
2354
  }
2287
2355
  // if micro-app run in micro application, delete all next generation application when unmount event received
2288
2356
  // unmount event will auto release by sandbox
2289
- function listenUmountOfNestedApp() {
2357
+ function initEnvOfNestedApp() {
2290
2358
  if (window.__MICRO_APP_ENVIRONMENT__) {
2291
2359
  releaseUnmountOfNestedApp();
2292
2360
  window.addEventListener('unmount', unmountNestedApp, false);
@@ -2299,35 +2367,30 @@ function isBoundedFunction(value) {
2299
2367
  return value.__MICRO_APP_IS_BOUND_FUNCTION__;
2300
2368
  return value.__MICRO_APP_IS_BOUND_FUNCTION__ = isBoundFunction(value);
2301
2369
  }
2302
- function isConstructor(value) {
2303
- var _a;
2370
+ function isConstructorFunction(value) {
2304
2371
  if (isBoolean(value.__MICRO_APP_IS_CONSTRUCTOR__))
2305
2372
  return value.__MICRO_APP_IS_CONSTRUCTOR__;
2306
- const valueStr = value.toString();
2307
- const result = (((_a = value.prototype) === null || _a === void 0 ? void 0 : _a.constructor) === value &&
2308
- Object.getOwnPropertyNames(value.prototype).length > 1) ||
2309
- /^function\s+[A-Z]/.test(valueStr) ||
2310
- /^class\s+/.test(valueStr);
2311
- return value.__MICRO_APP_IS_CONSTRUCTOR__ = result;
2373
+ return value.__MICRO_APP_IS_CONSTRUCTOR__ = isConstructor(value);
2312
2374
  }
2313
2375
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
2314
- function bindFunctionToRawWindow(rawWindow, value) {
2315
- if (value.__MICRO_APP_BOUND_WINDOW_FUNCTION__)
2316
- return value.__MICRO_APP_BOUND_WINDOW_FUNCTION__;
2317
- if (!isConstructor(value) && !isBoundedFunction(value)) {
2318
- const bindRawWindowValue = value.bind(rawWindow);
2376
+ function bindFunctionToRawObject(rawObject, value, key = 'WINDOW') {
2377
+ const cacheKey = `__MICRO_APP_BOUND_${key}_FUNCTION__`;
2378
+ if (value[cacheKey])
2379
+ return value[cacheKey];
2380
+ if (!isConstructorFunction(value) && !isBoundedFunction(value)) {
2381
+ const bindRawObjectValue = value.bind(rawObject);
2319
2382
  for (const key in value) {
2320
- bindRawWindowValue[key] = value[key];
2383
+ bindRawObjectValue[key] = value[key];
2321
2384
  }
2322
2385
  if (value.hasOwnProperty('prototype')) {
2323
- rawDefineProperty(bindRawWindowValue, 'prototype', {
2386
+ rawDefineProperty(bindRawObjectValue, 'prototype', {
2324
2387
  value: value.prototype,
2325
2388
  configurable: true,
2326
2389
  enumerable: false,
2327
2390
  writable: true,
2328
2391
  });
2329
2392
  }
2330
- return value.__MICRO_APP_BOUND_WINDOW_FUNCTION__ = bindRawWindowValue;
2393
+ return value[cacheKey] = bindRawObjectValue;
2331
2394
  }
2332
2395
  return value;
2333
2396
  }
@@ -2746,7 +2809,10 @@ function addHistoryListener(appName) {
2746
2809
  const rawWindow = globalEnv.rawWindow;
2747
2810
  // handle popstate event and distribute to child app
2748
2811
  const popStateHandler = (e) => {
2749
- // exclude hidden keep-alive app
2812
+ /**
2813
+ * 1. unmount app & hidden keep-alive app will not receive popstate event
2814
+ * 2. filter out onlyForBrowser
2815
+ */
2750
2816
  if (getActiveApps(true).includes(appName) && !e.onlyForBrowser) {
2751
2817
  const microPath = getMicroPathFromURL(appName);
2752
2818
  const app = appInstanceMap.get(appName);
@@ -2847,8 +2913,7 @@ function createMicroHistory(appName, microLocation) {
2847
2913
  const rawHistory = globalEnv.rawWindow.history;
2848
2914
  function getMicroHistoryMethod(methodName) {
2849
2915
  return function (...rests) {
2850
- if ((methodName === 'pushState' || methodName === 'replaceState') &&
2851
- (isString(rests[2]) || isURL(rests[2]))) {
2916
+ if (isString(rests[2]) || isURL(rests[2])) {
2852
2917
  const targetLocation = createURL(rests[2], microLocation.href);
2853
2918
  if (targetLocation.origin === microLocation.origin) {
2854
2919
  navigateWithNativeEvent(methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rawHistory.state, rests[0]), rests[1]);
@@ -2866,18 +2931,21 @@ function createMicroHistory(appName, microLocation) {
2866
2931
  }
2867
2932
  };
2868
2933
  }
2934
+ const pushState = getMicroHistoryMethod('pushState');
2935
+ const replaceState = getMicroHistoryMethod('replaceState');
2869
2936
  return new Proxy(rawHistory, {
2870
2937
  get(target, key) {
2871
2938
  if (key === 'state') {
2872
2939
  return getMicroState(appName, rawHistory.state);
2873
2940
  }
2874
- else if (isFunction(Reflect.get(target, key))) {
2875
- return getMicroHistoryMethod(key);
2941
+ else if (key === 'pushState') {
2942
+ return pushState;
2876
2943
  }
2877
- return Reflect.get(target, key);
2878
- },
2879
- set(target, key, value) {
2880
- return Reflect.set(target, key, value);
2944
+ else if (key === 'replaceState') {
2945
+ return replaceState;
2946
+ }
2947
+ const rawValue = Reflect.get(target, key);
2948
+ return isFunction(rawValue) ? bindFunctionToRawObject(target, rawValue, 'HISTORY') : rawValue;
2881
2949
  }
2882
2950
  });
2883
2951
  }
@@ -2894,7 +2962,7 @@ function nativeHistoryNavigate(methodName, fullPath, state = null, title = '') {
2894
2962
  /**
2895
2963
  * Navigate to new path, and dispatch native popStateEvent/hashChangeEvent to browser
2896
2964
  * Use scenes:
2897
- * 1. mount/unmount through updateBrowserURL with limited popstateEvent
2965
+ * 1. mount/unmount through attachRouteToBrowserURL with limited popstateEvent
2898
2966
  * 2. proxyHistory.pushState/replaceState with limited popstateEvent
2899
2967
  * 3. api microApp.router.push/replace
2900
2968
  * 4. proxyLocation.hash = xxx
@@ -2914,11 +2982,12 @@ function navigateWithNativeEvent(methodName, result, onlyForBrowser, state, titl
2914
2982
  dispatchNativeEvent(onlyForBrowser, oldHref);
2915
2983
  }
2916
2984
  /**
2917
- * update browser url when mount/unmount/hidden/show
2985
+ * update browser url when mount/unmount/hidden/show/attachToURL/attachAllToURL
2986
+ * just attach microRoute info to browser, dispatch event to base app(exclude child)
2918
2987
  * @param result result of add/remove microApp path on browser url
2919
2988
  * @param state history.state
2920
2989
  */
2921
- function updateBrowserURL(result, state) {
2990
+ function attachRouteToBrowserURL(result, state) {
2922
2991
  navigateWithNativeEvent('replaceState', result, true, state);
2923
2992
  }
2924
2993
  /**
@@ -2984,11 +3053,11 @@ function createRouterApi() {
2984
3053
  function createNavigationMethod(replace) {
2985
3054
  return function (to) {
2986
3055
  const appName = formatAppName(to.name);
2987
- // console.log(3333333, appInstanceMap.get(appName))
2988
3056
  if (appName && isString(to.path)) {
2989
3057
  const app = appInstanceMap.get(appName);
2990
- if (app && !app.sandBox)
2991
- return logError(`navigation failed, sandBox of app ${appName} is closed`);
3058
+ if (app && (!app.sandBox || !app.useMemoryRouter)) {
3059
+ return logError(`navigation failed, memory router of app ${appName} is closed`);
3060
+ }
2992
3061
  // active apps, include hidden keep-alive app
2993
3062
  if (getActiveApps().includes(appName)) {
2994
3063
  const microLocation = app.sandBox.proxyWindow.location;
@@ -3065,43 +3134,95 @@ function createRouterApi() {
3065
3134
  function clearRouterWhenUnmount(appName) {
3066
3135
  router.current.delete(appName);
3067
3136
  }
3068
- // defaultPage data
3069
- const defaultPageRecord = useMapRecord();
3070
3137
  /**
3071
- * defaultPage只在子应用初始化时生效,且优先级比浏览器上的子应用路由地址低
3138
+ * NOTE:
3139
+ * 1. sandbox not open
3140
+ * 2. useMemoryRouter is false
3141
+ */
3142
+ function commonHandlerForAttachToURL(appName) {
3143
+ const app = appInstanceMap.get(appName);
3144
+ if (app.sandBox && app.useMemoryRouter) {
3145
+ attachRouteToBrowserURL(setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, globalEnv.rawWindow.history.state, null));
3146
+ }
3147
+ }
3148
+ /**
3149
+ * Attach specified active app router info to browser url
3072
3150
  * @param appName app name
3073
- * @param path page path
3074
3151
  */
3075
- function setDefaultPage(appName, path) {
3152
+ function attachToURL(appName) {
3076
3153
  appName = formatAppName(appName);
3077
- if (!appName)
3078
- return noopFalse;
3079
- return defaultPageRecord.add(appName, path);
3154
+ if (appName && getActiveApps().includes(appName)) {
3155
+ commonHandlerForAttachToURL(appName);
3156
+ }
3080
3157
  }
3081
- function removeDefaultPage(appName) {
3082
- appName = formatAppName(appName);
3083
- if (!appName)
3084
- return false;
3085
- return defaultPageRecord.delete(appName);
3158
+ /**
3159
+ * Attach all active app router info to browser url
3160
+ */
3161
+ function attachAllToURL() {
3162
+ getActiveApps().forEach(appName => commonHandlerForAttachToURL(appName));
3163
+ }
3164
+ function createDefaultPageApi() {
3165
+ // defaultPage data
3166
+ const defaultPageRecord = useMapRecord();
3167
+ /**
3168
+ * defaultPage only effect when mount, and has lower priority than query on browser url
3169
+ * @param appName app name
3170
+ * @param path page path
3171
+ */
3172
+ function setDefaultPage(appName, path) {
3173
+ appName = formatAppName(appName);
3174
+ if (!appName || !path) {
3175
+ if (process.env.NODE_ENV !== 'production') {
3176
+ if (!appName) {
3177
+ logWarn(`setDefaultPage: invalid appName "${appName}"`);
3178
+ }
3179
+ else {
3180
+ logWarn('setDefaultPage: path is required');
3181
+ }
3182
+ }
3183
+ return noopFalse;
3184
+ }
3185
+ return defaultPageRecord.add(appName, path);
3186
+ }
3187
+ function removeDefaultPage(appName) {
3188
+ appName = formatAppName(appName);
3189
+ if (!appName)
3190
+ return false;
3191
+ return defaultPageRecord.delete(appName);
3192
+ }
3193
+ return {
3194
+ setDefaultPage,
3195
+ removeDefaultPage,
3196
+ getDefaultPage: defaultPageRecord.get,
3197
+ };
3198
+ }
3199
+ function createBaseRouterApi() {
3200
+ /**
3201
+ * Record base app router, let child app control base app navigation
3202
+ */
3203
+ let baseRouterProxy = null;
3204
+ function setBaseAppRouter(baseRouter) {
3205
+ if (isObject(baseRouter)) {
3206
+ baseRouterProxy = new Proxy(baseRouter, {
3207
+ get(target, key) {
3208
+ removeDomScope();
3209
+ const rawValue = Reflect.get(target, key);
3210
+ return isFunction(rawValue) ? bindFunctionToRawObject(target, rawValue, 'BASEROUTER') : rawValue;
3211
+ }
3212
+ });
3213
+ }
3214
+ else if (process.env.NODE_ENV !== 'production') {
3215
+ logWarn('setBaseAppRouter: Invalid base router');
3216
+ }
3217
+ }
3218
+ return {
3219
+ setBaseAppRouter,
3220
+ getBaseAppRouter: () => baseRouterProxy,
3221
+ };
3086
3222
  }
3087
3223
  // Router API for developer
3088
- const router = {
3089
- current: new Map(),
3090
- encode: encodeMicroPath,
3091
- decode: decodeMicroPath,
3092
- push: createNavigationMethod(false),
3093
- replace: createNavigationMethod(true),
3094
- go: createRawHistoryMethod('go'),
3095
- back: createRawHistoryMethod('back'),
3096
- forward: createRawHistoryMethod('forward'),
3097
- beforeEach: beforeGuards.add,
3098
- afterEach: afterGuards.add,
3099
- // attachToURL: 将指定的子应用路由信息添加到浏览器地址上
3100
- // attachAllToURL: 将所有正在运行的子应用路由信息添加到浏览器地址上
3101
- setDefaultPage,
3102
- removeDefaultPage,
3103
- getDefaultPage: defaultPageRecord.get,
3104
- };
3224
+ const router = Object.assign(Object.assign({ current: new Map(), encode: encodeMicroPath, decode: decodeMicroPath, push: createNavigationMethod(false), replace: createNavigationMethod(true), go: createRawHistoryMethod('go'), back: createRawHistoryMethod('back'), forward: createRawHistoryMethod('forward'), beforeEach: beforeGuards.add, afterEach: afterGuards.add, attachToURL,
3225
+ attachAllToURL }, createDefaultPageApi()), createBaseRouterApi());
3105
3226
  return {
3106
3227
  router,
3107
3228
  executeNavigationGuard,
@@ -3115,56 +3236,6 @@ const shadowLocationKeys = ['href', 'pathname', 'search', 'hash'];
3115
3236
  const locationKeys = [...shadowLocationKeys, 'host', 'hostname', 'port', 'protocol', 'search'];
3116
3237
  // origin, fullPath is necessary for guardLocation
3117
3238
  const guardLocationKeys = [...locationKeys, 'origin', 'fullPath'];
3118
- /**
3119
- * create guardLocation by microLocation, used for router guard
3120
- */
3121
- function createGuardLocation(appName, microLocation) {
3122
- const guardLocation = assign({ name: appName }, microLocation);
3123
- // The prototype values on the URL needs to be manually transferred
3124
- for (const key of guardLocationKeys)
3125
- guardLocation[key] = microLocation[key];
3126
- return guardLocation;
3127
- }
3128
- // for updateBrowserURLWithLocation when initial
3129
- function autoTriggerNavigationGuard(appName, microLocation) {
3130
- executeNavigationGuard(appName, createGuardLocation(appName, microLocation), createGuardLocation(appName, microLocation));
3131
- }
3132
- /**
3133
- * The following scenes will trigger location update:
3134
- * 1. pushState/replaceState
3135
- * 2. popStateEvent
3136
- * 3. query on browser url when init sub app
3137
- * 4. set defaultPage when when init sub app
3138
- * NOTE:
3139
- * 1. update browser URL first, and then update microLocation
3140
- * 2. the same fullPath will not trigger router guards
3141
- * @param appName app name
3142
- * @param path target path
3143
- * @param base base url
3144
- * @param microLocation micro app location
3145
- * @param type auto prevent
3146
- */
3147
- function updateMicroLocation(appName, path, microLocation, type) {
3148
- const newLocation = createURL(path, microLocation.href);
3149
- // record old values of microLocation to `from`
3150
- const from = createGuardLocation(appName, microLocation);
3151
- for (const key of locationKeys) {
3152
- if (shadowLocationKeys.includes(key)) {
3153
- // reference of shadowLocation
3154
- microLocation.shadowLocation[key] = newLocation[key];
3155
- }
3156
- else {
3157
- // @ts-ignore reference of microLocation
3158
- microLocation[key] = newLocation[key];
3159
- }
3160
- }
3161
- // update latest values of microLocation to `to`
3162
- const to = createGuardLocation(appName, microLocation);
3163
- // The hook called only when fullPath changed
3164
- if (type === 'auto' || (from.fullPath !== to.fullPath && type !== 'prevent')) {
3165
- executeNavigationGuard(appName, to, from);
3166
- }
3167
- }
3168
3239
  /**
3169
3240
  * Create location for microApp, each microApp has only one location object, it is a reference type
3170
3241
  * MDN https://developer.mozilla.org/en-US/docs/Web/API/Location
@@ -3309,6 +3380,56 @@ function createMicroLocation(appName, url) {
3309
3380
  shadowLocation,
3310
3381
  });
3311
3382
  }
3383
+ /**
3384
+ * create guardLocation by microLocation, used for router guard
3385
+ */
3386
+ function createGuardLocation(appName, microLocation) {
3387
+ const guardLocation = assign({ name: appName }, microLocation);
3388
+ // The prototype values on the URL needs to be manually transferred
3389
+ for (const key of guardLocationKeys)
3390
+ guardLocation[key] = microLocation[key];
3391
+ return guardLocation;
3392
+ }
3393
+ // for updateBrowserURLWithLocation when initial
3394
+ function autoTriggerNavigationGuard(appName, microLocation) {
3395
+ executeNavigationGuard(appName, createGuardLocation(appName, microLocation), createGuardLocation(appName, microLocation));
3396
+ }
3397
+ /**
3398
+ * The following scenes will trigger location update:
3399
+ * 1. pushState/replaceState
3400
+ * 2. popStateEvent
3401
+ * 3. query on browser url when init sub app
3402
+ * 4. set defaultPage when when init sub app
3403
+ * NOTE:
3404
+ * 1. update browser URL first, and then update microLocation
3405
+ * 2. the same fullPath will not trigger router guards
3406
+ * @param appName app name
3407
+ * @param path target path
3408
+ * @param base base url
3409
+ * @param microLocation micro app location
3410
+ * @param type auto prevent
3411
+ */
3412
+ function updateMicroLocation(appName, path, microLocation, type) {
3413
+ const newLocation = createURL(path, microLocation.href);
3414
+ // record old values of microLocation to `from`
3415
+ const from = createGuardLocation(appName, microLocation);
3416
+ for (const key of locationKeys) {
3417
+ if (shadowLocationKeys.includes(key)) {
3418
+ // reference of shadowLocation
3419
+ microLocation.shadowLocation[key] = newLocation[key];
3420
+ }
3421
+ else {
3422
+ // @ts-ignore reference of microLocation
3423
+ microLocation[key] = newLocation[key];
3424
+ }
3425
+ }
3426
+ // update latest values of microLocation to `to`
3427
+ const to = createGuardLocation(appName, microLocation);
3428
+ // The hook called only when fullPath changed
3429
+ if (type === 'auto' || (from.fullPath !== to.fullPath && type !== 'prevent')) {
3430
+ executeNavigationGuard(appName, to, from);
3431
+ }
3432
+ }
3312
3433
 
3313
3434
  /**
3314
3435
  * The router system has two operations: read and write
@@ -3344,7 +3465,7 @@ function updateBrowserURLWithLocation(appName, microLocation, defaultPage) {
3344
3465
  if (defaultPage)
3345
3466
  updateMicroLocation(appName, defaultPage, microLocation, 'prevent');
3346
3467
  // attach microApp route info to browser URL
3347
- updateBrowserURL(setMicroPathToURL(appName, microLocation), setMicroState(appName, globalEnv.rawWindow.history.state, null));
3468
+ attachRouteToBrowserURL(setMicroPathToURL(appName, microLocation), setMicroState(appName, globalEnv.rawWindow.history.state, null));
3348
3469
  // trigger guards after change browser URL
3349
3470
  autoTriggerNavigationGuard(appName, microLocation);
3350
3471
  }
@@ -3368,7 +3489,7 @@ function clearRouteStateFromURL(appName, url, microLocation, keepRouteState) {
3368
3489
  * called on sandbox.stop or hidden of keep-alive app
3369
3490
  */
3370
3491
  function removeStateAndPathFromBrowser(appName) {
3371
- updateBrowserURL(removeMicroPathFromURL(appName), removeMicroState(appName, globalEnv.rawWindow.history.state));
3492
+ attachRouteToBrowserURL(removeMicroPathFromURL(appName), removeMicroState(appName, globalEnv.rawWindow.history.state));
3372
3493
  }
3373
3494
 
3374
3495
  /**
@@ -3380,13 +3501,21 @@ function removeStateAndPathFromBrowser(appName) {
3380
3501
  * @param target proxy target
3381
3502
  */
3382
3503
  function createMicroFetch(url, target) {
3383
- if (!isUndefined(target) && !isFunction(target))
3384
- return target;
3385
- const rawFetch = target || globalEnv.rawWindow.fetch;
3504
+ const rawFetch = !isUndefined(target) ? target : globalEnv.rawWindow.fetch;
3505
+ if (!isFunction(rawFetch))
3506
+ return rawFetch;
3386
3507
  return function microFetch(input, init, ...rests) {
3387
3508
  if (isString(input) || isURL(input)) {
3388
3509
  input = createURL(input, url).toString();
3389
3510
  }
3511
+ /**
3512
+ * When fetch rewrite by baseApp, domScope still active when exec rawWindow.fetch
3513
+ * If baseApp operate dom in fetch, it will cause error
3514
+ * The same for XMLHttpRequest, EventSource
3515
+ * e.g.
3516
+ * baseApp: <script crossorigin src="https://sgm-static.jd.com/sgm-2.8.0.js" name="SGMH5" sid="6f88a6e4ba4b4ae5acef2ec22c075085" appKey="jdb-adminb2b-pc"></script>
3517
+ */
3518
+ removeDomScope();
3390
3519
  return rawFetch.call(globalEnv.rawWindow, input, init, ...rests);
3391
3520
  };
3392
3521
  }
@@ -3397,14 +3526,15 @@ function createMicroFetch(url, target) {
3397
3526
  * @param target proxy target
3398
3527
  */
3399
3528
  function createMicroXMLHttpRequest(url, target) {
3400
- if (!isUndefined(target) && !isFunction(target))
3401
- return target;
3402
- const rawXMLHttpRequest = target || globalEnv.rawWindow.XMLHttpRequest;
3529
+ const rawXMLHttpRequest = !isUndefined(target) ? target : globalEnv.rawWindow.XMLHttpRequest;
3530
+ if (!isConstructor(rawXMLHttpRequest))
3531
+ return rawXMLHttpRequest;
3403
3532
  return class MicroXMLHttpRequest extends rawXMLHttpRequest {
3404
3533
  open(method, reqUrl, ...rests) {
3405
3534
  if ((isString(reqUrl) && !/^f(ile|tp):\/\//.test(reqUrl)) || isURL(reqUrl)) {
3406
3535
  reqUrl = createURL(reqUrl, url).toString();
3407
3536
  }
3537
+ removeDomScope();
3408
3538
  super.open(method, reqUrl, ...rests);
3409
3539
  }
3410
3540
  };
@@ -3420,14 +3550,15 @@ function useMicroEventSource() {
3420
3550
  * @param target proxy target
3421
3551
  */
3422
3552
  function createMicroEventSource(appName, url, target) {
3423
- if (!isUndefined(target) && !isFunction(target))
3424
- return target;
3425
- const rawEventSource = target || globalEnv.rawWindow.EventSource;
3553
+ const rawEventSource = !isUndefined(target) ? target : globalEnv.rawWindow.EventSource;
3554
+ if (!isConstructor(rawEventSource))
3555
+ return rawEventSource;
3426
3556
  return class MicroEventSource extends rawEventSource {
3427
3557
  constructor(eventSourceUrl, eventSourceInitDict, ...rests) {
3428
3558
  if (isString(eventSourceUrl) || isURL(eventSourceUrl)) {
3429
3559
  eventSourceUrl = createURL(eventSourceUrl, url).toString();
3430
3560
  }
3561
+ removeDomScope();
3431
3562
  super(eventSourceUrl, eventSourceInitDict, ...rests);
3432
3563
  if (eventSourceMap) {
3433
3564
  const eventSourceList = eventSourceMap.get(appName);
@@ -3508,7 +3639,7 @@ class SandBox {
3508
3639
  if (++SandBox.activeCount === 1) {
3509
3640
  effectDocumentEvent();
3510
3641
  patchElementPrototypeMethods();
3511
- listenUmountOfNestedApp();
3642
+ initEnvOfNestedApp();
3512
3643
  }
3513
3644
  fixBabelPolyfill6();
3514
3645
  }
@@ -3604,7 +3735,7 @@ class SandBox {
3604
3735
  this.scopeProperties.includes(key))
3605
3736
  return Reflect.get(target, key);
3606
3737
  const rawValue = Reflect.get(rawWindow, key);
3607
- return isFunction(rawValue) ? bindFunctionToRawWindow(rawWindow, rawValue) : rawValue;
3738
+ return isFunction(rawValue) ? bindFunctionToRawObject(rawWindow, rawValue) : rawValue;
3608
3739
  },
3609
3740
  set: (target, key, value) => {
3610
3741
  if (this.active) {
@@ -3704,20 +3835,22 @@ class SandBox {
3704
3835
  pureCreateElement,
3705
3836
  router,
3706
3837
  });
3838
+ this.setProxyDocument(microAppWindow, appName);
3707
3839
  this.setMappingPropertiesWithRawDescriptor(microAppWindow);
3708
3840
  if (useMemoryRouter)
3709
3841
  this.setMicroAppRouter(microAppWindow, appName, url);
3710
3842
  }
3711
- /**
3712
- * init global properties of microAppWindow when exec sandBox.start
3713
- * @param microAppWindow micro window
3714
- * @param appName app name
3715
- * @param url app url
3716
- */
3717
- initGlobalKeysWhenStart(microAppWindow, appName, url) {
3718
- microAppWindow.hasOwnProperty = (key) => rawHasOwnProperty.call(microAppWindow, key) || rawHasOwnProperty.call(globalEnv.rawWindow, key);
3719
- this.setHijackProperties(microAppWindow, appName);
3720
- this.patchHijackRequest(microAppWindow, appName, url);
3843
+ setProxyDocument(microAppWindow, appName) {
3844
+ const proxyDocument = this.createProxyDocument(appName);
3845
+ rawDefineProperty(microAppWindow, 'document', {
3846
+ configurable: false,
3847
+ enumerable: true,
3848
+ get() {
3849
+ throttleDeferForSetAppName(appName);
3850
+ // return globalEnv.rawDocument
3851
+ return proxyDocument;
3852
+ },
3853
+ });
3721
3854
  }
3722
3855
  // properties associated with the native window
3723
3856
  setMappingPropertiesWithRawDescriptor(microAppWindow) {
@@ -3746,18 +3879,21 @@ class SandBox {
3746
3879
  };
3747
3880
  return descriptor;
3748
3881
  }
3882
+ /**
3883
+ * init global properties of microAppWindow when exec sandBox.start
3884
+ * @param microAppWindow micro window
3885
+ * @param appName app name
3886
+ * @param url app url
3887
+ */
3888
+ initGlobalKeysWhenStart(microAppWindow, appName, url) {
3889
+ microAppWindow.hasOwnProperty = (key) => rawHasOwnProperty.call(microAppWindow, key) || rawHasOwnProperty.call(globalEnv.rawWindow, key);
3890
+ this.setHijackProperty(microAppWindow, appName);
3891
+ this.patchRequestApi(microAppWindow, appName, url);
3892
+ }
3749
3893
  // set hijack Properties to microAppWindow
3750
- setHijackProperties(microAppWindow, appName) {
3894
+ setHijackProperty(microAppWindow, appName) {
3751
3895
  let modifiedEval, modifiedImage;
3752
3896
  rawDefineProperties(microAppWindow, {
3753
- document: {
3754
- configurable: true,
3755
- enumerable: true,
3756
- get() {
3757
- throttleDeferForSetAppName(appName);
3758
- return globalEnv.rawDocument;
3759
- },
3760
- },
3761
3897
  eval: {
3762
3898
  configurable: true,
3763
3899
  enumerable: false,
@@ -3783,7 +3919,7 @@ class SandBox {
3783
3919
  });
3784
3920
  }
3785
3921
  // rewrite fetch, XMLHttpRequest, EventSource
3786
- patchHijackRequest(microAppWindow, appName, url) {
3922
+ patchRequestApi(microAppWindow, appName, url) {
3787
3923
  let microFetch = createMicroFetch(url);
3788
3924
  let microXMLHttpRequest = createMicroXMLHttpRequest(url);
3789
3925
  let microEventSource = createMicroEventSource(appName, url);
@@ -3855,6 +3991,24 @@ class SandBox {
3855
3991
  removeRouteInfoForKeepAliveApp() {
3856
3992
  removeStateAndPathFromBrowser(this.proxyWindow.__MICRO_APP_NAME__);
3857
3993
  }
3994
+ createProxyDocument(appName) {
3995
+ const createElement = function (tagName, options) {
3996
+ const element = globalEnv.rawCreateElement.call(globalEnv.rawDocument, tagName, options);
3997
+ element.__MICRO_APP_NAME__ = appName;
3998
+ return element;
3999
+ };
4000
+ const proxyDocument = new Proxy(globalEnv.rawDocument, {
4001
+ get(target, key) {
4002
+ throttleDeferForSetAppName(appName);
4003
+ throttleDeferForParentNode(proxyDocument);
4004
+ if (key === 'createElement')
4005
+ return createElement;
4006
+ const rawValue = Reflect.get(target, key);
4007
+ return isFunction(rawValue) ? bindFunctionToRawObject(target, rawValue, 'DOCUMENT') : rawValue;
4008
+ },
4009
+ });
4010
+ return proxyDocument;
4011
+ }
3858
4012
  }
3859
4013
  SandBox.activeCount = 0; // number of active sandbox
3860
4014
 
@@ -4563,7 +4717,7 @@ function defineElement(tagName) {
4563
4717
  */
4564
4718
  updateSsrUrl(baseUrl) {
4565
4719
  if (this.getDisposeResult('ssr')) {
4566
- if (this.getDisposeResult('disable-memory-router')) {
4720
+ if (this.getDisposeResult('disable-memory-router') || this.getDisposeResult('disableSandbox')) {
4567
4721
  const rawLocation = globalEnv.rawWindow.location;
4568
4722
  this.ssrUrl = CompletionPath(rawLocation.pathname + rawLocation.search, baseUrl);
4569
4723
  }