@micro-zoe/micro-app 0.5.3 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- const version = '0.5.3';
1
+ const version = '0.7.0';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -41,6 +41,9 @@ function isBoundFunction(target) {
41
41
  function isShadowRoot(target) {
42
42
  return typeof ShadowRoot !== 'undefined' && target instanceof ShadowRoot;
43
43
  }
44
+ const rawDefineProperty = Object.defineProperty;
45
+ const rawDefineProperties = Object.defineProperties;
46
+ const rawHasOwnProperty = Object.prototype.hasOwnProperty;
44
47
  /**
45
48
  * format error log
46
49
  * @param msg message
@@ -230,6 +233,17 @@ let currentMicroAppName = null;
230
233
  function setCurrentAppName(appName) {
231
234
  currentMicroAppName = appName;
232
235
  }
236
+ let isWaitingForReset = false;
237
+ function throttleDeferForSetAppName(appName) {
238
+ if (!isWaitingForReset || currentMicroAppName !== appName) {
239
+ isWaitingForReset = true;
240
+ setCurrentAppName(appName);
241
+ defer(() => {
242
+ isWaitingForReset = false;
243
+ setCurrentAppName(null);
244
+ });
245
+ }
246
+ }
233
247
  // get the currently running app.name
234
248
  function getCurrentAppName() {
235
249
  return currentMicroAppName;
@@ -297,16 +311,16 @@ var ObservedAttrName;
297
311
  ObservedAttrName["URL"] = "url";
298
312
  })(ObservedAttrName || (ObservedAttrName = {}));
299
313
  // app status
300
- var appStatus;
301
- (function (appStatus) {
302
- appStatus["NOT_LOADED"] = "NOT_LOADED";
303
- appStatus["LOADING_SOURCE_CODE"] = "LOADING_SOURCE_CODE";
304
- appStatus["LOAD_SOURCE_FINISHED"] = "LOAD_SOURCE_FINISHED";
305
- appStatus["LOAD_SOURCE_ERROR"] = "LOAD_SOURCE_ERROR";
306
- appStatus["MOUNTING"] = "MOUNTING";
307
- appStatus["MOUNTED"] = "MOUNTED";
308
- appStatus["UNMOUNT"] = "UNMOUNT";
309
- })(appStatus || (appStatus = {}));
314
+ var appStates;
315
+ (function (appStates) {
316
+ appStates["NOT_LOADED"] = "NOT_LOADED";
317
+ appStates["LOADING_SOURCE_CODE"] = "LOADING_SOURCE_CODE";
318
+ appStates["LOAD_SOURCE_FINISHED"] = "LOAD_SOURCE_FINISHED";
319
+ appStates["LOAD_SOURCE_ERROR"] = "LOAD_SOURCE_ERROR";
320
+ appStates["MOUNTING"] = "MOUNTING";
321
+ appStates["MOUNTED"] = "MOUNTED";
322
+ appStates["UNMOUNT"] = "UNMOUNT";
323
+ })(appStates || (appStates = {}));
310
324
  // lifecycles
311
325
  var lifeCycles;
312
326
  (function (lifeCycles) {
@@ -315,7 +329,18 @@ var lifeCycles;
315
329
  lifeCycles["MOUNTED"] = "mounted";
316
330
  lifeCycles["UNMOUNT"] = "unmount";
317
331
  lifeCycles["ERROR"] = "error";
332
+ // 👇 keep-alive only
333
+ lifeCycles["BEFORESHOW"] = "beforeshow";
334
+ lifeCycles["AFTERSHOW"] = "aftershow";
335
+ lifeCycles["AFTERHIDDEN"] = "afterhidden";
318
336
  })(lifeCycles || (lifeCycles = {}));
337
+ // keep-alive status
338
+ var keepAliveStates;
339
+ (function (keepAliveStates) {
340
+ keepAliveStates["KEEP_ALIVE_SHOW"] = "KEEP_ALIVE_SHOW";
341
+ keepAliveStates["KEEP_ALIVE_HIDDEN"] = "KEEP_ALIVE_HIDDEN";
342
+ })(keepAliveStates || (keepAliveStates = {}));
343
+ const globalKeyToBeCached = 'window,self,globalThis,Array,Object,String,Boolean,Math,Number,Symbol,Date,Promise,Function,Proxy,WeakMap,WeakRef,WeakSet,Set,Map,Reflect,Element,Node,Document,RegExp,Error,TypeError,JSON,isNaN,parseFloat,parseInt,performance,console,decodeURI,encodeURI,decodeURIComponent,encodeURIComponent,location,navigator,undefined';
319
344
 
320
345
  /**
321
346
  * fetch source of html, js, css
@@ -333,6 +358,10 @@ function fetchSource(url, appName = null, options = {}) {
333
358
  }
334
359
 
335
360
  const globalEnv = {};
361
+ /**
362
+ * Note loop nesting
363
+ * Only prototype or unique values can be put here
364
+ */
336
365
  function initGlobalEnv() {
337
366
  if (isBrowser) {
338
367
  /**
@@ -340,12 +369,13 @@ function initGlobalEnv() {
340
369
  * pay attention to this binding
341
370
  */
342
371
  const rawSetAttribute = Element.prototype.setAttribute;
343
- const rawAppendChild = Node.prototype.appendChild;
344
- const rawInsertBefore = Node.prototype.insertBefore;
345
- const rawReplaceChild = Node.prototype.replaceChild;
346
- const rawRemoveChild = Node.prototype.removeChild;
372
+ const rawAppendChild = Element.prototype.appendChild;
373
+ const rawInsertBefore = Element.prototype.insertBefore;
374
+ const rawReplaceChild = Element.prototype.replaceChild;
375
+ const rawRemoveChild = Element.prototype.removeChild;
347
376
  const rawAppend = Element.prototype.append;
348
377
  const rawPrepend = Element.prototype.prepend;
378
+ const rawCloneNode = Element.prototype.cloneNode;
349
379
  const rawCreateElement = Document.prototype.createElement;
350
380
  const rawCreateElementNS = Document.prototype.createElementNS;
351
381
  const rawCreateDocumentFragment = Document.prototype.createDocumentFragment;
@@ -355,6 +385,13 @@ function initGlobalEnv() {
355
385
  const rawGetElementsByClassName = Document.prototype.getElementsByClassName;
356
386
  const rawGetElementsByTagName = Document.prototype.getElementsByTagName;
357
387
  const rawGetElementsByName = Document.prototype.getElementsByName;
388
+ const ImageProxy = new Proxy(Image, {
389
+ construct(Target, args) {
390
+ const elementImage = new Target(...args);
391
+ elementImage.__MICRO_APP_NAME__ = getCurrentAppName();
392
+ return elementImage;
393
+ },
394
+ });
358
395
  const rawWindow = Function('return window')();
359
396
  const rawDocument = Function('return document')();
360
397
  const supportModuleScript = isSupportModuleScript();
@@ -382,6 +419,7 @@ function initGlobalEnv() {
382
419
  rawRemoveChild,
383
420
  rawAppend,
384
421
  rawPrepend,
422
+ rawCloneNode,
385
423
  rawCreateElement,
386
424
  rawCreateElementNS,
387
425
  rawCreateDocumentFragment,
@@ -391,6 +429,7 @@ function initGlobalEnv() {
391
429
  rawGetElementsByClassName,
392
430
  rawGetElementsByTagName,
393
431
  rawGetElementsByName,
432
+ ImageProxy,
394
433
  // common global vars
395
434
  rawWindow,
396
435
  rawDocument,
@@ -409,13 +448,6 @@ function initGlobalEnv() {
409
448
  }
410
449
  }
411
450
 
412
- // https://developer.mozilla.org/zh-CN/docs/Web/API/CSSRule
413
- var CSSRuleType;
414
- (function (CSSRuleType) {
415
- CSSRuleType[CSSRuleType["STYLE_RULE"] = 1] = "STYLE_RULE";
416
- CSSRuleType[CSSRuleType["MEDIA_RULE"] = 4] = "MEDIA_RULE";
417
- CSSRuleType[CSSRuleType["SUPPORTS_RULE"] = 12] = "SUPPORTS_RULE";
418
- })(CSSRuleType || (CSSRuleType = {}));
419
451
  /**
420
452
  * Bind css scope
421
453
  * Special case:
@@ -496,14 +528,15 @@ function scopedPackRule(rule, prefix, packName) {
496
528
  function scopedRule(rules, prefix) {
497
529
  let result = '';
498
530
  for (const rule of rules) {
499
- switch (rule.type) {
500
- case CSSRuleType.STYLE_RULE:
531
+ // https://developer.mozilla.org/zh-CN/docs/Web/API/CSSRule
532
+ switch (rule.constructor.name) {
533
+ case 'CSSStyleRule':
501
534
  result += scopedStyleRule(rule, prefix);
502
535
  break;
503
- case CSSRuleType.MEDIA_RULE:
536
+ case 'CSSMediaRule':
504
537
  result += scopedPackRule(rule, prefix, 'media');
505
538
  break;
506
- case CSSRuleType.SUPPORTS_RULE:
539
+ case 'CSSSupportsRule':
507
540
  result += scopedPackRule(rule, prefix, 'supports');
508
541
  break;
509
542
  default:
@@ -897,17 +930,21 @@ function execScripts(scriptList, app, initedHook) {
897
930
  }
898
931
  }
899
932
  if (deferScriptPromise.length) {
900
- Promise.all(deferScriptPromise).then((res) => {
901
- res.forEach((code, index) => {
902
- const [url, info] = deferScriptInfo[index];
903
- info.code = info.code || code;
904
- runScript(url, app, info, false, initedHook);
905
- !info.module && initedHook(false);
906
- });
907
- initedHook(isUndefined(initedHook.moduleCount));
908
- }).catch((err) => {
933
+ promiseStream(deferScriptPromise, (res) => {
934
+ const info = deferScriptInfo[res.index][1];
935
+ info.code = info.code || res.data;
936
+ }, (err) => {
937
+ initedHook.errorCount = initedHook.errorCount ? ++initedHook.errorCount : 1;
909
938
  logError(err, app.name);
910
- initedHook(true);
939
+ }, () => {
940
+ deferScriptInfo.forEach(([url, info]) => {
941
+ if (info.code) {
942
+ runScript(url, app, info, false, initedHook);
943
+ !info.module && initedHook(false);
944
+ }
945
+ });
946
+ initedHook(isUndefined(initedHook.moduleCount) ||
947
+ initedHook.errorCount === deferScriptPromise.length);
911
948
  });
912
949
  }
913
950
  else {
@@ -1042,7 +1079,7 @@ function bindScope(url, app, code, module) {
1042
1079
  }
1043
1080
  if (app.sandBox && !module) {
1044
1081
  globalEnv.rawWindow.__MICRO_APP_PROXY_WINDOW__ = app.sandBox.proxyWindow;
1045
- return `;(function(window, self){with(window){;${code}\n}}).call(window.__MICRO_APP_PROXY_WINDOW__, window.__MICRO_APP_PROXY_WINDOW__, window.__MICRO_APP_PROXY_WINDOW__);`;
1082
+ return `;(function(proxyWindow){with(proxyWindow.__MICRO_APP_WINDOW__){(function(${globalKeyToBeCached}){;${code}\n}).call(proxyWindow,${globalKeyToBeCached})}})(window.__MICRO_APP_PROXY_WINDOW__);`;
1046
1083
  }
1047
1084
  return code;
1048
1085
  }
@@ -1180,498 +1217,199 @@ function extractHtml(app) {
1180
1217
  });
1181
1218
  }
1182
1219
 
1183
- const boundedMap = new WeakMap();
1184
- function isBoundedFunction(value) {
1185
- if (boundedMap.has(value)) {
1186
- return boundedMap.get(value);
1187
- }
1188
- // bind function
1189
- const boundFunction = isBoundFunction(value);
1190
- boundedMap.set(value, boundFunction);
1191
- return boundFunction;
1192
- }
1193
- const constructorMap = new WeakMap();
1194
- function isConstructor(value) {
1195
- if (constructorMap.has(value)) {
1196
- return constructorMap.get(value);
1197
- }
1198
- const valueStr = value.toString();
1199
- const result = (value.prototype &&
1200
- value.prototype.constructor === value &&
1201
- Object.getOwnPropertyNames(value.prototype).length > 1) ||
1202
- /^function\s+[A-Z]/.test(valueStr) ||
1203
- /^class\s+/.test(valueStr);
1204
- constructorMap.set(value, result);
1205
- return result;
1206
- }
1207
- const rawWindowMethodMap = new WeakMap();
1208
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
1209
- function bindFunctionToRawWidow(rawWindow, value) {
1210
- if (rawWindowMethodMap.has(value)) {
1211
- return rawWindowMethodMap.get(value);
1220
+ class EventCenter {
1221
+ constructor() {
1222
+ this.eventList = new Map();
1212
1223
  }
1213
- if (isFunction(value) && !isConstructor(value) && !isBoundedFunction(value)) {
1214
- const bindRawWindowValue = value.bind(rawWindow);
1215
- for (const key in value) {
1216
- bindRawWindowValue[key] = value[key];
1217
- }
1218
- if (value.hasOwnProperty('prototype') && !bindRawWindowValue.hasOwnProperty('prototype')) {
1219
- bindRawWindowValue.prototype = value.prototype;
1224
+ // whether the name is legal
1225
+ isLegalName(name) {
1226
+ if (!name) {
1227
+ logError('event-center: Invalid name');
1228
+ return false;
1220
1229
  }
1221
- rawWindowMethodMap.set(value, bindRawWindowValue);
1222
- return bindRawWindowValue;
1223
- }
1224
- return value;
1225
- }
1226
-
1227
- // document.onclick binding list, the binding function of each application is unique
1228
- const documentClickListMap = new Map();
1229
- let hasRewriteDocumentOnClick = false;
1230
- /**
1231
- * Rewrite document.onclick and execute it only once
1232
- */
1233
- function overwriteDocumentOnClick() {
1234
- hasRewriteDocumentOnClick = true;
1235
- if (Object.getOwnPropertyDescriptor(document, 'onclick')) {
1236
- return logWarn('Cannot redefine document property onclick');
1237
- }
1238
- const rawOnClick = document.onclick;
1239
- document.onclick = null;
1240
- let hasDocumentClickInited = false;
1241
- function onClickHandler(e) {
1242
- documentClickListMap.forEach((f) => {
1243
- isFunction(f) && f.call(document, e);
1244
- });
1230
+ return true;
1245
1231
  }
1246
- Object.defineProperty(document, 'onclick', {
1247
- configurable: true,
1248
- enumerable: true,
1249
- get() {
1250
- const appName = getCurrentAppName();
1251
- return appName ? documentClickListMap.get(appName) : documentClickListMap.get('base');
1252
- },
1253
- set(f) {
1254
- const appName = getCurrentAppName();
1255
- if (appName) {
1256
- documentClickListMap.set(appName, f);
1232
+ /**
1233
+ * add listener
1234
+ * @param name event name
1235
+ * @param f listener
1236
+ * @param autoTrigger If there is cached data when first bind listener, whether it needs to trigger, default is false
1237
+ */
1238
+ on(name, f, autoTrigger = false) {
1239
+ if (this.isLegalName(name)) {
1240
+ if (!isFunction(f)) {
1241
+ return logError('event-center: Invalid callback function');
1257
1242
  }
1258
- else {
1259
- documentClickListMap.set('base', f);
1243
+ let eventInfo = this.eventList.get(name);
1244
+ if (!eventInfo) {
1245
+ eventInfo = {
1246
+ data: {},
1247
+ callbacks: new Set(),
1248
+ };
1249
+ this.eventList.set(name, eventInfo);
1260
1250
  }
1261
- if (!hasDocumentClickInited && isFunction(f)) {
1262
- hasDocumentClickInited = true;
1263
- globalEnv.rawDocumentAddEventListener.call(globalEnv.rawDocument, 'click', onClickHandler, false);
1251
+ else if (autoTrigger && Object.getOwnPropertyNames(eventInfo.data).length) {
1252
+ // auto trigger when data not null
1253
+ f(eventInfo.data);
1264
1254
  }
1255
+ eventInfo.callbacks.add(f);
1265
1256
  }
1266
- });
1267
- rawOnClick && (document.onclick = rawOnClick);
1268
- }
1269
- /**
1270
- * The document event is globally, we need to clear these event bindings when micro application unmounted
1271
- */
1272
- const documentEventListenerMap = new Map();
1273
- function effectDocumentEvent() {
1274
- const { rawDocument, rawDocumentAddEventListener, rawDocumentRemoveEventListener, } = globalEnv;
1275
- !hasRewriteDocumentOnClick && overwriteDocumentOnClick();
1276
- document.addEventListener = function (type, listener, options) {
1277
- var _a;
1278
- const appName = getCurrentAppName();
1279
- /**
1280
- * ignore bound function of document event in umd mode, used to solve problem of react global events
1281
- */
1282
- if (appName && !(((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.umdMode) && isBoundFunction(listener))) {
1283
- const appListenersMap = documentEventListenerMap.get(appName);
1284
- if (appListenersMap) {
1285
- const appListenerList = appListenersMap.get(type);
1286
- if (appListenerList) {
1287
- appListenerList.add(listener);
1257
+ }
1258
+ // remove listener, but the data is not cleared
1259
+ off(name, f) {
1260
+ if (this.isLegalName(name)) {
1261
+ const eventInfo = this.eventList.get(name);
1262
+ if (eventInfo) {
1263
+ if (isFunction(f)) {
1264
+ eventInfo.callbacks.delete(f);
1288
1265
  }
1289
1266
  else {
1290
- appListenersMap.set(type, new Set([listener]));
1267
+ eventInfo.callbacks.clear();
1291
1268
  }
1292
1269
  }
1293
- else {
1294
- documentEventListenerMap.set(appName, new Map([[type, new Set([listener])]]));
1295
- }
1296
- listener && (listener.__MICRO_MARK_OPTIONS__ = options);
1297
1270
  }
1298
- rawDocumentAddEventListener.call(rawDocument, type, listener, options);
1299
- };
1300
- document.removeEventListener = function (type, listener, options) {
1301
- var _a;
1302
- const appName = getCurrentAppName();
1303
- if (appName && !(((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.umdMode) && isBoundFunction(listener))) {
1304
- const appListenersMap = documentEventListenerMap.get(appName);
1305
- if (appListenersMap) {
1306
- const appListenerList = appListenersMap.get(type);
1307
- if ((appListenerList === null || appListenerList === void 0 ? void 0 : appListenerList.size) && appListenerList.has(listener)) {
1308
- appListenerList.delete(listener);
1271
+ }
1272
+ // dispatch data
1273
+ dispatch(name, data) {
1274
+ if (this.isLegalName(name)) {
1275
+ if (!isPlainObject(data)) {
1276
+ return logError('event-center: data must be object');
1277
+ }
1278
+ let eventInfo = this.eventList.get(name);
1279
+ if (eventInfo) {
1280
+ // Update when the data is not equal
1281
+ if (eventInfo.data !== data) {
1282
+ eventInfo.data = data;
1283
+ for (const f of eventInfo.callbacks) {
1284
+ f(data);
1285
+ }
1309
1286
  }
1310
1287
  }
1288
+ else {
1289
+ eventInfo = {
1290
+ data: data,
1291
+ callbacks: new Set(),
1292
+ };
1293
+ this.eventList.set(name, eventInfo);
1294
+ }
1311
1295
  }
1312
- rawDocumentRemoveEventListener.call(rawDocument, type, listener, options);
1313
- };
1314
- }
1315
- // Clear the document event agent
1316
- function releaseEffectDocumentEvent() {
1317
- document.addEventListener = globalEnv.rawDocumentAddEventListener;
1318
- document.removeEventListener = globalEnv.rawDocumentRemoveEventListener;
1296
+ }
1297
+ // get data
1298
+ getData(name) {
1299
+ var _a;
1300
+ const eventInfo = this.eventList.get(name);
1301
+ return (_a = eventInfo === null || eventInfo === void 0 ? void 0 : eventInfo.data) !== null && _a !== void 0 ? _a : null;
1302
+ }
1319
1303
  }
1304
+
1305
+ const eventCenter = new EventCenter();
1320
1306
  /**
1321
1307
  * Format event name
1322
- * @param type event name
1323
- * @param microWindow micro window
1308
+ * @param appName app.name
1309
+ * @param fromBaseApp is from base app
1324
1310
  */
1325
- function formatEventType(type, microWindow) {
1326
- if (type === 'unmount') {
1327
- return `unmount-${microWindow.__MICRO_APP_NAME__}`;
1328
- }
1329
- return type;
1311
+ function formatEventName(appName, fromBaseApp) {
1312
+ if (!isString(appName) || !appName)
1313
+ return '';
1314
+ return fromBaseApp ? `__from_base_app_${appName}__` : `__from_micro_app_${appName}__`;
1330
1315
  }
1331
- /**
1332
- * Rewrite side-effect events
1333
- * @param microWindow micro window
1334
- */
1335
- function effect(microWindow) {
1336
- const appName = microWindow.__MICRO_APP_NAME__;
1337
- const eventListenerMap = new Map();
1338
- const intervalIdMap = new Map();
1339
- const timeoutIdMap = new Map();
1340
- const { rawWindow, rawDocument, rawWindowAddEventListener, rawWindowRemoveEventListener, rawSetInterval, rawSetTimeout, rawClearInterval, rawClearTimeout, rawDocumentRemoveEventListener, } = globalEnv;
1341
- // listener may be null, e.g test-passive
1342
- microWindow.addEventListener = function (type, listener, options) {
1343
- type = formatEventType(type, microWindow);
1344
- const listenerList = eventListenerMap.get(type);
1345
- if (listenerList) {
1346
- listenerList.add(listener);
1316
+ // Global data
1317
+ class EventCenterForGlobal {
1318
+ /**
1319
+ * add listener of global data
1320
+ * @param cb listener
1321
+ * @param autoTrigger If there is cached data when first bind listener, whether it needs to trigger, default is false
1322
+ */
1323
+ addGlobalDataListener(cb, autoTrigger) {
1324
+ const appName = this.appName;
1325
+ // if appName exists, this is in sub app
1326
+ if (appName) {
1327
+ cb.__APP_NAME__ = appName;
1328
+ cb.__AUTO_TRIGGER__ = autoTrigger;
1347
1329
  }
1348
- else {
1349
- eventListenerMap.set(type, new Set([listener]));
1330
+ eventCenter.on('global', cb, autoTrigger);
1331
+ }
1332
+ /**
1333
+ * remove listener of global data
1334
+ * @param cb listener
1335
+ */
1336
+ removeGlobalDataListener(cb) {
1337
+ isFunction(cb) && eventCenter.off('global', cb);
1338
+ }
1339
+ /**
1340
+ * dispatch global data
1341
+ * @param data data
1342
+ */
1343
+ setGlobalData(data) {
1344
+ // clear dom scope before dispatch global data, apply to micro app
1345
+ removeDomScope();
1346
+ eventCenter.dispatch('global', data);
1347
+ }
1348
+ /**
1349
+ * get global data
1350
+ */
1351
+ getGlobalData() {
1352
+ return eventCenter.getData('global');
1353
+ }
1354
+ /**
1355
+ * clear all listener of global data
1356
+ * if appName exists, only the specified functions is cleared
1357
+ * if appName not exists, only clear the base app functions
1358
+ */
1359
+ clearGlobalDataListener() {
1360
+ const appName = this.appName;
1361
+ const eventInfo = eventCenter.eventList.get('global');
1362
+ if (eventInfo) {
1363
+ for (const cb of eventInfo.callbacks) {
1364
+ if ((appName && appName === cb.__APP_NAME__) ||
1365
+ !(appName || cb.__APP_NAME__)) {
1366
+ eventInfo.callbacks.delete(cb);
1367
+ }
1368
+ }
1350
1369
  }
1351
- listener && (listener.__MICRO_MARK_OPTIONS__ = options);
1352
- rawWindowAddEventListener.call(rawWindow, type, listener, options);
1353
- };
1354
- microWindow.removeEventListener = function (type, listener, options) {
1355
- type = formatEventType(type, microWindow);
1356
- const listenerList = eventListenerMap.get(type);
1357
- if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
1358
- listenerList.delete(listener);
1359
- }
1360
- rawWindowRemoveEventListener.call(rawWindow, type, listener, options);
1361
- };
1362
- microWindow.setInterval = function (handler, timeout, ...args) {
1363
- const intervalId = rawSetInterval.call(rawWindow, handler, timeout, ...args);
1364
- intervalIdMap.set(intervalId, { handler, timeout, args });
1365
- return intervalId;
1366
- };
1367
- microWindow.setTimeout = function (handler, timeout, ...args) {
1368
- const timeoutId = rawSetTimeout.call(rawWindow, handler, timeout, ...args);
1369
- timeoutIdMap.set(timeoutId, { handler, timeout, args });
1370
- return timeoutId;
1371
- };
1372
- microWindow.clearInterval = function (intervalId) {
1373
- intervalIdMap.delete(intervalId);
1374
- rawClearInterval.call(rawWindow, intervalId);
1375
- };
1376
- microWindow.clearTimeout = function (timeoutId) {
1377
- timeoutIdMap.delete(timeoutId);
1378
- rawClearTimeout.call(rawWindow, timeoutId);
1379
- };
1380
- const umdWindowListenerMap = new Map();
1381
- const umdDocumentListenerMap = new Map();
1382
- let umdIntervalIdMap = new Map();
1383
- let umdTimeoutIdMap = new Map();
1384
- let umdOnClickHandler;
1385
- // record event and timer before exec umdMountHook
1386
- const recordUmdEffect = () => {
1387
- // record window event
1388
- eventListenerMap.forEach((listenerList, type) => {
1389
- if (listenerList.size) {
1390
- umdWindowListenerMap.set(type, new Set(listenerList));
1391
- }
1392
- });
1393
- // record timers
1394
- if (intervalIdMap.size) {
1395
- umdIntervalIdMap = new Map(intervalIdMap);
1396
- }
1397
- if (timeoutIdMap.size) {
1398
- umdTimeoutIdMap = new Map(timeoutIdMap);
1399
- }
1400
- // record onclick handler
1401
- umdOnClickHandler = documentClickListMap.get(appName);
1402
- // record document event
1403
- const documentAppListenersMap = documentEventListenerMap.get(appName);
1404
- if (documentAppListenersMap) {
1405
- documentAppListenersMap.forEach((listenerList, type) => {
1406
- if (listenerList.size) {
1407
- umdDocumentListenerMap.set(type, new Set(listenerList));
1408
- }
1409
- });
1410
- }
1411
- };
1412
- // rebuild event and timer before remount umd app
1413
- const rebuildUmdEffect = () => {
1414
- // rebuild window event
1415
- umdWindowListenerMap.forEach((listenerList, type) => {
1416
- for (const listener of listenerList) {
1417
- microWindow.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_MARK_OPTIONS__);
1418
- }
1419
- });
1420
- // rebuild timer
1421
- umdIntervalIdMap.forEach((info) => {
1422
- microWindow.setInterval(info.handler, info.timeout, ...info.args);
1423
- });
1424
- umdTimeoutIdMap.forEach((info) => {
1425
- microWindow.setTimeout(info.handler, info.timeout, ...info.args);
1426
- });
1427
- // rebuild onclick event
1428
- umdOnClickHandler && documentClickListMap.set(appName, umdOnClickHandler);
1429
- // rebuild document event
1430
- setCurrentAppName(appName);
1431
- umdDocumentListenerMap.forEach((listenerList, type) => {
1432
- for (const listener of listenerList) {
1433
- document.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_MARK_OPTIONS__);
1434
- }
1435
- });
1436
- setCurrentAppName(null);
1437
- };
1438
- // release all event listener & interval & timeout when unmount app
1439
- const releaseEffect = () => {
1440
- // Clear window binding events
1441
- if (eventListenerMap.size) {
1442
- eventListenerMap.forEach((listenerList, type) => {
1443
- for (const listener of listenerList) {
1444
- rawWindowRemoveEventListener.call(rawWindow, type, listener);
1445
- }
1446
- });
1447
- eventListenerMap.clear();
1448
- }
1449
- // Clear timers
1450
- if (intervalIdMap.size) {
1451
- intervalIdMap.forEach((_, intervalId) => {
1452
- rawClearInterval.call(rawWindow, intervalId);
1453
- });
1454
- intervalIdMap.clear();
1455
- }
1456
- if (timeoutIdMap.size) {
1457
- timeoutIdMap.forEach((_, timeoutId) => {
1458
- rawClearTimeout.call(rawWindow, timeoutId);
1459
- });
1460
- timeoutIdMap.clear();
1461
- }
1462
- // Clear the function bound by micro application through document.onclick
1463
- documentClickListMap.delete(appName);
1464
- // Clear document binding event
1465
- const documentAppListenersMap = documentEventListenerMap.get(appName);
1466
- if (documentAppListenersMap) {
1467
- documentAppListenersMap.forEach((listenerList, type) => {
1468
- for (const listener of listenerList) {
1469
- rawDocumentRemoveEventListener.call(rawDocument, type, listener);
1470
- }
1471
- });
1472
- documentAppListenersMap.clear();
1473
- }
1474
- };
1475
- return {
1476
- recordUmdEffect,
1477
- rebuildUmdEffect,
1478
- releaseEffect,
1479
- };
1480
- }
1481
-
1482
- class EventCenter {
1483
- constructor() {
1484
- this.eventList = new Map();
1485
- }
1486
- // whether the name is legal
1487
- isLegalName(name) {
1488
- if (!name) {
1489
- logError('event-center: Invalid name');
1490
- return false;
1491
- }
1492
- return true;
1493
1370
  }
1371
+ }
1372
+ // Event center for base app
1373
+ class EventCenterForBaseApp extends EventCenterForGlobal {
1494
1374
  /**
1495
1375
  * add listener
1496
- * @param name event name
1497
- * @param f listener
1376
+ * @param appName app.name
1377
+ * @param cb listener
1498
1378
  * @param autoTrigger If there is cached data when first bind listener, whether it needs to trigger, default is false
1499
1379
  */
1500
- on(name, f, autoTrigger = false) {
1501
- if (this.isLegalName(name)) {
1502
- if (!isFunction(f)) {
1503
- return logError('event-center: Invalid callback function');
1504
- }
1505
- let eventInfo = this.eventList.get(name);
1506
- if (!eventInfo) {
1507
- eventInfo = {
1508
- data: {},
1509
- callbacks: new Set(),
1510
- };
1511
- this.eventList.set(name, eventInfo);
1512
- }
1513
- else if (autoTrigger && Object.getOwnPropertyNames(eventInfo.data).length) {
1514
- // auto trigger when data not null
1515
- f(eventInfo.data);
1516
- }
1517
- eventInfo.callbacks.add(f);
1518
- }
1519
- }
1520
- // remove listener, but the data is not cleared
1521
- off(name, f) {
1522
- if (this.isLegalName(name)) {
1523
- const eventInfo = this.eventList.get(name);
1524
- if (eventInfo) {
1525
- if (isFunction(f)) {
1526
- eventInfo.callbacks.delete(f);
1527
- }
1528
- else {
1529
- eventInfo.callbacks.clear();
1530
- }
1531
- }
1532
- }
1533
- }
1534
- // dispatch data
1535
- dispatch(name, data) {
1536
- if (this.isLegalName(name)) {
1537
- if (!isPlainObject(data)) {
1538
- return logError('event-center: data must be object');
1539
- }
1540
- let eventInfo = this.eventList.get(name);
1541
- if (eventInfo) {
1542
- // Update when the data is not equal
1543
- if (eventInfo.data !== data) {
1544
- eventInfo.data = data;
1545
- for (const f of eventInfo.callbacks) {
1546
- f(data);
1547
- }
1548
- }
1549
- }
1550
- else {
1551
- eventInfo = {
1552
- data: data,
1553
- callbacks: new Set(),
1554
- };
1555
- this.eventList.set(name, eventInfo);
1556
- }
1557
- }
1558
- }
1559
- // get data
1560
- getData(name) {
1561
- var _a;
1562
- const eventInfo = this.eventList.get(name);
1563
- return (_a = eventInfo === null || eventInfo === void 0 ? void 0 : eventInfo.data) !== null && _a !== void 0 ? _a : null;
1380
+ addDataListener(appName, cb, autoTrigger) {
1381
+ eventCenter.on(formatEventName(formatAppName(appName), false), cb, autoTrigger);
1564
1382
  }
1565
- }
1566
-
1567
- const eventCenter = new EventCenter();
1568
- /**
1569
- * Format event name
1570
- * @param appName app.name
1571
- * @param fromBaseApp is from base app
1572
- */
1573
- function formatEventName(appName, fromBaseApp) {
1574
- if (!isString(appName) || !appName)
1575
- return '';
1576
- return fromBaseApp ? `__from_base_app_${appName}__` : `__from_micro_app_${appName}__`;
1577
- }
1578
- // Global data
1579
- class EventCenterForGlobal {
1580
1383
  /**
1581
- * add listener of global data
1384
+ * remove listener
1385
+ * @param appName app.name
1582
1386
  * @param cb listener
1583
- * @param autoTrigger If there is cached data when first bind listener, whether it needs to trigger, default is false
1584
1387
  */
1585
- addGlobalDataListener(cb, autoTrigger) {
1586
- const appName = this.appName;
1587
- // if appName exists, this is in sub app
1588
- if (appName) {
1589
- cb.__APP_NAME__ = appName;
1590
- cb.__AUTO_TRIGGER__ = autoTrigger;
1591
- }
1592
- eventCenter.on('global', cb, autoTrigger);
1388
+ removeDataListener(appName, cb) {
1389
+ isFunction(cb) && eventCenter.off(formatEventName(formatAppName(appName), false), cb);
1593
1390
  }
1594
1391
  /**
1595
- * remove listener of global data
1596
- * @param cb listener
1392
+ * get data from micro app or base app
1393
+ * @param appName app.name
1394
+ * @param fromBaseApp whether get data from base app, default is false
1597
1395
  */
1598
- removeGlobalDataListener(cb) {
1599
- isFunction(cb) && eventCenter.off('global', cb);
1396
+ getData(appName, fromBaseApp = false) {
1397
+ return eventCenter.getData(formatEventName(formatAppName(appName), fromBaseApp));
1600
1398
  }
1601
1399
  /**
1602
- * dispatch global data
1400
+ * Dispatch data to the specified micro app
1401
+ * @param appName app.name
1603
1402
  * @param data data
1604
1403
  */
1605
- setGlobalData(data) {
1606
- // clear dom scope before dispatch global data, apply to micro app
1607
- removeDomScope();
1608
- eventCenter.dispatch('global', data);
1404
+ setData(appName, data) {
1405
+ eventCenter.dispatch(formatEventName(formatAppName(appName), true), data);
1609
1406
  }
1610
1407
  /**
1611
- * get global data
1408
+ * clear all listener for specified micro app
1409
+ * @param appName app.name
1612
1410
  */
1613
- getGlobalData() {
1614
- return eventCenter.getData('global');
1615
- }
1616
- /**
1617
- * clear all listener of global data
1618
- * if appName exists, only the specified functions is cleared
1619
- * if appName not exists, only clear the base app functions
1620
- */
1621
- clearGlobalDataListener() {
1622
- const appName = this.appName;
1623
- const eventInfo = eventCenter.eventList.get('global');
1624
- if (eventInfo) {
1625
- for (const cb of eventInfo.callbacks) {
1626
- if ((appName && appName === cb.__APP_NAME__) ||
1627
- !(appName || cb.__APP_NAME__)) {
1628
- eventInfo.callbacks.delete(cb);
1629
- }
1630
- }
1631
- }
1632
- }
1633
- }
1634
- // Event center for base app
1635
- class EventCenterForBaseApp extends EventCenterForGlobal {
1636
- /**
1637
- * add listener
1638
- * @param appName app.name
1639
- * @param cb listener
1640
- * @param autoTrigger If there is cached data when first bind listener, whether it needs to trigger, default is false
1641
- */
1642
- addDataListener(appName, cb, autoTrigger) {
1643
- eventCenter.on(formatEventName(formatAppName(appName), false), cb, autoTrigger);
1644
- }
1645
- /**
1646
- * remove listener
1647
- * @param appName app.name
1648
- * @param cb listener
1649
- */
1650
- removeDataListener(appName, cb) {
1651
- isFunction(cb) && eventCenter.off(formatEventName(formatAppName(appName), false), cb);
1652
- }
1653
- /**
1654
- * get data from micro app or base app
1655
- * @param appName app.name
1656
- * @param fromBaseApp whether get data from base app, default is false
1657
- */
1658
- getData(appName, fromBaseApp = false) {
1659
- return eventCenter.getData(formatEventName(formatAppName(appName), fromBaseApp));
1660
- }
1661
- /**
1662
- * Dispatch data to the specified micro app
1663
- * @param appName app.name
1664
- * @param data data
1665
- */
1666
- setData(appName, data) {
1667
- eventCenter.dispatch(formatEventName(formatAppName(appName), true), data);
1668
- }
1669
- /**
1670
- * clear all listener for specified micro app
1671
- * @param appName app.name
1672
- */
1673
- clearDataListener(appName) {
1674
- eventCenter.off(formatEventName(formatAppName(appName), false));
1411
+ clearDataListener(appName) {
1412
+ eventCenter.off(formatEventName(formatAppName(appName), false));
1675
1413
  }
1676
1414
  }
1677
1415
  // Event center for sub app
@@ -1760,178 +1498,365 @@ function rebuildDataCenterSnapshot(microAppEventCneter) {
1760
1498
  }
1761
1499
  }
1762
1500
 
1763
- // Variables that can escape to rawWindow
1764
- const staticEscapeProperties = [
1765
- 'System',
1766
- '__cjsWrapper',
1767
- '__REACT_ERROR_OVERLAY_GLOBAL_HOOK__',
1768
- ];
1769
- // Variables that can only assigned to rawWindow
1770
- const escapeSetterKeyList = [
1771
- 'location',
1772
- ];
1773
- const unscopables = {
1774
- undefined: true,
1775
- Array: true,
1776
- Object: true,
1777
- String: true,
1778
- Boolean: true,
1779
- Math: true,
1780
- Number: true,
1781
- Symbol: true,
1782
- parseFloat: true,
1783
- Float32Array: true,
1784
- };
1501
+ /* eslint-disable no-return-assign */
1502
+ function isBoundedFunction(value) {
1503
+ if (isBoolean(value.__MICRO_APP_ISBOUND_FUNCTION))
1504
+ return value.__MICRO_APP_ISBOUND_FUNCTION;
1505
+ return value.__MICRO_APP_ISBOUND_FUNCTION = isBoundFunction(value);
1506
+ }
1507
+ function isConstructor(value) {
1508
+ var _a;
1509
+ if (isBoolean(value.__MICRO_APP_ISCONSTRUCTOR))
1510
+ return value.__MICRO_APP_ISCONSTRUCTOR;
1511
+ const valueStr = value.toString();
1512
+ const result = (((_a = value.prototype) === null || _a === void 0 ? void 0 : _a.constructor) === value &&
1513
+ Object.getOwnPropertyNames(value.prototype).length > 1) ||
1514
+ /^function\s+[A-Z]/.test(valueStr) ||
1515
+ /^class\s+/.test(valueStr);
1516
+ return value.__MICRO_APP_ISCONSTRUCTOR = result;
1517
+ }
1518
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
1519
+ function bindFunctionToRawWindow(rawWindow, value) {
1520
+ if (value.__MICRO_APP_BOUND_WINDOW_FUNCTION)
1521
+ return value.__MICRO_APP_BOUND_WINDOW_FUNCTION;
1522
+ if (!isConstructor(value) && !isBoundedFunction(value)) {
1523
+ const bindRawWindowValue = value.bind(rawWindow);
1524
+ for (const key in value) {
1525
+ bindRawWindowValue[key] = value[key];
1526
+ }
1527
+ if (value.hasOwnProperty('prototype')) {
1528
+ rawDefineProperty(bindRawWindowValue, 'prototype', {
1529
+ value: value.prototype,
1530
+ configurable: true,
1531
+ enumerable: false,
1532
+ writable: true,
1533
+ });
1534
+ }
1535
+ return value.__MICRO_APP_BOUND_WINDOW_FUNCTION = bindRawWindowValue;
1536
+ }
1537
+ return value;
1538
+ }
1539
+
1540
+ // document.onclick binding list, the binding function of each application is unique
1541
+ const documentClickListMap = new Map();
1542
+ let hasRewriteDocumentOnClick = false;
1785
1543
  /**
1786
- * macro task to solve the rendering problem of vue3
1544
+ * Rewrite document.onclick and execute it only once
1787
1545
  */
1788
- let macroTimer;
1789
- function macroTask(fn) {
1790
- macroTimer && clearTimeout(macroTimer);
1791
- macroTimer = setTimeout(fn, 0);
1546
+ function overwriteDocumentOnClick() {
1547
+ hasRewriteDocumentOnClick = true;
1548
+ if (Object.getOwnPropertyDescriptor(document, 'onclick')) {
1549
+ return logWarn('Cannot redefine document property onclick');
1550
+ }
1551
+ const rawOnClick = document.onclick;
1552
+ document.onclick = null;
1553
+ let hasDocumentClickInited = false;
1554
+ function onClickHandler(e) {
1555
+ documentClickListMap.forEach((f) => {
1556
+ isFunction(f) && f.call(document, e);
1557
+ });
1558
+ }
1559
+ rawDefineProperty(document, 'onclick', {
1560
+ configurable: true,
1561
+ enumerable: true,
1562
+ get() {
1563
+ const appName = getCurrentAppName();
1564
+ return appName ? documentClickListMap.get(appName) : documentClickListMap.get('base');
1565
+ },
1566
+ set(f) {
1567
+ const appName = getCurrentAppName();
1568
+ if (appName) {
1569
+ documentClickListMap.set(appName, f);
1570
+ }
1571
+ else {
1572
+ documentClickListMap.set('base', f);
1573
+ }
1574
+ if (!hasDocumentClickInited && isFunction(f)) {
1575
+ hasDocumentClickInited = true;
1576
+ globalEnv.rawDocumentAddEventListener.call(globalEnv.rawDocument, 'click', onClickHandler, false);
1577
+ }
1578
+ }
1579
+ });
1580
+ rawOnClick && (document.onclick = rawOnClick);
1792
1581
  }
1793
- class SandBox {
1794
- constructor(appName, url, macro) {
1795
- // Scoped global Properties(Properties that can only get and set in microWindow, will not escape to rawWindow)
1796
- this.scopeProperties = ['webpackJsonp'];
1797
- // Properties that can be escape to rawWindow
1798
- this.escapeProperties = [];
1799
- // Properties newly added to microWindow
1800
- this.injectedKeys = new Set();
1801
- // Properties escape to rawWindow, cleared when unmount
1802
- this.escapeKeys = new Set();
1803
- // sandbox state
1804
- this.active = false;
1805
- this.microWindow = {}; // Proxy target
1806
- const rawWindow = globalEnv.rawWindow;
1807
- const rawDocument = globalEnv.rawDocument;
1808
- const descriptorTargetMap = new Map();
1809
- const hasOwnProperty = (key) => this.microWindow.hasOwnProperty(key) || rawWindow.hasOwnProperty(key);
1810
- // get scopeProperties and escapeProperties from plugins
1811
- this.getScopeProperties(appName);
1812
- // inject global properties
1813
- this.inject(this.microWindow, appName, url);
1814
- // Rewrite global event listener & timeout
1815
- Object.assign(this, effect(this.microWindow));
1816
- this.proxyWindow = new Proxy(this.microWindow, {
1817
- get: (target, key) => {
1818
- if (key === Symbol.unscopables)
1819
- return unscopables;
1820
- if (['window', 'self', 'globalThis'].includes(key)) {
1821
- return this.proxyWindow;
1822
- }
1823
- if (key === 'top' || key === 'parent') {
1824
- if (rawWindow === rawWindow.parent) { // not in iframe
1825
- return this.proxyWindow;
1826
- }
1827
- return Reflect.get(rawWindow, key); // iframe
1828
- }
1829
- if (key === 'hasOwnProperty')
1830
- return hasOwnProperty;
1831
- if (key === 'document' || key === 'eval') {
1832
- if (this.active) {
1833
- setCurrentAppName(appName);
1834
- (macro ? macroTask : defer)(() => setCurrentAppName(null));
1835
- }
1836
- switch (key) {
1837
- case 'document':
1838
- return rawDocument;
1839
- case 'eval':
1840
- return eval;
1841
- }
1842
- }
1843
- if (Reflect.has(target, key)) {
1844
- return Reflect.get(target, key);
1845
- }
1846
- if (this.scopeProperties.includes(key) ||
1847
- (isString(key) && /^__MICRO_APP_/.test(key))) {
1848
- return Reflect.get(target, key);
1582
+ /**
1583
+ * The document event is globally, we need to clear these event bindings when micro application unmounted
1584
+ */
1585
+ const documentEventListenerMap = new Map();
1586
+ function effectDocumentEvent() {
1587
+ const { rawDocument, rawDocumentAddEventListener, rawDocumentRemoveEventListener, } = globalEnv;
1588
+ !hasRewriteDocumentOnClick && overwriteDocumentOnClick();
1589
+ document.addEventListener = function (type, listener, options) {
1590
+ var _a;
1591
+ const appName = getCurrentAppName();
1592
+ /**
1593
+ * ignore bound function of document event in umd mode, used to solve problem of react global events
1594
+ */
1595
+ if (appName && !(((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.umdMode) && isBoundFunction(listener))) {
1596
+ const appListenersMap = documentEventListenerMap.get(appName);
1597
+ if (appListenersMap) {
1598
+ const appListenerList = appListenersMap.get(type);
1599
+ if (appListenerList) {
1600
+ appListenerList.add(listener);
1849
1601
  }
1850
- const rawValue = Reflect.get(rawWindow, key);
1851
- return bindFunctionToRawWidow(rawWindow, rawValue);
1852
- },
1853
- set: (target, key, value) => {
1854
- if (this.active) {
1855
- if (escapeSetterKeyList.includes(key)) {
1856
- Reflect.set(rawWindow, key, value);
1857
- }
1858
- else if (!target.hasOwnProperty(key) &&
1859
- rawWindow.hasOwnProperty(key) &&
1860
- !this.scopeProperties.includes(key)) {
1861
- const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
1862
- const { writable, configurable, enumerable } = descriptor;
1863
- if (writable) {
1864
- Object.defineProperty(target, key, {
1865
- configurable,
1866
- enumerable,
1867
- writable,
1868
- value,
1869
- });
1870
- this.injectedKeys.add(key);
1871
- }
1872
- }
1873
- else {
1874
- Reflect.set(target, key, value);
1875
- this.injectedKeys.add(key);
1876
- }
1877
- if ((this.escapeProperties.includes(key) ||
1878
- (staticEscapeProperties.includes(key) && !Reflect.has(rawWindow, key))) &&
1879
- !this.scopeProperties.includes(key)) {
1880
- Reflect.set(rawWindow, key, value);
1881
- this.escapeKeys.add(key);
1882
- }
1602
+ else {
1603
+ appListenersMap.set(type, new Set([listener]));
1883
1604
  }
1884
- return true;
1885
- },
1886
- has: (target, key) => {
1887
- if (this.scopeProperties.includes(key))
1888
- return key in target;
1889
- return key in unscopables || key in target || key in rawWindow;
1890
- },
1891
- // Object.getOwnPropertyDescriptor(window, key)
1892
- // TODO: use set
1893
- getOwnPropertyDescriptor: (target, key) => {
1894
- if (target.hasOwnProperty(key)) {
1895
- descriptorTargetMap.set(key, 'target');
1896
- return Object.getOwnPropertyDescriptor(target, key);
1605
+ }
1606
+ else {
1607
+ documentEventListenerMap.set(appName, new Map([[type, new Set([listener])]]));
1608
+ }
1609
+ listener && (listener.__MICRO_APP_MARK_OPTIONS__ = options);
1610
+ }
1611
+ rawDocumentAddEventListener.call(rawDocument, type, listener, options);
1612
+ };
1613
+ document.removeEventListener = function (type, listener, options) {
1614
+ var _a;
1615
+ const appName = getCurrentAppName();
1616
+ if (appName && !(((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.umdMode) && isBoundFunction(listener))) {
1617
+ const appListenersMap = documentEventListenerMap.get(appName);
1618
+ if (appListenersMap) {
1619
+ const appListenerList = appListenersMap.get(type);
1620
+ if ((appListenerList === null || appListenerList === void 0 ? void 0 : appListenerList.size) && appListenerList.has(listener)) {
1621
+ appListenerList.delete(listener);
1897
1622
  }
1898
- if (rawWindow.hasOwnProperty(key)) {
1899
- // like console, alert ...
1900
- descriptorTargetMap.set(key, 'rawWindow');
1901
- const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
1902
- if (descriptor && !descriptor.configurable) {
1903
- descriptor.configurable = true;
1904
- }
1905
- return descriptor;
1623
+ }
1624
+ }
1625
+ rawDocumentRemoveEventListener.call(rawDocument, type, listener, options);
1626
+ };
1627
+ }
1628
+ // Clear the document event agent
1629
+ function releaseEffectDocumentEvent() {
1630
+ document.addEventListener = globalEnv.rawDocumentAddEventListener;
1631
+ document.removeEventListener = globalEnv.rawDocumentRemoveEventListener;
1632
+ }
1633
+ // this events should be sent to the specified app
1634
+ const formatEventList = ['unmount', 'appstate-change'];
1635
+ /**
1636
+ * Format event name
1637
+ * @param type event name
1638
+ * @param microAppWindow micro window
1639
+ */
1640
+ function formatEventType(type, microAppWindow) {
1641
+ if (formatEventList.includes(type)) {
1642
+ return `${type}-${microAppWindow.__MICRO_APP_NAME__}`;
1643
+ }
1644
+ return type;
1645
+ }
1646
+ /**
1647
+ * Rewrite side-effect events
1648
+ * @param microAppWindow micro window
1649
+ */
1650
+ function effect(microAppWindow) {
1651
+ const appName = microAppWindow.__MICRO_APP_NAME__;
1652
+ const eventListenerMap = new Map();
1653
+ const intervalIdMap = new Map();
1654
+ const timeoutIdMap = new Map();
1655
+ const { rawWindow, rawDocument, rawWindowAddEventListener, rawWindowRemoveEventListener, rawSetInterval, rawSetTimeout, rawClearInterval, rawClearTimeout, rawDocumentRemoveEventListener, } = globalEnv;
1656
+ // listener may be null, e.g test-passive
1657
+ microAppWindow.addEventListener = function (type, listener, options) {
1658
+ type = formatEventType(type, microAppWindow);
1659
+ const listenerList = eventListenerMap.get(type);
1660
+ if (listenerList) {
1661
+ listenerList.add(listener);
1662
+ }
1663
+ else {
1664
+ eventListenerMap.set(type, new Set([listener]));
1665
+ }
1666
+ listener && (listener.__MICRO_APP_MARK_OPTIONS__ = options);
1667
+ rawWindowAddEventListener.call(rawWindow, type, listener, options);
1668
+ };
1669
+ microAppWindow.removeEventListener = function (type, listener, options) {
1670
+ type = formatEventType(type, microAppWindow);
1671
+ const listenerList = eventListenerMap.get(type);
1672
+ if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
1673
+ listenerList.delete(listener);
1674
+ }
1675
+ rawWindowRemoveEventListener.call(rawWindow, type, listener, options);
1676
+ };
1677
+ microAppWindow.setInterval = function (handler, timeout, ...args) {
1678
+ const intervalId = rawSetInterval.call(rawWindow, handler, timeout, ...args);
1679
+ intervalIdMap.set(intervalId, { handler, timeout, args });
1680
+ return intervalId;
1681
+ };
1682
+ microAppWindow.setTimeout = function (handler, timeout, ...args) {
1683
+ const timeoutId = rawSetTimeout.call(rawWindow, handler, timeout, ...args);
1684
+ timeoutIdMap.set(timeoutId, { handler, timeout, args });
1685
+ return timeoutId;
1686
+ };
1687
+ microAppWindow.clearInterval = function (intervalId) {
1688
+ intervalIdMap.delete(intervalId);
1689
+ rawClearInterval.call(rawWindow, intervalId);
1690
+ };
1691
+ microAppWindow.clearTimeout = function (timeoutId) {
1692
+ timeoutIdMap.delete(timeoutId);
1693
+ rawClearTimeout.call(rawWindow, timeoutId);
1694
+ };
1695
+ const umdWindowListenerMap = new Map();
1696
+ const umdDocumentListenerMap = new Map();
1697
+ let umdIntervalIdMap = new Map();
1698
+ let umdTimeoutIdMap = new Map();
1699
+ let umdOnClickHandler;
1700
+ // record event and timer before exec umdMountHook
1701
+ const recordUmdEffect = () => {
1702
+ // record window event
1703
+ eventListenerMap.forEach((listenerList, type) => {
1704
+ if (listenerList.size) {
1705
+ umdWindowListenerMap.set(type, new Set(listenerList));
1706
+ }
1707
+ });
1708
+ // record timers
1709
+ if (intervalIdMap.size) {
1710
+ umdIntervalIdMap = new Map(intervalIdMap);
1711
+ }
1712
+ if (timeoutIdMap.size) {
1713
+ umdTimeoutIdMap = new Map(timeoutIdMap);
1714
+ }
1715
+ // record onclick handler
1716
+ umdOnClickHandler = documentClickListMap.get(appName);
1717
+ // record document event
1718
+ const documentAppListenersMap = documentEventListenerMap.get(appName);
1719
+ if (documentAppListenersMap) {
1720
+ documentAppListenersMap.forEach((listenerList, type) => {
1721
+ if (listenerList.size) {
1722
+ umdDocumentListenerMap.set(type, new Set(listenerList));
1906
1723
  }
1907
- return undefined;
1908
- },
1909
- // Object.defineProperty(window, key, Descriptor)
1910
- defineProperty: (target, key, value) => {
1911
- const from = descriptorTargetMap.get(key);
1912
- if (from === 'rawWindow') {
1913
- return Reflect.defineProperty(rawWindow, key, value);
1724
+ });
1725
+ }
1726
+ };
1727
+ // rebuild event and timer before remount umd app
1728
+ const rebuildUmdEffect = () => {
1729
+ // rebuild window event
1730
+ umdWindowListenerMap.forEach((listenerList, type) => {
1731
+ for (const listener of listenerList) {
1732
+ microAppWindow.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
1733
+ }
1734
+ });
1735
+ // rebuild timer
1736
+ umdIntervalIdMap.forEach((info) => {
1737
+ microAppWindow.setInterval(info.handler, info.timeout, ...info.args);
1738
+ });
1739
+ umdTimeoutIdMap.forEach((info) => {
1740
+ microAppWindow.setTimeout(info.handler, info.timeout, ...info.args);
1741
+ });
1742
+ // rebuild onclick event
1743
+ umdOnClickHandler && documentClickListMap.set(appName, umdOnClickHandler);
1744
+ // rebuild document event
1745
+ setCurrentAppName(appName);
1746
+ umdDocumentListenerMap.forEach((listenerList, type) => {
1747
+ for (const listener of listenerList) {
1748
+ document.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
1749
+ }
1750
+ });
1751
+ setCurrentAppName(null);
1752
+ };
1753
+ // release all event listener & interval & timeout when unmount app
1754
+ const releaseEffect = () => {
1755
+ // Clear window binding events
1756
+ if (eventListenerMap.size) {
1757
+ eventListenerMap.forEach((listenerList, type) => {
1758
+ for (const listener of listenerList) {
1759
+ rawWindowRemoveEventListener.call(rawWindow, type, listener);
1914
1760
  }
1915
- return Reflect.defineProperty(target, key, value);
1916
- },
1917
- // Object.getOwnPropertyNames(window)
1918
- ownKeys: (target) => {
1919
- return unique(Reflect.ownKeys(rawWindow).concat(Reflect.ownKeys(target)));
1920
- },
1921
- deleteProperty: (target, key) => {
1922
- if (target.hasOwnProperty(key)) {
1923
- this.injectedKeys.has(key) && this.injectedKeys.delete(key);
1924
- this.escapeKeys.has(key) && Reflect.deleteProperty(rawWindow, key);
1925
- return Reflect.deleteProperty(target, key);
1761
+ });
1762
+ eventListenerMap.clear();
1763
+ }
1764
+ // Clear timers
1765
+ if (intervalIdMap.size) {
1766
+ intervalIdMap.forEach((_, intervalId) => {
1767
+ rawClearInterval.call(rawWindow, intervalId);
1768
+ });
1769
+ intervalIdMap.clear();
1770
+ }
1771
+ if (timeoutIdMap.size) {
1772
+ timeoutIdMap.forEach((_, timeoutId) => {
1773
+ rawClearTimeout.call(rawWindow, timeoutId);
1774
+ });
1775
+ timeoutIdMap.clear();
1776
+ }
1777
+ // Clear the function bound by micro application through document.onclick
1778
+ documentClickListMap.delete(appName);
1779
+ // Clear document binding event
1780
+ const documentAppListenersMap = documentEventListenerMap.get(appName);
1781
+ if (documentAppListenersMap) {
1782
+ documentAppListenersMap.forEach((listenerList, type) => {
1783
+ for (const listener of listenerList) {
1784
+ rawDocumentRemoveEventListener.call(rawDocument, type, listener);
1926
1785
  }
1927
- return true;
1928
- },
1929
- });
1786
+ });
1787
+ documentAppListenersMap.clear();
1788
+ }
1789
+ };
1790
+ return {
1791
+ recordUmdEffect,
1792
+ rebuildUmdEffect,
1793
+ releaseEffect,
1794
+ };
1795
+ }
1796
+ // window.addEventListener('mousedown', (e: Event) => {
1797
+ // const targetNode = e.target
1798
+ // const activeApps = getActiveApps(true)
1799
+ // let isScopeOfMicroApp = false
1800
+ // for (const appName of activeApps) {
1801
+ // const app = appInstanceMap.get(appName)!
1802
+ // if (targetNode instanceof Node && app.container!.contains(targetNode)) {
1803
+ // isScopeOfMicroApp = true
1804
+ // // console.log(111111, appName)
1805
+ // setCurrentAppName(appName)
1806
+ // break
1807
+ // }
1808
+ // }
1809
+ // if (!isScopeOfMicroApp) {
1810
+ // setCurrentAppName(null)
1811
+ // }
1812
+ // }, false)
1813
+ // let isWaitingForMacroReset = false
1814
+ // window.addEventListener('mouseup', () => {
1815
+ // if (!isWaitingForMacroReset && getCurrentAppName()) {
1816
+ // isWaitingForMacroReset = true
1817
+ // setTimeout(() => {
1818
+ // setCurrentAppName(null)
1819
+ // isWaitingForMacroReset = false
1820
+ // })
1821
+ // }
1822
+ // }, false)
1823
+
1824
+ // Variables that can escape to rawWindow
1825
+ const staticEscapeProperties = [
1826
+ 'System',
1827
+ '__cjsWrapper',
1828
+ ];
1829
+ // Variables that can only assigned to rawWindow
1830
+ const escapeSetterKeyList = [
1831
+ 'location',
1832
+ ];
1833
+ const globalPropertyList = ['window', 'self', 'globalThis'];
1834
+ class SandBox {
1835
+ constructor(appName, url) {
1836
+ // Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
1837
+ this.scopeProperties = ['webpackJsonp'];
1838
+ // Properties that can be escape to rawWindow
1839
+ this.escapeProperties = [];
1840
+ // Properties newly added to microAppWindow
1841
+ this.injectedKeys = new Set();
1842
+ // Properties escape to rawWindow, cleared when unmount
1843
+ this.escapeKeys = new Set();
1844
+ // sandbox state
1845
+ this.active = false;
1846
+ this.microAppWindow = {}; // Proxy target
1847
+ // get scopeProperties and escapeProperties from plugins
1848
+ this.getScopeProperties(appName);
1849
+ // create proxyWindow with Proxy(microAppWindow)
1850
+ this.proxyWindow = this.createProxyWindow();
1851
+ // inject global properties
1852
+ this.initMicroAppWindow(this.microAppWindow, appName, url);
1853
+ // Rewrite global event listener & timeout
1854
+ Object.assign(this, effect(this.microAppWindow));
1930
1855
  }
1931
1856
  start(baseroute) {
1932
1857
  if (!this.active) {
1933
1858
  this.active = true;
1934
- this.microWindow.__MICRO_APP_BASE_ROUTE__ = this.microWindow.__MICRO_APP_BASE_URL__ = baseroute;
1859
+ this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute;
1935
1860
  // BUG FIX: bable-polyfill@6.x
1936
1861
  globalEnv.rawWindow._babelPolyfill && (globalEnv.rawWindow._babelPolyfill = false);
1937
1862
  if (++SandBox.activeCount === 1) {
@@ -1943,10 +1868,10 @@ class SandBox {
1943
1868
  if (this.active) {
1944
1869
  this.active = false;
1945
1870
  this.releaseEffect();
1946
- this.microWindow.microApp.clearDataListener();
1947
- this.microWindow.microApp.clearGlobalDataListener();
1871
+ this.microAppWindow.microApp.clearDataListener();
1872
+ this.microAppWindow.microApp.clearGlobalDataListener();
1948
1873
  this.injectedKeys.forEach((key) => {
1949
- Reflect.deleteProperty(this.microWindow, key);
1874
+ Reflect.deleteProperty(this.microAppWindow, key);
1950
1875
  });
1951
1876
  this.injectedKeys.clear();
1952
1877
  this.escapeKeys.forEach((key) => {
@@ -1960,12 +1885,12 @@ class SandBox {
1960
1885
  }
1961
1886
  // record umd snapshot before the first execution of umdHookMount
1962
1887
  recordUmdSnapshot() {
1963
- this.microWindow.__MICRO_APP_UMD_MODE__ = true;
1888
+ this.microAppWindow.__MICRO_APP_UMD_MODE__ = true;
1964
1889
  this.recordUmdEffect();
1965
- recordDataCenterSnapshot(this.microWindow.microApp);
1890
+ recordDataCenterSnapshot(this.microAppWindow.microApp);
1966
1891
  this.recordUmdinjectedValues = new Map();
1967
1892
  this.injectedKeys.forEach((key) => {
1968
- this.recordUmdinjectedValues.set(key, Reflect.get(this.microWindow, key));
1893
+ this.recordUmdinjectedValues.set(key, Reflect.get(this.microAppWindow, key));
1969
1894
  });
1970
1895
  }
1971
1896
  // rebuild umd snapshot before remount umd app
@@ -1974,7 +1899,7 @@ class SandBox {
1974
1899
  Reflect.set(this.proxyWindow, key, value);
1975
1900
  });
1976
1901
  this.rebuildUmdEffect();
1977
- rebuildDataCenterSnapshot(this.microWindow.microApp);
1902
+ rebuildDataCenterSnapshot(this.microAppWindow.microApp);
1978
1903
  }
1979
1904
  /**
1980
1905
  * get scopeProperties and escapeProperties from plugins
@@ -2009,25 +1934,183 @@ class SandBox {
2009
1934
  }
2010
1935
  }
2011
1936
  }
1937
+ // create proxyWindow with Proxy(microAppWindow)
1938
+ createProxyWindow() {
1939
+ const rawWindow = globalEnv.rawWindow;
1940
+ const descriptorTargetMap = new Map();
1941
+ // window.xxx will trigger proxy
1942
+ return new Proxy(this.microAppWindow, {
1943
+ get: (target, key) => {
1944
+ if (Reflect.has(target, key) ||
1945
+ (isString(key) && /^__MICRO_APP_/.test(key)) ||
1946
+ this.scopeProperties.includes(key))
1947
+ return Reflect.get(target, key);
1948
+ const rawValue = Reflect.get(rawWindow, key);
1949
+ return isFunction(rawValue) ? bindFunctionToRawWindow(rawWindow, rawValue) : rawValue;
1950
+ },
1951
+ set: (target, key, value) => {
1952
+ if (this.active) {
1953
+ if (escapeSetterKeyList.includes(key)) {
1954
+ Reflect.set(rawWindow, key, value);
1955
+ }
1956
+ else if (
1957
+ // target.hasOwnProperty has been rewritten
1958
+ !rawHasOwnProperty.call(target, key) &&
1959
+ rawHasOwnProperty.call(rawWindow, key) &&
1960
+ !this.scopeProperties.includes(key)) {
1961
+ const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
1962
+ const { configurable, enumerable, writable, set } = descriptor;
1963
+ // set value because it can be set
1964
+ rawDefineProperty(target, key, {
1965
+ value,
1966
+ configurable,
1967
+ enumerable,
1968
+ writable: writable !== null && writable !== void 0 ? writable : !!set,
1969
+ });
1970
+ this.injectedKeys.add(key);
1971
+ }
1972
+ else {
1973
+ Reflect.set(target, key, value);
1974
+ this.injectedKeys.add(key);
1975
+ }
1976
+ if ((this.escapeProperties.includes(key) ||
1977
+ (staticEscapeProperties.includes(key) && !Reflect.has(rawWindow, key))) &&
1978
+ !this.scopeProperties.includes(key)) {
1979
+ Reflect.set(rawWindow, key, value);
1980
+ this.escapeKeys.add(key);
1981
+ }
1982
+ }
1983
+ return true;
1984
+ },
1985
+ has: (target, key) => {
1986
+ if (this.scopeProperties.includes(key))
1987
+ return key in target;
1988
+ return key in target || key in rawWindow;
1989
+ },
1990
+ // Object.getOwnPropertyDescriptor(window, key)
1991
+ getOwnPropertyDescriptor: (target, key) => {
1992
+ if (rawHasOwnProperty.call(target, key)) {
1993
+ descriptorTargetMap.set(key, 'target');
1994
+ return Object.getOwnPropertyDescriptor(target, key);
1995
+ }
1996
+ if (rawHasOwnProperty.call(rawWindow, key)) {
1997
+ descriptorTargetMap.set(key, 'rawWindow');
1998
+ const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
1999
+ if (descriptor && !descriptor.configurable) {
2000
+ descriptor.configurable = true;
2001
+ }
2002
+ return descriptor;
2003
+ }
2004
+ return undefined;
2005
+ },
2006
+ // Object.defineProperty(window, key, Descriptor)
2007
+ defineProperty: (target, key, value) => {
2008
+ const from = descriptorTargetMap.get(key);
2009
+ if (from === 'rawWindow') {
2010
+ return Reflect.defineProperty(rawWindow, key, value);
2011
+ }
2012
+ return Reflect.defineProperty(target, key, value);
2013
+ },
2014
+ // Object.getOwnPropertyNames(window)
2015
+ ownKeys: (target) => {
2016
+ return unique(Reflect.ownKeys(rawWindow).concat(Reflect.ownKeys(target)));
2017
+ },
2018
+ deleteProperty: (target, key) => {
2019
+ if (rawHasOwnProperty.call(target, key)) {
2020
+ this.injectedKeys.has(key) && this.injectedKeys.delete(key);
2021
+ this.escapeKeys.has(key) && Reflect.deleteProperty(rawWindow, key);
2022
+ return Reflect.deleteProperty(target, key);
2023
+ }
2024
+ return true;
2025
+ },
2026
+ });
2027
+ }
2012
2028
  /**
2013
- * inject global properties to microWindow
2014
- * @param microWindow micro window
2029
+ * inject global properties to microAppWindow
2030
+ * @param microAppWindow micro window
2015
2031
  * @param appName app name
2016
2032
  * @param url app url
2017
2033
  */
2018
- inject(microWindow, appName, url) {
2019
- microWindow.__MICRO_APP_ENVIRONMENT__ = true;
2020
- microWindow.__MICRO_APP_NAME__ = appName;
2021
- microWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
2022
- microWindow.microApp = new EventCenterForMicroApp(appName);
2023
- microWindow.rawWindow = globalEnv.rawWindow;
2024
- microWindow.rawDocument = globalEnv.rawDocument;
2025
- microWindow.removeDomScope = removeDomScope;
2034
+ initMicroAppWindow(microAppWindow, appName, url) {
2035
+ microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
2036
+ microAppWindow.__MICRO_APP_NAME__ = appName;
2037
+ microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
2038
+ microAppWindow.__MICRO_APP_WINDOW__ = microAppWindow;
2039
+ microAppWindow.microApp = new EventCenterForMicroApp(appName);
2040
+ microAppWindow.rawWindow = globalEnv.rawWindow;
2041
+ microAppWindow.rawDocument = globalEnv.rawDocument;
2042
+ microAppWindow.removeDomScope = removeDomScope;
2043
+ microAppWindow.hasOwnProperty = (key) => rawHasOwnProperty.call(microAppWindow, key) || rawHasOwnProperty.call(globalEnv.rawWindow, key);
2044
+ this.setMappingPropertiesWithRawDescriptor(microAppWindow);
2045
+ this.setHijackProperties(microAppWindow, appName);
2046
+ }
2047
+ // properties associated with the native window
2048
+ setMappingPropertiesWithRawDescriptor(microAppWindow) {
2049
+ let topValue, parentValue;
2050
+ const rawWindow = globalEnv.rawWindow;
2051
+ if (rawWindow === rawWindow.parent) { // not in iframe
2052
+ topValue = parentValue = this.proxyWindow;
2053
+ }
2054
+ else { // in iframe
2055
+ topValue = rawWindow.top;
2056
+ parentValue = rawWindow.parent;
2057
+ }
2058
+ rawDefineProperty(microAppWindow, 'top', this.createDescriptorFormicroAppWindow('top', topValue));
2059
+ rawDefineProperty(microAppWindow, 'parent', this.createDescriptorFormicroAppWindow('parent', parentValue));
2060
+ globalPropertyList.forEach((key) => {
2061
+ rawDefineProperty(microAppWindow, key, this.createDescriptorFormicroAppWindow(key, this.proxyWindow));
2062
+ });
2063
+ }
2064
+ createDescriptorFormicroAppWindow(key, value) {
2065
+ const { configurable = true, enumerable = true, writable, set } = Object.getOwnPropertyDescriptor(globalEnv.rawWindow, key) || {};
2066
+ const descriptor = {
2067
+ value,
2068
+ configurable,
2069
+ enumerable,
2070
+ writable: writable !== null && writable !== void 0 ? writable : !!set
2071
+ };
2072
+ return descriptor;
2073
+ }
2074
+ // set hijack Properties to microAppWindow
2075
+ setHijackProperties(microAppWindow, appName) {
2076
+ let modifiedEval, modifiedImage;
2077
+ rawDefineProperties(microAppWindow, {
2078
+ document: {
2079
+ get() {
2080
+ throttleDeferForSetAppName(appName);
2081
+ return globalEnv.rawDocument;
2082
+ },
2083
+ configurable: false,
2084
+ enumerable: true,
2085
+ },
2086
+ eval: {
2087
+ get() {
2088
+ throttleDeferForSetAppName(appName);
2089
+ return modifiedEval || eval;
2090
+ },
2091
+ set: (value) => {
2092
+ modifiedEval = value;
2093
+ },
2094
+ configurable: true,
2095
+ enumerable: false,
2096
+ },
2097
+ Image: {
2098
+ get() {
2099
+ throttleDeferForSetAppName(appName);
2100
+ return modifiedImage || globalEnv.ImageProxy;
2101
+ },
2102
+ set: (value) => {
2103
+ modifiedImage = value;
2104
+ },
2105
+ configurable: true,
2106
+ enumerable: false,
2107
+ },
2108
+ });
2026
2109
  }
2027
2110
  }
2028
2111
  SandBox.activeCount = 0; // number of active sandbox
2029
2112
 
2030
- function eventHandler$1(event, element) {
2113
+ function formatEventInfo(event, element) {
2031
2114
  Object.defineProperties(event, {
2032
2115
  currentTarget: {
2033
2116
  get() {
@@ -2066,7 +2149,7 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
2066
2149
  const event = new CustomEvent(lifecycleName, {
2067
2150
  detail,
2068
2151
  });
2069
- eventHandler$1(event, element);
2152
+ formatEventInfo(event, element);
2070
2153
  // global hooks
2071
2154
  // @ts-ignore
2072
2155
  if (isFunction((_a = microApp.lifeCycles) === null || _a === void 0 ? void 0 : _a[lifecycleName])) {
@@ -2076,19 +2159,25 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
2076
2159
  element.dispatchEvent(event);
2077
2160
  }
2078
2161
  /**
2079
- * Dispatch unmount event to micro app
2080
- * @param appName app.name
2162
+ * Dispatch custom event to micro app
2163
+ * @param eventName event name
2164
+ * @param appName app name
2165
+ * @param detail event detail
2081
2166
  */
2082
- function dispatchUnmountToMicroApp(appName) {
2083
- const event = new CustomEvent(`unmount-${appName}`);
2167
+ function dispatchCustomEventToMicroApp(eventName, appName, detail = {}) {
2168
+ const event = new CustomEvent(`${eventName}-${appName}`, {
2169
+ detail,
2170
+ });
2084
2171
  window.dispatchEvent(event);
2085
2172
  }
2086
2173
 
2087
2174
  // micro app instances
2088
2175
  const appInstanceMap = new Map();
2089
2176
  class CreateApp {
2090
- constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, macro, baseroute, }) {
2091
- this.status = appStatus.NOT_LOADED;
2177
+ constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, baseroute, }) {
2178
+ this.state = appStates.NOT_LOADED;
2179
+ this.keepAliveState = null;
2180
+ this.keepAliveContainer = null;
2092
2181
  this.loadSourceLevel = 0;
2093
2182
  this.umdHookMount = null;
2094
2183
  this.umdHookUnmount = null;
@@ -2096,7 +2185,6 @@ class CreateApp {
2096
2185
  this.umdMode = false;
2097
2186
  this.isPrefetch = false;
2098
2187
  this.container = null;
2099
- this.macro = false;
2100
2188
  this.baseroute = '';
2101
2189
  this.sandBox = null;
2102
2190
  this.container = container !== null && container !== void 0 ? container : null;
@@ -2108,17 +2196,16 @@ class CreateApp {
2108
2196
  this.url = url;
2109
2197
  this.useSandbox = useSandbox;
2110
2198
  this.scopecss = this.useSandbox && scopecss;
2111
- this.macro = macro !== null && macro !== void 0 ? macro : false;
2112
2199
  this.source = {
2113
2200
  links: new Map(),
2114
2201
  scripts: new Map(),
2115
2202
  };
2116
2203
  this.loadSourceCode();
2117
- this.useSandbox && (this.sandBox = new SandBox(name, url, this.macro));
2204
+ this.useSandbox && (this.sandBox = new SandBox(name, url));
2118
2205
  }
2119
2206
  // Load resources
2120
2207
  loadSourceCode() {
2121
- this.status = appStatus.LOADING_SOURCE_CODE;
2208
+ this.state = appStates.LOADING_SOURCE_CODE;
2122
2209
  extractHtml(this);
2123
2210
  }
2124
2211
  /**
@@ -2127,9 +2214,9 @@ class CreateApp {
2127
2214
  onLoad(html) {
2128
2215
  if (++this.loadSourceLevel === 2) {
2129
2216
  this.source.html = html;
2130
- if (this.isPrefetch || appStatus.UNMOUNT === this.status)
2217
+ if (this.isPrefetch || appStates.UNMOUNT === this.state)
2131
2218
  return;
2132
- this.status = appStatus.LOAD_SOURCE_FINISHED;
2219
+ this.state = appStates.LOAD_SOURCE_FINISHED;
2133
2220
  this.mount();
2134
2221
  }
2135
2222
  }
@@ -2139,9 +2226,9 @@ class CreateApp {
2139
2226
  */
2140
2227
  onLoadError(e) {
2141
2228
  this.loadSourceLevel = -1;
2142
- if (appStatus.UNMOUNT !== this.status) {
2229
+ if (appStates.UNMOUNT !== this.state) {
2143
2230
  this.onerror(e);
2144
- this.status = appStatus.LOAD_SOURCE_ERROR;
2231
+ this.state = appStates.LOAD_SOURCE_ERROR;
2145
2232
  }
2146
2233
  }
2147
2234
  /**
@@ -2158,11 +2245,11 @@ class CreateApp {
2158
2245
  this.container = (_a = this.container) !== null && _a !== void 0 ? _a : container;
2159
2246
  this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : this.baseroute;
2160
2247
  if (this.loadSourceLevel !== 2) {
2161
- this.status = appStatus.LOADING_SOURCE_CODE;
2248
+ this.state = appStates.LOADING_SOURCE_CODE;
2162
2249
  return;
2163
2250
  }
2164
2251
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
2165
- this.status = appStatus.MOUNTING;
2252
+ this.state = appStates.MOUNTING;
2166
2253
  cloneContainer(this.source.html, this.container, !this.umdMode);
2167
2254
  (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.start(this.baseroute);
2168
2255
  let umdHookMountResult; // result of mount function
@@ -2222,20 +2309,23 @@ class CreateApp {
2222
2309
  * dispatch mounted event when app run finished
2223
2310
  */
2224
2311
  dispatchMountedEvent() {
2225
- if (appStatus.UNMOUNT !== this.status) {
2226
- this.status = appStatus.MOUNTED;
2312
+ if (appStates.UNMOUNT !== this.state) {
2313
+ this.state = appStates.MOUNTED;
2227
2314
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.MOUNTED);
2228
2315
  }
2229
2316
  }
2230
2317
  /**
2231
2318
  * unmount app
2232
2319
  * @param destroy completely destroy, delete cache resources
2320
+ * @param unmountcb callback of unmount
2233
2321
  */
2234
- unmount(destroy) {
2235
- if (this.status === appStatus.LOAD_SOURCE_ERROR) {
2322
+ unmount(destroy, unmountcb) {
2323
+ if (this.state === appStates.LOAD_SOURCE_ERROR) {
2236
2324
  destroy = true;
2237
2325
  }
2238
- this.status = appStatus.UNMOUNT;
2326
+ this.state = appStates.UNMOUNT;
2327
+ this.keepAliveState = null;
2328
+ this.keepAliveContainer = null;
2239
2329
  // result of unmount function
2240
2330
  let umdHookUnmountResult;
2241
2331
  /**
@@ -2251,50 +2341,84 @@ class CreateApp {
2251
2341
  }
2252
2342
  }
2253
2343
  // dispatch unmount event to micro app
2254
- dispatchUnmountToMicroApp(this.name);
2255
- this.handleUnmounted(destroy, umdHookUnmountResult);
2344
+ dispatchCustomEventToMicroApp('unmount', this.name);
2345
+ this.handleUnmounted(destroy, umdHookUnmountResult, unmountcb);
2256
2346
  }
2257
2347
  /**
2258
2348
  * handle for promise umdHookUnmount
2349
+ * @param destroy completely destroy, delete cache resources
2259
2350
  * @param umdHookUnmountResult result of umdHookUnmount
2351
+ * @param unmountcb callback of unmount
2260
2352
  */
2261
- handleUnmounted(destroy, umdHookUnmountResult) {
2353
+ handleUnmounted(destroy, umdHookUnmountResult, unmountcb) {
2262
2354
  if (isPromise(umdHookUnmountResult)) {
2263
2355
  umdHookUnmountResult
2264
- .then(() => this.actionsForUnmount(destroy))
2265
- .catch(() => this.actionsForUnmount(destroy));
2356
+ .then(() => this.actionsForUnmount(destroy, unmountcb))
2357
+ .catch(() => this.actionsForUnmount(destroy, unmountcb));
2266
2358
  }
2267
2359
  else {
2268
- this.actionsForUnmount(destroy);
2360
+ this.actionsForUnmount(destroy, unmountcb);
2269
2361
  }
2270
2362
  }
2271
2363
  /**
2272
2364
  * actions for unmount app
2273
2365
  * @param destroy completely destroy, delete cache resources
2366
+ * @param unmountcb callback of unmount
2274
2367
  */
2275
- actionsForUnmount(destroy) {
2368
+ actionsForUnmount(destroy, unmountcb) {
2276
2369
  var _a;
2277
- // dispatch unmount event to base app
2278
- dispatchLifecyclesEvent(this.container, this.name, lifeCycles.UNMOUNT);
2279
2370
  (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.stop();
2280
2371
  if (destroy) {
2281
- this.actionsForCompletelyDestory();
2372
+ this.actionsForCompletelyDestroy();
2282
2373
  }
2283
2374
  else if (this.umdMode && this.container.childElementCount) {
2284
- /**
2285
- * In umd mode, ui frameworks will no longer create style elements to head in lazy load page when render again, so we should save container to keep these elements
2286
- */
2287
2375
  cloneContainer(this.container, this.source.html, false);
2288
2376
  }
2377
+ // dispatch unmount event to base app
2378
+ dispatchLifecyclesEvent(this.container, this.name, lifeCycles.UNMOUNT);
2379
+ this.container.innerHTML = '';
2289
2380
  this.container = null;
2381
+ unmountcb && unmountcb();
2290
2382
  }
2291
2383
  // actions for completely destroy
2292
- actionsForCompletelyDestory() {
2384
+ actionsForCompletelyDestroy() {
2293
2385
  if (!this.useSandbox && this.umdMode) {
2294
2386
  delete window[this.libraryName];
2295
2387
  }
2296
2388
  appInstanceMap.delete(this.name);
2297
2389
  }
2390
+ // hidden app when disconnectedCallback called with keep-alive
2391
+ hiddenKeepAliveApp() {
2392
+ const oldContainer = this.container;
2393
+ cloneContainer(this.container, this.keepAliveContainer ? this.keepAliveContainer : (this.keepAliveContainer = document.createElement('div')), false);
2394
+ this.container = this.keepAliveContainer;
2395
+ this.keepAliveState = keepAliveStates.KEEP_ALIVE_HIDDEN;
2396
+ // event should dispatch before clone node
2397
+ // dispatch afterhidden event to micro-app
2398
+ dispatchCustomEventToMicroApp('appstate-change', this.name, {
2399
+ appState: 'afterhidden',
2400
+ });
2401
+ // dispatch afterhidden event to base app
2402
+ dispatchLifecyclesEvent(oldContainer, this.name, lifeCycles.AFTERHIDDEN);
2403
+ }
2404
+ // show app when connectedCallback called with keep-alive
2405
+ showKeepAliveApp(container) {
2406
+ // dispatch beforeshow event to micro-app
2407
+ dispatchCustomEventToMicroApp('appstate-change', this.name, {
2408
+ appState: 'beforeshow',
2409
+ });
2410
+ // dispatch beforeshow event to base app
2411
+ dispatchLifecyclesEvent(container, this.name, lifeCycles.BEFORESHOW);
2412
+ cloneContainer(this.container, container, false);
2413
+ this.container = container;
2414
+ this.keepAliveState = keepAliveStates.KEEP_ALIVE_SHOW;
2415
+ // dispatch aftershow event to micro-app
2416
+ dispatchCustomEventToMicroApp('appstate-change', this.name, {
2417
+ appState: 'aftershow',
2418
+ });
2419
+ // dispatch aftershow event to base app
2420
+ dispatchLifecyclesEvent(this.container, this.name, lifeCycles.AFTERSHOW);
2421
+ }
2298
2422
  /**
2299
2423
  * app rendering error
2300
2424
  * @param e Error
@@ -2302,15 +2426,19 @@ class CreateApp {
2302
2426
  onerror(e) {
2303
2427
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.ERROR, e);
2304
2428
  }
2305
- // get app status
2306
- getAppStatus() {
2307
- return this.status;
2429
+ // get app state
2430
+ getAppState() {
2431
+ return this.state;
2432
+ }
2433
+ // get keep-alive state
2434
+ getKeepAliveState() {
2435
+ return this.keepAliveState;
2308
2436
  }
2309
2437
  // get umd library, if it not exist, return empty object
2310
2438
  getUmdLibraryHooks() {
2311
2439
  var _a, _b;
2312
2440
  // after execScripts, the app maybe unmounted
2313
- if (appStatus.UNMOUNT !== this.status) {
2441
+ if (appStates.UNMOUNT !== this.state) {
2314
2442
  const global = ((_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow) !== null && _b !== void 0 ? _b : globalEnv.rawWindow);
2315
2443
  this.libraryName = getRootContainer(this.container).getAttribute('library') || `micro-app-${this.name}`;
2316
2444
  // do not use isObject
@@ -2319,20 +2447,6 @@ class CreateApp {
2319
2447
  return {};
2320
2448
  }
2321
2449
  }
2322
- // if app not prefetch & not unmount, then app is active
2323
- function getActiveApps() {
2324
- const activeApps = [];
2325
- appInstanceMap.forEach((app, appName) => {
2326
- if (appStatus.UNMOUNT !== app.getAppStatus() && !app.isPrefetch) {
2327
- activeApps.push(appName);
2328
- }
2329
- });
2330
- return activeApps;
2331
- }
2332
- // get all registered apps
2333
- function getAllApps() {
2334
- return Array.from(appInstanceMap.keys());
2335
- }
2336
2450
 
2337
2451
  // Record element and map element
2338
2452
  const dynamicElementInMicroAppMap = new WeakMap();
@@ -2527,13 +2641,13 @@ function patchElementPrototypeMethods() {
2527
2641
  }
2528
2642
  };
2529
2643
  // prototype methods of add element👇
2530
- Node.prototype.appendChild = function appendChild(newChild) {
2644
+ Element.prototype.appendChild = function appendChild(newChild) {
2531
2645
  return commonElementHander(this, newChild, null, globalEnv.rawAppendChild);
2532
2646
  };
2533
- Node.prototype.insertBefore = function insertBefore(newChild, refChild) {
2647
+ Element.prototype.insertBefore = function insertBefore(newChild, refChild) {
2534
2648
  return commonElementHander(this, newChild, refChild, globalEnv.rawInsertBefore);
2535
2649
  };
2536
- Node.prototype.replaceChild = function replaceChild(newChild, oldChild) {
2650
+ Element.prototype.replaceChild = function replaceChild(newChild, oldChild) {
2537
2651
  return commonElementHander(this, newChild, oldChild, globalEnv.rawReplaceChild);
2538
2652
  };
2539
2653
  Element.prototype.append = function append(...nodes) {
@@ -2552,7 +2666,7 @@ function patchElementPrototypeMethods() {
2552
2666
  }
2553
2667
  };
2554
2668
  // prototype methods of delete element👇
2555
- Node.prototype.removeChild = function removeChild(oldChild) {
2669
+ Element.prototype.removeChild = function removeChild(oldChild) {
2556
2670
  if (oldChild === null || oldChild === void 0 ? void 0 : oldChild.__MICRO_APP_NAME__) {
2557
2671
  const app = appInstanceMap.get(oldChild.__MICRO_APP_NAME__);
2558
2672
  if (app === null || app === void 0 ? void 0 : app.container) {
@@ -2562,6 +2676,12 @@ function patchElementPrototypeMethods() {
2562
2676
  }
2563
2677
  return globalEnv.rawRemoveChild.call(this, oldChild);
2564
2678
  };
2679
+ // patch cloneNode
2680
+ Element.prototype.cloneNode = function cloneNode(deep) {
2681
+ const clonedNode = globalEnv.rawCloneNode.call(this, deep);
2682
+ this.__MICRO_APP_NAME__ && (clonedNode.__MICRO_APP_NAME__ = this.__MICRO_APP_NAME__);
2683
+ return clonedNode;
2684
+ };
2565
2685
  }
2566
2686
  /**
2567
2687
  * Mark the newly created element in the micro application
@@ -2680,12 +2800,13 @@ function releasePatches() {
2680
2800
  setCurrentAppName(null);
2681
2801
  releasePatchDocument();
2682
2802
  Element.prototype.setAttribute = globalEnv.rawSetAttribute;
2683
- Node.prototype.appendChild = globalEnv.rawAppendChild;
2684
- Node.prototype.insertBefore = globalEnv.rawInsertBefore;
2685
- Node.prototype.replaceChild = globalEnv.rawReplaceChild;
2686
- Node.prototype.removeChild = globalEnv.rawRemoveChild;
2803
+ Element.prototype.appendChild = globalEnv.rawAppendChild;
2804
+ Element.prototype.insertBefore = globalEnv.rawInsertBefore;
2805
+ Element.prototype.replaceChild = globalEnv.rawReplaceChild;
2806
+ Element.prototype.removeChild = globalEnv.rawRemoveChild;
2687
2807
  Element.prototype.append = globalEnv.rawAppend;
2688
2808
  Element.prototype.prepend = globalEnv.rawPrepend;
2809
+ Element.prototype.cloneNode = globalEnv.rawCloneNode;
2689
2810
  }
2690
2811
  // Set the style of micro-app-head and micro-app-body
2691
2812
  let hasRejectMicroAppStyle = false;
@@ -2700,7 +2821,7 @@ function rejectMicroAppStyle() {
2700
2821
  }
2701
2822
 
2702
2823
  function unmountNestedApp() {
2703
- replaseUnmountOfNestedApp();
2824
+ releaseUnmountOfNestedApp();
2704
2825
  appInstanceMap.forEach(app => {
2705
2826
  // @ts-ignore
2706
2827
  app.container && getRootContainer(app.container).disconnectedCallback();
@@ -2718,7 +2839,7 @@ function listenUmountOfNestedApp() {
2718
2839
  }
2719
2840
  }
2720
2841
  // release listener
2721
- function replaseUnmountOfNestedApp() {
2842
+ function releaseUnmountOfNestedApp() {
2722
2843
  if (window.__MICRO_APP_ENVIRONMENT__) {
2723
2844
  window.removeEventListener('unmount', unmountNestedApp, false);
2724
2845
  }
@@ -2745,7 +2866,6 @@ function defineElement(tagName) {
2745
2866
  * handle for change of name an url after element inited
2746
2867
  */
2747
2868
  this.handleAttributeUpdate = () => {
2748
- var _a;
2749
2869
  this.isWating = false;
2750
2870
  const formatAttrName = formatAppName(this.getAttribute('name'));
2751
2871
  const formatAttrUrl = formatAppURL(this.getAttribute('url'), this.appName);
@@ -2753,44 +2873,27 @@ function defineElement(tagName) {
2753
2873
  const existApp = appInstanceMap.get(formatAttrName);
2754
2874
  if (formatAttrName !== this.appName && existApp) {
2755
2875
  // handling of cached and non-prefetch apps
2756
- if (appStatus.UNMOUNT !== existApp.getAppStatus() && !existApp.isPrefetch) {
2876
+ if (appStates.UNMOUNT !== existApp.getAppState() &&
2877
+ keepAliveStates.KEEP_ALIVE_HIDDEN !== existApp.getKeepAliveState() &&
2878
+ !existApp.isPrefetch) {
2757
2879
  this.setAttribute('name', this.appName);
2758
- return logError(`an app named ${formatAttrName} already exists`, this.appName);
2880
+ return logError(`app name conflict, an app named ${formatAttrName} is running`, this.appName);
2759
2881
  }
2760
2882
  }
2761
2883
  if (formatAttrName !== this.appName || formatAttrUrl !== this.appUrl) {
2762
- this.handleUnmount(formatAttrName === this.appName);
2763
- /**
2764
- * change ssrUrl in ssr mode
2765
- * do not add judgment of formatAttrUrl === this.appUrl
2766
- */
2767
- if (this.getDisposeResult('ssr')) {
2768
- this.ssrUrl = CompletionPath(globalEnv.rawWindow.location.pathname, formatAttrUrl);
2769
- }
2770
- else if (this.ssrUrl) {
2771
- this.ssrUrl = '';
2772
- }
2773
- this.appName = formatAttrName;
2774
- this.appUrl = formatAttrUrl;
2775
- ((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this).innerHTML = '';
2776
- if (formatAttrName !== this.getAttribute('name')) {
2777
- this.setAttribute('name', this.appName);
2884
+ if (formatAttrName === this.appName) {
2885
+ this.handleUnmount(true, () => {
2886
+ this.actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp);
2887
+ });
2778
2888
  }
2779
- /**
2780
- * when existApp not null:
2781
- * scene1: if formatAttrName and this.appName are equal: exitApp is the current app, the url must be different, existApp has been unmounted
2782
- * scene2: if formatAttrName and this.appName are different: existApp must be prefetch or unmounted, if url is equal, then just mount, if url is different, then create new app to replace existApp
2783
- * scene3: url is different but ssrUrl is equal
2784
- * scene4: url is equal but ssrUrl is different, if url is equal, name must different
2785
- */
2786
- if (existApp &&
2787
- existApp.url === this.appUrl &&
2788
- existApp.ssrUrl === this.ssrUrl) {
2789
- // mount app
2790
- this.handleAppMount(existApp);
2889
+ else if (this.getKeepAliveModeResult()) {
2890
+ this.handleHiddenKeepAliveApp();
2891
+ this.actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp);
2791
2892
  }
2792
2893
  else {
2793
- this.handleCreateApp();
2894
+ this.handleUnmount(this.getDestroyCompatibleResult(), () => {
2895
+ this.actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp);
2896
+ });
2794
2897
  }
2795
2898
  }
2796
2899
  }
@@ -2814,8 +2917,8 @@ function defineElement(tagName) {
2814
2917
  // inline: whether js runs in inline script mode, default is false
2815
2918
  // disableScopecss: whether disable css scoped, default is false
2816
2919
  // disableSandbox: whether disable sandbox, default is false
2817
- // macro: used to solve the async render problem of vue3, default is false
2818
2920
  // baseRoute: route prefix, default is ''
2921
+ // keep-alive: open keep-alive mode
2819
2922
  connectedCallback() {
2820
2923
  this.hasConnected = true;
2821
2924
  if (!elementInstanceMap.has(this)) {
@@ -2826,10 +2929,17 @@ function defineElement(tagName) {
2826
2929
  }
2827
2930
  disconnectedCallback() {
2828
2931
  this.hasConnected = false;
2829
- elementInstanceMap.delete(this);
2830
- this.handleUnmount(this.getDisposeResult('destroy') || this.getDisposeResult('destory'));
2831
- if (elementInstanceMap.size === 0) {
2832
- releasePatches();
2932
+ // keep-alive
2933
+ if (this.getKeepAliveModeResult()) {
2934
+ this.handleHiddenKeepAliveApp();
2935
+ }
2936
+ else {
2937
+ elementInstanceMap.delete(this);
2938
+ this.handleUnmount(this.getDestroyCompatibleResult(), () => {
2939
+ if (elementInstanceMap.size === 0) {
2940
+ releasePatches();
2941
+ }
2942
+ });
2833
2943
  }
2834
2944
  }
2835
2945
  attributeChangedCallback(attr, _oldVal, newVal) {
@@ -2873,7 +2983,7 @@ function defineElement(tagName) {
2873
2983
  if (elementInstanceMap.set(this, true).size === 1) {
2874
2984
  patchElementPrototypeMethods();
2875
2985
  rejectMicroAppStyle();
2876
- replaseUnmountOfNestedApp();
2986
+ releaseUnmountOfNestedApp();
2877
2987
  listenUmountOfNestedApp();
2878
2988
  }
2879
2989
  }
@@ -2892,15 +3002,21 @@ function defineElement(tagName) {
2892
3002
  else if (this.ssrUrl) {
2893
3003
  this.ssrUrl = '';
2894
3004
  }
2895
- const app = appInstanceMap.get(this.appName);
2896
- if (app) {
3005
+ if (appInstanceMap.has(this.appName)) {
3006
+ const app = appInstanceMap.get(this.appName);
2897
3007
  const existAppUrl = app.ssrUrl || app.url;
2898
3008
  const activeAppUrl = this.ssrUrl || this.appUrl;
2899
- if (existAppUrl === activeAppUrl && (app.isPrefetch ||
2900
- app.getAppStatus() === appStatus.UNMOUNT)) {
3009
+ // keep-alive don't care about ssrUrl
3010
+ // Even if the keep-alive app is pushed into the background, it is still active and cannot be replaced. Otherwise, it is difficult for developers to troubleshoot in case of conflict and will leave developers at a loss
3011
+ if (app.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN &&
3012
+ app.url === this.appUrl) {
3013
+ this.handleShowKeepAliveApp(app);
3014
+ }
3015
+ else if (existAppUrl === activeAppUrl && (app.isPrefetch ||
3016
+ app.getAppState() === appStates.UNMOUNT)) {
2901
3017
  this.handleAppMount(app);
2902
3018
  }
2903
- else if (app.isPrefetch || app.getAppStatus() === appStatus.UNMOUNT) {
3019
+ else if (app.isPrefetch || app.getAppState() === appStates.UNMOUNT) {
2904
3020
  /**
2905
3021
  * url is different & old app is unmounted or prefetch, create new app to replace old one
2906
3022
  */
@@ -2908,7 +3024,56 @@ function defineElement(tagName) {
2908
3024
  this.handleCreateApp();
2909
3025
  }
2910
3026
  else {
2911
- logError(`an app named ${this.appName} already exists`, this.appName);
3027
+ logError(`app name conflict, an app named ${this.appName} is running`, this.appName);
3028
+ }
3029
+ }
3030
+ else {
3031
+ this.handleCreateApp();
3032
+ }
3033
+ }
3034
+ // remount app or create app if attribute url or name change
3035
+ actionsForAttributeChange(formatAttrName, formatAttrUrl, existApp) {
3036
+ var _a;
3037
+ /**
3038
+ * change ssrUrl in ssr mode
3039
+ * do not add judgment of formatAttrUrl === this.appUrl
3040
+ */
3041
+ if (this.getDisposeResult('ssr')) {
3042
+ this.ssrUrl = CompletionPath(globalEnv.rawWindow.location.pathname, formatAttrUrl);
3043
+ }
3044
+ else if (this.ssrUrl) {
3045
+ this.ssrUrl = '';
3046
+ }
3047
+ this.appName = formatAttrName;
3048
+ this.appUrl = formatAttrUrl;
3049
+ ((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this).innerHTML = '';
3050
+ if (formatAttrName !== this.getAttribute('name')) {
3051
+ this.setAttribute('name', this.appName);
3052
+ }
3053
+ /**
3054
+ * when existApp not null: this.appName === existApp.name
3055
+ * scene1: if formatAttrName and this.appName are equal: exitApp is the current app, the url must be different, existApp has been unmounted
3056
+ * scene2: if formatAttrName and this.appName are different: existApp must be prefetch or unmounted, if url is equal, then just mount, if url is different, then create new app to replace existApp
3057
+ * scene3: url is different but ssrUrl is equal
3058
+ * scene4: url is equal but ssrUrl is different, if url is equal, name must different
3059
+ * scene5: if existApp is KEEP_ALIVE_HIDDEN, name must different
3060
+ */
3061
+ if (existApp) {
3062
+ if (existApp.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN) {
3063
+ if (existApp.url === this.appUrl) {
3064
+ this.handleShowKeepAliveApp(existApp);
3065
+ }
3066
+ else {
3067
+ // the hidden keep-alive app is still active
3068
+ logError(`app name conflict, an app named ${this.appName} is running`, this.appName);
3069
+ }
3070
+ }
3071
+ else if (existApp.url === this.appUrl && existApp.ssrUrl === this.ssrUrl) {
3072
+ // mount app
3073
+ this.handleAppMount(existApp);
3074
+ }
3075
+ else {
3076
+ this.handleCreateApp();
2912
3077
  }
2913
3078
  }
2914
3079
  else {
@@ -2949,7 +3114,7 @@ function defineElement(tagName) {
2949
3114
  * fix of unmounted umd app with disableSandbox
2950
3115
  */
2951
3116
  if (appInstanceMap.has(this.appName)) {
2952
- appInstanceMap.get(this.appName).actionsForCompletelyDestory();
3117
+ appInstanceMap.get(this.appName).actionsForCompletelyDestroy();
2953
3118
  }
2954
3119
  const instance = new CreateApp({
2955
3120
  name: this.appName,
@@ -2959,7 +3124,6 @@ function defineElement(tagName) {
2959
3124
  inline: this.getDisposeResult('inline'),
2960
3125
  scopecss: !(this.getDisposeResult('disableScopecss') || this.getDisposeResult('shadowDOM')),
2961
3126
  useSandbox: !this.getDisposeResult('disableSandbox'),
2962
- macro: this.getDisposeResult('macro'),
2963
3127
  baseroute: this.getBaseRouteCompatible(),
2964
3128
  });
2965
3129
  appInstanceMap.set(this.appName, instance);
@@ -2968,10 +3132,24 @@ function defineElement(tagName) {
2968
3132
  * unmount app
2969
3133
  * @param destroy delete cache resources when unmount
2970
3134
  */
2971
- handleUnmount(destroy) {
3135
+ handleUnmount(destroy, unmountcb) {
3136
+ const app = appInstanceMap.get(this.appName);
3137
+ if (app &&
3138
+ app.getAppState() !== appStates.UNMOUNT)
3139
+ app.unmount(destroy, unmountcb);
3140
+ }
3141
+ // hidden app when disconnectedCallback called with keep-alive
3142
+ handleHiddenKeepAliveApp() {
2972
3143
  const app = appInstanceMap.get(this.appName);
2973
- if (app && appStatus.UNMOUNT !== app.getAppStatus())
2974
- app.unmount(destroy);
3144
+ if (app &&
3145
+ app.getAppState() !== appStates.UNMOUNT &&
3146
+ app.getKeepAliveState() !== keepAliveStates.KEEP_ALIVE_HIDDEN)
3147
+ app.hiddenKeepAliveApp();
3148
+ }
3149
+ // show app when connectedCallback called with keep-alive
3150
+ handleShowKeepAliveApp(app) {
3151
+ // must be asnyc
3152
+ defer(() => { var _a; return app.showKeepAliveApp((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this); });
2975
3153
  }
2976
3154
  /**
2977
3155
  * Get configuration
@@ -2980,7 +3158,27 @@ function defineElement(tagName) {
2980
3158
  */
2981
3159
  getDisposeResult(name) {
2982
3160
  // @ts-ignore
2983
- return (this.hasAttribute(name) || microApp[name]) && this.getAttribute(name) !== 'false';
3161
+ return (this.compatibleSpecialProperties(name) || microApp[name]) && this.compatibleDisablSpecialProperties(name);
3162
+ }
3163
+ // compatible of disableScopecss & disableSandbox
3164
+ compatibleSpecialProperties(name) {
3165
+ if (name === 'disableScopecss') {
3166
+ return this.hasAttribute('disableScopecss') || this.hasAttribute('disable-scopecss');
3167
+ }
3168
+ else if (name === 'disableSandbox') {
3169
+ return this.hasAttribute('disableSandbox') || this.hasAttribute('disable-sandbox');
3170
+ }
3171
+ return this.hasAttribute(name);
3172
+ }
3173
+ // compatible of disableScopecss & disableSandbox
3174
+ compatibleDisablSpecialProperties(name) {
3175
+ if (name === 'disableScopecss') {
3176
+ return this.getAttribute('disableScopecss') !== 'false' && this.getAttribute('disable-scopecss') !== 'false';
3177
+ }
3178
+ else if (name === 'disableSandbox') {
3179
+ return this.getAttribute('disableSandbox') !== 'false' && this.getAttribute('disable-sandbox') !== 'false';
3180
+ }
3181
+ return this.getAttribute(name) !== 'false';
2984
3182
  }
2985
3183
  /**
2986
3184
  * 2021-09-08
@@ -2991,6 +3189,16 @@ function defineElement(tagName) {
2991
3189
  var _a, _b;
2992
3190
  return (_b = (_a = this.getAttribute('baseroute')) !== null && _a !== void 0 ? _a : this.getAttribute('baseurl')) !== null && _b !== void 0 ? _b : '';
2993
3191
  }
3192
+ // compatible of destroy
3193
+ getDestroyCompatibleResult() {
3194
+ return this.getDisposeResult('destroy') || this.getDisposeResult('destory');
3195
+ }
3196
+ /**
3197
+ * destroy has priority over destroy keep-alive
3198
+ */
3199
+ getKeepAliveModeResult() {
3200
+ return this.getDisposeResult('keep-alive') && !this.getDestroyCompatibleResult();
3201
+ }
2994
3202
  /**
2995
3203
  * Data from the base application
2996
3204
  */
@@ -3040,13 +3248,12 @@ function filterPreFetchTarget(apps) {
3040
3248
  * url: string,
3041
3249
  * disableScopecss?: boolean,
3042
3250
  * disableSandbox?: boolean,
3043
- * macro?: boolean,
3044
3251
  * },
3045
3252
  * ...
3046
3253
  * ])
3047
3254
  * Note:
3048
3255
  * 1: preFetch is asynchronous and is performed only when the browser is idle
3049
- * 2: disableScopecss, disableSandbox, macro must be same with micro-app element, if conflict, the one who executes first shall prevail
3256
+ * 2: disableScopecss, disableSandbox must be same with micro-app element, if conflict, the one who executes first shall prevail
3050
3257
  * @param apps micro apps
3051
3258
  */
3052
3259
  function preFetch(apps) {
@@ -3056,13 +3263,12 @@ function preFetch(apps) {
3056
3263
  requestIdleCallback(() => {
3057
3264
  isFunction(apps) && (apps = apps());
3058
3265
  filterPreFetchTarget(apps).forEach((item) => {
3059
- var _a, _b, _c;
3266
+ var _a, _b;
3060
3267
  const app = new CreateApp({
3061
3268
  name: item.name,
3062
3269
  url: item.url,
3063
3270
  scopecss: !((_a = item.disableScopecss) !== null && _a !== void 0 ? _a : microApp.disableScopecss),
3064
3271
  useSandbox: !((_b = item.disableSandbox) !== null && _b !== void 0 ? _b : microApp.disableSandbox),
3065
- macro: (_c = item.macro) !== null && _c !== void 0 ? _c : microApp.macro,
3066
3272
  });
3067
3273
  app.isPrefetch = true;
3068
3274
  appInstanceMap.set(item.name, app);
@@ -3112,6 +3318,99 @@ function getGlobalAssets(assets) {
3112
3318
  }
3113
3319
  }
3114
3320
 
3321
+ /**
3322
+ * if app not prefetch & not unmount, then app is active
3323
+ * @param excludeHiddenApp exclude hidden keep-alive app
3324
+ * @returns active apps
3325
+ */
3326
+ function getActiveApps(excludeHiddenApp) {
3327
+ const activeApps = [];
3328
+ appInstanceMap.forEach((app, appName) => {
3329
+ if (appStates.UNMOUNT !== app.getAppState() &&
3330
+ !app.isPrefetch &&
3331
+ (!excludeHiddenApp ||
3332
+ keepAliveStates.KEEP_ALIVE_HIDDEN !== app.getKeepAliveState())) {
3333
+ activeApps.push(appName);
3334
+ }
3335
+ });
3336
+ return activeApps;
3337
+ }
3338
+ // get all registered apps
3339
+ function getAllApps() {
3340
+ return Array.from(appInstanceMap.keys());
3341
+ }
3342
+ /**
3343
+ * unmount app by appname
3344
+ * @param appName
3345
+ * @param options unmountAppParams
3346
+ * @returns Promise<void>
3347
+ */
3348
+ function unmountApp(appName, options) {
3349
+ const app = appInstanceMap.get(formatAppName(appName));
3350
+ return new Promise((reslove) => {
3351
+ if (app) {
3352
+ if (app.getAppState() === appStates.UNMOUNT || app.isPrefetch) {
3353
+ if (options === null || options === void 0 ? void 0 : options.destroy) {
3354
+ app.actionsForCompletelyDestroy();
3355
+ }
3356
+ reslove();
3357
+ }
3358
+ else if (app.getKeepAliveState() === keepAliveStates.KEEP_ALIVE_HIDDEN) {
3359
+ if (options === null || options === void 0 ? void 0 : options.destroy) {
3360
+ app.unmount(true, reslove);
3361
+ }
3362
+ else if (options === null || options === void 0 ? void 0 : options.clearAliveState) {
3363
+ app.unmount(false, reslove);
3364
+ }
3365
+ else {
3366
+ reslove();
3367
+ }
3368
+ }
3369
+ else {
3370
+ const container = getRootContainer(app.container);
3371
+ const unmountHandler = () => {
3372
+ container.removeEventListener('unmount', unmountHandler);
3373
+ container.removeEventListener('afterhidden', afterhiddenHandler);
3374
+ reslove();
3375
+ };
3376
+ const afterhiddenHandler = () => {
3377
+ container.removeEventListener('unmount', unmountHandler);
3378
+ container.removeEventListener('afterhidden', afterhiddenHandler);
3379
+ reslove();
3380
+ };
3381
+ container.addEventListener('unmount', unmountHandler);
3382
+ container.addEventListener('afterhidden', afterhiddenHandler);
3383
+ if (options === null || options === void 0 ? void 0 : options.destroy) {
3384
+ let destroyAttrValue, destoryAttrValue;
3385
+ container.hasAttribute('destroy') && (destroyAttrValue = container.getAttribute('destroy'));
3386
+ container.hasAttribute('destory') && (destoryAttrValue = container.getAttribute('destory'));
3387
+ container.setAttribute('destroy', 'true');
3388
+ container.parentNode.removeChild(container);
3389
+ container.removeAttribute('destroy');
3390
+ typeof destroyAttrValue === 'string' && container.setAttribute('destroy', destroyAttrValue);
3391
+ typeof destoryAttrValue === 'string' && container.setAttribute('destory', destoryAttrValue);
3392
+ }
3393
+ else if ((options === null || options === void 0 ? void 0 : options.clearAliveState) && container.hasAttribute('keep-alive')) {
3394
+ const keepAliveAttrValue = container.getAttribute('keep-alive');
3395
+ container.removeAttribute('keep-alive');
3396
+ container.parentNode.removeChild(container);
3397
+ container.setAttribute('keep-alive', keepAliveAttrValue);
3398
+ }
3399
+ else {
3400
+ container.parentNode.removeChild(container);
3401
+ }
3402
+ }
3403
+ }
3404
+ else {
3405
+ logWarn(`app ${appName} does not exist`);
3406
+ reslove();
3407
+ }
3408
+ });
3409
+ }
3410
+ // unmount all apps in turn
3411
+ function unmountAllApps(options) {
3412
+ return Array.from(appInstanceMap.keys()).reduce((pre, next) => pre.then(() => unmountApp(next, options)), Promise.resolve());
3413
+ }
3115
3414
  class MicroApp extends EventCenterForBaseApp {
3116
3415
  constructor() {
3117
3416
  super(...arguments);
@@ -3146,7 +3445,6 @@ class MicroApp extends EventCenterForBaseApp {
3146
3445
  this.inline = options.inline;
3147
3446
  this.disableScopecss = options.disableScopecss;
3148
3447
  this.disableSandbox = options.disableSandbox;
3149
- this.macro = options.macro;
3150
3448
  this.ssr = options.ssr;
3151
3449
  isFunction(options.fetch) && (this.fetch = options.fetch);
3152
3450
  isPlainObject(options.lifeCycles) && (this.lifeCycles = options.lifeCycles);
@@ -3175,5 +3473,5 @@ class MicroApp extends EventCenterForBaseApp {
3175
3473
  var microApp = new MicroApp();
3176
3474
 
3177
3475
  export default microApp;
3178
- export { EventCenterForMicroApp, getActiveApps, getAllApps, preFetch, pureCreateElement, removeDomScope, version };
3476
+ export { EventCenterForMicroApp, MicroApp, getActiveApps, getAllApps, preFetch, pureCreateElement, removeDomScope, unmountAllApps, unmountApp, version };
3179
3477
  //# sourceMappingURL=index.esm.js.map