@micro-zoe/micro-app 1.0.0-alpha.7 → 1.0.0-alpha.8

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.7';
1
+ const version = '1.0.0-alpha.8';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -33,6 +33,10 @@ function isString(target) {
33
33
  function isBoolean(target) {
34
34
  return typeof target === 'boolean';
35
35
  }
36
+ // is Number
37
+ function isNumber(target) {
38
+ return typeof target === 'number';
39
+ }
36
40
  // is function
37
41
  function isFunction(target) {
38
42
  return typeof target === 'function';
@@ -565,6 +569,7 @@ var MicroAppConfig;
565
569
  MicroAppConfig["SSR"] = "ssr";
566
570
  MicroAppConfig["FIBER"] = "fiber";
567
571
  })(MicroAppConfig || (MicroAppConfig = {}));
572
+ const PREFETCH_LEVEL = [1, 2, 3];
568
573
  /**
569
574
  * global key must be static key, they can not rewrite
570
575
  * e.g.
@@ -1365,10 +1370,9 @@ class Adapter {
1365
1370
  ];
1366
1371
  this.injectReactHRMProperty();
1367
1372
  }
1368
- // TODO: __DEV__ process.env.NODE_ENV !== 'production'
1369
1373
  // adapter for react
1370
1374
  injectReactHRMProperty() {
1371
- if (process.env.NODE_ENV !== 'production') {
1375
+ if ((process.env.NODE_ENV !== 'production')) {
1372
1376
  // react child in non-react env
1373
1377
  this.staticEscapeProperties.push('__REACT_ERROR_OVERLAY_GLOBAL_HOOK__');
1374
1378
  // in react parent
@@ -1392,7 +1396,7 @@ function fixBabelPolyfill6() {
1392
1396
  */
1393
1397
  function fixReactHMRConflict(app) {
1394
1398
  var _a;
1395
- if (process.env.NODE_ENV !== 'production') {
1399
+ if ((process.env.NODE_ENV !== 'production')) {
1396
1400
  const rawReactErrorHook = globalEnv.rawWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__;
1397
1401
  const childReactErrorHook = (_a = app.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow.__REACT_ERROR_OVERLAY_GLOBAL_HOOK__;
1398
1402
  if (rawReactErrorHook && childReactErrorHook) {
@@ -1525,11 +1529,11 @@ function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild
1525
1529
  configurable: true,
1526
1530
  get() {
1527
1531
  /**
1528
- * When operate child from parentNode async, Element.prototype may reset
1532
+ * When operate child from parentNode async, may have been unmount
1529
1533
  * e.g.
1530
1534
  * target.parentNode.remove(target)
1531
1535
  */
1532
- return Element.prototype.removeChild === globalEnv.rawRemoveChild ? hijackParent : document.body;
1536
+ return !app.container ? hijackParent : document.body;
1533
1537
  },
1534
1538
  });
1535
1539
  }
@@ -1547,8 +1551,7 @@ function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild
1547
1551
  }
1548
1552
  return targetChild;
1549
1553
  }
1550
- // TODO: __DEV__
1551
- if (process.env.NODE_ENV !== 'production' &&
1554
+ if ((process.env.NODE_ENV !== 'production') &&
1552
1555
  targetChild instanceof HTMLIFrameElement &&
1553
1556
  rawMethod === globalEnv.rawAppendChild) {
1554
1557
  fixReactHMRConflict(app);
@@ -1664,7 +1667,6 @@ function patchElementPrototypeMethods() {
1664
1667
  };
1665
1668
  // prototype methods of delete element👇
1666
1669
  Element.prototype.removeChild = function removeChild(oldChild) {
1667
- var _a;
1668
1670
  if (oldChild === null || oldChild === void 0 ? void 0 : oldChild.__MICRO_APP_NAME__) {
1669
1671
  const app = appInstanceMap.get(oldChild.__MICRO_APP_NAME__);
1670
1672
  if (app === null || app === void 0 ? void 0 : app.container) {
@@ -1673,8 +1675,8 @@ function patchElementPrototypeMethods() {
1673
1675
  try {
1674
1676
  return globalEnv.rawRemoveChild.call(this, oldChild);
1675
1677
  }
1676
- catch (_b) {
1677
- return (_a = oldChild === null || oldChild === void 0 ? void 0 : oldChild.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(oldChild);
1678
+ catch (_a) {
1679
+ return ((oldChild === null || oldChild === void 0 ? void 0 : oldChild.parentNode) && globalEnv.rawRemoveChild.call(oldChild.parentNode, oldChild));
1678
1680
  }
1679
1681
  }
1680
1682
  return globalEnv.rawRemoveChild.call(this, oldChild);
@@ -2213,7 +2215,7 @@ function fetchScriptsFromHtml(wrapElement, app) {
2213
2215
  for (const address of scriptList) {
2214
2216
  const scriptInfo = sourceCenter.script.getInfo(address);
2215
2217
  const appSpaceData = scriptInfo.appSpace[app.name];
2216
- if ((!appSpaceData.defer && !appSpaceData.async) || app.isPrefetch) {
2218
+ if ((!appSpaceData.defer && !appSpaceData.async) || (app.isPrefetch && !app.isPrerender)) {
2217
2219
  fetchScriptPromise.push(scriptInfo.code ? scriptInfo.code : fetchSource(address, app.name));
2218
2220
  fetchScriptPromiseInfo.push([address, scriptInfo]);
2219
2221
  }
@@ -2254,7 +2256,7 @@ function fetchScriptSuccess(address, scriptInfo, code, app) {
2254
2256
  * 2. if app is inline or script is esmodule, skip this step
2255
2257
  * 3. if global parseResult not exist, the current script occupies the position, when js is reused, parseResult is reference
2256
2258
  */
2257
- if (app.isPrefetch) {
2259
+ if (app.isPrefetch && app.prefetchLevel === 2) {
2258
2260
  const appSpaceData = scriptInfo.appSpace[app.name];
2259
2261
  /**
2260
2262
  * When prefetch app is replaced by a new app in the processing phase, since the scriptInfo is common, when the scriptInfo of the prefetch app is processed, it may have already been processed.
@@ -2639,17 +2641,19 @@ class EventCenter {
2639
2641
  const force = eventInfo.force;
2640
2642
  eventInfo.tempData = null;
2641
2643
  eventInfo.force = false;
2644
+ let resArr;
2642
2645
  if (force || !this.isEqual(eventInfo.data, tempData)) {
2643
2646
  eventInfo.data = tempData || eventInfo.data;
2644
2647
  for (const f of eventInfo.callbacks) {
2645
- f(eventInfo.data);
2648
+ const res = f(eventInfo.data);
2649
+ res && (resArr !== null && resArr !== void 0 ? resArr : (resArr = [])).push(res);
2646
2650
  }
2647
2651
  (_b = (_a = temRecordStep[name]).dispatchDataEvent) === null || _b === void 0 ? void 0 : _b.call(_a);
2648
2652
  /**
2649
2653
  * WARING:
2650
2654
  * If data of other app is sent in nextStep, it may cause confusion of tempData and force
2651
2655
  */
2652
- temRecordStep[name].nextStepList.forEach((nextStep) => nextStep());
2656
+ temRecordStep[name].nextStepList.forEach((nextStep) => nextStep(resArr));
2653
2657
  }
2654
2658
  }
2655
2659
  };
@@ -2824,7 +2828,7 @@ class EventCenterForGlobal {
2824
2828
  setGlobalData(data, nextStep, force) {
2825
2829
  // clear dom scope before dispatch global data, apply to micro app
2826
2830
  removeDomScope();
2827
- eventCenter.dispatch('global', data, () => isFunction(nextStep) && nextStep(), force);
2831
+ eventCenter.dispatch('global', data, (resArr) => isFunction(nextStep) && nextStep(resArr), force);
2828
2832
  }
2829
2833
  forceSetGlobalData(data, nextStep) {
2830
2834
  this.setGlobalData(data, nextStep, true);
@@ -2892,7 +2896,7 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
2892
2896
  * @param data data
2893
2897
  */
2894
2898
  setData(appName, data, nextStep, force) {
2895
- eventCenter.dispatch(formatEventName(formatAppName(appName), true), data, () => isFunction(nextStep) && nextStep(), force);
2899
+ eventCenter.dispatch(formatEventName(formatAppName(appName), true), data, (resArr) => isFunction(nextStep) && nextStep(resArr), force);
2896
2900
  }
2897
2901
  forceSetData(appName, data, nextStep) {
2898
2902
  this.setData(appName, data, nextStep, true);
@@ -2948,7 +2952,7 @@ class EventCenterForMicroApp extends EventCenterForGlobal {
2948
2952
  */
2949
2953
  dispatch(data, nextStep, force) {
2950
2954
  removeDomScope();
2951
- eventCenter.dispatch(formatEventName(this.appName, false), data, () => isFunction(nextStep) && nextStep(), force, () => {
2955
+ eventCenter.dispatch(formatEventName(this.appName, false), data, (resArr) => isFunction(nextStep) && nextStep(resArr), force, () => {
2952
2956
  const app = appInstanceMap.get(this.appName);
2953
2957
  if ((app === null || app === void 0 ? void 0 : app.container) && isPlainObject(data)) {
2954
2958
  const event = new CustomEvent('datachange', {
@@ -3158,12 +3162,14 @@ function effectDocumentEvent() {
3158
3162
  const { rawDocument, rawDocumentAddEventListener, rawDocumentRemoveEventListener, } = globalEnv;
3159
3163
  !hasRewriteDocumentOnClick && overwriteDocumentOnClick();
3160
3164
  document.addEventListener = function (type, listener, options) {
3161
- var _a;
3162
3165
  const appName = getCurrentAppName();
3163
3166
  /**
3164
3167
  * ignore bound function of document event in umd mode, used to solve problem of react global events
3168
+ * update in 2022-09-02:
3169
+ * boundFunction is no longer exclude, because events in UMD mode will not cleared from v1.0.0-alpha.4
3170
+ * if (appName && !(appInstanceMap.get(appName)?.umdMode && isBoundFunction(listener))) {
3165
3171
  */
3166
- if (appName && !(((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.umdMode) && isBoundFunction(listener))) {
3172
+ if (appName) {
3167
3173
  const appListenersMap = documentEventListenerMap.get(appName);
3168
3174
  if (appListenersMap) {
3169
3175
  const appListenerList = appListenersMap.get(type);
@@ -3182,9 +3188,13 @@ function effectDocumentEvent() {
3182
3188
  rawDocumentAddEventListener.call(rawDocument, type, listener, options);
3183
3189
  };
3184
3190
  document.removeEventListener = function (type, listener, options) {
3185
- var _a;
3186
3191
  const appName = getCurrentAppName();
3187
- if (appName && !(((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.umdMode) && isBoundFunction(listener))) {
3192
+ /**
3193
+ * update in 2022-09-02:
3194
+ * boundFunction is no longer exclude, because events in UMD mode will not cleared from v1.0.0-alpha.4
3195
+ * if (appName && !(appInstanceMap.get(appName)?.umdMode && isBoundFunction(listener))) {
3196
+ */
3197
+ if (appName) {
3188
3198
  const appListenersMap = documentEventListenerMap.get(appName);
3189
3199
  if (appListenersMap) {
3190
3200
  const appListenerList = appListenersMap.get(type);
@@ -3249,69 +3259,80 @@ function effect(appName, microAppWindow) {
3249
3259
  timeoutIdMap.delete(timeoutId);
3250
3260
  rawClearTimeout.call(rawWindow, timeoutId);
3251
3261
  };
3252
- const umdWindowListenerMap = new Map();
3253
- const umdDocumentListenerMap = new Map();
3254
- let umdIntervalIdMap = new Map();
3255
- let umdTimeoutIdMap = new Map();
3256
- let umdOnClickHandler;
3257
- const clearUmdSnapshotData = () => {
3258
- umdWindowListenerMap.clear();
3259
- umdIntervalIdMap.clear();
3260
- umdTimeoutIdMap.clear();
3261
- umdDocumentListenerMap.clear();
3262
- umdOnClickHandler = null;
3262
+ const sstWindowListenerMap = new Map();
3263
+ const sstDocumentListenerMap = new Map();
3264
+ let sstIntervalIdMap = new Map();
3265
+ let sstTimeoutIdMap = new Map();
3266
+ let sstOnClickHandler;
3267
+ const clearSnapshotData = () => {
3268
+ sstWindowListenerMap.clear();
3269
+ sstIntervalIdMap.clear();
3270
+ sstTimeoutIdMap.clear();
3271
+ sstDocumentListenerMap.clear();
3272
+ sstOnClickHandler = null;
3263
3273
  };
3264
- // record event and timer before exec umdMountHook
3265
- const recordUmdEffect = () => {
3274
+ /**
3275
+ * record event and timer
3276
+ * Scenes:
3277
+ * 1. exec umdMountHook in umd mode
3278
+ * 2. hidden keep-alive app
3279
+ * 3. after init prerender app
3280
+ */
3281
+ const recordEffect = () => {
3266
3282
  // record window event
3267
3283
  eventListenerMap.forEach((listenerList, type) => {
3268
3284
  if (listenerList.size) {
3269
- umdWindowListenerMap.set(type, new Set(listenerList));
3285
+ sstWindowListenerMap.set(type, new Set(listenerList));
3270
3286
  }
3271
3287
  });
3272
3288
  // record timers
3273
3289
  if (intervalIdMap.size) {
3274
- umdIntervalIdMap = new Map(intervalIdMap);
3290
+ sstIntervalIdMap = new Map(intervalIdMap);
3275
3291
  }
3276
3292
  if (timeoutIdMap.size) {
3277
- umdTimeoutIdMap = new Map(timeoutIdMap);
3293
+ sstTimeoutIdMap = new Map(timeoutIdMap);
3278
3294
  }
3279
3295
  // record onclick handler
3280
- umdOnClickHandler = documentClickListMap.get(appName);
3296
+ sstOnClickHandler = documentClickListMap.get(appName);
3281
3297
  // record document event
3282
3298
  const documentAppListenersMap = documentEventListenerMap.get(appName);
3283
3299
  if (documentAppListenersMap) {
3284
3300
  documentAppListenersMap.forEach((listenerList, type) => {
3285
3301
  if (listenerList.size) {
3286
- umdDocumentListenerMap.set(type, new Set(listenerList));
3302
+ sstDocumentListenerMap.set(type, new Set(listenerList));
3287
3303
  }
3288
3304
  });
3289
3305
  }
3290
3306
  };
3291
- // rebuild event and timer before remount umd app
3292
- const rebuildUmdEffect = () => {
3307
+ // rebuild event and timer before remount app
3308
+ const rebuildEffect = () => {
3293
3309
  // rebuild window event
3294
- umdWindowListenerMap.forEach((listenerList, type) => {
3310
+ sstWindowListenerMap.forEach((listenerList, type) => {
3295
3311
  for (const listener of listenerList) {
3296
3312
  microAppWindow.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
3297
3313
  }
3298
3314
  });
3299
3315
  // rebuild timer
3300
- umdIntervalIdMap.forEach((info) => {
3316
+ sstIntervalIdMap.forEach((info) => {
3301
3317
  microAppWindow.setInterval(info.handler, info.timeout, ...info.args);
3302
3318
  });
3303
- umdTimeoutIdMap.forEach((info) => {
3319
+ sstTimeoutIdMap.forEach((info) => {
3304
3320
  microAppWindow.setTimeout(info.handler, info.timeout, ...info.args);
3305
3321
  });
3306
3322
  // rebuild onclick event
3307
- umdOnClickHandler && documentClickListMap.set(appName, umdOnClickHandler);
3308
- // rebuild document event
3309
- umdDocumentListenerMap.forEach((listenerList, type) => {
3323
+ sstOnClickHandler && documentClickListMap.set(appName, sstOnClickHandler);
3324
+ /**
3325
+ * rebuild document event
3326
+ * WARNING!!: do not delete setCurrentAppName & removeDomScope
3327
+ */
3328
+ setCurrentAppName(appName);
3329
+ sstDocumentListenerMap.forEach((listenerList, type) => {
3310
3330
  for (const listener of listenerList) {
3311
3331
  document.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
3312
3332
  }
3313
3333
  });
3314
- clearUmdSnapshotData();
3334
+ removeDomScope();
3335
+ clearSnapshotData();
3315
3336
  };
3316
3337
  // release all event listener & interval & timeout when unmount app
3317
3338
  const releaseEffect = () => {
@@ -3351,8 +3372,8 @@ function effect(appName, microAppWindow) {
3351
3372
  }
3352
3373
  };
3353
3374
  return {
3354
- recordUmdEffect,
3355
- rebuildUmdEffect,
3375
+ recordEffect,
3376
+ rebuildEffect,
3356
3377
  releaseEffect,
3357
3378
  };
3358
3379
  }
@@ -3378,7 +3399,6 @@ function removeMicroState(appName, rawState) {
3378
3399
  delete rawState.microAppState;
3379
3400
  }
3380
3401
  }
3381
- // 生成新的state对象
3382
3402
  return assign({}, rawState);
3383
3403
  }
3384
3404
  // get micro app state form origin state
@@ -3390,12 +3410,15 @@ const ENC_AD_RE = /&/g; // %M1
3390
3410
  const ENC_EQ_RE = /=/g; // %M2
3391
3411
  const DEC_AD_RE = /%M1/g; // &
3392
3412
  const DEC_EQ_RE = /%M2/g; // =
3413
+ // encode path with special symbol
3393
3414
  function encodeMicroPath(path) {
3394
3415
  return encodeURIComponent(commonDecode(path).replace(ENC_AD_RE, '%M1').replace(ENC_EQ_RE, '%M2'));
3395
3416
  }
3417
+ // decode path
3396
3418
  function decodeMicroPath(path) {
3397
3419
  return commonDecode(path).replace(DEC_AD_RE, '&').replace(DEC_EQ_RE, '=');
3398
3420
  }
3421
+ // Recursively resolve address
3399
3422
  function commonDecode(path) {
3400
3423
  try {
3401
3424
  const decPath = decodeURIComponent(path);
@@ -3407,12 +3430,15 @@ function commonDecode(path) {
3407
3430
  return path;
3408
3431
  }
3409
3432
  }
3410
- // 格式化query参数key,防止与原有参数的冲突
3433
+ // Format the query parameter key to prevent conflicts with the original parameters
3411
3434
  function formatQueryAppName(appName) {
3412
3435
  // return `app-${appName}`
3413
3436
  return appName;
3414
3437
  }
3415
- // 根据浏览器url参数,获取当前子应用的path
3438
+ /**
3439
+ * Get app fullPath from browser url
3440
+ * @param appName app.name
3441
+ */
3416
3442
  function getMicroPathFromURL(appName) {
3417
3443
  var _a, _b;
3418
3444
  const rawLocation = globalEnv.rawWindow.location;
@@ -3420,15 +3446,23 @@ function getMicroPathFromURL(appName) {
3420
3446
  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)]);
3421
3447
  return isString(microPath) ? decodeMicroPath(microPath) : null;
3422
3448
  }
3423
- // 将name=encodeUrl地址插入到浏览器url上
3449
+ /**
3450
+ * Attach child app fullPath to browser url
3451
+ * @param appName app.name
3452
+ * @param microLocation location of child app
3453
+ */
3424
3454
  function setMicroPathToURL(appName, microLocation) {
3425
3455
  let { pathname, search, hash } = globalEnv.rawWindow.location;
3426
3456
  const queryObject = getQueryObjectFromURL(search, hash);
3427
3457
  const encodedMicroPath = encodeMicroPath(microLocation.pathname +
3428
3458
  microLocation.search +
3429
3459
  microLocation.hash);
3430
- let isAttach2Hash = false; // 基座是否是hash模式,这个其实也不准,只是表示参数加到了hash上
3431
- // hash存在且search不存在,则认为是hash路由
3460
+ /**
3461
+ * Is parent is hash router
3462
+ * In fact, this is not true. It just means that the parameter is added to the hash
3463
+ */
3464
+ let isAttach2Hash = false;
3465
+ // If hash exists and search does not exist, it is considered as a hash route
3432
3466
  if (hash && !search) {
3433
3467
  isAttach2Hash = true;
3434
3468
  if (queryObject.hashQuery) {
@@ -3458,7 +3492,11 @@ function setMicroPathToURL(appName, microLocation) {
3458
3492
  isAttach2Hash,
3459
3493
  };
3460
3494
  }
3461
- // 将name=encodeUrl的参数从浏览器url上删除
3495
+ /**
3496
+ * Delete child app fullPath from browser url
3497
+ * @param appName app.name
3498
+ * @param targetLocation target Location, default is rawLocation
3499
+ */
3462
3500
  function removeMicroPathFromURL(appName, targetLocation) {
3463
3501
  var _a, _b, _c, _d;
3464
3502
  let { pathname, search, hash } = targetLocation || globalEnv.rawWindow.location;
@@ -3481,7 +3519,7 @@ function removeMicroPathFromURL(appName, targetLocation) {
3481
3519
  };
3482
3520
  }
3483
3521
  /**
3484
- * 根据location获取query对象
3522
+ * Format search, hash to object
3485
3523
  */
3486
3524
  function getQueryObjectFromURL(search, hash) {
3487
3525
  const queryObject = {};
@@ -3503,6 +3541,18 @@ function getNoHashMicroPathFromURL(appName, baseUrl) {
3503
3541
  const formatLocation = createURL(microPath, baseUrl);
3504
3542
  return formatLocation.origin + formatLocation.pathname + formatLocation.search;
3505
3543
  }
3544
+ /**
3545
+ * Effect app is an app that can perform route navigation
3546
+ * NOTE: Invalid app action
3547
+ * 1. prevent update browser url, dispatch popStateEvent, reload browser
3548
+ * 2. It can update path with pushState/replaceState
3549
+ * 3. Can not update path outside (with router api)
3550
+ * 3. Can not update path by location
3551
+ */
3552
+ function isEffectiveApp(appName) {
3553
+ const app = appInstanceMap.get(appName);
3554
+ return !!(app && !app.isPrefetch);
3555
+ }
3506
3556
 
3507
3557
  /**
3508
3558
  * dispatch PopStateEvent & HashChangeEvent to child app
@@ -3519,7 +3569,8 @@ function addHistoryListener(appName) {
3519
3569
  * 1. unmount app & hidden keep-alive app will not receive popstate event
3520
3570
  * 2. filter out onlyForBrowser
3521
3571
  */
3522
- if (getActiveApps(true).includes(appName) && !e.onlyForBrowser) {
3572
+ if (getActiveApps({ excludeHiddenApp: true, excludePreRender: true }).includes(appName) &&
3573
+ !e.onlyForBrowser) {
3523
3574
  const microPath = getMicroPathFromURL(appName);
3524
3575
  const app = appInstanceMap.get(appName);
3525
3576
  const proxyWindow = app.sandBox.proxyWindow;
@@ -3607,15 +3658,18 @@ function dispatchNativeHashChangeEvent(oldHref) {
3607
3658
  }
3608
3659
  /**
3609
3660
  * dispatch popstate & hashchange event to browser
3661
+ * @param appName app.name
3610
3662
  * @param onlyForBrowser only dispatch event to browser
3611
3663
  * @param oldHref old href of rawWindow.location
3612
3664
  */
3613
- function dispatchNativeEvent(onlyForBrowser, oldHref) {
3665
+ function dispatchNativeEvent(appName, onlyForBrowser, oldHref) {
3614
3666
  // clear element scope before dispatch global event
3615
3667
  removeDomScope();
3616
- dispatchNativePopStateEvent(onlyForBrowser);
3617
- if (oldHref) {
3618
- dispatchNativeHashChangeEvent(oldHref);
3668
+ if (isEffectiveApp(appName)) {
3669
+ dispatchNativePopStateEvent(onlyForBrowser);
3670
+ if (oldHref) {
3671
+ dispatchNativeHashChangeEvent(oldHref);
3672
+ }
3619
3673
  }
3620
3674
  }
3621
3675
 
@@ -3632,7 +3686,7 @@ function createMicroHistory(appName, microLocation) {
3632
3686
  if (isString(rests[2]) || isURL(rests[2])) {
3633
3687
  const targetLocation = createURL(rests[2], microLocation.href);
3634
3688
  if (targetLocation.origin === microLocation.origin) {
3635
- navigateWithNativeEvent(methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rests[0]), rests[1]);
3689
+ navigateWithNativeEvent(appName, methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rests[0]), rests[1]);
3636
3690
  const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3637
3691
  if (targetFullPath !== microLocation.fullPath) {
3638
3692
  updateMicroLocation(appName, targetFullPath, microLocation);
@@ -3640,7 +3694,7 @@ function createMicroHistory(appName, microLocation) {
3640
3694
  return void 0;
3641
3695
  }
3642
3696
  }
3643
- nativeHistoryNavigate(methodName, rests[2], rests[0], rests[1]);
3697
+ nativeHistoryNavigate(appName, methodName, rests[2], rests[0], rests[1]);
3644
3698
  };
3645
3699
  }
3646
3700
  const pushState = getMicroHistoryMethod('pushState');
@@ -3672,14 +3726,17 @@ function createMicroHistory(appName, microLocation) {
3672
3726
  }
3673
3727
  /**
3674
3728
  * navigate to new path base on native method of history
3729
+ * @param appName app.name
3675
3730
  * @param methodName pushState/replaceState
3676
3731
  * @param fullPath full path
3677
3732
  * @param state history.state, default is null
3678
3733
  * @param title history.title, default is ''
3679
3734
  */
3680
- function nativeHistoryNavigate(methodName, fullPath, state = null, title = '') {
3681
- const method = methodName === 'pushState' ? globalEnv.rawPushState : globalEnv.rawReplaceState;
3682
- method.call(globalEnv.rawWindow.history, state, title, fullPath);
3735
+ function nativeHistoryNavigate(appName, methodName, fullPath, state = null, title = '') {
3736
+ if (isEffectiveApp(appName)) {
3737
+ const method = methodName === 'pushState' ? globalEnv.rawPushState : globalEnv.rawReplaceState;
3738
+ method.call(globalEnv.rawWindow.history, state, title, fullPath);
3739
+ }
3683
3740
  }
3684
3741
  /**
3685
3742
  * Navigate to new path, and dispatch native popStateEvent/hashChangeEvent to browser
@@ -3688,29 +3745,33 @@ function nativeHistoryNavigate(methodName, fullPath, state = null, title = '') {
3688
3745
  * 2. proxyHistory.pushState/replaceState with limited popstateEvent
3689
3746
  * 3. api microApp.router.push/replace
3690
3747
  * 4. proxyLocation.hash = xxx
3748
+ * @param appName app.name
3691
3749
  * @param methodName pushState/replaceState
3692
3750
  * @param result result of add/remove microApp path on browser url
3693
3751
  * @param onlyForBrowser only dispatch event to browser
3694
3752
  * @param state history.state, not required
3695
3753
  * @param title history.title, not required
3696
3754
  */
3697
- function navigateWithNativeEvent(methodName, result, onlyForBrowser, state, title) {
3698
- const rawLocation = globalEnv.rawWindow.location;
3699
- const oldFullPath = rawLocation.pathname + rawLocation.search + rawLocation.hash;
3700
- const oldHref = result.isAttach2Hash && oldFullPath !== result.fullPath ? rawLocation.href : null;
3701
- // navigate with native history method
3702
- nativeHistoryNavigate(methodName, result.fullPath, state, title);
3703
- if (oldFullPath !== result.fullPath)
3704
- dispatchNativeEvent(onlyForBrowser, oldHref);
3755
+ function navigateWithNativeEvent(appName, methodName, result, onlyForBrowser, state, title) {
3756
+ if (isEffectiveApp(appName)) {
3757
+ const rawLocation = globalEnv.rawWindow.location;
3758
+ const oldFullPath = rawLocation.pathname + rawLocation.search + rawLocation.hash;
3759
+ const oldHref = result.isAttach2Hash && oldFullPath !== result.fullPath ? rawLocation.href : null;
3760
+ // navigate with native history method
3761
+ nativeHistoryNavigate(appName, methodName, result.fullPath, state, title);
3762
+ if (oldFullPath !== result.fullPath)
3763
+ dispatchNativeEvent(appName, onlyForBrowser, oldHref);
3764
+ }
3705
3765
  }
3706
3766
  /**
3707
3767
  * update browser url when mount/unmount/hidden/show/attachToURL/attachAllToURL
3708
3768
  * just attach microRoute info to browser, dispatch event to base app(exclude child)
3769
+ * @param appName app.name
3709
3770
  * @param result result of add/remove microApp path on browser url
3710
3771
  * @param state history.state
3711
3772
  */
3712
- function attachRouteToBrowserURL(result, state) {
3713
- navigateWithNativeEvent('replaceState', result, true, state);
3773
+ function attachRouteToBrowserURL(appName, result, state) {
3774
+ navigateWithNativeEvent(appName, 'replaceState', result, true, state);
3714
3775
  }
3715
3776
  /**
3716
3777
  * When path is same, keep the microAppState in history.state
@@ -3740,10 +3801,13 @@ function reWriteHistoryMethod(method) {
3740
3801
  * 2. Unable to catch when base app navigate with location
3741
3802
  * 3. When in nest app, rawPushState/rawReplaceState has been modified by parent
3742
3803
  */
3743
- getActiveApps(true).forEach(appName => {
3804
+ getActiveApps({
3805
+ excludeHiddenApp: true,
3806
+ excludePreRender: true,
3807
+ }).forEach(appName => {
3744
3808
  const app = appInstanceMap.get(appName);
3745
3809
  if (app.sandBox && app.useMemoryRouter && !getMicroPathFromURL(appName)) {
3746
- attachRouteToBrowserURL(setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName)));
3810
+ attachRouteToBrowserURL(appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName)));
3747
3811
  }
3748
3812
  });
3749
3813
  // fix bug for nest app
@@ -3775,7 +3839,7 @@ function createRouterApi() {
3775
3839
  * @param state to.state
3776
3840
  */
3777
3841
  function navigateWithRawHistory(appName, methodName, targetLocation, state) {
3778
- navigateWithNativeEvent(methodName, setMicroPathToURL(appName, targetLocation), false, setMicroState(appName, state !== null && state !== void 0 ? state : null));
3842
+ navigateWithNativeEvent(appName, methodName, setMicroPathToURL(appName, targetLocation), false, setMicroState(appName, state !== null && state !== void 0 ? state : null));
3779
3843
  // clear element scope after navigate
3780
3844
  removeDomScope();
3781
3845
  }
@@ -3796,7 +3860,7 @@ function createRouterApi() {
3796
3860
  return logError(`navigation failed, memory router of app ${appName} is closed`);
3797
3861
  }
3798
3862
  // active apps, include hidden keep-alive app
3799
- if (getActiveApps().includes(appName)) {
3863
+ if (getActiveApps({ excludePreRender: true }).includes(appName)) {
3800
3864
  const microLocation = app.sandBox.proxyWindow.location;
3801
3865
  const targetLocation = createURL(to.path, microLocation.href);
3802
3866
  // Only get path data, even if the origin is different from microApp
@@ -3879,7 +3943,7 @@ function createRouterApi() {
3879
3943
  function commonHandlerForAttachToURL(appName) {
3880
3944
  const app = appInstanceMap.get(appName);
3881
3945
  if (app.sandBox && app.useMemoryRouter) {
3882
- attachRouteToBrowserURL(setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName)));
3946
+ attachRouteToBrowserURL(appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName)));
3883
3947
  }
3884
3948
  }
3885
3949
  /**
@@ -3894,10 +3958,14 @@ function createRouterApi() {
3894
3958
  }
3895
3959
  /**
3896
3960
  * Attach all active app router info to browser url
3897
- * exclude hidden keep-alive app
3961
+ * @param includeHiddenApp include hidden keep-alive app
3962
+ * @param includePreRender include preRender app
3898
3963
  */
3899
- function attachAllToURL(includeHiddenApp = false) {
3900
- getActiveApps(!includeHiddenApp).forEach(appName => commonHandlerForAttachToURL(appName));
3964
+ function attachAllToURL({ includeHiddenApp = false, includePreRender = false, }) {
3965
+ getActiveApps({
3966
+ excludeHiddenApp: !includeHiddenApp,
3967
+ excludePreRender: !includePreRender,
3968
+ }).forEach(appName => commonHandlerForAttachToURL(appName));
3901
3969
  }
3902
3970
  function createDefaultPageApi() {
3903
3971
  // defaultPage data
@@ -3912,7 +3980,7 @@ function createRouterApi() {
3912
3980
  function setDefaultPage(options) {
3913
3981
  const appName = formatAppName(options.name);
3914
3982
  if (!appName || !options.path) {
3915
- if (process.env.NODE_ENV !== 'production') {
3983
+ if ((process.env.NODE_ENV !== 'production')) {
3916
3984
  if (!appName) {
3917
3985
  logWarn(`setDefaultPage: invalid appName "${appName}"`);
3918
3986
  }
@@ -3955,7 +4023,7 @@ function createRouterApi() {
3955
4023
  }
3956
4024
  });
3957
4025
  }
3958
- else if (process.env.NODE_ENV !== 'production') {
4026
+ else if ((process.env.NODE_ENV !== 'production')) {
3959
4027
  logWarn('setBaseAppRouter: Invalid base router');
3960
4028
  }
3961
4029
  }
@@ -4023,13 +4091,13 @@ function createMicroLocation(appName, url) {
4023
4091
  if (targetLocation.hash !== shadowLocation.hash) {
4024
4092
  if (setMicroPathResult.isAttach2Hash)
4025
4093
  oldHref = rawLocation.href;
4026
- nativeHistoryNavigate(methodName, setMicroPathResult.fullPath);
4094
+ nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath);
4027
4095
  }
4028
4096
  if (targetLocation.hash) {
4029
- dispatchNativeEvent(false, oldHref);
4097
+ dispatchNativeEvent(appName, false, oldHref);
4030
4098
  }
4031
4099
  else {
4032
- rawLocation.reload();
4100
+ rawReload();
4033
4101
  }
4034
4102
  return void 0;
4035
4103
  /**
@@ -4038,8 +4106,8 @@ function createMicroLocation(appName, url) {
4038
4106
  */
4039
4107
  }
4040
4108
  else if (setMicroPathResult.isAttach2Hash) {
4041
- nativeHistoryNavigate(methodName, setMicroPathResult.fullPath);
4042
- rawLocation.reload();
4109
+ nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath);
4110
+ rawReload();
4043
4111
  return void 0;
4044
4112
  }
4045
4113
  value = setMicroPathResult.fullPath;
@@ -4069,7 +4137,7 @@ function createMicroLocation(appName, url) {
4069
4137
  // When the browser url has a hash value, the same pathname/search will not refresh browser
4070
4138
  if (targetLocation[key] === shadowLocation[key] && shadowLocation.hash) {
4071
4139
  // The href has not changed, not need to dispatch hashchange event
4072
- dispatchNativeEvent(false);
4140
+ dispatchNativeEvent(appName, false);
4073
4141
  }
4074
4142
  else {
4075
4143
  /**
@@ -4078,19 +4146,24 @@ function createMicroLocation(appName, url) {
4078
4146
  * pathname: /path ==> /path#hash, /path ==> /path?query
4079
4147
  * search: ?query ==> ?query#hash
4080
4148
  */
4081
- nativeHistoryNavigate(targetLocation[key] === shadowLocation[key] ? 'replaceState' : 'pushState', setMicroPathToURL(appName, targetLocation).fullPath);
4082
- rawLocation.reload();
4149
+ nativeHistoryNavigate(appName, targetLocation[key] === shadowLocation[key] ? 'replaceState' : 'pushState', setMicroPathToURL(appName, targetLocation).fullPath);
4150
+ rawReload();
4083
4151
  }
4084
4152
  }
4153
+ function rawReload() {
4154
+ isEffectiveApp(appName) && rawLocation.reload();
4155
+ }
4085
4156
  /**
4086
4157
  * Special processing for four keys: href, pathname, search and hash
4087
4158
  * They take values from shadowLocation, and require special operations when assigning values
4088
4159
  */
4089
4160
  rawDefineProperties(microLocation, {
4090
4161
  href: createPropertyDescriptor(() => shadowLocation.href, (value) => {
4091
- const targetPath = commonHandler(value, 'pushState');
4092
- if (targetPath)
4093
- rawLocation.href = targetPath;
4162
+ if (isEffectiveApp(appName)) {
4163
+ const targetPath = commonHandler(value, 'pushState');
4164
+ if (targetPath)
4165
+ rawLocation.href = targetPath;
4166
+ }
4094
4167
  }),
4095
4168
  pathname: createPropertyDescriptor(() => shadowLocation.pathname, (value) => {
4096
4169
  const targetPath = ('/' + value).replace(/^\/+/, '/') + shadowLocation.search + shadowLocation.hash;
@@ -4105,16 +4178,18 @@ function createMicroLocation(appName, url) {
4105
4178
  const targetLocation = createURL(targetPath, url);
4106
4179
  // The same hash will not trigger popStateEvent
4107
4180
  if (targetLocation.hash !== shadowLocation.hash) {
4108
- navigateWithNativeEvent('pushState', setMicroPathToURL(appName, targetLocation), false);
4181
+ navigateWithNativeEvent(appName, 'pushState', setMicroPathToURL(appName, targetLocation), false);
4109
4182
  }
4110
4183
  }),
4111
4184
  fullPath: createPropertyDescriptor(() => shadowLocation.pathname + shadowLocation.search + shadowLocation.hash, noop),
4112
4185
  });
4113
4186
  const createLocationMethod = (locationMethodName) => {
4114
4187
  return function (value) {
4115
- const targetPath = commonHandler(value, locationMethodName === 'assign' ? 'pushState' : 'replaceState');
4116
- if (targetPath)
4117
- rawLocation[locationMethodName](targetPath);
4188
+ if (isEffectiveApp(appName)) {
4189
+ const targetPath = commonHandler(value, locationMethodName === 'assign' ? 'pushState' : 'replaceState');
4190
+ if (targetPath)
4191
+ rawLocation[locationMethodName](targetPath);
4192
+ }
4118
4193
  };
4119
4194
  };
4120
4195
  return assign(microLocation, {
@@ -4213,7 +4288,7 @@ function updateBrowserURLWithLocation(appName, microLocation, defaultPage) {
4213
4288
  if (defaultPage)
4214
4289
  updateMicroLocation(appName, defaultPage, microLocation, 'prevent');
4215
4290
  // attach microApp route info to browser URL
4216
- attachRouteToBrowserURL(setMicroPathToURL(appName, microLocation), setMicroState(appName, null));
4291
+ attachRouteToBrowserURL(appName, setMicroPathToURL(appName, microLocation), setMicroState(appName, null));
4217
4292
  // trigger guards after change browser URL
4218
4293
  autoTriggerNavigationGuard(appName, microLocation);
4219
4294
  }
@@ -4237,7 +4312,7 @@ function clearRouteStateFromURL(appName, url, microLocation, keepRouteState) {
4237
4312
  * called on sandbox.stop or hidden of keep-alive app
4238
4313
  */
4239
4314
  function removeStateAndPathFromBrowser(appName) {
4240
- attachRouteToBrowserURL(removeMicroPathFromURL(appName), removeMicroState(appName, globalEnv.rawWindow.history.state));
4315
+ attachRouteToBrowserURL(appName, removeMicroPathFromURL(appName), removeMicroState(appName, globalEnv.rawWindow.history.state));
4241
4316
  }
4242
4317
 
4243
4318
  /**
@@ -4369,7 +4444,7 @@ class SandBox {
4369
4444
  // create proxyWindow with Proxy(microAppWindow)
4370
4445
  this.proxyWindow = this.createProxyWindow(appName);
4371
4446
  // Rewrite global event listener & timeout
4372
- assign(this, effect(appName, this.microAppWindow));
4447
+ this.effectController = effect(appName, this.microAppWindow);
4373
4448
  // inject global properties
4374
4449
  this.initStaticGlobalKeys(this.microAppWindow, appName, url);
4375
4450
  }
@@ -4420,10 +4495,8 @@ class SandBox {
4420
4495
  */
4421
4496
  stop({ umdMode, keepRouteState, clearEventSource, clearData, }) {
4422
4497
  if (this.active) {
4423
- this.releaseEffect();
4424
- this.microAppWindow.microApp.clearDataListener();
4425
- this.microAppWindow.microApp.clearGlobalDataListener();
4426
- clearData && this.microAppWindow.microApp.clearData();
4498
+ // clear global event, timeout, data listener
4499
+ this.releaseGlobalEffect(clearData);
4427
4500
  if (this.removeHistoryListener) {
4428
4501
  this.clearRouteState(keepRouteState);
4429
4502
  // release listener of popstate
@@ -4456,10 +4529,33 @@ class SandBox {
4456
4529
  this.active = false;
4457
4530
  }
4458
4531
  }
4459
- // record umd snapshot before the first execution of umdHookMount
4460
- recordUmdSnapshot() {
4532
+ /**
4533
+ * clear global event, timeout, data listener
4534
+ * Scenes:
4535
+ * 1. unmount of normal/umd app
4536
+ * 2. hidden keep-alive app
4537
+ * 3. after init prerender app
4538
+ * @param clearData clear data from base app
4539
+ */
4540
+ releaseGlobalEffect(clearData = false) {
4541
+ this.effectController.releaseEffect();
4542
+ this.microAppWindow.microApp.clearDataListener();
4543
+ this.microAppWindow.microApp.clearGlobalDataListener();
4544
+ if (clearData) {
4545
+ microApp.clearData(this.microAppWindow.__MICRO_APP_NAME__);
4546
+ this.microAppWindow.microApp.clearData();
4547
+ }
4548
+ }
4549
+ /**
4550
+ * record umd snapshot before the first execution of umdHookMount
4551
+ * Scenes:
4552
+ * 1. exec umdMountHook in umd mode
4553
+ * 2. hidden keep-alive app
4554
+ * 3. after init prerender app
4555
+ */
4556
+ recordEffectSnapshot() {
4461
4557
  // this.microAppWindow.__MICRO_APP_UMD_MODE__ = true
4462
- this.recordUmdEffect();
4558
+ this.effectController.recordEffect();
4463
4559
  recordDataCenterSnapshot(this.microAppWindow.microApp);
4464
4560
  // this.recordUmdInjectedValues = new Map<PropertyKey, unknown>()
4465
4561
  // this.injectedKeys.forEach((key: PropertyKey) => {
@@ -4467,13 +4563,17 @@ class SandBox {
4467
4563
  // })
4468
4564
  }
4469
4565
  // rebuild umd snapshot before remount umd app
4470
- rebuildUmdSnapshot() {
4566
+ rebuildEffectSnapshot() {
4471
4567
  // this.recordUmdInjectedValues!.forEach((value: unknown, key: PropertyKey) => {
4472
4568
  // Reflect.set(this.proxyWindow, key, value)
4473
4569
  // })
4474
- this.rebuildUmdEffect();
4570
+ this.effectController.rebuildEffect();
4475
4571
  rebuildDataCenterSnapshot(this.microAppWindow.microApp);
4476
4572
  }
4573
+ // set __MICRO_APP_PRE_RENDER__ state
4574
+ setPreRenderState(state) {
4575
+ this.microAppWindow.__MICRO_APP_PRE_RENDER__ = state;
4576
+ }
4477
4577
  /**
4478
4578
  * get scopeProperties and escapeProperties from plugins & adapter
4479
4579
  * @param appName app name
@@ -4607,6 +4707,7 @@ class SandBox {
4607
4707
  microAppWindow.__MICRO_APP_URL__ = url;
4608
4708
  microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
4609
4709
  microAppWindow.__MICRO_APP_WINDOW__ = microAppWindow;
4710
+ microAppWindow.__MICRO_APP_PRE_RENDER__ = false;
4610
4711
  microAppWindow.rawWindow = globalEnv.rawWindow;
4611
4712
  microAppWindow.rawDocument = globalEnv.rawDocument;
4612
4713
  microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
@@ -4918,7 +5019,7 @@ function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
4918
5019
  // micro app instances
4919
5020
  const appInstanceMap = new Map();
4920
5021
  class CreateApp {
4921
- constructor({ name, url, container, scopecss, useSandbox, inline, esmodule, ssrUrl, isPrefetch, }) {
5022
+ constructor({ name, url, container, scopecss, useSandbox, inline, esmodule, ssrUrl, isPrefetch, prefetchLevel, }) {
4922
5023
  this.state = appStates.CREATED;
4923
5024
  this.keepAliveState = null;
4924
5025
  this.keepAliveContainer = null;
@@ -4928,7 +5029,6 @@ class CreateApp {
4928
5029
  this.libraryName = null;
4929
5030
  this.umdMode = false;
4930
5031
  this.sandBox = null;
4931
- this.keepRouteState = false;
4932
5032
  this.fiber = false;
4933
5033
  this.useMemoryRouter = true;
4934
5034
  this.name = name;
@@ -4940,8 +5040,10 @@ class CreateApp {
4940
5040
  // not exist when prefetch 👇
4941
5041
  this.container = container !== null && container !== void 0 ? container : null;
4942
5042
  this.ssrUrl = ssrUrl !== null && ssrUrl !== void 0 ? ssrUrl : '';
4943
- // not exist when normal 👇
5043
+ // exist only prefetch 👇
4944
5044
  this.isPrefetch = isPrefetch !== null && isPrefetch !== void 0 ? isPrefetch : false;
5045
+ this.isPrerender = prefetchLevel === 3;
5046
+ this.prefetchLevel = prefetchLevel;
4945
5047
  // init actions
4946
5048
  appInstanceMap.set(this.name, this);
4947
5049
  this.source = { html: null, links: new Set(), scripts: new Set() };
@@ -4956,14 +5058,46 @@ class CreateApp {
4956
5058
  /**
4957
5059
  * When resource is loaded, mount app if it is not prefetch or unmount
4958
5060
  */
4959
- onLoad(html) {
5061
+ onLoad(html, defaultPage, disablePatchRequest) {
5062
+ var _a;
4960
5063
  if (++this.loadSourceLevel === 2) {
4961
5064
  this.source.html = html;
4962
5065
  this.state = appStates.LOADED;
4963
5066
  if (!this.isPrefetch && appStates.UNMOUNT !== this.state) {
4964
- // @ts-ignore
4965
5067
  getRootContainer(this.container).mount(this);
4966
5068
  }
5069
+ else if (this.isPrerender) {
5070
+ /**
5071
+ * PreRender is an option of prefetch, it will render app during prefetch
5072
+ * Limit:
5073
+ * 1. fiber forced on
5074
+ * 2. only virtual router support
5075
+ *
5076
+ * NOTE: (4P: not - update browser url, dispatch popstateEvent, reload window, dispatch lifecycle event)
5077
+ * 1. pushState/replaceState in child can update microLocation, but will not attach router info to browser url
5078
+ * 2. prevent dispatch popstate/hashchange event to browser
5079
+ * 3. all navigation actions of location are invalid (In the future, we can consider update microLocation without trigger browser reload)
5080
+ * 4. lifecycle event will not trigger when prerender
5081
+ *
5082
+ * Special scenes
5083
+ * 1. unmount prerender app when loading
5084
+ * 2. unmount prerender app when exec js
5085
+ * 2. unmount prerender app after exec js
5086
+ */
5087
+ const container = pureCreateElement('div');
5088
+ container.setAttribute('prerender', 'true');
5089
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.setPreRenderState(true);
5090
+ this.mount({
5091
+ container,
5092
+ inline: this.inline,
5093
+ useMemoryRouter: true,
5094
+ baseroute: '',
5095
+ fiber: true,
5096
+ esmodule: this.esmodule,
5097
+ defaultPage: defaultPage !== null && defaultPage !== void 0 ? defaultPage : '',
5098
+ disablePatchRequest: disablePatchRequest !== null && disablePatchRequest !== void 0 ? disablePatchRequest : false,
5099
+ });
5100
+ }
4967
5101
  }
4968
5102
  }
4969
5103
  /**
@@ -4980,29 +5114,86 @@ class CreateApp {
4980
5114
  /**
4981
5115
  * mount app
4982
5116
  * @param container app container
4983
- * @param inline js runs in inline mode
5117
+ * @param inline run js in inline mode
5118
+ * @param useMemoryRouter use virtual router
5119
+ * @param defaultPage default page of virtual router
4984
5120
  * @param baseroute route prefix, default is ''
4985
- * @param keepRouteState keep route state when unmount, default is false
4986
5121
  * @param disablePatchRequest prevent rewrite request method of child app
5122
+ * @param fiber run js in fiber mode
5123
+ * @param esmodule support type='module' script
4987
5124
  */
4988
- mount({ container, inline, esmodule, useMemoryRouter, baseroute, keepRouteState, defaultPage, disablePatchRequest, fiber, }) {
4989
- var _a, _b;
5125
+ mount({ container, inline, useMemoryRouter, defaultPage, baseroute, disablePatchRequest, fiber, esmodule, }) {
5126
+ var _a, _b, _c, _d, _e, _f;
5127
+ if (this.loadSourceLevel !== 2) {
5128
+ /**
5129
+ * unmount prefetch app when loading source, when mount again before loading end,
5130
+ * isPrefetch & isPrerender will be reset, and this.container sill be null
5131
+ * so we should set this.container
5132
+ */
5133
+ this.container = container;
5134
+ // mount before prerender exec mount (loading source), set isPrerender to false
5135
+ this.isPrerender = false;
5136
+ // reset app state to LOADING
5137
+ this.state = appStates.LOADING;
5138
+ return;
5139
+ }
5140
+ /**
5141
+ * Mount app with prerender, this.container is empty
5142
+ * When rendering again, identify prerender by this.container
5143
+ * Transfer the contents of div to the <micro-app> tag
5144
+ *
5145
+ * Special scenes:
5146
+ * 1. mount before prerender exec mount (loading source)
5147
+ * 2. mount when prerender js executing
5148
+ * 3. mount after prerender js exec end
5149
+ *
5150
+ * TODO: test shadowDOM
5151
+ */
5152
+ if (this.container instanceof HTMLDivElement &&
5153
+ this.container.hasAttribute('prerender')) {
5154
+ /**
5155
+ * rebuild effect event of window, document, data center
5156
+ * explain:
5157
+ * 1. rebuild before exec mount, do nothing
5158
+ * 2. rebuild when js executing, recovery recorded effect event, because prerender fiber mode
5159
+ * 3. rebuild after js exec end, normal recovery effect event
5160
+ */
5161
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.rebuildEffectSnapshot();
5162
+ // current this.container is <div prerender='true'></div>
5163
+ cloneContainer(this.container, container, false);
5164
+ /**
5165
+ * set this.container to <micro-app></micro-app>
5166
+ * NOTE:
5167
+ * must before exec this.preRenderEvent?.forEach((cb) => cb())
5168
+ */
5169
+ this.container = container;
5170
+ (_b = this.preRenderEvent) === null || _b === void 0 ? void 0 : _b.forEach((cb) => cb());
5171
+ // reset isPrerender config
5172
+ this.isPrerender = false;
5173
+ this.preRenderEvent = undefined;
5174
+ // attach router info to browser url
5175
+ router.attachToURL(this.name);
5176
+ return (_c = this.sandBox) === null || _c === void 0 ? void 0 : _c.setPreRenderState(false);
5177
+ }
4990
5178
  this.container = container;
4991
5179
  this.inline = inline;
4992
5180
  this.esmodule = esmodule;
4993
- this.keepRouteState = keepRouteState;
4994
5181
  this.fiber = fiber;
4995
5182
  // use in sandbox/effect
4996
5183
  this.useMemoryRouter = useMemoryRouter;
4997
5184
  // this.hiddenRouter = hiddenRouter ?? this.hiddenRouter
4998
- if (this.loadSourceLevel !== 2) {
4999
- this.state = appStates.LOADING;
5000
- return;
5185
+ const dispatchBeforeMount = () => {
5186
+ dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
5187
+ };
5188
+ if (this.isPrerender) {
5189
+ ((_d = this.preRenderEvent) !== null && _d !== void 0 ? _d : (this.preRenderEvent = [])).push(dispatchBeforeMount);
5190
+ }
5191
+ else {
5192
+ dispatchBeforeMount();
5001
5193
  }
5002
- dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
5003
5194
  this.state = appStates.MOUNTING;
5004
5195
  cloneContainer(this.source.html, this.container, !this.umdMode);
5005
- (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.start({
5196
+ (_e = this.sandBox) === null || _e === void 0 ? void 0 : _e.start({
5006
5197
  umdMode: this.umdMode,
5007
5198
  baseroute,
5008
5199
  useMemoryRouter,
@@ -5014,6 +5205,7 @@ class CreateApp {
5014
5205
  let hasDispatchMountedEvent = false;
5015
5206
  // if all js are executed, param isFinished will be true
5016
5207
  execScripts(this, (isFinished) => {
5208
+ var _a;
5017
5209
  if (!this.umdMode) {
5018
5210
  const { mount, unmount } = this.getUmdLibraryHooks();
5019
5211
  /**
@@ -5027,7 +5219,7 @@ class CreateApp {
5027
5219
  this.umdMode = true;
5028
5220
  if (this.sandBox)
5029
5221
  this.sandBox.proxyWindow.__MICRO_APP_UMD_MODE__ = true;
5030
- // this.sandBox?.recordUmdSnapshot()
5222
+ // this.sandBox?.recordEffectSnapshot()
5031
5223
  try {
5032
5224
  umdHookMountResult = this.umdHookMount(microApp.getData(this.name, true));
5033
5225
  }
@@ -5038,12 +5230,19 @@ class CreateApp {
5038
5230
  }
5039
5231
  if (!hasDispatchMountedEvent && (isFinished === true || this.umdMode)) {
5040
5232
  hasDispatchMountedEvent = true;
5041
- this.handleMounted(umdHookMountResult);
5233
+ const dispatchMounted = () => this.handleMounted(umdHookMountResult);
5234
+ if (this.isPrerender) {
5235
+ ((_a = this.preRenderEvent) !== null && _a !== void 0 ? _a : (this.preRenderEvent = [])).push(dispatchMounted);
5236
+ this.recordAndReleaseEffect();
5237
+ }
5238
+ else {
5239
+ dispatchMounted();
5240
+ }
5042
5241
  }
5043
5242
  });
5044
5243
  }
5045
5244
  else {
5046
- (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.rebuildUmdSnapshot();
5245
+ (_f = this.sandBox) === null || _f === void 0 ? void 0 : _f.rebuildEffectSnapshot();
5047
5246
  try {
5048
5247
  umdHookMountResult = this.umdHookMount();
5049
5248
  }
@@ -5083,9 +5282,11 @@ class CreateApp {
5083
5282
  * unmount app
5084
5283
  * NOTE: Do not add any params on account of unmountApp
5085
5284
  * @param destroy completely destroy, delete cache resources
5285
+ * @param clearData clear data of dateCenter
5286
+ * @param keepRouteState keep route state when unmount, default is false
5086
5287
  * @param unmountcb callback of unmount
5087
5288
  */
5088
- unmount({ destroy, clearData, unmountcb, }) {
5289
+ unmount({ destroy, clearData, keepRouteState, unmountcb, }) {
5089
5290
  if (this.state === appStates.LOAD_FAILED) {
5090
5291
  destroy = true;
5091
5292
  }
@@ -5110,30 +5311,40 @@ class CreateApp {
5110
5311
  callFnWithTryCatch(this.getGlobalEventListener(microGlobalEvent.ONUNMOUNT), this.name, `window.${microGlobalEvent.ONUNMOUNT}`);
5111
5312
  // dispatch unmount event to micro app
5112
5313
  dispatchCustomEventToMicroApp('unmount', this.name);
5113
- this.handleUnmounted(destroy, clearData, umdHookUnmountResult, unmountcb);
5314
+ this.handleUnmounted(destroy, clearData, keepRouteState, umdHookUnmountResult, unmountcb);
5114
5315
  }
5115
5316
  /**
5116
5317
  * handle for promise umdHookUnmount
5117
5318
  * @param destroy completely destroy, delete cache resources
5319
+ * @param clearData clear data of dateCenter
5320
+ * @param keepRouteState keep route state when unmount, default is false
5118
5321
  * @param umdHookUnmountResult result of umdHookUnmount
5119
5322
  * @param unmountcb callback of unmount
5120
5323
  */
5121
- handleUnmounted(destroy, clearData, umdHookUnmountResult, unmountcb) {
5324
+ handleUnmounted(destroy, clearData, keepRouteState, umdHookUnmountResult, unmountcb) {
5325
+ const unmountParam = {
5326
+ destroy,
5327
+ clearData,
5328
+ keepRouteState,
5329
+ unmountcb,
5330
+ };
5122
5331
  if (isPromise(umdHookUnmountResult)) {
5123
5332
  umdHookUnmountResult
5124
- .then(() => this.actionsForUnmount(destroy, clearData, unmountcb))
5125
- .catch(() => this.actionsForUnmount(destroy, clearData, unmountcb));
5333
+ .then(() => this.actionsForUnmount(unmountParam))
5334
+ .catch(() => this.actionsForUnmount(unmountParam));
5126
5335
  }
5127
5336
  else {
5128
- this.actionsForUnmount(destroy, clearData, unmountcb);
5337
+ this.actionsForUnmount(unmountParam);
5129
5338
  }
5130
5339
  }
5131
5340
  /**
5132
5341
  * actions for unmount app
5133
5342
  * @param destroy completely destroy, delete cache resources
5343
+ * @param clearData clear data of dateCenter
5344
+ * @param keepRouteState keep route state when unmount, default is false
5134
5345
  * @param unmountcb callback of unmount
5135
5346
  */
5136
- actionsForUnmount(destroy, clearData, unmountcb) {
5347
+ actionsForUnmount({ destroy, clearData, keepRouteState, unmountcb }) {
5137
5348
  var _a, _b;
5138
5349
  if (destroy) {
5139
5350
  this.actionsForCompletelyDestroy();
@@ -5142,10 +5353,7 @@ class CreateApp {
5142
5353
  cloneContainer(this.container, this.source.html, false);
5143
5354
  }
5144
5355
  if (this.umdMode) {
5145
- (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordUmdSnapshot();
5146
- }
5147
- if (clearData || destroy) {
5148
- microApp.clearData(this.name);
5356
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordEffectSnapshot();
5149
5357
  }
5150
5358
  /**
5151
5359
  * this.container maybe contains micro-app element, stop sandbox should exec after cloneContainer
@@ -5155,7 +5363,7 @@ class CreateApp {
5155
5363
  */
5156
5364
  (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.stop({
5157
5365
  umdMode: this.umdMode,
5158
- keepRouteState: this.keepRouteState && !destroy,
5366
+ keepRouteState: keepRouteState && !destroy,
5159
5367
  clearEventSource: !this.umdMode || destroy,
5160
5368
  clearData: clearData || destroy,
5161
5369
  });
@@ -5164,9 +5372,14 @@ class CreateApp {
5164
5372
  }
5165
5373
  // dispatch unmount event to base app
5166
5374
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.UNMOUNT);
5375
+ this.resetConfig();
5376
+ unmountcb && unmountcb();
5377
+ }
5378
+ resetConfig() {
5167
5379
  this.container.innerHTML = '';
5168
5380
  this.container = null;
5169
- unmountcb && unmountcb();
5381
+ this.isPrerender = false;
5382
+ this.preRenderEvent = undefined;
5170
5383
  }
5171
5384
  // actions for completely destroy
5172
5385
  actionsForCompletelyDestroy() {
@@ -5192,11 +5405,13 @@ class CreateApp {
5192
5405
  dispatchLifecyclesEvent(oldContainer, this.name, lifeCycles.AFTERHIDDEN);
5193
5406
  // called after lifeCyclesEvent
5194
5407
  (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.removeRouteInfoForKeepAliveApp();
5408
+ this.recordAndReleaseEffect();
5195
5409
  callback && callback();
5196
5410
  }
5197
5411
  // show app when connectedCallback called with keep-alive
5198
5412
  showKeepAliveApp(container) {
5199
- var _a;
5413
+ var _a, _b;
5414
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.rebuildEffectSnapshot();
5200
5415
  // dispatch beforeShow event to micro-app
5201
5416
  dispatchCustomEventToMicroApp('appstate-change', this.name, {
5202
5417
  appState: 'beforeshow',
@@ -5207,7 +5422,7 @@ class CreateApp {
5207
5422
  this.container = container;
5208
5423
  this.keepAliveState = keepAliveStates.KEEP_ALIVE_SHOW;
5209
5424
  // called before lifeCyclesEvent
5210
- (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.setRouteInfoForKeepAliveApp();
5425
+ (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.setRouteInfoForKeepAliveApp();
5211
5426
  // dispatch afterShow event to micro-app
5212
5427
  dispatchCustomEventToMicroApp('appstate-change', this.name, {
5213
5428
  appState: 'aftershow',
@@ -5253,6 +5468,17 @@ class CreateApp {
5253
5468
  const listener = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow[eventName];
5254
5469
  return isFunction(listener) ? listener : null;
5255
5470
  }
5471
+ /**
5472
+ * Record global effect and then release (effect: global event, timeout, data listener)
5473
+ * Scenes:
5474
+ * 1. hidden keep-alive app
5475
+ * 2. after init prerender app
5476
+ */
5477
+ recordAndReleaseEffect() {
5478
+ var _a, _b;
5479
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordEffectSnapshot();
5480
+ (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.releaseGlobalEffect();
5481
+ }
5256
5482
  }
5257
5483
 
5258
5484
  /**
@@ -5448,7 +5674,7 @@ function defineElement(tagName) {
5448
5674
  this.handleAppMount(app);
5449
5675
  }
5450
5676
  else if (app.isPrefetch || app.getAppState() === appStates.UNMOUNT) {
5451
- if (process.env.NODE_ENV !== 'production' &&
5677
+ if ((process.env.NODE_ENV !== 'production') &&
5452
5678
  app.scopecss === this.isScopecss() &&
5453
5679
  app.useSandbox === this.isSandbox()) {
5454
5680
  /**
@@ -5561,14 +5787,12 @@ function defineElement(tagName) {
5561
5787
  app.mount({
5562
5788
  container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
5563
5789
  inline: this.getDisposeResult('inline'),
5564
- esmodule: this.getDisposeResult('esmodule'),
5565
5790
  useMemoryRouter: !this.getDisposeResult('disable-memory-router'),
5566
- baseroute: this.getBaseRouteCompatible(),
5567
- keepRouteState: this.getDisposeResult('keep-router-state'),
5568
5791
  defaultPage: this.getDefaultPageValue(),
5569
- hiddenRouter: this.getDisposeResult('hidden-router'),
5792
+ baseroute: this.getBaseRouteCompatible(),
5570
5793
  disablePatchRequest: this.getDisposeResult('disable-patch-request'),
5571
5794
  fiber: this.getDisposeResult('fiber'),
5795
+ esmodule: this.getDisposeResult('esmodule'),
5572
5796
  });
5573
5797
  }
5574
5798
  /**
@@ -5582,6 +5806,7 @@ function defineElement(tagName) {
5582
5806
  app.unmount({
5583
5807
  destroy,
5584
5808
  clearData: this.getDisposeResult('clear-data'),
5809
+ keepRouteState: this.getDisposeResult('keep-router-state'),
5585
5810
  unmountcb,
5586
5811
  });
5587
5812
  }
@@ -5729,19 +5954,37 @@ function defineElement(tagName) {
5729
5954
  * 2: disableScopecss, disableSandbox, disableMemoryRouter must be same with micro-app element, if conflict, the one who executes first shall prevail
5730
5955
  * @param apps micro apps
5731
5956
  */
5732
- function preFetch(apps) {
5957
+ function preFetch(apps, delay) {
5733
5958
  if (!isBrowser) {
5734
5959
  return logError('preFetch is only supported in browser environment');
5735
5960
  }
5736
5961
  requestIdleCallback(() => {
5737
- isFunction(apps) && (apps = apps());
5738
- if (isArray(apps)) {
5739
- apps.reduce((pre, next) => pre.then(() => preFetchInSerial(next)), Promise.resolve());
5740
- }
5962
+ const delayTime = delay !== null && delay !== void 0 ? delay : microApp.options.prefetchDelay;
5963
+ setTimeout(() => {
5964
+ // releasePrefetchEffect()
5965
+ preFetchInSerial(apps);
5966
+ }, isNumber(delayTime) ? delayTime : 3000);
5741
5967
  });
5968
+ // const handleOnLoad = (): void => {
5969
+ // releasePrefetchEffect()
5970
+ // requestIdleCallback(() => {
5971
+ // preFetchInSerial(apps)
5972
+ // })
5973
+ // }
5974
+ // const releasePrefetchEffect = (): void => {
5975
+ // window.removeEventListener('load', handleOnLoad)
5976
+ // clearTimeout(preFetchTime)
5977
+ // }
5978
+ // window.addEventListener('load', handleOnLoad)
5979
+ }
5980
+ function preFetchInSerial(apps) {
5981
+ isFunction(apps) && (apps = apps());
5982
+ if (isArray(apps)) {
5983
+ apps.reduce((pre, next) => pre.then(() => preFetchAction(next)), Promise.resolve());
5984
+ }
5742
5985
  }
5743
5986
  // sequential preload app
5744
- function preFetchInSerial(options) {
5987
+ function preFetchAction(options) {
5745
5988
  return promiseRequestIdle((resolve) => {
5746
5989
  var _a, _b, _c, _d, _e, _f;
5747
5990
  if (isPlainObject(options) && navigator.onLine) {
@@ -5751,21 +5994,22 @@ function preFetchInSerial(options) {
5751
5994
  const app = new CreateApp({
5752
5995
  name: options.name,
5753
5996
  url: options.url,
5997
+ isPrefetch: true,
5754
5998
  scopecss: !((_b = (_a = options['disable-scopecss']) !== null && _a !== void 0 ? _a : options.disableScopecss) !== null && _b !== void 0 ? _b : microApp.options['disable-scopecss']),
5755
5999
  useSandbox: !((_d = (_c = options['disable-sandbox']) !== null && _c !== void 0 ? _c : options.disableSandbox) !== null && _d !== void 0 ? _d : microApp.options['disable-sandbox']),
5756
6000
  inline: (_e = options.inline) !== null && _e !== void 0 ? _e : microApp.options.inline,
5757
6001
  esmodule: (_f = options.esmodule) !== null && _f !== void 0 ? _f : microApp.options.esmodule,
5758
- isPrefetch: true,
6002
+ prefetchLevel: options.level && PREFETCH_LEVEL.includes(options.level) ? options.level : microApp.options.prefetchLevel && PREFETCH_LEVEL.includes(microApp.options.prefetchLevel) ? microApp.options.prefetchLevel : 2,
5759
6003
  });
5760
6004
  const oldOnload = app.onLoad;
5761
6005
  const oldOnLoadError = app.onLoadError;
5762
6006
  app.onLoad = (html) => {
5763
6007
  resolve();
5764
- oldOnload.call(app, html);
6008
+ oldOnload.call(app, html, options['default-page'], options['disable-patch-request']);
5765
6009
  };
5766
- app.onLoadError = (e) => {
6010
+ app.onLoadError = (...rests) => {
5767
6011
  resolve();
5768
- oldOnLoadError.call(app, e);
6012
+ oldOnLoadError.call(app, ...rests);
5769
6013
  };
5770
6014
  }
5771
6015
  else {
@@ -5823,13 +6067,14 @@ function fetchGlobalResources(resources, suffix, sourceHandler) {
5823
6067
  /**
5824
6068
  * if app not prefetch & not unmount, then app is active
5825
6069
  * @param excludeHiddenApp exclude hidden keep-alive app, default is false
6070
+ * @param excludePreRender exclude pre render app
5826
6071
  * @returns active apps
5827
6072
  */
5828
- function getActiveApps(excludeHiddenApp = false) {
6073
+ function getActiveApps({ excludeHiddenApp = false, excludePreRender = false, } = {}) {
5829
6074
  const activeApps = [];
5830
6075
  appInstanceMap.forEach((app, appName) => {
5831
6076
  if (appStates.UNMOUNT !== app.getAppState() &&
5832
- !app.isPrefetch &&
6077
+ (!app.isPrefetch || (app.isPrerender && !excludePreRender)) &&
5833
6078
  (!excludeHiddenApp ||
5834
6079
  keepAliveStates.KEEP_ALIVE_HIDDEN !== app.getKeepAliveState())) {
5835
6080
  activeApps.push(appName);
@@ -5862,6 +6107,7 @@ function unmountApp(appName, options) {
5862
6107
  app.unmount({
5863
6108
  destroy: true,
5864
6109
  clearData: true,
6110
+ keepRouteState: true,
5865
6111
  unmountcb: resolve.bind(null, true)
5866
6112
  });
5867
6113
  }
@@ -5869,6 +6115,7 @@ function unmountApp(appName, options) {
5869
6115
  app.unmount({
5870
6116
  destroy: false,
5871
6117
  clearData: !!options.clearData,
6118
+ keepRouteState: true,
5872
6119
  unmountcb: resolve.bind(null, true)
5873
6120
  });
5874
6121
  }