@micro-zoe/micro-app 1.0.0-beta.0 → 1.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- const version = '1.0.0-beta.0';
1
+ const version = '1.0.0-beta.2';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -114,6 +114,10 @@ function isBaseElement(target) {
114
114
  var _a, _b;
115
115
  return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'BASE';
116
116
  }
117
+ function isMicroAppBody(target) {
118
+ var _a, _b;
119
+ return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'MICRO-APP-BODY';
120
+ }
117
121
  // is ProxyDocument
118
122
  function isProxyDocument(target) {
119
123
  return toString.call(target) === '[object ProxyDocument]';
@@ -1167,231 +1171,578 @@ function createSourceCenter() {
1167
1171
  }
1168
1172
  var sourceCenter = createSourceCenter();
1169
1173
 
1174
+ const scriptTypes = ['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module', 'systemjs-module', 'systemjs-importmap'];
1175
+ // whether use type='module' script
1176
+ function isTypeModule(app, scriptInfo) {
1177
+ return scriptInfo.appSpace[app.name].module && (!app.useSandbox || app.iframe);
1178
+ }
1179
+ // special script element
1180
+ function isSpecialScript(app, scriptInfo) {
1181
+ const attrs = scriptInfo.appSpace[app.name].attrs;
1182
+ return attrs.has('id');
1183
+ }
1170
1184
  /**
1171
- *
1185
+ * whether to run js in inline mode
1186
+ * scene:
1187
+ * 1. inline config for app
1188
+ * 2. inline attr in script element
1189
+ * 3. module script
1190
+ * 4. script with special attr
1191
+ */
1192
+ function isInlineMode(app, scriptInfo) {
1193
+ return (app.inline ||
1194
+ scriptInfo.appSpace[app.name].inline ||
1195
+ isTypeModule(app, scriptInfo) ||
1196
+ isSpecialScript(app, scriptInfo) ||
1197
+ app.iframe);
1198
+ }
1199
+ // TODO: iframe重新插入window前后不一致,通过iframe Function创建的函数无法复用
1200
+ function getEffectWindow(app) {
1201
+ return app.iframe ? app.sandBox.microAppWindow : globalEnv.rawWindow;
1202
+ }
1203
+ // Convert string code to function
1204
+ function code2Function(app, code) {
1205
+ const targetWindow = getEffectWindow(app);
1206
+ return new targetWindow.Function(code);
1207
+ }
1208
+ /**
1209
+ * If the appSpace of the current js address has other app, try to reuse parsedFunction of other app
1172
1210
  * @param appName app.name
1173
- * @param linkInfo linkInfo of current address
1211
+ * @param scriptInfo scriptInfo of current address
1212
+ * @param currentCode pure code of current address
1174
1213
  */
1175
- function getExistParseCode(appName, prefix, linkInfo) {
1176
- const appSpace = linkInfo.appSpace;
1214
+ function getExistParseResult(app, scriptInfo, currentCode) {
1215
+ const appSpace = scriptInfo.appSpace;
1177
1216
  for (const item in appSpace) {
1178
- if (item !== appName) {
1217
+ if (item !== app.name) {
1179
1218
  const appSpaceData = appSpace[item];
1180
- if (appSpaceData.parsedCode) {
1181
- return appSpaceData.parsedCode.replace(new RegExp(createPrefix(item, true), 'g'), prefix);
1219
+ if (appSpaceData.parsedCode === currentCode && appSpaceData.parsedFunction) {
1220
+ return appSpaceData.parsedFunction;
1182
1221
  }
1183
1222
  }
1184
1223
  }
1185
1224
  }
1186
- // transfer the attributes on the link to convertStyle
1187
- function setConvertStyleAttr(convertStyle, attrs) {
1225
+ /**
1226
+ * get parsedFunction from exist data or parsedCode
1227
+ * @returns parsedFunction
1228
+ */
1229
+ function getParsedFunction(app, scriptInfo, parsedCode) {
1230
+ return getExistParseResult(app, scriptInfo, parsedCode) || code2Function(app, parsedCode);
1231
+ }
1232
+ // Prevent randomly created strings from repeating
1233
+ function getUniqueNonceSrc() {
1234
+ const nonceStr = createNonceSrc();
1235
+ if (sourceCenter.script.hasInfo(nonceStr)) {
1236
+ return getUniqueNonceSrc();
1237
+ }
1238
+ return nonceStr;
1239
+ }
1240
+ // transfer the attributes on the script to convertScript
1241
+ function setConvertScriptAttr(convertScript, attrs) {
1188
1242
  attrs.forEach((value, key) => {
1189
- if (key === 'rel')
1243
+ if ((key === 'type' && value === 'module') || key === 'defer' || key === 'async')
1190
1244
  return;
1191
- if (key === 'href')
1192
- key = 'data-origin-href';
1193
- convertStyle.setAttribute(key, value);
1245
+ if (key === 'src')
1246
+ key = 'data-origin-src';
1247
+ globalEnv.rawSetAttribute.call(convertScript, key, value);
1194
1248
  });
1195
1249
  }
1250
+ // wrap code in sandbox
1251
+ function isWrapInSandBox(app, scriptInfo) {
1252
+ return app.useSandbox && !isTypeModule(app, scriptInfo);
1253
+ }
1254
+ function getSandboxType(app, scriptInfo) {
1255
+ return isWrapInSandBox(app, scriptInfo) ? app.iframe ? 'iframe' : 'with' : 'disable';
1256
+ }
1196
1257
  /**
1197
- * Extract link elements
1198
- * @param link link element
1199
- * @param parent parent element of link
1258
+ * Extract script elements
1259
+ * @param script script element
1260
+ * @param parent parent element of script
1200
1261
  * @param app app
1201
- * @param microAppHead micro-app-head element
1202
1262
  * @param isDynamic dynamic insert
1203
1263
  */
1204
- function extractLinkFromHtml(link, parent, app, isDynamic = false) {
1205
- const rel = link.getAttribute('rel');
1206
- let href = link.getAttribute('href');
1264
+ function extractScriptElement(script, parent, app, isDynamic = false) {
1265
+ var _a;
1207
1266
  let replaceComment = null;
1208
- if (rel === 'stylesheet' && href) {
1209
- href = CompletionPath(href, app.url);
1210
- let linkInfo = sourceCenter.link.getInfo(href);
1267
+ let src = script.getAttribute('src');
1268
+ if (src)
1269
+ src = CompletionPath(src, app.url);
1270
+ if (script.hasAttribute('exclude') || checkExcludeUrl(src, app.name)) {
1271
+ replaceComment = document.createComment('script element with exclude attribute removed by micro-app');
1272
+ }
1273
+ else if ((script.type &&
1274
+ !scriptTypes.includes(script.type)) ||
1275
+ script.hasAttribute('ignore') ||
1276
+ checkIgnoreUrl(src, app.name)) {
1277
+ // 配置为忽略的脚本,清空 rawDocument.currentScript,避免被忽略的脚本内获取 currentScript 出错
1278
+ if ((_a = globalEnv.rawDocument) === null || _a === void 0 ? void 0 : _a.currentScript) {
1279
+ delete globalEnv.rawDocument.currentScript;
1280
+ }
1281
+ return null;
1282
+ }
1283
+ else if ((globalEnv.supportModuleScript && script.noModule) ||
1284
+ (!globalEnv.supportModuleScript && script.type === 'module')) {
1285
+ replaceComment = document.createComment(`${script.noModule ? 'noModule' : 'module'} script ignored by micro-app`);
1286
+ }
1287
+ else if (src) { // remote script
1288
+ let scriptInfo = sourceCenter.script.getInfo(src);
1211
1289
  const appSpaceData = {
1212
- attrs: getAttributes(link),
1290
+ async: script.hasAttribute('async'),
1291
+ defer: script.defer || script.type === 'module',
1292
+ module: script.type === 'module',
1293
+ inline: script.hasAttribute('inline'),
1294
+ pure: script.hasAttribute('pure'),
1295
+ attrs: getAttributes(script),
1213
1296
  };
1214
- if (!linkInfo) {
1215
- linkInfo = {
1297
+ if (!scriptInfo) {
1298
+ scriptInfo = {
1216
1299
  code: '',
1300
+ isExternal: true,
1217
1301
  appSpace: {
1218
1302
  [app.name]: appSpaceData,
1219
1303
  }
1220
1304
  };
1221
1305
  }
1222
1306
  else {
1223
- linkInfo.appSpace[app.name] = linkInfo.appSpace[app.name] || appSpaceData;
1307
+ /**
1308
+ * Reuse when appSpace exists
1309
+ * NOTE:
1310
+ * 1. The same static script, appSpace must be the same (in fact, it may be different when url change)
1311
+ * 2. The same dynamic script, appSpace may be the same, but we still reuse appSpace, which should pay attention
1312
+ */
1313
+ scriptInfo.appSpace[app.name] = scriptInfo.appSpace[app.name] || appSpaceData;
1224
1314
  }
1225
- sourceCenter.link.setInfo(href, linkInfo);
1315
+ sourceCenter.script.setInfo(src, scriptInfo);
1226
1316
  if (!isDynamic) {
1227
- app.source.links.add(href);
1228
- replaceComment = document.createComment(`link element with href=${href} move to micro-app-head as style element`);
1229
- linkInfo.appSpace[app.name].placeholder = replaceComment;
1317
+ app.source.scripts.add(src);
1318
+ replaceComment = document.createComment(`script with src='${src}' extract by micro-app`);
1230
1319
  }
1231
1320
  else {
1232
- return { address: href, linkInfo };
1321
+ return { address: src, scriptInfo };
1233
1322
  }
1234
1323
  }
1235
- else if (rel && ['prefetch', 'preload', 'prerender', 'icon', 'apple-touch-icon'].includes(rel)) {
1236
- // preload prefetch icon ....
1237
- if (isDynamic) {
1238
- replaceComment = document.createComment(`link element with rel=${rel}${href ? ' & href=' + href : ''} removed by micro-app`);
1324
+ else if (script.textContent) { // inline script
1325
+ /**
1326
+ * NOTE:
1327
+ * 1. Each inline script is unique
1328
+ * 2. Every dynamic created inline script will be re-executed
1329
+ * ACTION:
1330
+ * 1. Delete dynamic inline script info after exec
1331
+ * 2. Delete static inline script info when destroy
1332
+ */
1333
+ const nonceStr = getUniqueNonceSrc();
1334
+ const scriptInfo = {
1335
+ code: script.textContent,
1336
+ isExternal: false,
1337
+ appSpace: {
1338
+ [app.name]: {
1339
+ async: false,
1340
+ defer: script.type === 'module',
1341
+ module: script.type === 'module',
1342
+ inline: script.hasAttribute('inline'),
1343
+ pure: script.hasAttribute('pure'),
1344
+ attrs: getAttributes(script),
1345
+ }
1346
+ }
1347
+ };
1348
+ if (!isDynamic) {
1349
+ app.source.scripts.add(nonceStr);
1350
+ sourceCenter.script.setInfo(nonceStr, scriptInfo);
1351
+ replaceComment = document.createComment('inline script extract by micro-app');
1239
1352
  }
1240
1353
  else {
1241
- parent.removeChild(link);
1354
+ // Because each dynamic script is unique, it is not put into sourceCenter
1355
+ return { address: nonceStr, scriptInfo };
1242
1356
  }
1243
1357
  }
1244
- else if (href) {
1245
- // dns-prefetch preconnect modulepreload search ....
1246
- link.setAttribute('href', CompletionPath(href, app.url));
1358
+ else if (!isDynamic) {
1359
+ /**
1360
+ * script with empty src or empty script.textContent remove in static html
1361
+ * & not removed if it created by dynamic
1362
+ */
1363
+ replaceComment = document.createComment('script element removed by micro-app');
1247
1364
  }
1248
1365
  if (isDynamic) {
1249
1366
  return { replaceComment };
1250
1367
  }
1251
- else if (replaceComment) {
1252
- return parent.replaceChild(replaceComment, link);
1368
+ else {
1369
+ return parent === null || parent === void 0 ? void 0 : parent.replaceChild(replaceComment, script);
1253
1370
  }
1254
1371
  }
1255
1372
  /**
1256
- * Get link remote resources
1257
- * @param wrapElement htmlDom
1258
- * @param app app
1259
- * @param microAppHead micro-app-head
1373
+ * get assets plugins
1374
+ * @param appName app name
1260
1375
  */
1261
- function fetchLinksFromHtml(wrapElement, app, microAppHead, fiberStyleResult) {
1262
- const styleList = Array.from(app.source.links);
1263
- const fetchLinkPromise = styleList.map((address) => {
1264
- const linkInfo = sourceCenter.link.getInfo(address);
1265
- return linkInfo.code ? linkInfo.code : fetchSource(address, app.name);
1266
- });
1267
- const fiberLinkTasks = fiberStyleResult ? [] : null;
1268
- promiseStream(fetchLinkPromise, (res) => {
1269
- injectFiberTask(fiberLinkTasks, () => fetchLinkSuccess(styleList[res.index], res.data, microAppHead, app));
1270
- }, (err) => {
1271
- logError(err, app.name);
1272
- }, () => {
1273
- /**
1274
- * 1. If fiberStyleResult exist, fiberLinkTasks must exist
1275
- * 2. Download link source while processing style
1276
- * 3. Process style first, and then process link
1277
- */
1278
- if (fiberStyleResult) {
1279
- fiberStyleResult.then(() => {
1280
- fiberLinkTasks.push(() => Promise.resolve(app.onLoad(wrapElement)));
1281
- serialExecFiberTasks(fiberLinkTasks);
1282
- });
1283
- }
1284
- else {
1285
- app.onLoad(wrapElement);
1286
- }
1287
- });
1376
+ function getAssetsPlugins(appName) {
1377
+ var _a, _b, _c;
1378
+ const globalPlugins = ((_a = microApp.options.plugins) === null || _a === void 0 ? void 0 : _a.global) || [];
1379
+ const modulePlugins = ((_c = (_b = microApp.options.plugins) === null || _b === void 0 ? void 0 : _b.modules) === null || _c === void 0 ? void 0 : _c[appName]) || [];
1380
+ return [...globalPlugins, ...modulePlugins];
1288
1381
  }
1289
1382
  /**
1290
- * Fetch link succeeded, replace placeholder with style tag
1291
- * NOTE:
1292
- * 1. Only exec when init, no longer exec when remount
1293
- * 2. Only handler html link element, not dynamic link or style
1294
- * 3. The same prefix can reuse parsedCode
1295
- * 4. Async exec with requestIdleCallback in prefetch or fiber
1296
- * 5. appSpace[app.name].placeholder/attrs must exist
1297
- * @param address resource address
1298
- * @param code link source code
1299
- * @param microAppHead micro-app-head
1300
- * @param app app instance
1383
+ * whether the address needs to be excluded
1384
+ * @param address css or js link
1385
+ * @param plugins microApp plugins
1301
1386
  */
1302
- function fetchLinkSuccess(address, code, microAppHead, app) {
1303
- /**
1304
- * linkInfo must exist, but linkInfo.code not
1305
- * so we set code to linkInfo.code
1306
- */
1307
- const linkInfo = sourceCenter.link.getInfo(address);
1308
- linkInfo.code = code;
1309
- const appSpaceData = linkInfo.appSpace[app.name];
1310
- const placeholder = appSpaceData.placeholder;
1311
- /**
1312
- * When prefetch app is replaced by a new app in the processing phase, since the linkInfo is common, when the linkInfo of the prefetch app is processed, it may have already been processed.
1313
- * This causes placeholder to be possibly null
1314
- * e.g.
1315
- * 1. prefetch app.url different from <micro-app></micro-app>
1316
- * 2. prefetch param different from <micro-app></micro-app>
1317
- */
1318
- if (placeholder) {
1319
- const convertStyle = pureCreateElement('style');
1320
- handleConvertStyle(app, address, convertStyle, linkInfo, appSpaceData.attrs);
1321
- if (placeholder.parentNode) {
1322
- placeholder.parentNode.replaceChild(convertStyle, placeholder);
1323
- }
1324
- else {
1325
- microAppHead.appendChild(convertStyle);
1387
+ function checkExcludeUrl(address, appName) {
1388
+ if (!address)
1389
+ return false;
1390
+ const plugins = getAssetsPlugins(appName) || [];
1391
+ return plugins.some(plugin => {
1392
+ if (!plugin.excludeChecker)
1393
+ return false;
1394
+ return plugin.excludeChecker(address);
1395
+ });
1396
+ }
1397
+ /**
1398
+ * whether the address needs to be ignore
1399
+ * @param address css or js link
1400
+ * @param plugins microApp plugins
1401
+ */
1402
+ function checkIgnoreUrl(address, appName) {
1403
+ if (!address)
1404
+ return false;
1405
+ const plugins = getAssetsPlugins(appName) || [];
1406
+ return plugins.some(plugin => {
1407
+ if (!plugin.ignoreChecker)
1408
+ return false;
1409
+ return plugin.ignoreChecker(address);
1410
+ });
1411
+ }
1412
+ /**
1413
+ * Get remote resources of script
1414
+ * @param wrapElement htmlDom
1415
+ * @param app app
1416
+ */
1417
+ function fetchScriptsFromHtml(wrapElement, app) {
1418
+ const scriptList = Array.from(app.source.scripts);
1419
+ const fetchScriptPromise = [];
1420
+ const fetchScriptPromiseInfo = [];
1421
+ for (const address of scriptList) {
1422
+ const scriptInfo = sourceCenter.script.getInfo(address);
1423
+ const appSpaceData = scriptInfo.appSpace[app.name];
1424
+ if ((!appSpaceData.defer && !appSpaceData.async) || (app.isPrefetch && !app.isPrerender)) {
1425
+ fetchScriptPromise.push(scriptInfo.code ? scriptInfo.code : fetchSource(address, app.name));
1426
+ fetchScriptPromiseInfo.push([address, scriptInfo]);
1326
1427
  }
1327
- // clear placeholder
1328
- appSpaceData.placeholder = null;
1428
+ }
1429
+ const fiberScriptTasks = app.isPrefetch || app.fiber ? [] : null;
1430
+ if (fetchScriptPromise.length) {
1431
+ promiseStream(fetchScriptPromise, (res) => {
1432
+ injectFiberTask(fiberScriptTasks, () => fetchScriptSuccess(fetchScriptPromiseInfo[res.index][0], fetchScriptPromiseInfo[res.index][1], res.data, app));
1433
+ }, (err) => {
1434
+ logError(err, app.name);
1435
+ }, () => {
1436
+ if (fiberScriptTasks) {
1437
+ fiberScriptTasks.push(() => Promise.resolve(app.onLoad(wrapElement)));
1438
+ serialExecFiberTasks(fiberScriptTasks);
1439
+ }
1440
+ else {
1441
+ app.onLoad(wrapElement);
1442
+ }
1443
+ });
1444
+ }
1445
+ else {
1446
+ app.onLoad(wrapElement);
1329
1447
  }
1330
1448
  }
1331
1449
  /**
1332
- * Get parsedCode, update convertStyle
1333
- * Actions:
1334
- * 1. get scope css (through scopedCSS or oldData)
1335
- * 2. record parsedCode
1336
- * 3. set parsedCode to convertStyle if need
1337
- * @param app app instance
1338
- * @param address resource address
1339
- * @param convertStyle converted style
1340
- * @param linkInfo linkInfo in sourceCenter
1341
- * @param attrs attrs of link
1450
+ * fetch js succeeded, record the code value
1451
+ * @param address script address
1452
+ * @param scriptInfo resource script info
1453
+ * @param data code
1342
1454
  */
1343
- function handleConvertStyle(app, address, convertStyle, linkInfo, attrs) {
1344
- if (app.scopecss) {
1345
- const appSpaceData = linkInfo.appSpace[app.name];
1346
- appSpaceData.prefix = appSpaceData.prefix || createPrefix(app.name);
1455
+ function fetchScriptSuccess(address, scriptInfo, code, app) {
1456
+ // reset scriptInfo.code
1457
+ scriptInfo.code = code;
1458
+ /**
1459
+ * Pre parse script for prefetch, improve rendering performance
1460
+ * NOTE:
1461
+ * 1. if global parseResult exist, skip this step
1462
+ * 2. if app is inline or script is esmodule, skip this step
1463
+ * 3. if global parseResult not exist, the current script occupies the position, when js is reused, parseResult is reference
1464
+ */
1465
+ if (app.isPrefetch && app.prefetchLevel === 2) {
1466
+ const appSpaceData = scriptInfo.appSpace[app.name];
1467
+ /**
1468
+ * When prefetch app is replaced by a new app in the processing phase, since the scriptInfo is common, when the scriptInfo of the prefetch app is processed, it may have already been processed.
1469
+ * This causes parsedCode to already exist when preloading ends
1470
+ * e.g.
1471
+ * 1. prefetch app.url different from <micro-app></micro-app>
1472
+ * 2. prefetch param different from <micro-app></micro-app>
1473
+ */
1347
1474
  if (!appSpaceData.parsedCode) {
1348
- const existParsedCode = getExistParseCode(app.name, appSpaceData.prefix, linkInfo);
1349
- if (!existParsedCode) {
1350
- convertStyle.textContent = linkInfo.code;
1351
- scopedCSS(convertStyle, app, address);
1475
+ appSpaceData.parsedCode = bindScope(address, app, code, scriptInfo);
1476
+ appSpaceData.sandboxType = getSandboxType(app, scriptInfo);
1477
+ if (!isInlineMode(app, scriptInfo)) {
1478
+ try {
1479
+ appSpaceData.parsedFunction = getParsedFunction(app, scriptInfo, appSpaceData.parsedCode);
1480
+ }
1481
+ catch (err) {
1482
+ logError('Something went wrong while handling preloaded resources', app.name, '\n', err);
1483
+ }
1484
+ }
1485
+ }
1486
+ }
1487
+ }
1488
+ /**
1489
+ * Execute js in the mount lifecycle
1490
+ * @param app app
1491
+ * @param initHook callback for umd mode
1492
+ */
1493
+ function execScripts(app, initHook) {
1494
+ const fiberScriptTasks = app.fiber ? [] : null;
1495
+ const scriptList = Array.from(app.source.scripts);
1496
+ const deferScriptPromise = [];
1497
+ const deferScriptInfo = [];
1498
+ for (const address of scriptList) {
1499
+ const scriptInfo = sourceCenter.script.getInfo(address);
1500
+ const appSpaceData = scriptInfo.appSpace[app.name];
1501
+ // Notice the second render
1502
+ if (appSpaceData.defer || appSpaceData.async) {
1503
+ // TODO: defer和module彻底分开,不要混在一起
1504
+ if (scriptInfo.isExternal && !scriptInfo.code && !(app.iframe && appSpaceData.module)) {
1505
+ deferScriptPromise.push(fetchSource(address, app.name));
1352
1506
  }
1353
1507
  else {
1354
- convertStyle.textContent = existParsedCode;
1508
+ deferScriptPromise.push(scriptInfo.code);
1355
1509
  }
1356
- appSpaceData.parsedCode = convertStyle.textContent;
1510
+ deferScriptInfo.push([address, scriptInfo]);
1511
+ isTypeModule(app, scriptInfo) && (initHook.moduleCount = initHook.moduleCount ? ++initHook.moduleCount : 1);
1357
1512
  }
1358
1513
  else {
1359
- convertStyle.textContent = appSpaceData.parsedCode;
1514
+ injectFiberTask(fiberScriptTasks, () => {
1515
+ runScript(address, app, scriptInfo);
1516
+ initHook(false);
1517
+ });
1360
1518
  }
1361
1519
  }
1520
+ if (deferScriptPromise.length) {
1521
+ promiseStream(deferScriptPromise, (res) => {
1522
+ const scriptInfo = deferScriptInfo[res.index][1];
1523
+ scriptInfo.code = scriptInfo.code || res.data;
1524
+ }, (err) => {
1525
+ initHook.errorCount = initHook.errorCount ? ++initHook.errorCount : 1;
1526
+ logError(err, app.name);
1527
+ }, () => {
1528
+ deferScriptInfo.forEach(([address, scriptInfo]) => {
1529
+ if (isString(scriptInfo.code)) {
1530
+ injectFiberTask(fiberScriptTasks, () => {
1531
+ runScript(address, app, scriptInfo, initHook);
1532
+ !isTypeModule(app, scriptInfo) && initHook(false);
1533
+ });
1534
+ }
1535
+ });
1536
+ /**
1537
+ * Fiber wraps js in requestIdleCallback and executes it in sequence
1538
+ * NOTE:
1539
+ * 1. In order to ensure the execution order, wait for all js loaded and then execute
1540
+ * 2. If js create a dynamic script, it may be errors in the execution order, because the subsequent js is wrapped in requestIdleCallback, even putting dynamic script in requestIdleCallback doesn't solve it
1541
+ *
1542
+ * BUG: NOTE.2 - execution order problem
1543
+ */
1544
+ if (fiberScriptTasks) {
1545
+ fiberScriptTasks.push(() => Promise.resolve(initHook(isUndefined(initHook.moduleCount) ||
1546
+ initHook.errorCount === deferScriptPromise.length)));
1547
+ serialExecFiberTasks(fiberScriptTasks);
1548
+ }
1549
+ else {
1550
+ initHook(isUndefined(initHook.moduleCount) ||
1551
+ initHook.errorCount === deferScriptPromise.length);
1552
+ }
1553
+ });
1554
+ }
1362
1555
  else {
1363
- convertStyle.textContent = linkInfo.code;
1556
+ if (fiberScriptTasks) {
1557
+ fiberScriptTasks.push(() => Promise.resolve(initHook(true)));
1558
+ serialExecFiberTasks(fiberScriptTasks);
1559
+ }
1560
+ else {
1561
+ initHook(true);
1562
+ }
1364
1563
  }
1365
- setConvertStyleAttr(convertStyle, attrs);
1366
1564
  }
1367
1565
  /**
1368
- * Handle css of dynamic link
1369
- * @param address link address
1566
+ * run code
1567
+ * @param address script address
1370
1568
  * @param app app
1371
- * @param linkInfo linkInfo
1372
- * @param originLink origin link element
1569
+ * @param scriptInfo script info
1570
+ * @param callback callback of module script
1373
1571
  */
1374
- function formatDynamicLink(address, app, linkInfo, originLink) {
1375
- const convertStyle = pureCreateElement('style');
1376
- const handleDynamicLink = () => {
1377
- handleConvertStyle(app, address, convertStyle, linkInfo, linkInfo.appSpace[app.name].attrs);
1378
- dispatchOnLoadEvent(originLink);
1379
- };
1380
- if (linkInfo.code) {
1381
- defer(handleDynamicLink);
1572
+ function runScript(address, app, scriptInfo, callback, replaceElement) {
1573
+ try {
1574
+ actionsBeforeRunScript(app);
1575
+ const appSpaceData = scriptInfo.appSpace[app.name];
1576
+ const sandboxType = getSandboxType(app, scriptInfo);
1577
+ /**
1578
+ * NOTE:
1579
+ * 1. plugins and wrapCode will only be executed once
1580
+ * 2. if parsedCode not exist, parsedFunction is not exist
1581
+ * 3. if parsedCode exist, parsedFunction does not necessarily exist
1582
+ */
1583
+ if (!appSpaceData.parsedCode || appSpaceData.sandboxType !== sandboxType) {
1584
+ appSpaceData.parsedCode = bindScope(address, app, scriptInfo.code, scriptInfo);
1585
+ appSpaceData.sandboxType = sandboxType;
1586
+ appSpaceData.parsedFunction = null;
1587
+ }
1588
+ if (isInlineMode(app, scriptInfo)) {
1589
+ const scriptElement = replaceElement || pureCreateElement('script');
1590
+ runCode2InlineScript(address, appSpaceData.parsedCode, isTypeModule(app, scriptInfo), scriptElement, appSpaceData.attrs, callback);
1591
+ if (!replaceElement) {
1592
+ // TEST IGNORE
1593
+ const parent = app.iframe ? app.sandBox.microBody : app.querySelector('micro-app-body');
1594
+ parent === null || parent === void 0 ? void 0 : parent.appendChild(scriptElement);
1595
+ }
1596
+ }
1597
+ else {
1598
+ runParsedFunction(app, scriptInfo);
1599
+ }
1382
1600
  }
1383
- else {
1384
- fetchSource(address, app.name).then((data) => {
1385
- linkInfo.code = data;
1386
- handleDynamicLink();
1387
- }).catch((err) => {
1388
- logError(err, app.name);
1389
- dispatchOnErrorEvent(originLink);
1390
- });
1601
+ catch (e) {
1602
+ console.error(`[micro-app from ${replaceElement ? 'runDynamicScript' : 'runScript'}] app ${app.name}: `, e, address);
1391
1603
  }
1392
- return convertStyle;
1393
1604
  }
1394
-
1605
+ /**
1606
+ * Get dynamically created remote script
1607
+ * @param address script address
1608
+ * @param app app instance
1609
+ * @param scriptInfo scriptInfo
1610
+ * @param originScript origin script element
1611
+ */
1612
+ function runDynamicRemoteScript(address, app, scriptInfo, originScript) {
1613
+ const replaceElement = isInlineMode(app, scriptInfo) ? pureCreateElement('script') : document.createComment('dynamic script extract by micro-app');
1614
+ const dispatchScriptOnLoadEvent = () => dispatchOnLoadEvent(originScript);
1615
+ const runDynamicScript = () => {
1616
+ const descriptor = Object.getOwnPropertyDescriptor(globalEnv.rawDocument, 'currentScript');
1617
+ if (!descriptor || descriptor.configurable) {
1618
+ Object.defineProperty(globalEnv.rawDocument, 'currentScript', {
1619
+ value: originScript,
1620
+ configurable: true,
1621
+ });
1622
+ }
1623
+ runScript(address, app, scriptInfo, dispatchScriptOnLoadEvent, replaceElement);
1624
+ !isTypeModule(app, scriptInfo) && dispatchScriptOnLoadEvent();
1625
+ };
1626
+ if (scriptInfo.code || (app.iframe && scriptInfo.appSpace[app.name].module)) {
1627
+ defer(runDynamicScript);
1628
+ }
1629
+ else {
1630
+ fetchSource(address, app.name).then((code) => {
1631
+ scriptInfo.code = code;
1632
+ runDynamicScript();
1633
+ }).catch((err) => {
1634
+ logError(err, app.name);
1635
+ dispatchOnErrorEvent(originScript);
1636
+ });
1637
+ }
1638
+ return replaceElement;
1639
+ }
1640
+ /**
1641
+ * Get dynamically created inline script
1642
+ * @param address script address
1643
+ * @param app app instance
1644
+ * @param scriptInfo scriptInfo
1645
+ */
1646
+ function runDynamicInlineScript(address, app, scriptInfo) {
1647
+ const replaceElement = isInlineMode(app, scriptInfo) ? pureCreateElement('script') : document.createComment('dynamic script extract by micro-app');
1648
+ runScript(address, app, scriptInfo, void 0, replaceElement);
1649
+ return replaceElement;
1650
+ }
1651
+ /**
1652
+ * common handle for inline script
1653
+ * @param address script address
1654
+ * @param code bound code
1655
+ * @param module type='module' of script
1656
+ * @param scriptElement target script element
1657
+ * @param attrs attributes of script element
1658
+ * @param callback callback of module script
1659
+ */
1660
+ function runCode2InlineScript(address, code, module, scriptElement, attrs, callback) {
1661
+ if (module) {
1662
+ // module script is async, transform it to a blob for subsequent operations
1663
+ if (isInlineScript(address)) {
1664
+ const blob = new Blob([code], { type: 'text/javascript' });
1665
+ scriptElement.src = URL.createObjectURL(blob);
1666
+ }
1667
+ else {
1668
+ scriptElement.src = address;
1669
+ }
1670
+ globalEnv.rawSetAttribute.call(scriptElement, 'type', 'module');
1671
+ if (callback) {
1672
+ callback.moduleCount && callback.moduleCount--;
1673
+ /**
1674
+ * module script will execute onload method only after it insert to document/iframe
1675
+ */
1676
+ scriptElement.onload = callback.bind(scriptElement, callback.moduleCount === 0);
1677
+ }
1678
+ }
1679
+ else {
1680
+ scriptElement.textContent = code;
1681
+ }
1682
+ setConvertScriptAttr(scriptElement, attrs);
1683
+ }
1684
+ // init & run code2Function
1685
+ function runParsedFunction(app, scriptInfo) {
1686
+ const appSpaceData = scriptInfo.appSpace[app.name];
1687
+ if (!appSpaceData.parsedFunction) {
1688
+ appSpaceData.parsedFunction = getParsedFunction(app, scriptInfo, appSpaceData.parsedCode);
1689
+ }
1690
+ appSpaceData.parsedFunction.call(getEffectWindow(app));
1691
+ }
1692
+ /**
1693
+ * bind js scope
1694
+ * @param app app
1695
+ * @param code code
1696
+ * @param scriptInfo source script info
1697
+ */
1698
+ function bindScope(address, app, code, scriptInfo) {
1699
+ // TODO: 1、cache 2、esm code is null
1700
+ if (isPlainObject(microApp.options.plugins)) {
1701
+ code = usePlugins(address, code, app.name, microApp.options.plugins);
1702
+ }
1703
+ if (isWrapInSandBox(app, scriptInfo)) {
1704
+ return app.iframe ? `(function(window,self,global,location){;${code}\n${isInlineScript(address) ? '' : `//# sourceURL=${address}\n`}}).call(window.__MICRO_APP_SANDBOX__.proxyWindow,window.__MICRO_APP_SANDBOX__.proxyWindow,window.__MICRO_APP_SANDBOX__.proxyWindow,window.__MICRO_APP_SANDBOX__.proxyWindow,window.__MICRO_APP_SANDBOX__.proxyLocation);` : `;(function(proxyWindow){with(proxyWindow.__MICRO_APP_WINDOW__){(function(${globalKeyToBeCached}){;${code}\n${isInlineScript(address) ? '' : `//# sourceURL=${address}\n`}}).call(proxyWindow,${globalKeyToBeCached})}})(window.__MICRO_APP_PROXY_WINDOW__);`;
1705
+ }
1706
+ return code;
1707
+ }
1708
+ /**
1709
+ * actions before run script
1710
+ */
1711
+ function actionsBeforeRunScript(app) {
1712
+ setActiveProxyWindow(app);
1713
+ }
1714
+ /**
1715
+ * set active sandBox.proxyWindow to window.__MICRO_APP_PROXY_WINDOW__
1716
+ */
1717
+ function setActiveProxyWindow(app) {
1718
+ if (app.sandBox) {
1719
+ globalEnv.rawWindow.__MICRO_APP_PROXY_WINDOW__ = app.sandBox.proxyWindow;
1720
+ }
1721
+ }
1722
+ /**
1723
+ * Call the plugin to process the file
1724
+ * @param address script address
1725
+ * @param code code
1726
+ * @param appName app name
1727
+ * @param plugins plugin list
1728
+ */
1729
+ function usePlugins(address, code, appName, plugins) {
1730
+ var _a;
1731
+ const newCode = processCode(plugins.global, code, address);
1732
+ return processCode((_a = plugins.modules) === null || _a === void 0 ? void 0 : _a[appName], newCode, address);
1733
+ }
1734
+ function processCode(configs, code, address) {
1735
+ if (!isArray(configs)) {
1736
+ return code;
1737
+ }
1738
+ return configs.reduce((preCode, config) => {
1739
+ if (isPlainObject(config) && isFunction(config.loader)) {
1740
+ return config.loader(preCode, address);
1741
+ }
1742
+ return preCode;
1743
+ }, code);
1744
+ }
1745
+
1395
1746
  class Adapter {
1396
1747
  constructor() {
1397
1748
  // keys that can only assigned to rawWindow
@@ -1517,37 +1868,55 @@ function patchElementTree(container, appName) {
1517
1868
  function updateElementInfo(node, appName) {
1518
1869
  var _a, _b;
1519
1870
  const proxyWindow = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.sandBox) === null || _b === void 0 ? void 0 : _b.proxyWindow;
1520
- if (proxyWindow && isNode(node) && !node.__MICRO_APP_NAME__) {
1521
- // TODO: 测试baseURI和ownerDocument在with沙箱中是否正确
1871
+ if (isNode(node) &&
1872
+ !node.__MICRO_APP_NAME__ &&
1873
+ !node.__PURE_ELEMENT__ &&
1874
+ proxyWindow) {
1875
+ /**
1876
+ * TODO:
1877
+ * 1. 测试baseURI和ownerDocument在with沙箱中是否正确
1878
+ * 经过验证with沙箱不能重写ownerDocument,否则react点击事件会触发两次
1879
+ * 2. with沙箱所有node设置__MICRO_APP_NAME__都使用updateElementInfo
1880
+ * 3. 性能: defineProperty的性能肯定不如直接设置
1881
+ */
1522
1882
  rawDefineProperties(node, {
1523
1883
  baseURI: {
1524
1884
  configurable: true,
1525
1885
  get: () => proxyWindow.location.href,
1526
1886
  },
1527
- ownerDocument: {
1528
- configurable: true,
1529
- get: () => proxyWindow.document,
1530
- },
1531
1887
  __MICRO_APP_NAME__: {
1532
1888
  configurable: true,
1533
1889
  writable: true,
1534
1890
  value: appName,
1535
1891
  },
1536
1892
  });
1893
+ if (isIframeSandbox(appName)) {
1894
+ rawDefineProperty(node, 'ownerDocument', {
1895
+ configurable: true,
1896
+ get: () => proxyWindow.document,
1897
+ });
1898
+ }
1537
1899
  }
1538
1900
  return node;
1539
1901
  }
1540
1902
 
1541
1903
  // Record element and map element
1542
1904
  const dynamicElementInMicroAppMap = new WeakMap();
1905
+ // Get the map element
1906
+ function getMappingNode(node) {
1907
+ var _a;
1908
+ return (_a = dynamicElementInMicroAppMap.get(node)) !== null && _a !== void 0 ? _a : node;
1909
+ }
1543
1910
  /**
1544
1911
  * Process the new node and format the style, link and script element
1545
- * @param parent parent node
1546
1912
  * @param child new node
1547
1913
  * @param app app
1548
1914
  */
1549
- function handleNewNode(parent, child, app) {
1550
- if (isStyleElement(child)) {
1915
+ function handleNewNode(child, app) {
1916
+ if (dynamicElementInMicroAppMap.has(child)) {
1917
+ return dynamicElementInMicroAppMap.get(child);
1918
+ }
1919
+ else if (isStyleElement(child)) {
1551
1920
  if (child.hasAttribute('exclude')) {
1552
1921
  const replaceComment = document.createComment('style element with exclude attribute ignored by micro-app');
1553
1922
  dynamicElementInMicroAppMap.set(child, replaceComment);
@@ -1571,7 +1940,7 @@ function handleNewNode(parent, child, app) {
1571
1940
  microApp.options.excludeAssetFilter(child.href))) {
1572
1941
  return child;
1573
1942
  }
1574
- const { address, linkInfo, replaceComment } = extractLinkFromHtml(child, parent, app, true);
1943
+ const { address, linkInfo, replaceComment } = extractLinkFromHtml(child, null, app, true);
1575
1944
  if (address && linkInfo) {
1576
1945
  const replaceStyle = formatDynamicLink(address, app, linkInfo, child);
1577
1946
  dynamicElementInMicroAppMap.set(child, replaceStyle);
@@ -1589,7 +1958,7 @@ function handleNewNode(parent, child, app) {
1589
1958
  microApp.options.excludeAssetFilter(child.src)) {
1590
1959
  return child;
1591
1960
  }
1592
- const { replaceComment, address, scriptInfo } = extractScriptElement(child, parent, app, true) || {};
1961
+ const { replaceComment, address, scriptInfo } = extractScriptElement(child, null, app, true) || {};
1593
1962
  if (address && scriptInfo) {
1594
1963
  // remote script or inline script
1595
1964
  const replaceElement = scriptInfo.isExternal ? runDynamicRemoteScript(address, app, scriptInfo, child) : runDynamicInlineScript(address, app, scriptInfo);
@@ -1620,25 +1989,39 @@ function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild
1620
1989
  */
1621
1990
  if (hijackParent) {
1622
1991
  /**
1623
- * Adapter for
1992
+ * If parentNode is <micro-app-body>, return rawDocument.body
1993
+ * Scenes:
1994
+ * 1. element-ui@2/lib/utils/vue-popper.js
1995
+ * if (this.popperElm.parentNode === document.body) ...
1624
1996
  * WARNING:
1625
- * Verifying that the parentNode of the targetChild points to document.body will cause other problems ?
1997
+ * 1. When operate child from parentNode async, may have been unmount
1998
+ * e.g. target.parentNode.remove(target)
1999
+ * ISSUE:
2000
+ * 1. https://github.com/micro-zoe/micro-app/issues/739
2001
+ * Solution: Return the true value when node not in document
1626
2002
  */
1627
2003
  if (!isIframeSandbox(app.name) &&
1628
- hijackParent.tagName === 'MICRO-APP-BODY' &&
2004
+ isMicroAppBody(hijackParent) &&
1629
2005
  rawMethod !== globalEnv.rawRemoveChild) {
1630
2006
  const descriptor = Object.getOwnPropertyDescriptor(targetChild, 'parentNode');
1631
- if (!descriptor || descriptor.configurable) {
1632
- rawDefineProperty(targetChild, 'parentNode', {
1633
- configurable: true,
1634
- get() {
1635
- /**
1636
- * When operate child from parentNode async, may have been unmount
1637
- * e.g.
1638
- * target.parentNode.remove(target)
1639
- */
1640
- return !app.container ? hijackParent : document.body;
2007
+ if ((!descriptor || descriptor.configurable) && !targetChild.__MICRO_APP_HAS_DPN__) {
2008
+ rawDefineProperties(targetChild, {
2009
+ parentNode: {
2010
+ configurable: true,
2011
+ get() {
2012
+ var _a, _b;
2013
+ const result = globalEnv.rawParentNodeDesc.get.call(this);
2014
+ if (isMicroAppBody(result) && app.container) {
2015
+ // TODO: remove getRootElementParentNode
2016
+ return ((_b = (_a = microApp.options).getRootElementParentNode) === null || _b === void 0 ? void 0 : _b.call(_a, this, app.name)) || document.body;
2017
+ }
2018
+ return result;
2019
+ },
1641
2020
  },
2021
+ __MICRO_APP_HAS_DPN__: {
2022
+ configurable: true,
2023
+ get: () => true,
2024
+ }
1642
2025
  });
1643
2026
  }
1644
2027
  }
@@ -1679,6 +2062,9 @@ function getHijackParent(parent, targetChild, app) {
1679
2062
  }
1680
2063
  return app.querySelector('micro-app-body');
1681
2064
  }
2065
+ if (app.iframe && isScriptElement(targetChild)) {
2066
+ return app.sandBox.microBody;
2067
+ }
1682
2068
  }
1683
2069
  return null;
1684
2070
  }
@@ -1691,10 +2077,25 @@ function invokeRawMethod(rawMethod, parent, targetChild, passiveChild) {
1691
2077
  function isPendMethod(method) {
1692
2078
  return method === globalEnv.rawAppend || method === globalEnv.rawPrepend;
1693
2079
  }
1694
- // Get the map element
1695
- function getMappingNode(node) {
1696
- var _a;
1697
- return (_a = dynamicElementInMicroAppMap.get(node)) !== null && _a !== void 0 ? _a : node;
2080
+ /**
2081
+ * Attempt to complete the static resource address again before insert the node
2082
+ * @param app app instance
2083
+ * @param newChild target node
2084
+ */
2085
+ function completePathDynamic(app, newChild) {
2086
+ if (isElement(newChild)) {
2087
+ if (/^(img|script)$/i.test(newChild.tagName)) {
2088
+ if (newChild.hasAttribute('src')) {
2089
+ globalEnv.rawSetAttribute.call(newChild, 'src', CompletionPath(newChild.getAttribute('src'), app.url));
2090
+ }
2091
+ if (newChild.hasAttribute('srcset')) {
2092
+ globalEnv.rawSetAttribute.call(newChild, 'srcset', CompletionPath(newChild.getAttribute('srcset'), app.url));
2093
+ }
2094
+ }
2095
+ else if (/^link$/i.test(newChild.tagName) && newChild.hasAttribute('href')) {
2096
+ globalEnv.rawSetAttribute.call(newChild, 'href', CompletionPath(newChild.getAttribute('href'), app.url));
2097
+ }
2098
+ }
1698
2099
  }
1699
2100
  /**
1700
2101
  * method of handle new node
@@ -1712,37 +2113,11 @@ function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
1712
2113
  newChild.__MICRO_APP_NAME__ = newChild.__MICRO_APP_NAME__ || currentAppName;
1713
2114
  const app = appInstanceMap.get(newChild.__MICRO_APP_NAME__);
1714
2115
  if (app === null || app === void 0 ? void 0 : app.container) {
1715
- if (isElement(newChild)) {
1716
- if (/^(img|script)$/i.test(newChild.tagName)) {
1717
- if (newChild.hasAttribute('src')) {
1718
- globalEnv.rawSetAttribute.call(newChild, 'src', CompletionPath(newChild.getAttribute('src'), app.url));
1719
- }
1720
- if (newChild.hasAttribute('srcset')) {
1721
- globalEnv.rawSetAttribute.call(newChild, 'srcset', CompletionPath(newChild.getAttribute('srcset'), app.url));
1722
- }
1723
- }
1724
- else if (/^link$/i.test(newChild.tagName) && newChild.hasAttribute('href')) {
1725
- globalEnv.rawSetAttribute.call(newChild, 'href', CompletionPath(newChild.getAttribute('href'), app.url));
1726
- }
1727
- }
1728
- return invokePrototypeMethod(app, rawMethod, parent, handleNewNode(parent, newChild, app), passiveChild && getMappingNode(passiveChild));
1729
- }
1730
- else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
1731
- return rawMethod.call(parent, newChild);
2116
+ completePathDynamic(app, newChild);
2117
+ return invokePrototypeMethod(app, rawMethod, parent, handleNewNode(newChild, app), passiveChild && getMappingNode(passiveChild));
1732
2118
  }
1733
2119
  }
1734
- else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
1735
- if (!isNode(newChild) && currentAppName) {
1736
- const app = appInstanceMap.get(currentAppName);
1737
- if (app === null || app === void 0 ? void 0 : app.container) {
1738
- if (parent === document.head) {
1739
- return rawMethod.call(app.querySelector('micro-app-head'), newChild);
1740
- }
1741
- else if (parent === document.body) {
1742
- return rawMethod.call(app.querySelector('micro-app-body'), newChild);
1743
- }
1744
- }
1745
- }
2120
+ if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
1746
2121
  return rawMethod.call(parent, newChild);
1747
2122
  }
1748
2123
  return rawMethod.call(parent, newChild, passiveChild);
@@ -1752,33 +2127,37 @@ function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
1752
2127
  */
1753
2128
  function patchElementAndDocument() {
1754
2129
  patchDocument();
2130
+ const rawRootElement = globalEnv.rawRootElement;
1755
2131
  // prototype methods of add element👇
1756
- Element.prototype.appendChild = function appendChild(newChild) {
2132
+ rawRootElement.prototype.appendChild = function appendChild(newChild) {
1757
2133
  return commonElementHandler(this, newChild, null, globalEnv.rawAppendChild);
1758
2134
  };
1759
- Element.prototype.insertBefore = function insertBefore(newChild, refChild) {
2135
+ rawRootElement.prototype.insertBefore = function insertBefore(newChild, refChild) {
1760
2136
  return commonElementHandler(this, newChild, refChild, globalEnv.rawInsertBefore);
1761
2137
  };
1762
- Element.prototype.replaceChild = function replaceChild(newChild, oldChild) {
2138
+ rawRootElement.prototype.replaceChild = function replaceChild(newChild, oldChild) {
1763
2139
  return commonElementHandler(this, newChild, oldChild, globalEnv.rawReplaceChild);
1764
2140
  };
1765
- Element.prototype.append = function append(...nodes) {
2141
+ rawRootElement.prototype.append = function append(...nodes) {
1766
2142
  let i = 0;
1767
- const length = nodes.length;
1768
- while (i < length) {
1769
- commonElementHandler(this, nodes[i], null, globalEnv.rawAppend);
2143
+ while (i < nodes.length) {
2144
+ let node = nodes[i];
2145
+ node = isNode(node) ? node : globalEnv.rawCreateTextNode.call(globalEnv.rawDocument, node);
2146
+ commonElementHandler(this, markElement(node), null, globalEnv.rawAppend);
1770
2147
  i++;
1771
2148
  }
1772
2149
  };
1773
- Element.prototype.prepend = function prepend(...nodes) {
2150
+ rawRootElement.prototype.prepend = function prepend(...nodes) {
1774
2151
  let i = nodes.length;
1775
2152
  while (i > 0) {
1776
- commonElementHandler(this, nodes[i - 1], null, globalEnv.rawPrepend);
2153
+ let node = nodes[i - 1];
2154
+ node = isNode(node) ? node : globalEnv.rawCreateTextNode.call(globalEnv.rawDocument, node);
2155
+ commonElementHandler(this, markElement(node), null, globalEnv.rawPrepend);
1777
2156
  i--;
1778
2157
  }
1779
2158
  };
1780
2159
  // prototype methods of delete element👇
1781
- Element.prototype.removeChild = function removeChild(oldChild) {
2160
+ rawRootElement.prototype.removeChild = function removeChild(oldChild) {
1782
2161
  if (oldChild === null || oldChild === void 0 ? void 0 : oldChild.__MICRO_APP_NAME__) {
1783
2162
  const app = appInstanceMap.get(oldChild.__MICRO_APP_NAME__);
1784
2163
  if (app === null || app === void 0 ? void 0 : app.container) {
@@ -1793,8 +2172,20 @@ function patchElementAndDocument() {
1793
2172
  }
1794
2173
  return globalEnv.rawRemoveChild.call(this, oldChild);
1795
2174
  };
2175
+ rawRootElement.prototype.insertAdjacentElement = function (where, element) {
2176
+ var _a;
2177
+ if (element === null || element === void 0 ? void 0 : element.__MICRO_APP_NAME__) {
2178
+ const app = appInstanceMap.get(element.__MICRO_APP_NAME__);
2179
+ if (app === null || app === void 0 ? void 0 : app.container) {
2180
+ element = handleNewNode(element, app);
2181
+ const realParent = (_a = getHijackParent(this, element, app)) !== null && _a !== void 0 ? _a : this;
2182
+ return globalEnv.rawInsertAdjacentElement.call(realParent, where, element);
2183
+ }
2184
+ }
2185
+ return globalEnv.rawInsertAdjacentElement.call(this, where, element);
2186
+ };
1796
2187
  // patch cloneNode
1797
- Element.prototype.cloneNode = function cloneNode(deep) {
2188
+ rawRootElement.prototype.cloneNode = function cloneNode(deep) {
1798
2189
  const clonedNode = globalEnv.rawCloneNode.call(this, deep);
1799
2190
  this.__MICRO_APP_NAME__ && (clonedNode.__MICRO_APP_NAME__ = this.__MICRO_APP_NAME__);
1800
2191
  return clonedNode;
@@ -1814,17 +2205,63 @@ function patchElementAndDocument() {
1814
2205
  }
1815
2206
  return null;
1816
2207
  }
1817
- Element.prototype.querySelector = function querySelector(selectors) {
2208
+ rawRootElement.prototype.querySelector = function querySelector(selectors) {
1818
2209
  var _a;
1819
2210
  const target = (_a = getQueryTarget(this)) !== null && _a !== void 0 ? _a : this;
1820
2211
  return globalEnv.rawElementQuerySelector.call(target, selectors);
1821
2212
  };
1822
- Element.prototype.querySelectorAll = function querySelectorAll(selectors) {
2213
+ rawRootElement.prototype.querySelectorAll = function querySelectorAll(selectors) {
1823
2214
  var _a;
1824
2215
  const target = (_a = getQueryTarget(this)) !== null && _a !== void 0 ? _a : this;
1825
2216
  return globalEnv.rawElementQuerySelectorAll.call(target, selectors);
1826
2217
  };
1827
- rawDefineProperty(Element.prototype, 'innerHTML', {
2218
+ // rewrite setAttribute, complete resource address
2219
+ rawRootElement.prototype.setAttribute = function setAttribute(key, value) {
2220
+ const appName = this.__MICRO_APP_NAME__ || getCurrentAppName();
2221
+ if (appName &&
2222
+ appInstanceMap.has(appName) &&
2223
+ (((key === 'src' || key === 'srcset') && /^(img|script|video|audio|source|embed)$/i.test(this.tagName)) ||
2224
+ (key === 'href' && /^link$/i.test(this.tagName)))) {
2225
+ const app = appInstanceMap.get(appName);
2226
+ value = CompletionPath(value, app.url);
2227
+ }
2228
+ globalEnv.rawSetAttribute.call(this, key, value);
2229
+ };
2230
+ /**
2231
+ * TODO: 兼容直接通过img.src等操作设置的资源
2232
+ * NOTE:
2233
+ * 1. 卸载时恢复原始值
2234
+ * 2. 循环嵌套的情况
2235
+ * 3. 放在global_env中统一处理
2236
+ * 4. 是否和completePathDynamic的作用重复?
2237
+ */
2238
+ // const protoAttrList: Array<[HTMLElement, string]> = [
2239
+ // [HTMLImageElement.prototype, 'src'],
2240
+ // [HTMLScriptElement.prototype, 'src'],
2241
+ // [HTMLLinkElement.prototype, 'href'],
2242
+ // ]
2243
+ // protoAttrList.forEach(([target, attr]) => {
2244
+ // const { enumerable, configurable, get, set } = Object.getOwnPropertyDescriptor(target, attr) || {
2245
+ // enumerable: true,
2246
+ // configurable: true,
2247
+ // }
2248
+ // rawDefineProperty(target, attr, {
2249
+ // enumerable,
2250
+ // configurable,
2251
+ // get: function () {
2252
+ // return get?.call(this)
2253
+ // },
2254
+ // set: function (value) {
2255
+ // const currentAppName = getCurrentAppName()
2256
+ // if (currentAppName && appInstanceMap.has(currentAppName)) {
2257
+ // const app = appInstanceMap.get(currentAppName)
2258
+ // value = CompletionPath(value, app!.url)
2259
+ // }
2260
+ // set?.call(this, value)
2261
+ // },
2262
+ // })
2263
+ // })
2264
+ rawDefineProperty(rawRootElement.prototype, 'innerHTML', {
1828
2265
  configurable: true,
1829
2266
  enumerable: true,
1830
2267
  get() {
@@ -1840,7 +2277,9 @@ function patchElementAndDocument() {
1840
2277
  });
1841
2278
  }
1842
2279
  });
1843
- // Abandon this way at 2023.2.28 before v1.0.0-beta.0, it will cause vue2 throw error when render again
2280
+ /**
2281
+ * NOTE:Abandon this way at 2023.2.28 before v1.0.0-beta.0, it will cause vue2 throw error when render again
2282
+ */
1844
2283
  // rawDefineProperty(Node.prototype, 'parentNode', {
1845
2284
  // configurable: true,
1846
2285
  // enumerable: true,
@@ -1857,8 +2296,8 @@ function patchElementAndDocument() {
1857
2296
  // * BUG:
1858
2297
  // * 1. vue2 umdMode, throw error when render again (<div id='app'></div> will be deleted when render again )
1859
2298
  // */
1860
- // if (result?.tagName === 'MICRO-APP-BODY' && appInstanceMap.get(this.__MICRO_APP_NAME__)?.container) {
1861
- // return globalEnv.rawDocument.body
2299
+ // if (isMicroAppBody(result) && appInstanceMap.get(this.__MICRO_APP_NAME__)?.container) {
2300
+ // return document.body
1862
2301
  // }
1863
2302
  // return result
1864
2303
  // },
@@ -1895,33 +2334,35 @@ function patchDocument() {
1895
2334
  const element = globalEnv.rawCreateDocumentFragment.call(getBindTarget(this));
1896
2335
  return markElement(element);
1897
2336
  };
2337
+ // rawRootDocument.prototype.createTextNode = function createTextNode (data: string): Text {
2338
+ // const element = globalEnv.rawCreateTextNode.call(getBindTarget(this), data)
2339
+ // return markElement(element)
2340
+ // }
1898
2341
  // query element👇
1899
2342
  function querySelector(selectors) {
1900
- var _a, _b, _c;
2343
+ var _a, _b;
1901
2344
  const _this = getBindTarget(this);
1902
2345
  const currentAppName = getCurrentAppName();
1903
2346
  if (!currentAppName ||
1904
- !((_a = appInstanceMap.get(currentAppName)) === null || _a === void 0 ? void 0 : _a.container) ||
1905
2347
  !selectors ||
1906
2348
  isUniqueElement(selectors) ||
1907
2349
  // see https://github.com/micro-zoe/micro-app/issues/56
1908
2350
  rawDocument !== _this) {
1909
2351
  return globalEnv.rawQuerySelector.call(_this, selectors);
1910
2352
  }
1911
- return (_c = (_b = appInstanceMap.get(currentAppName)) === null || _b === void 0 ? void 0 : _b.querySelector(selectors)) !== null && _c !== void 0 ? _c : null;
2353
+ return (_b = (_a = appInstanceMap.get(currentAppName)) === null || _a === void 0 ? void 0 : _a.querySelector(selectors)) !== null && _b !== void 0 ? _b : null;
1912
2354
  }
1913
2355
  function querySelectorAll(selectors) {
1914
- var _a, _b, _c;
2356
+ var _a, _b;
1915
2357
  const _this = getBindTarget(this);
1916
2358
  const currentAppName = getCurrentAppName();
1917
2359
  if (!currentAppName ||
1918
- !((_a = appInstanceMap.get(currentAppName)) === null || _a === void 0 ? void 0 : _a.container) ||
1919
2360
  !selectors ||
1920
2361
  isUniqueElement(selectors) ||
1921
2362
  rawDocument !== _this) {
1922
2363
  return globalEnv.rawQuerySelectorAll.call(_this, selectors);
1923
2364
  }
1924
- return (_c = (_b = appInstanceMap.get(currentAppName)) === null || _b === void 0 ? void 0 : _b.querySelectorAll(selectors)) !== null && _c !== void 0 ? _c : [];
2365
+ return (_b = (_a = appInstanceMap.get(currentAppName)) === null || _a === void 0 ? void 0 : _a.querySelectorAll(selectors)) !== null && _b !== void 0 ? _b : [];
1925
2366
  }
1926
2367
  rawRootDocument.prototype.querySelector = querySelector;
1927
2368
  rawRootDocument.prototype.querySelectorAll = querySelectorAll;
@@ -1979,45 +2420,6 @@ function patchDocument() {
1979
2420
  }
1980
2421
  };
1981
2422
  }
1982
- /**
1983
- * patchSetAttribute is different from other patch
1984
- * NOTE:
1985
- * 1. it not dependent on sandbox
1986
- * 2. it should exec when first micro-app-element created & release when all app unmounted
1987
- */
1988
- let hasRewriteSetAttribute = false;
1989
- function patchSetAttribute() {
1990
- if (hasRewriteSetAttribute)
1991
- return;
1992
- hasRewriteSetAttribute = true;
1993
- Element.prototype.setAttribute = function setAttribute(key, value) {
1994
- if (/^micro-app(-\S+)?/i.test(this.tagName) && key === 'data') {
1995
- if (isPlainObject(value)) {
1996
- const cloneValue = {};
1997
- Object.getOwnPropertyNames(value).forEach((ownKey) => {
1998
- if (!(isString(ownKey) && ownKey.indexOf('__') === 0)) {
1999
- cloneValue[ownKey] = value[ownKey];
2000
- }
2001
- });
2002
- this.data = cloneValue;
2003
- }
2004
- else if (value !== '[object Object]') {
2005
- logWarn('property data must be an object', this.getAttribute('name'));
2006
- }
2007
- }
2008
- else {
2009
- const appName = this.__MICRO_APP_NAME__ || getCurrentAppName();
2010
- if (appName &&
2011
- appInstanceMap.has(appName) &&
2012
- (((key === 'src' || key === 'srcset') && /^(img|script|video|audio|source|embed)$/i.test(this.tagName)) ||
2013
- (key === 'href' && /^link$/i.test(this.tagName)))) {
2014
- const app = appInstanceMap.get(appName);
2015
- value = CompletionPath(value, app.url);
2016
- }
2017
- globalEnv.rawSetAttribute.call(this, key, value);
2018
- }
2019
- };
2020
- }
2021
2423
  function releasePatchDocument() {
2022
2424
  const rawRootDocument = globalEnv.rawRootDocument;
2023
2425
  rawRootDocument.prototype.createElement = globalEnv.rawCreateElement;
@@ -2034,21 +2436,18 @@ function releasePatchDocument() {
2034
2436
  function releasePatchElementAndDocument() {
2035
2437
  removeDomScope();
2036
2438
  releasePatchDocument();
2037
- Element.prototype.appendChild = globalEnv.rawAppendChild;
2038
- Element.prototype.insertBefore = globalEnv.rawInsertBefore;
2039
- Element.prototype.replaceChild = globalEnv.rawReplaceChild;
2040
- Element.prototype.removeChild = globalEnv.rawRemoveChild;
2041
- Element.prototype.append = globalEnv.rawAppend;
2042
- Element.prototype.prepend = globalEnv.rawPrepend;
2043
- Element.prototype.cloneNode = globalEnv.rawCloneNode;
2044
- Element.prototype.querySelector = globalEnv.rawElementQuerySelector;
2045
- Element.prototype.querySelectorAll = globalEnv.rawElementQuerySelectorAll;
2046
- rawDefineProperty(Element.prototype, 'innerHTML', globalEnv.rawInnerHTMLDesc);
2047
- }
2048
- // exec when last child unmount
2049
- function releasePatchSetAttribute() {
2050
- hasRewriteSetAttribute = false;
2051
- Element.prototype.setAttribute = globalEnv.rawSetAttribute;
2439
+ const rawRootElement = globalEnv.rawRootElement;
2440
+ rawRootElement.prototype.appendChild = globalEnv.rawAppendChild;
2441
+ rawRootElement.prototype.insertBefore = globalEnv.rawInsertBefore;
2442
+ rawRootElement.prototype.replaceChild = globalEnv.rawReplaceChild;
2443
+ rawRootElement.prototype.removeChild = globalEnv.rawRemoveChild;
2444
+ rawRootElement.prototype.append = globalEnv.rawAppend;
2445
+ rawRootElement.prototype.prepend = globalEnv.rawPrepend;
2446
+ rawRootElement.prototype.cloneNode = globalEnv.rawCloneNode;
2447
+ rawRootElement.prototype.querySelector = globalEnv.rawElementQuerySelector;
2448
+ rawRootElement.prototype.querySelectorAll = globalEnv.rawElementQuerySelectorAll;
2449
+ rawRootElement.prototype.setAttribute = globalEnv.rawSetAttribute;
2450
+ rawDefineProperty(rawRootElement.prototype, 'innerHTML', globalEnv.rawInnerHTMLDesc);
2052
2451
  }
2053
2452
  // Set the style of micro-app-head and micro-app-body
2054
2453
  let hasRejectMicroAppStyle = false;
@@ -2077,676 +2476,326 @@ function initGlobalEnv() {
2077
2476
  const rawWindow = window.rawWindow || Function('return window')();
2078
2477
  const rawDocument = window.rawDocument || Function('return document')();
2079
2478
  const rawRootDocument = rawWindow.Document || Function('return Document')();
2080
- const supportModuleScript = isSupportModuleScript();
2081
- /**
2082
- * save patch raw methods
2083
- * pay attention to this binding
2084
- */
2085
- const rawSetAttribute = Element.prototype.setAttribute;
2086
- const rawAppendChild = Element.prototype.appendChild;
2087
- const rawInsertBefore = Element.prototype.insertBefore;
2088
- const rawReplaceChild = Element.prototype.replaceChild;
2089
- const rawRemoveChild = Element.prototype.removeChild;
2090
- const rawAppend = Element.prototype.append;
2091
- const rawPrepend = Element.prototype.prepend;
2092
- const rawCloneNode = Element.prototype.cloneNode;
2093
- const rawElementQuerySelector = Element.prototype.querySelector;
2094
- const rawElementQuerySelectorAll = Element.prototype.querySelectorAll;
2095
- const rawInnerHTMLDesc = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML');
2096
- const rawParentNodeDesc = Object.getOwnPropertyDescriptor(Node.prototype, 'parentNode');
2479
+ const rawRootElement = rawWindow.Element;
2480
+ const rawRootNode = rawWindow.Node;
2481
+ const rawRootEventTarget = rawWindow.EventTarget;
2482
+ // save patch raw methods, pay attention to this binding
2483
+ const rawSetAttribute = rawRootElement.prototype.setAttribute;
2484
+ const rawAppendChild = rawRootElement.prototype.appendChild;
2485
+ const rawInsertBefore = rawRootElement.prototype.insertBefore;
2486
+ const rawReplaceChild = rawRootElement.prototype.replaceChild;
2487
+ const rawRemoveChild = rawRootElement.prototype.removeChild;
2488
+ const rawAppend = rawRootElement.prototype.append;
2489
+ const rawPrepend = rawRootElement.prototype.prepend;
2490
+ const rawCloneNode = rawRootElement.prototype.cloneNode;
2491
+ const rawElementQuerySelector = rawRootElement.prototype.querySelector;
2492
+ const rawElementQuerySelectorAll = rawRootElement.prototype.querySelectorAll;
2493
+ const rawInsertAdjacentElement = rawRootElement.prototype.insertAdjacentElement;
2494
+ const rawInnerHTMLDesc = Object.getOwnPropertyDescriptor(rawRootElement.prototype, 'innerHTML');
2495
+ const rawParentNodeDesc = Object.getOwnPropertyDescriptor(rawRootNode.prototype, 'parentNode');
2496
+ // Document proto methods
2097
2497
  const rawCreateElement = rawRootDocument.prototype.createElement;
2098
2498
  const rawCreateElementNS = rawRootDocument.prototype.createElementNS;
2099
2499
  const rawCreateDocumentFragment = rawRootDocument.prototype.createDocumentFragment;
2100
- const rawCreateTextNode = rawRootDocument.prototype.createTextNode;
2101
- const rawQuerySelector = rawRootDocument.prototype.querySelector;
2102
- const rawQuerySelectorAll = rawRootDocument.prototype.querySelectorAll;
2103
- const rawGetElementById = rawRootDocument.prototype.getElementById;
2104
- const rawGetElementsByClassName = rawRootDocument.prototype.getElementsByClassName;
2105
- const rawGetElementsByTagName = rawRootDocument.prototype.getElementsByTagName;
2106
- const rawGetElementsByName = rawRootDocument.prototype.getElementsByName;
2107
- const ImageProxy = new Proxy(Image, {
2108
- construct(Target, args) {
2109
- const elementImage = new Target(...args);
2110
- elementImage.__MICRO_APP_NAME__ = getCurrentAppName();
2111
- return elementImage;
2112
- },
2113
- });
2114
- /**
2115
- * save effect raw methods
2116
- * pay attention to this binding, especially setInterval, setTimeout, clearInterval, clearTimeout
2117
- */
2118
- const rawSetInterval = rawWindow.setInterval;
2119
- const rawSetTimeout = rawWindow.setTimeout;
2120
- const rawClearInterval = rawWindow.clearInterval;
2121
- const rawClearTimeout = rawWindow.clearTimeout;
2122
- const rawPushState = rawWindow.history.pushState;
2123
- const rawReplaceState = rawWindow.history.replaceState;
2124
- const rawWindowAddEventListener = rawWindow.addEventListener;
2125
- const rawWindowRemoveEventListener = rawWindow.removeEventListener;
2126
- const rawDocumentAddEventListener = rawDocument.addEventListener;
2127
- const rawDocumentRemoveEventListener = rawDocument.removeEventListener;
2128
- // TODO: 统一使用 EventTarget 去掉上面四个
2129
- const rawAddEventListener = EventTarget.prototype.addEventListener;
2130
- const rawRemoveEventListener = EventTarget.prototype.removeEventListener;
2131
- assign(globalEnv, {
2132
- // common global vars
2133
- rawWindow,
2134
- rawDocument,
2135
- rawRootDocument,
2136
- supportModuleScript,
2137
- // source/patch
2138
- rawSetAttribute,
2139
- rawAppendChild,
2140
- rawInsertBefore,
2141
- rawReplaceChild,
2142
- rawRemoveChild,
2143
- rawAppend,
2144
- rawPrepend,
2145
- rawCloneNode,
2146
- rawElementQuerySelector,
2147
- rawElementQuerySelectorAll,
2148
- rawInnerHTMLDesc,
2149
- rawParentNodeDesc,
2150
- rawCreateElement,
2151
- rawCreateElementNS,
2152
- rawCreateDocumentFragment,
2153
- rawCreateTextNode,
2154
- rawQuerySelector,
2155
- rawQuerySelectorAll,
2156
- rawGetElementById,
2157
- rawGetElementsByClassName,
2158
- rawGetElementsByTagName,
2159
- rawGetElementsByName,
2160
- ImageProxy,
2161
- // sandbox/effect
2162
- rawWindowAddEventListener,
2163
- rawWindowRemoveEventListener,
2164
- rawSetInterval,
2165
- rawSetTimeout,
2166
- rawClearInterval,
2167
- rawClearTimeout,
2168
- rawDocumentAddEventListener,
2169
- rawDocumentRemoveEventListener,
2170
- rawPushState,
2171
- rawReplaceState,
2172
- rawAddEventListener,
2173
- rawRemoveEventListener,
2174
- });
2175
- // global effect
2176
- rejectMicroAppStyle();
2177
- }
2178
- }
2179
-
2180
- const scriptTypes = ['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module', 'systemjs-module', 'systemjs-importmap'];
2181
- // whether use type='module' script
2182
- function isTypeModule(app, scriptInfo) {
2183
- return scriptInfo.appSpace[app.name].module && (!app.useSandbox || app.iframe);
2184
- }
2185
- // special script element
2186
- function isSpecialScript(app, scriptInfo) {
2187
- const attrs = scriptInfo.appSpace[app.name].attrs;
2188
- return attrs.has('id');
2189
- }
2190
- /**
2191
- * whether to run js in inline mode
2192
- * scene:
2193
- * 1. inline config for app
2194
- * 2. inline attr in script element
2195
- * 3. module script
2196
- * 4. script with special attr
2197
- */
2198
- function isInlineMode(app, scriptInfo) {
2199
- return (app.inline ||
2200
- scriptInfo.appSpace[app.name].inline ||
2201
- isTypeModule(app, scriptInfo) ||
2202
- isSpecialScript(app, scriptInfo) ||
2203
- app.iframe);
2204
- }
2205
- // TODO: iframe重新插入window前后不一致,通过iframe Function创建的函数无法复用
2206
- function getEffectWindow(app) {
2207
- return app.iframe ? app.sandBox.microAppWindow : globalEnv.rawWindow;
2208
- }
2209
- // Convert string code to function
2210
- function code2Function(app, code) {
2211
- const targetWindow = getEffectWindow(app);
2212
- return new targetWindow.Function(code);
2213
- }
2214
- /**
2215
- * If the appSpace of the current js address has other app, try to reuse parsedFunction of other app
2216
- * @param appName app.name
2217
- * @param scriptInfo scriptInfo of current address
2218
- * @param currentCode pure code of current address
2219
- */
2220
- function getExistParseResult(app, scriptInfo, currentCode) {
2221
- const appSpace = scriptInfo.appSpace;
2222
- for (const item in appSpace) {
2223
- if (item !== app.name) {
2224
- const appSpaceData = appSpace[item];
2225
- if (appSpaceData.parsedCode === currentCode && appSpaceData.parsedFunction) {
2226
- return appSpaceData.parsedFunction;
2227
- }
2228
- }
2229
- }
2230
- }
2231
- /**
2232
- * get parsedFunction from exist data or parsedCode
2233
- * @returns parsedFunction
2234
- */
2235
- function getParsedFunction(app, scriptInfo, parsedCode) {
2236
- return getExistParseResult(app, scriptInfo, parsedCode) || code2Function(app, parsedCode);
2237
- }
2238
- // Prevent randomly created strings from repeating
2239
- function getUniqueNonceSrc() {
2240
- const nonceStr = createNonceSrc();
2241
- if (sourceCenter.script.hasInfo(nonceStr)) {
2242
- return getUniqueNonceSrc();
2243
- }
2244
- return nonceStr;
2245
- }
2246
- // transfer the attributes on the script to convertScript
2247
- function setConvertScriptAttr(convertScript, attrs) {
2248
- attrs.forEach((value, key) => {
2249
- if ((key === 'type' && value === 'module') || key === 'defer' || key === 'async')
2250
- return;
2251
- if (key === 'src')
2252
- key = 'data-origin-src';
2253
- convertScript.setAttribute(key, value);
2254
- });
2255
- }
2256
- // wrap code in sandbox
2257
- function isWrapInSandBox(app, scriptInfo) {
2258
- return app.useSandbox && !isTypeModule(app, scriptInfo);
2259
- }
2260
- function getSandboxType(app, scriptInfo) {
2261
- return isWrapInSandBox(app, scriptInfo) ? app.iframe ? 'iframe' : 'with' : 'disable';
2262
- }
2263
- /**
2264
- * Extract script elements
2265
- * @param script script element
2266
- * @param parent parent element of script
2267
- * @param app app
2268
- * @param isDynamic dynamic insert
2269
- */
2270
- function extractScriptElement(script, parent, app, isDynamic = false) {
2271
- var _a;
2272
- let replaceComment = null;
2273
- let src = script.getAttribute('src');
2274
- if (src)
2275
- src = CompletionPath(src, app.url);
2276
- if (script.hasAttribute('exclude') || checkExcludeUrl(src, app.name)) {
2277
- replaceComment = document.createComment('script element with exclude attribute removed by micro-app');
2278
- }
2279
- else if ((script.type &&
2280
- !scriptTypes.includes(script.type)) ||
2281
- script.hasAttribute('ignore') ||
2282
- checkIgnoreUrl(src, app.name)) {
2283
- // 配置为忽略的脚本,清空 rawDocument.currentScript,避免被忽略的脚本内获取 currentScript 出错
2284
- if ((_a = globalEnv.rawDocument) === null || _a === void 0 ? void 0 : _a.currentScript) {
2285
- delete globalEnv.rawDocument.currentScript;
2286
- }
2287
- return null;
2288
- }
2289
- else if ((globalEnv.supportModuleScript && script.noModule) ||
2290
- (!globalEnv.supportModuleScript && script.type === 'module')) {
2291
- replaceComment = document.createComment(`${script.noModule ? 'noModule' : 'module'} script ignored by micro-app`);
2292
- }
2293
- else if (src) { // remote script
2294
- let scriptInfo = sourceCenter.script.getInfo(src);
2295
- const appSpaceData = {
2296
- async: script.hasAttribute('async'),
2297
- defer: script.defer || script.type === 'module',
2298
- module: script.type === 'module',
2299
- inline: script.hasAttribute('inline'),
2300
- pure: script.hasAttribute('pure'),
2301
- attrs: getAttributes(script),
2302
- };
2303
- if (!scriptInfo) {
2304
- scriptInfo = {
2305
- code: '',
2306
- isExternal: true,
2307
- appSpace: {
2308
- [app.name]: appSpaceData,
2309
- }
2310
- };
2311
- }
2312
- else {
2313
- /**
2314
- * Reuse when appSpace exists
2315
- * NOTE:
2316
- * 1. The same static script, appSpace must be the same (in fact, it may be different when url change)
2317
- * 2. The same dynamic script, appSpace may be the same, but we still reuse appSpace, which should pay attention
2318
- */
2319
- scriptInfo.appSpace[app.name] = scriptInfo.appSpace[app.name] || appSpaceData;
2320
- }
2321
- sourceCenter.script.setInfo(src, scriptInfo);
2322
- if (!isDynamic) {
2323
- app.source.scripts.add(src);
2324
- replaceComment = document.createComment(`script with src='${src}' extract by micro-app`);
2325
- }
2326
- else {
2327
- return { address: src, scriptInfo };
2328
- }
2329
- }
2330
- else if (script.textContent) { // inline script
2331
- /**
2332
- * NOTE:
2333
- * 1. Each inline script is unique
2334
- * 2. Every dynamic created inline script will be re-executed
2335
- * ACTION:
2336
- * 1. Delete dynamic inline script info after exec
2337
- * 2. Delete static inline script info when destroy
2338
- */
2339
- const nonceStr = getUniqueNonceSrc();
2340
- const scriptInfo = {
2341
- code: script.textContent,
2342
- isExternal: false,
2343
- appSpace: {
2344
- [app.name]: {
2345
- async: false,
2346
- defer: script.type === 'module',
2347
- module: script.type === 'module',
2348
- inline: script.hasAttribute('inline'),
2349
- pure: script.hasAttribute('pure'),
2350
- attrs: getAttributes(script),
2351
- }
2352
- }
2353
- };
2354
- if (!isDynamic) {
2355
- app.source.scripts.add(nonceStr);
2356
- sourceCenter.script.setInfo(nonceStr, scriptInfo);
2357
- replaceComment = document.createComment('inline script extract by micro-app');
2358
- }
2359
- else {
2360
- // Because each dynamic script is unique, it is not put into sourceCenter
2361
- return { address: nonceStr, scriptInfo };
2362
- }
2363
- }
2364
- else if (!isDynamic) {
2365
- /**
2366
- * script with empty src or empty script.textContent remove in static html
2367
- * & not removed if it created by dynamic
2368
- */
2369
- replaceComment = document.createComment('script element removed by micro-app');
2370
- }
2371
- if (isDynamic) {
2372
- return { replaceComment };
2373
- }
2374
- else {
2375
- return parent.replaceChild(replaceComment, script);
2376
- }
2377
- }
2378
- /**
2379
- * get assets plugins
2380
- * @param appName app name
2381
- */
2382
- function getAssetsPlugins(appName) {
2383
- var _a, _b, _c;
2384
- const globalPlugins = ((_a = microApp.options.plugins) === null || _a === void 0 ? void 0 : _a.global) || [];
2385
- const modulePlugins = ((_c = (_b = microApp.options.plugins) === null || _b === void 0 ? void 0 : _b.modules) === null || _c === void 0 ? void 0 : _c[appName]) || [];
2386
- return [...globalPlugins, ...modulePlugins];
2387
- }
2388
- /**
2389
- * whether the address needs to be excluded
2390
- * @param address css or js link
2391
- * @param plugins microApp plugins
2392
- */
2393
- function checkExcludeUrl(address, appName) {
2394
- if (!address)
2395
- return false;
2396
- const plugins = getAssetsPlugins(appName) || [];
2397
- return plugins.some(plugin => {
2398
- if (!plugin.excludeChecker)
2399
- return false;
2400
- return plugin.excludeChecker(address);
2401
- });
2402
- }
2403
- /**
2404
- * whether the address needs to be ignore
2405
- * @param address css or js link
2406
- * @param plugins microApp plugins
2407
- */
2408
- function checkIgnoreUrl(address, appName) {
2409
- if (!address)
2410
- return false;
2411
- const plugins = getAssetsPlugins(appName) || [];
2412
- return plugins.some(plugin => {
2413
- if (!plugin.ignoreChecker)
2414
- return false;
2415
- return plugin.ignoreChecker(address);
2416
- });
2417
- }
2418
- /**
2419
- * Get remote resources of script
2420
- * @param wrapElement htmlDom
2421
- * @param app app
2422
- */
2423
- function fetchScriptsFromHtml(wrapElement, app) {
2424
- const scriptList = Array.from(app.source.scripts);
2425
- const fetchScriptPromise = [];
2426
- const fetchScriptPromiseInfo = [];
2427
- for (const address of scriptList) {
2428
- const scriptInfo = sourceCenter.script.getInfo(address);
2429
- const appSpaceData = scriptInfo.appSpace[app.name];
2430
- if ((!appSpaceData.defer && !appSpaceData.async) || (app.isPrefetch && !app.isPrerender)) {
2431
- fetchScriptPromise.push(scriptInfo.code ? scriptInfo.code : fetchSource(address, app.name));
2432
- fetchScriptPromiseInfo.push([address, scriptInfo]);
2433
- }
2434
- }
2435
- const fiberScriptTasks = app.isPrefetch || app.fiber ? [] : null;
2436
- if (fetchScriptPromise.length) {
2437
- promiseStream(fetchScriptPromise, (res) => {
2438
- injectFiberTask(fiberScriptTasks, () => fetchScriptSuccess(fetchScriptPromiseInfo[res.index][0], fetchScriptPromiseInfo[res.index][1], res.data, app));
2439
- }, (err) => {
2440
- logError(err, app.name);
2441
- }, () => {
2442
- if (fiberScriptTasks) {
2443
- fiberScriptTasks.push(() => Promise.resolve(app.onLoad(wrapElement)));
2444
- serialExecFiberTasks(fiberScriptTasks);
2445
- }
2446
- else {
2447
- app.onLoad(wrapElement);
2448
- }
2500
+ const rawCreateTextNode = rawRootDocument.prototype.createTextNode;
2501
+ const rawQuerySelector = rawRootDocument.prototype.querySelector;
2502
+ const rawQuerySelectorAll = rawRootDocument.prototype.querySelectorAll;
2503
+ const rawGetElementById = rawRootDocument.prototype.getElementById;
2504
+ const rawGetElementsByClassName = rawRootDocument.prototype.getElementsByClassName;
2505
+ const rawGetElementsByTagName = rawRootDocument.prototype.getElementsByTagName;
2506
+ const rawGetElementsByName = rawRootDocument.prototype.getElementsByName;
2507
+ const ImageProxy = new Proxy(Image, {
2508
+ construct(Target, args) {
2509
+ const elementImage = new Target(...args);
2510
+ const currentAppName = getCurrentAppName();
2511
+ if (currentAppName)
2512
+ elementImage.__MICRO_APP_NAME__ = currentAppName;
2513
+ return elementImage;
2514
+ },
2449
2515
  });
2450
- }
2451
- else {
2452
- app.onLoad(wrapElement);
2516
+ /**
2517
+ * save effect raw methods
2518
+ * pay attention to this binding, especially setInterval, setTimeout, clearInterval, clearTimeout
2519
+ */
2520
+ const rawSetInterval = rawWindow.setInterval;
2521
+ const rawSetTimeout = rawWindow.setTimeout;
2522
+ const rawClearInterval = rawWindow.clearInterval;
2523
+ const rawClearTimeout = rawWindow.clearTimeout;
2524
+ const rawPushState = rawWindow.history.pushState;
2525
+ const rawReplaceState = rawWindow.history.replaceState;
2526
+ const rawAddEventListener = rawRootEventTarget.prototype.addEventListener;
2527
+ const rawRemoveEventListener = rawRootEventTarget.prototype.removeEventListener;
2528
+ assign(globalEnv, {
2529
+ supportModuleScript: isSupportModuleScript(),
2530
+ // common global vars
2531
+ rawWindow,
2532
+ rawDocument,
2533
+ rawRootDocument,
2534
+ rawRootElement,
2535
+ rawRootNode,
2536
+ // source/patch
2537
+ rawSetAttribute,
2538
+ rawAppendChild,
2539
+ rawInsertBefore,
2540
+ rawReplaceChild,
2541
+ rawRemoveChild,
2542
+ rawAppend,
2543
+ rawPrepend,
2544
+ rawCloneNode,
2545
+ rawElementQuerySelector,
2546
+ rawElementQuerySelectorAll,
2547
+ rawInsertAdjacentElement,
2548
+ rawInnerHTMLDesc,
2549
+ rawParentNodeDesc,
2550
+ rawCreateElement,
2551
+ rawCreateElementNS,
2552
+ rawCreateDocumentFragment,
2553
+ rawCreateTextNode,
2554
+ rawQuerySelector,
2555
+ rawQuerySelectorAll,
2556
+ rawGetElementById,
2557
+ rawGetElementsByClassName,
2558
+ rawGetElementsByTagName,
2559
+ rawGetElementsByName,
2560
+ ImageProxy,
2561
+ // sandbox/effect
2562
+ rawSetInterval,
2563
+ rawSetTimeout,
2564
+ rawClearInterval,
2565
+ rawClearTimeout,
2566
+ rawPushState,
2567
+ rawReplaceState,
2568
+ rawAddEventListener,
2569
+ rawRemoveEventListener,
2570
+ });
2571
+ // global effect
2572
+ rejectMicroAppStyle();
2453
2573
  }
2454
2574
  }
2575
+
2455
2576
  /**
2456
- * fetch js succeeded, record the code value
2457
- * @param address script address
2458
- * @param scriptInfo resource script info
2459
- * @param data code
2577
+ *
2578
+ * @param appName app.name
2579
+ * @param linkInfo linkInfo of current address
2460
2580
  */
2461
- function fetchScriptSuccess(address, scriptInfo, code, app) {
2462
- // reset scriptInfo.code
2463
- scriptInfo.code = code;
2464
- /**
2465
- * Pre parse script for prefetch, improve rendering performance
2466
- * NOTE:
2467
- * 1. if global parseResult exist, skip this step
2468
- * 2. if app is inline or script is esmodule, skip this step
2469
- * 3. if global parseResult not exist, the current script occupies the position, when js is reused, parseResult is reference
2470
- */
2471
- if (app.isPrefetch && app.prefetchLevel === 2) {
2472
- const appSpaceData = scriptInfo.appSpace[app.name];
2473
- /**
2474
- * When prefetch app is replaced by a new app in the processing phase, since the scriptInfo is common, when the scriptInfo of the prefetch app is processed, it may have already been processed.
2475
- * This causes parsedCode to already exist when preloading ends
2476
- * e.g.
2477
- * 1. prefetch app.url different from <micro-app></micro-app>
2478
- * 2. prefetch param different from <micro-app></micro-app>
2479
- */
2480
- if (!appSpaceData.parsedCode) {
2481
- appSpaceData.parsedCode = bindScope(address, app, code, scriptInfo);
2482
- appSpaceData.sandboxType = getSandboxType(app, scriptInfo);
2483
- if (!isInlineMode(app, scriptInfo)) {
2484
- try {
2485
- appSpaceData.parsedFunction = getParsedFunction(app, scriptInfo, appSpaceData.parsedCode);
2486
- }
2487
- catch (err) {
2488
- logError('Something went wrong while handling preloaded resources', app.name, '\n', err);
2489
- }
2581
+ function getExistParseCode(appName, prefix, linkInfo) {
2582
+ const appSpace = linkInfo.appSpace;
2583
+ for (const item in appSpace) {
2584
+ if (item !== appName) {
2585
+ const appSpaceData = appSpace[item];
2586
+ if (appSpaceData.parsedCode) {
2587
+ return appSpaceData.parsedCode.replace(new RegExp(createPrefix(item, true), 'g'), prefix);
2490
2588
  }
2491
2589
  }
2492
2590
  }
2493
2591
  }
2592
+ // transfer the attributes on the link to convertStyle
2593
+ function setConvertStyleAttr(convertStyle, attrs) {
2594
+ attrs.forEach((value, key) => {
2595
+ if (key === 'rel')
2596
+ return;
2597
+ if (key === 'href')
2598
+ key = 'data-origin-href';
2599
+ globalEnv.rawSetAttribute.call(convertStyle, key, value);
2600
+ });
2601
+ }
2494
2602
  /**
2495
- * Execute js in the mount lifecycle
2603
+ * Extract link elements
2604
+ * @param link link element
2605
+ * @param parent parent element of link
2496
2606
  * @param app app
2497
- * @param initHook callback for umd mode
2607
+ * @param microAppHead micro-app-head element
2608
+ * @param isDynamic dynamic insert
2498
2609
  */
2499
- function execScripts(app, initHook) {
2500
- const fiberScriptTasks = app.fiber ? [] : null;
2501
- const scriptList = Array.from(app.source.scripts);
2502
- const deferScriptPromise = [];
2503
- const deferScriptInfo = [];
2504
- for (const address of scriptList) {
2505
- const scriptInfo = sourceCenter.script.getInfo(address);
2506
- const appSpaceData = scriptInfo.appSpace[app.name];
2507
- // Notice the second render
2508
- if (appSpaceData.defer || appSpaceData.async) {
2509
- // TODO: defer和module彻底分开,不要混在一起
2510
- if (scriptInfo.isExternal && !scriptInfo.code && !(app.iframe && appSpaceData.module)) {
2511
- deferScriptPromise.push(fetchSource(address, app.name));
2512
- }
2513
- else {
2514
- deferScriptPromise.push(scriptInfo.code);
2515
- }
2516
- deferScriptInfo.push([address, scriptInfo]);
2517
- isTypeModule(app, scriptInfo) && (initHook.moduleCount = initHook.moduleCount ? ++initHook.moduleCount : 1);
2610
+ function extractLinkFromHtml(link, parent, app, isDynamic = false) {
2611
+ const rel = link.getAttribute('rel');
2612
+ let href = link.getAttribute('href');
2613
+ let replaceComment = null;
2614
+ if (rel === 'stylesheet' && href) {
2615
+ href = CompletionPath(href, app.url);
2616
+ let linkInfo = sourceCenter.link.getInfo(href);
2617
+ const appSpaceData = {
2618
+ attrs: getAttributes(link),
2619
+ };
2620
+ if (!linkInfo) {
2621
+ linkInfo = {
2622
+ code: '',
2623
+ appSpace: {
2624
+ [app.name]: appSpaceData,
2625
+ }
2626
+ };
2518
2627
  }
2519
2628
  else {
2520
- injectFiberTask(fiberScriptTasks, () => {
2521
- runScript(address, app, scriptInfo);
2522
- initHook(false);
2523
- });
2629
+ linkInfo.appSpace[app.name] = linkInfo.appSpace[app.name] || appSpaceData;
2630
+ }
2631
+ sourceCenter.link.setInfo(href, linkInfo);
2632
+ if (!isDynamic) {
2633
+ app.source.links.add(href);
2634
+ replaceComment = document.createComment(`link element with href=${href} move to micro-app-head as style element`);
2635
+ linkInfo.appSpace[app.name].placeholder = replaceComment;
2636
+ }
2637
+ else {
2638
+ return { address: href, linkInfo };
2524
2639
  }
2525
2640
  }
2526
- if (deferScriptPromise.length) {
2527
- promiseStream(deferScriptPromise, (res) => {
2528
- const scriptInfo = deferScriptInfo[res.index][1];
2529
- scriptInfo.code = scriptInfo.code || res.data;
2530
- }, (err) => {
2531
- initHook.errorCount = initHook.errorCount ? ++initHook.errorCount : 1;
2532
- logError(err, app.name);
2533
- }, () => {
2534
- deferScriptInfo.forEach(([address, scriptInfo]) => {
2535
- if (isString(scriptInfo.code)) {
2536
- injectFiberTask(fiberScriptTasks, () => {
2537
- runScript(address, app, scriptInfo, initHook);
2538
- !isTypeModule(app, scriptInfo) && initHook(false);
2539
- });
2540
- }
2541
- });
2542
- /**
2543
- * Fiber wraps js in requestIdleCallback and executes it in sequence
2544
- * NOTE:
2545
- * 1. In order to ensure the execution order, wait for all js loaded and then execute
2546
- * 2. If js create a dynamic script, it may be errors in the execution order, because the subsequent js is wrapped in requestIdleCallback, even putting dynamic script in requestIdleCallback doesn't solve it
2547
- *
2548
- * BUG: NOTE.2 - execution order problem
2549
- */
2550
- if (fiberScriptTasks) {
2551
- fiberScriptTasks.push(() => Promise.resolve(initHook(isUndefined(initHook.moduleCount) ||
2552
- initHook.errorCount === deferScriptPromise.length)));
2553
- serialExecFiberTasks(fiberScriptTasks);
2554
- }
2555
- else {
2556
- initHook(isUndefined(initHook.moduleCount) ||
2557
- initHook.errorCount === deferScriptPromise.length);
2558
- }
2559
- });
2560
- }
2561
- else {
2562
- if (fiberScriptTasks) {
2563
- fiberScriptTasks.push(() => Promise.resolve(initHook(true)));
2564
- serialExecFiberTasks(fiberScriptTasks);
2641
+ else if (rel && ['prefetch', 'preload', 'prerender', 'icon', 'apple-touch-icon'].includes(rel)) {
2642
+ // preload prefetch icon ....
2643
+ if (isDynamic) {
2644
+ replaceComment = document.createComment(`link element with rel=${rel}${href ? ' & href=' + href : ''} removed by micro-app`);
2565
2645
  }
2566
2646
  else {
2567
- initHook(true);
2647
+ parent === null || parent === void 0 ? void 0 : parent.removeChild(link);
2568
2648
  }
2569
2649
  }
2650
+ else if (href) {
2651
+ // dns-prefetch preconnect modulepreload search ....
2652
+ globalEnv.rawSetAttribute.call(link, 'href', CompletionPath(href, app.url));
2653
+ }
2654
+ if (isDynamic) {
2655
+ return { replaceComment };
2656
+ }
2657
+ else if (replaceComment) {
2658
+ return parent === null || parent === void 0 ? void 0 : parent.replaceChild(replaceComment, link);
2659
+ }
2570
2660
  }
2571
2661
  /**
2572
- * run code
2573
- * @param address script address
2662
+ * Get link remote resources
2663
+ * @param wrapElement htmlDom
2574
2664
  * @param app app
2575
- * @param scriptInfo script info
2576
- * @param callback callback of module script
2665
+ * @param microAppHead micro-app-head
2577
2666
  */
2578
- function runScript(address, app, scriptInfo, callback, replaceElement) {
2579
- try {
2580
- actionsBeforeRunScript(app);
2581
- const appSpaceData = scriptInfo.appSpace[app.name];
2582
- const sandboxType = getSandboxType(app, scriptInfo);
2583
- /**
2584
- * NOTE:
2585
- * 1. plugins and wrapCode will only be executed once
2586
- * 2. if parsedCode not exist, parsedFunction is not exist
2587
- * 3. if parsedCode exist, parsedFunction does not necessarily exist
2667
+ function fetchLinksFromHtml(wrapElement, app, microAppHead, fiberStyleResult) {
2668
+ const styleList = Array.from(app.source.links);
2669
+ const fetchLinkPromise = styleList.map((address) => {
2670
+ const linkInfo = sourceCenter.link.getInfo(address);
2671
+ return linkInfo.code ? linkInfo.code : fetchSource(address, app.name);
2672
+ });
2673
+ const fiberLinkTasks = fiberStyleResult ? [] : null;
2674
+ promiseStream(fetchLinkPromise, (res) => {
2675
+ injectFiberTask(fiberLinkTasks, () => fetchLinkSuccess(styleList[res.index], res.data, microAppHead, app));
2676
+ }, (err) => {
2677
+ logError(err, app.name);
2678
+ }, () => {
2679
+ /**
2680
+ * 1. If fiberStyleResult exist, fiberLinkTasks must exist
2681
+ * 2. Download link source while processing style
2682
+ * 3. Process style first, and then process link
2588
2683
  */
2589
- if (!appSpaceData.parsedCode || appSpaceData.sandboxType !== sandboxType) {
2590
- appSpaceData.parsedCode = bindScope(address, app, scriptInfo.code, scriptInfo);
2591
- appSpaceData.sandboxType = sandboxType;
2592
- appSpaceData.parsedFunction = null;
2593
- }
2594
- if (isInlineMode(app, scriptInfo)) {
2595
- const scriptElement = replaceElement || pureCreateElement('script');
2596
- runCode2InlineScript(address, appSpaceData.parsedCode, isTypeModule(app, scriptInfo), scriptElement, appSpaceData.attrs, callback);
2597
- if (!replaceElement) {
2598
- // TEST IGNORE
2599
- const parent = app.iframe ? app.sandBox.microBody : app.querySelector('micro-app-body');
2600
- parent === null || parent === void 0 ? void 0 : parent.appendChild(scriptElement);
2601
- }
2684
+ if (fiberStyleResult) {
2685
+ fiberStyleResult.then(() => {
2686
+ fiberLinkTasks.push(() => Promise.resolve(app.onLoad(wrapElement)));
2687
+ serialExecFiberTasks(fiberLinkTasks);
2688
+ });
2602
2689
  }
2603
2690
  else {
2604
- runParsedFunction(app, scriptInfo);
2691
+ app.onLoad(wrapElement);
2605
2692
  }
2606
- }
2607
- catch (e) {
2608
- console.error(`[micro-app from ${replaceElement ? 'runDynamicScript' : 'runScript'}] app ${app.name}: `, e, address);
2609
- }
2693
+ });
2610
2694
  }
2611
2695
  /**
2612
- * Get dynamically created remote script
2613
- * @param address script address
2696
+ * Fetch link succeeded, replace placeholder with style tag
2697
+ * NOTE:
2698
+ * 1. Only exec when init, no longer exec when remount
2699
+ * 2. Only handler html link element, not dynamic link or style
2700
+ * 3. The same prefix can reuse parsedCode
2701
+ * 4. Async exec with requestIdleCallback in prefetch or fiber
2702
+ * 5. appSpace[app.name].placeholder/attrs must exist
2703
+ * @param address resource address
2704
+ * @param code link source code
2705
+ * @param microAppHead micro-app-head
2614
2706
  * @param app app instance
2615
- * @param scriptInfo scriptInfo
2616
- * @param originScript origin script element
2617
2707
  */
2618
- function runDynamicRemoteScript(address, app, scriptInfo, originScript) {
2619
- const replaceElement = isInlineMode(app, scriptInfo) ? pureCreateElement('script') : document.createComment('dynamic script extract by micro-app');
2620
- const dispatchScriptOnLoadEvent = () => dispatchOnLoadEvent(originScript);
2621
- const runDynamicScript = () => {
2622
- const descriptor = Object.getOwnPropertyDescriptor(globalEnv.rawDocument, 'currentScript');
2623
- if (!descriptor || descriptor.configurable) {
2624
- Object.defineProperty(globalEnv.rawDocument, 'currentScript', {
2625
- value: originScript,
2626
- configurable: true,
2627
- });
2708
+ function fetchLinkSuccess(address, code, microAppHead, app) {
2709
+ /**
2710
+ * linkInfo must exist, but linkInfo.code not
2711
+ * so we set code to linkInfo.code
2712
+ */
2713
+ const linkInfo = sourceCenter.link.getInfo(address);
2714
+ linkInfo.code = code;
2715
+ const appSpaceData = linkInfo.appSpace[app.name];
2716
+ const placeholder = appSpaceData.placeholder;
2717
+ /**
2718
+ * When prefetch app is replaced by a new app in the processing phase, since the linkInfo is common, when the linkInfo of the prefetch app is processed, it may have already been processed.
2719
+ * This causes placeholder to be possibly null
2720
+ * e.g.
2721
+ * 1. prefetch app.url different from <micro-app></micro-app>
2722
+ * 2. prefetch param different from <micro-app></micro-app>
2723
+ */
2724
+ if (placeholder) {
2725
+ const convertStyle = pureCreateElement('style');
2726
+ handleConvertStyle(app, address, convertStyle, linkInfo, appSpaceData.attrs);
2727
+ if (placeholder.parentNode) {
2728
+ placeholder.parentNode.replaceChild(convertStyle, placeholder);
2628
2729
  }
2629
- runScript(address, app, scriptInfo, dispatchScriptOnLoadEvent, replaceElement);
2630
- !isTypeModule(app, scriptInfo) && dispatchScriptOnLoadEvent();
2631
- };
2632
- if (scriptInfo.code || (app.iframe && scriptInfo.appSpace[app.name].module)) {
2633
- defer(runDynamicScript);
2634
- }
2635
- else {
2636
- fetchSource(address, app.name).then((code) => {
2637
- scriptInfo.code = code;
2638
- runDynamicScript();
2639
- }).catch((err) => {
2640
- logError(err, app.name);
2641
- dispatchOnErrorEvent(originScript);
2642
- });
2730
+ else {
2731
+ microAppHead.appendChild(convertStyle);
2732
+ }
2733
+ // clear placeholder
2734
+ appSpaceData.placeholder = null;
2643
2735
  }
2644
- return replaceElement;
2645
2736
  }
2646
2737
  /**
2647
- * Get dynamically created inline script
2648
- * @param address script address
2738
+ * Get parsedCode, update convertStyle
2739
+ * Actions:
2740
+ * 1. get scope css (through scopedCSS or oldData)
2741
+ * 2. record parsedCode
2742
+ * 3. set parsedCode to convertStyle if need
2649
2743
  * @param app app instance
2650
- * @param scriptInfo scriptInfo
2651
- */
2652
- function runDynamicInlineScript(address, app, scriptInfo) {
2653
- const replaceElement = isInlineMode(app, scriptInfo) ? pureCreateElement('script') : document.createComment('dynamic script extract by micro-app');
2654
- runScript(address, app, scriptInfo, void 0, replaceElement);
2655
- return replaceElement;
2656
- }
2657
- /**
2658
- * common handle for inline script
2659
- * @param address script address
2660
- * @param code bound code
2661
- * @param module type='module' of script
2662
- * @param scriptElement target script element
2663
- * @param attrs attributes of script element
2664
- * @param callback callback of module script
2744
+ * @param address resource address
2745
+ * @param convertStyle converted style
2746
+ * @param linkInfo linkInfo in sourceCenter
2747
+ * @param attrs attrs of link
2665
2748
  */
2666
- function runCode2InlineScript(address, code, module, scriptElement, attrs, callback) {
2667
- if (module) {
2668
- // module script is async, transform it to a blob for subsequent operations
2669
- if (isInlineScript(address)) {
2670
- const blob = new Blob([code], { type: 'text/javascript' });
2671
- scriptElement.src = URL.createObjectURL(blob);
2749
+ function handleConvertStyle(app, address, convertStyle, linkInfo, attrs) {
2750
+ if (app.scopecss) {
2751
+ const appSpaceData = linkInfo.appSpace[app.name];
2752
+ appSpaceData.prefix = appSpaceData.prefix || createPrefix(app.name);
2753
+ if (!appSpaceData.parsedCode) {
2754
+ const existParsedCode = getExistParseCode(app.name, appSpaceData.prefix, linkInfo);
2755
+ if (!existParsedCode) {
2756
+ convertStyle.textContent = linkInfo.code;
2757
+ scopedCSS(convertStyle, app, address);
2758
+ }
2759
+ else {
2760
+ convertStyle.textContent = existParsedCode;
2761
+ }
2762
+ appSpaceData.parsedCode = convertStyle.textContent;
2672
2763
  }
2673
2764
  else {
2674
- scriptElement.src = address;
2675
- }
2676
- scriptElement.setAttribute('type', 'module');
2677
- if (callback) {
2678
- callback.moduleCount && callback.moduleCount--;
2679
- /**
2680
- * module script will execute onload method only after it insert to document/iframe
2681
- */
2682
- scriptElement.onload = callback.bind(scriptElement, callback.moduleCount === 0);
2765
+ convertStyle.textContent = appSpaceData.parsedCode;
2683
2766
  }
2684
2767
  }
2685
2768
  else {
2686
- scriptElement.textContent = code;
2687
- }
2688
- setConvertScriptAttr(scriptElement, attrs);
2689
- }
2690
- // init & run code2Function
2691
- function runParsedFunction(app, scriptInfo) {
2692
- const appSpaceData = scriptInfo.appSpace[app.name];
2693
- if (!appSpaceData.parsedFunction) {
2694
- appSpaceData.parsedFunction = getParsedFunction(app, scriptInfo, appSpaceData.parsedCode);
2769
+ convertStyle.textContent = linkInfo.code;
2695
2770
  }
2696
- appSpaceData.parsedFunction.call(getEffectWindow(app));
2771
+ setConvertStyleAttr(convertStyle, attrs);
2697
2772
  }
2698
2773
  /**
2699
- * bind js scope
2774
+ * Handle css of dynamic link
2775
+ * @param address link address
2700
2776
  * @param app app
2701
- * @param code code
2702
- * @param scriptInfo source script info
2703
- */
2704
- function bindScope(address, app, code, scriptInfo) {
2705
- // TODO: 1、cache 2、esm code is null
2706
- if (isPlainObject(microApp.options.plugins)) {
2707
- code = usePlugins(address, code, app.name, microApp.options.plugins);
2708
- }
2709
- if (isWrapInSandBox(app, scriptInfo)) {
2710
- return app.iframe ? `(function(window,self,global,location){;${code}\n${isInlineScript(address) ? '' : `//# sourceURL=${address}\n`}}).call(window.__MICRO_APP_SANDBOX__.proxyWindow,window.__MICRO_APP_SANDBOX__.proxyWindow,window.__MICRO_APP_SANDBOX__.proxyWindow,window.__MICRO_APP_SANDBOX__.proxyWindow,window.__MICRO_APP_SANDBOX__.proxyLocation);` : `;(function(proxyWindow){with(proxyWindow.__MICRO_APP_WINDOW__){(function(${globalKeyToBeCached}){;${code}\n${isInlineScript(address) ? '' : `//# sourceURL=${address}\n`}}).call(proxyWindow,${globalKeyToBeCached})}})(window.__MICRO_APP_PROXY_WINDOW__);`;
2711
- }
2712
- return code;
2713
- }
2714
- /**
2715
- * actions before run script
2716
- */
2717
- function actionsBeforeRunScript(app) {
2718
- setActiveProxyWindow(app);
2719
- }
2720
- /**
2721
- * set active sandBox.proxyWindow to window.__MICRO_APP_PROXY_WINDOW__
2777
+ * @param linkInfo linkInfo
2778
+ * @param originLink origin link element
2722
2779
  */
2723
- function setActiveProxyWindow(app) {
2724
- if (app.sandBox) {
2725
- globalEnv.rawWindow.__MICRO_APP_PROXY_WINDOW__ = app.sandBox.proxyWindow;
2780
+ function formatDynamicLink(address, app, linkInfo, originLink) {
2781
+ const convertStyle = pureCreateElement('style');
2782
+ const handleDynamicLink = () => {
2783
+ handleConvertStyle(app, address, convertStyle, linkInfo, linkInfo.appSpace[app.name].attrs);
2784
+ dispatchOnLoadEvent(originLink);
2785
+ };
2786
+ if (linkInfo.code) {
2787
+ defer(handleDynamicLink);
2726
2788
  }
2727
- }
2728
- /**
2729
- * Call the plugin to process the file
2730
- * @param address script address
2731
- * @param code code
2732
- * @param appName app name
2733
- * @param plugins plugin list
2734
- */
2735
- function usePlugins(address, code, appName, plugins) {
2736
- var _a;
2737
- const newCode = processCode(plugins.global, code, address);
2738
- return processCode((_a = plugins.modules) === null || _a === void 0 ? void 0 : _a[appName], newCode, address);
2739
- }
2740
- function processCode(configs, code, address) {
2741
- if (!isArray(configs)) {
2742
- return code;
2789
+ else {
2790
+ fetchSource(address, app.name).then((data) => {
2791
+ linkInfo.code = data;
2792
+ handleDynamicLink();
2793
+ }).catch((err) => {
2794
+ logError(err, app.name);
2795
+ dispatchOnErrorEvent(originLink);
2796
+ });
2743
2797
  }
2744
- return configs.reduce((preCode, config) => {
2745
- if (isPlainObject(config) && isFunction(config.loader)) {
2746
- return config.loader(preCode, address);
2747
- }
2748
- return preCode;
2749
- }, code);
2798
+ return convertStyle;
2750
2799
  }
2751
2800
 
2752
2801
  /**
@@ -2778,7 +2827,7 @@ function flatChildren(parent, app, microAppHead, fiberStyleTasks) {
2778
2827
  extractLinkFromHtml(dom, parent, app);
2779
2828
  }
2780
2829
  else if (dom.hasAttribute('href')) {
2781
- dom.setAttribute('href', CompletionPath(dom.getAttribute('href'), app.url));
2830
+ globalEnv.rawSetAttribute.call(dom, 'href', CompletionPath(dom.getAttribute('href'), app.url));
2782
2831
  }
2783
2832
  }
2784
2833
  else if (isStyleElement(dom)) {
@@ -2793,7 +2842,7 @@ function flatChildren(parent, app, microAppHead, fiberStyleTasks) {
2793
2842
  extractScriptElement(dom, parent, app);
2794
2843
  }
2795
2844
  else if (isImageElement(dom) && dom.hasAttribute('src')) {
2796
- dom.setAttribute('src', CompletionPath(dom.getAttribute('src'), app.url));
2845
+ globalEnv.rawSetAttribute.call(dom, 'src', CompletionPath(dom.getAttribute('src'), app.url));
2797
2846
  }
2798
2847
  /**
2799
2848
  * Don't remove meta and title, they have some special scenes
@@ -2845,6 +2894,40 @@ function extractSourceDom(htmlStr, app) {
2845
2894
  }
2846
2895
  }
2847
2896
 
2897
+ /* eslint-disable no-return-assign */
2898
+ function isBoundedFunction(value) {
2899
+ if (isBoolean(value.__MICRO_APP_IS_BOUND_FUNCTION__))
2900
+ return value.__MICRO_APP_IS_BOUND_FUNCTION__;
2901
+ return value.__MICRO_APP_IS_BOUND_FUNCTION__ = isBoundFunction(value);
2902
+ }
2903
+ function isConstructorFunction(value) {
2904
+ if (isBoolean(value.__MICRO_APP_IS_CONSTRUCTOR__))
2905
+ return value.__MICRO_APP_IS_CONSTRUCTOR__;
2906
+ return value.__MICRO_APP_IS_CONSTRUCTOR__ = isConstructor(value);
2907
+ }
2908
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
2909
+ function bindFunctionToRawTarget(value, rawTarget, key = 'WINDOW') {
2910
+ if (isFunction(value) && !isConstructorFunction(value) && !isBoundedFunction(value)) {
2911
+ const cacheKey = `__MICRO_APP_BOUND_${key}_FUNCTION__`;
2912
+ if (value[cacheKey])
2913
+ return value[cacheKey];
2914
+ const bindRawObjectValue = value.bind(rawTarget);
2915
+ for (const key in value) {
2916
+ bindRawObjectValue[key] = value[key];
2917
+ }
2918
+ if (value.hasOwnProperty('prototype')) {
2919
+ rawDefineProperty(bindRawObjectValue, 'prototype', {
2920
+ value: value.prototype,
2921
+ configurable: true,
2922
+ enumerable: false,
2923
+ writable: true,
2924
+ });
2925
+ }
2926
+ return value[cacheKey] = bindRawObjectValue;
2927
+ }
2928
+ return value;
2929
+ }
2930
+
2848
2931
  class EventCenter {
2849
2932
  constructor() {
2850
2933
  this.eventList = new Map();
@@ -3300,148 +3383,197 @@ function initEnvOfNestedApp() {
3300
3383
  }
3301
3384
  }
3302
3385
 
3303
- /* eslint-disable no-return-assign */
3304
- function isBoundedFunction(value) {
3305
- if (isBoolean(value.__MICRO_APP_IS_BOUND_FUNCTION__))
3306
- return value.__MICRO_APP_IS_BOUND_FUNCTION__;
3307
- return value.__MICRO_APP_IS_BOUND_FUNCTION__ = isBoundFunction(value);
3308
- }
3309
- function isConstructorFunction(value) {
3310
- if (isBoolean(value.__MICRO_APP_IS_CONSTRUCTOR__))
3311
- return value.__MICRO_APP_IS_CONSTRUCTOR__;
3312
- return value.__MICRO_APP_IS_CONSTRUCTOR__ = isConstructor(value);
3313
- }
3314
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
3315
- function bindFunctionToRawTarget(value, rawTarget, key = 'WINDOW') {
3316
- if (isFunction(value) && !isConstructorFunction(value) && !isBoundedFunction(value)) {
3317
- const cacheKey = `__MICRO_APP_BOUND_${key}_FUNCTION__`;
3318
- if (value[cacheKey])
3319
- return value[cacheKey];
3320
- const bindRawObjectValue = value.bind(rawTarget);
3321
- for (const key in value) {
3322
- bindRawObjectValue[key] = value[key];
3323
- }
3324
- if (value.hasOwnProperty('prototype')) {
3325
- rawDefineProperty(bindRawObjectValue, 'prototype', {
3326
- value: value.prototype,
3327
- configurable: true,
3328
- enumerable: false,
3329
- writable: true,
3330
- });
3386
+ function createMicroDocument(appName, proxyDocument) {
3387
+ const { rawDocument, rawRootDocument } = globalEnv;
3388
+ class MicroDocument {
3389
+ static [Symbol.hasInstance](target) {
3390
+ let proto = target;
3391
+ while (proto = Object.getPrototypeOf(proto)) {
3392
+ if (proto === MicroDocument.prototype) {
3393
+ return true;
3394
+ }
3395
+ }
3396
+ return (target === proxyDocument ||
3397
+ target instanceof rawRootDocument);
3331
3398
  }
3332
- return value[cacheKey] = bindRawObjectValue;
3333
3399
  }
3334
- return value;
3400
+ /**
3401
+ * TIP:
3402
+ * 1. child class __proto__, which represents the inherit of the constructor, always points to the parent class
3403
+ * 2. child class prototype.__proto__, which represents the inherit of methods, always points to parent class prototype
3404
+ * e.g.
3405
+ * class B extends A {}
3406
+ * B.__proto__ === A // true
3407
+ * B.prototype.__proto__ === A.prototype // true
3408
+ */
3409
+ Object.setPrototypeOf(MicroDocument, rawRootDocument);
3410
+ // Object.create(rawRootDocument.prototype) will cause MicroDocument and proxyDocument methods not same when exec Document.prototype.xxx = xxx in child app
3411
+ Object.setPrototypeOf(MicroDocument.prototype, new Proxy(rawRootDocument.prototype, {
3412
+ get(target, key) {
3413
+ throttleDeferForSetAppName(appName);
3414
+ return bindFunctionToRawTarget(Reflect.get(target, key), rawDocument, 'DOCUMENT');
3415
+ },
3416
+ set(target, key, value) {
3417
+ Reflect.set(target, key, value);
3418
+ return true;
3419
+ }
3420
+ }));
3421
+ return MicroDocument;
3335
3422
  }
3336
-
3337
- // document.onclick binding list, the binding function of each application is unique
3338
- const documentClickListMap = new Map();
3339
- let hasRewriteDocumentOnClick = false;
3340
3423
  /**
3341
- * Rewrite document.onclick and execute it only once
3424
+ * Create new document and Document
3342
3425
  */
3343
- function overwriteDocumentOnClick() {
3344
- hasRewriteDocumentOnClick = true;
3345
- if (Object.getOwnPropertyDescriptor(document, 'onclick')) {
3346
- return logWarn('Cannot redefine document property onclick');
3347
- }
3348
- const rawOnClick = document.onclick;
3349
- document.onclick = null;
3350
- let hasDocumentClickInited = false;
3351
- function onClickHandler(e) {
3352
- documentClickListMap.forEach((f) => {
3353
- isFunction(f) && f.call(document, e);
3354
- });
3426
+ function createProxyDocument(appName, sandbox) {
3427
+ const eventListenerMap = new Map();
3428
+ const sstEventListenerMap = new Map();
3429
+ let onClickHandler = null;
3430
+ let sstOnClickHandler = null;
3431
+ const { rawDocument, rawCreateElement, rawAddEventListener, rawRemoveEventListener, } = globalEnv;
3432
+ function createElement(tagName, options) {
3433
+ const element = rawCreateElement.call(rawDocument, tagName, options);
3434
+ element.__MICRO_APP_NAME__ = appName;
3435
+ return element;
3355
3436
  }
3356
- rawDefineProperty(document, 'onclick', {
3357
- configurable: true,
3358
- enumerable: true,
3359
- get() {
3360
- const appName = getCurrentAppName();
3361
- return appName ? documentClickListMap.get(appName) : documentClickListMap.get('base');
3362
- },
3363
- set(f) {
3364
- const appName = getCurrentAppName();
3365
- if (appName) {
3366
- documentClickListMap.set(appName, f);
3367
- }
3368
- else {
3369
- documentClickListMap.set('base', f);
3437
+ /**
3438
+ * TODO:
3439
+ * 1. listener 是否需要绑定proxyDocument,否则函数中的this指向原生window
3440
+ * 2. 相似代码提取为公共方法(with, iframe)
3441
+ */
3442
+ function addEventListener(type, listener, options) {
3443
+ const listenerList = eventListenerMap.get(type);
3444
+ if (listenerList) {
3445
+ listenerList.add(listener);
3446
+ }
3447
+ else {
3448
+ eventListenerMap.set(type, new Set([listener]));
3449
+ }
3450
+ listener && (listener.__MICRO_APP_MARK_OPTIONS__ = options);
3451
+ rawAddEventListener.call(rawDocument, type, listener, options);
3452
+ }
3453
+ function removeEventListener(type, listener, options) {
3454
+ const listenerList = eventListenerMap.get(type);
3455
+ if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
3456
+ listenerList.delete(listener);
3457
+ }
3458
+ rawRemoveEventListener.call(rawDocument, type, listener, options);
3459
+ }
3460
+ // reset snapshot data
3461
+ const reset = () => {
3462
+ sstEventListenerMap.clear();
3463
+ sstOnClickHandler = null;
3464
+ };
3465
+ /**
3466
+ * NOTE:
3467
+ * 1. about timer(events & properties should record & rebuild at all modes, exclude default mode)
3468
+ * 2. record maybe call twice when unmount prerender, keep-alive app manually with umd mode
3469
+ * 4 modes: default-mode、umd-mode、prerender、keep-alive
3470
+ * Solution:
3471
+ * 1. default-mode(normal): clear events & timers, not record & rebuild anything
3472
+ * 2. umd-mode(normal): not clear timers, record & rebuild events
3473
+ * 3. prerender/keep-alive(default, umd): not clear timers, record & rebuild events
3474
+ */
3475
+ const record = () => {
3476
+ // record onclick handler
3477
+ sstOnClickHandler = sstOnClickHandler || onClickHandler;
3478
+ // record document event
3479
+ eventListenerMap.forEach((listenerList, type) => {
3480
+ if (listenerList.size) {
3481
+ sstEventListenerMap.set(type, new Set(listenerList));
3370
3482
  }
3371
- if (!hasDocumentClickInited && isFunction(f)) {
3372
- hasDocumentClickInited = true;
3373
- globalEnv.rawDocumentAddEventListener.call(globalEnv.rawDocument, 'click', onClickHandler, false);
3483
+ });
3484
+ };
3485
+ // rebuild event and timer before remount app
3486
+ const rebuild = () => {
3487
+ // rebuild onclick event
3488
+ if (sstOnClickHandler)
3489
+ proxyDocument.onclick = sstOnClickHandler;
3490
+ // rebuild document event
3491
+ sstEventListenerMap.forEach((listenerList, type) => {
3492
+ for (const listener of listenerList) {
3493
+ proxyDocument.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
3374
3494
  }
3495
+ });
3496
+ reset();
3497
+ };
3498
+ // release all event listener & interval & timeout when unmount app
3499
+ const release = () => {
3500
+ // Clear the function bound by micro app through document.onclick
3501
+ if (isFunction(onClickHandler)) {
3502
+ rawRemoveEventListener.call(rawDocument, 'click', onClickHandler);
3503
+ onClickHandler = null;
3375
3504
  }
3376
- });
3377
- rawOnClick && (document.onclick = rawOnClick);
3378
- }
3379
- /**
3380
- * The document event is globally, we need to clear these event bindings when micro application unmounted
3381
- */
3382
- const documentEventListenerMap = new Map();
3383
- function effectDocumentEvent() {
3384
- const { rawDocument, rawDocumentAddEventListener, rawDocumentRemoveEventListener, } = globalEnv;
3385
- !hasRewriteDocumentOnClick && overwriteDocumentOnClick();
3386
- document.addEventListener = function (type, listener, options) {
3387
- const appName = getCurrentAppName();
3388
- /**
3389
- * ignore bound function of document event in umd mode, used to solve problem of react global events
3390
- * update in 2022-09-02:
3391
- * boundFunction is no longer exclude, because events in UMD mode will not cleared from v1.0.0-alpha.4
3392
- * if (appName && !(appInstanceMap.get(appName)?.umdMode && isBoundFunction(listener))) {
3393
- */
3394
- if (appName) {
3395
- const appListenersMap = documentEventListenerMap.get(appName);
3396
- if (appListenersMap) {
3397
- const appListenerList = appListenersMap.get(type);
3398
- if (appListenerList) {
3399
- appListenerList.add(listener);
3505
+ // Clear document binding event
3506
+ if (eventListenerMap.size) {
3507
+ eventListenerMap.forEach((listenerList, type) => {
3508
+ for (const listener of listenerList) {
3509
+ rawRemoveEventListener.call(rawDocument, type, listener);
3400
3510
  }
3401
- else {
3402
- appListenersMap.set(type, new Set([listener]));
3511
+ });
3512
+ eventListenerMap.clear();
3513
+ }
3514
+ };
3515
+ const proxyDocument = new Proxy(rawDocument, {
3516
+ get: (target, key) => {
3517
+ throttleDeferForSetAppName(appName);
3518
+ throttleDeferForParentNode(proxyDocument);
3519
+ if (key === 'createElement')
3520
+ return createElement;
3521
+ if (key === Symbol.toStringTag)
3522
+ return 'ProxyDocument';
3523
+ if (key === 'defaultView')
3524
+ return sandbox.proxyWindow;
3525
+ if (key === 'onclick')
3526
+ return onClickHandler;
3527
+ if (key === 'addEventListener')
3528
+ return addEventListener;
3529
+ if (key === 'removeEventListener')
3530
+ return removeEventListener;
3531
+ return bindFunctionToRawTarget(Reflect.get(target, key), rawDocument, 'DOCUMENT');
3532
+ },
3533
+ set: (target, key, value) => {
3534
+ if (key === 'onclick') {
3535
+ if (isFunction(onClickHandler)) {
3536
+ rawRemoveEventListener.call(rawDocument, 'click', onClickHandler, false);
3537
+ }
3538
+ // TODO: listener 是否需要绑定proxyDocument,否则函数中的this指向原生window
3539
+ if (isFunction(value)) {
3540
+ rawAddEventListener.call(rawDocument, 'click', value, false);
3403
3541
  }
3542
+ onClickHandler = value;
3404
3543
  }
3405
3544
  else {
3406
- documentEventListenerMap.set(appName, new Map([[type, new Set([listener])]]));
3545
+ /**
3546
+ * 1. Fix TypeError: Illegal invocation when set document.title
3547
+ * 2. If the set method returns false, and the assignment happened in strict-mode code, a TypeError will be thrown.
3548
+ */
3549
+ Reflect.set(target, key, value);
3407
3550
  }
3408
- listener && (listener.__MICRO_APP_MARK_OPTIONS__ = options);
3551
+ return true;
3409
3552
  }
3410
- rawDocumentAddEventListener.call(rawDocument, type, listener, options);
3411
- };
3412
- document.removeEventListener = function (type, listener, options) {
3413
- const appName = getCurrentAppName();
3414
- /**
3415
- * update in 2022-09-02:
3416
- * boundFunction is no longer exclude, because events in UMD mode will not cleared from v1.0.0-alpha.4
3417
- * if (appName && !(appInstanceMap.get(appName)?.umdMode && isBoundFunction(listener))) {
3418
- */
3419
- if (appName) {
3420
- const appListenersMap = documentEventListenerMap.get(appName);
3421
- if (appListenersMap) {
3422
- const appListenerList = appListenersMap.get(type);
3423
- if ((appListenerList === null || appListenerList === void 0 ? void 0 : appListenerList.size) && appListenerList.has(listener)) {
3424
- appListenerList.delete(listener);
3425
- }
3426
- }
3553
+ });
3554
+ return {
3555
+ proxyDocument,
3556
+ MicroDocument: createMicroDocument(appName, proxyDocument),
3557
+ documentEffect: {
3558
+ reset,
3559
+ record,
3560
+ rebuild,
3561
+ release,
3427
3562
  }
3428
- rawDocumentRemoveEventListener.call(rawDocument, type, listener, options);
3429
3563
  };
3430
3564
  }
3431
- // Clear the document event agent
3432
- function releaseEffectDocumentEvent() {
3433
- document.addEventListener = globalEnv.rawDocumentAddEventListener;
3434
- document.removeEventListener = globalEnv.rawDocumentRemoveEventListener;
3435
- }
3565
+
3436
3566
  /**
3437
3567
  * Rewrite side-effect events
3438
3568
  * @param microAppWindow micro window
3439
3569
  */
3440
- function effect(appName, microAppWindow) {
3570
+ function patchWindowEffect(appName, microAppWindow) {
3441
3571
  const eventListenerMap = new Map();
3572
+ const sstEventListenerMap = new Map();
3442
3573
  const intervalIdMap = new Map();
3443
3574
  const timeoutIdMap = new Map();
3444
- const { rawWindow, rawDocument, rawWindowAddEventListener, rawWindowRemoveEventListener, rawSetInterval, rawSetTimeout, rawClearInterval, rawClearTimeout, rawDocumentRemoveEventListener, } = globalEnv;
3575
+ const { rawWindow, rawAddEventListener, rawRemoveEventListener, rawSetInterval, rawSetTimeout, rawClearInterval, rawClearTimeout, } = globalEnv;
3576
+ // TODO: listener 是否需要绑定microAppWindow,否则函数中的this指向原生window
3445
3577
  // listener may be null, e.g test-passive
3446
3578
  microAppWindow.addEventListener = function (type, listener, options) {
3447
3579
  type = formatEventName(type, appName);
@@ -3453,7 +3585,7 @@ function effect(appName, microAppWindow) {
3453
3585
  eventListenerMap.set(type, new Set([listener]));
3454
3586
  }
3455
3587
  listener && (listener.__MICRO_APP_MARK_OPTIONS__ = options);
3456
- rawWindowAddEventListener.call(rawWindow, type, listener, options);
3588
+ rawAddEventListener.call(rawWindow, type, listener, options);
3457
3589
  };
3458
3590
  microAppWindow.removeEventListener = function (type, listener, options) {
3459
3591
  type = formatEventName(type, appName);
@@ -3461,7 +3593,7 @@ function effect(appName, microAppWindow) {
3461
3593
  if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
3462
3594
  listenerList.delete(listener);
3463
3595
  }
3464
- rawWindowRemoveEventListener.call(rawWindow, type, listener, options);
3596
+ rawRemoveEventListener.call(rawWindow, type, listener, options);
3465
3597
  };
3466
3598
  microAppWindow.setInterval = function (handler, timeout, ...args) {
3467
3599
  const intervalId = rawSetInterval.call(rawWindow, handler, timeout, ...args);
@@ -3481,14 +3613,9 @@ function effect(appName, microAppWindow) {
3481
3613
  timeoutIdMap.delete(timeoutId);
3482
3614
  rawClearTimeout.call(rawWindow, timeoutId);
3483
3615
  };
3484
- const sstWindowListenerMap = new Map();
3485
- const sstDocumentListenerMap = new Map();
3486
- let sstOnClickHandler;
3487
3616
  // reset snapshot data
3488
3617
  const reset = () => {
3489
- sstWindowListenerMap.clear();
3490
- sstDocumentListenerMap.clear();
3491
- sstOnClickHandler = null;
3618
+ sstEventListenerMap.clear();
3492
3619
  };
3493
3620
  /**
3494
3621
  * NOTE:
@@ -3504,57 +3631,33 @@ function effect(appName, microAppWindow) {
3504
3631
  // record window event
3505
3632
  eventListenerMap.forEach((listenerList, type) => {
3506
3633
  if (listenerList.size) {
3507
- sstWindowListenerMap.set(type, new Set(listenerList));
3634
+ sstEventListenerMap.set(type, new Set(listenerList));
3508
3635
  }
3509
3636
  });
3510
- // record onclick handler
3511
- sstOnClickHandler = sstOnClickHandler || documentClickListMap.get(appName);
3512
- // record document event
3513
- const documentAppListenersMap = documentEventListenerMap.get(appName);
3514
- if (documentAppListenersMap) {
3515
- documentAppListenersMap.forEach((listenerList, type) => {
3516
- if (listenerList.size) {
3517
- sstDocumentListenerMap.set(type, new Set(listenerList));
3518
- }
3519
- });
3520
- }
3521
3637
  };
3522
3638
  // rebuild event and timer before remount app
3523
3639
  const rebuild = () => {
3524
3640
  // rebuild window event
3525
- sstWindowListenerMap.forEach((listenerList, type) => {
3641
+ sstEventListenerMap.forEach((listenerList, type) => {
3526
3642
  for (const listener of listenerList) {
3527
3643
  microAppWindow.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
3528
3644
  }
3529
3645
  });
3530
- // rebuild onclick event
3531
- sstOnClickHandler && documentClickListMap.set(appName, sstOnClickHandler);
3532
- /**
3533
- * rebuild document event
3534
- * WARNING!!: do not delete setCurrentAppName & removeDomScope
3535
- */
3536
- setCurrentAppName(appName);
3537
- sstDocumentListenerMap.forEach((listenerList, type) => {
3538
- for (const listener of listenerList) {
3539
- document.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
3540
- }
3541
- });
3542
- removeDomScope();
3543
3646
  reset();
3544
3647
  };
3545
3648
  // release all event listener & interval & timeout when unmount app
3546
- const release = ({ umdMode, isPrerender, keepAlive, destroy }) => {
3649
+ const release = (clearTimer) => {
3547
3650
  // Clear window binding events
3548
3651
  if (eventListenerMap.size) {
3549
3652
  eventListenerMap.forEach((listenerList, type) => {
3550
3653
  for (const listener of listenerList) {
3551
- rawWindowRemoveEventListener.call(rawWindow, type, listener);
3654
+ rawRemoveEventListener.call(rawWindow, type, listener);
3552
3655
  }
3553
3656
  });
3554
3657
  eventListenerMap.clear();
3555
3658
  }
3556
3659
  // default mode(not keep-alive or isPrerender)
3557
- if ((!umdMode && !keepAlive && !isPrerender) || destroy) {
3660
+ if (clearTimer) {
3558
3661
  intervalIdMap.forEach((_, intervalId) => {
3559
3662
  rawClearInterval.call(rawWindow, intervalId);
3560
3663
  });
@@ -3564,18 +3667,6 @@ function effect(appName, microAppWindow) {
3564
3667
  intervalIdMap.clear();
3565
3668
  timeoutIdMap.clear();
3566
3669
  }
3567
- // Clear the function bound by micro application through document.onclick
3568
- documentClickListMap.delete(appName);
3569
- // Clear document binding event
3570
- const documentAppListenersMap = documentEventListenerMap.get(appName);
3571
- if (documentAppListenersMap) {
3572
- documentAppListenersMap.forEach((listenerList, type) => {
3573
- for (const listener of listenerList) {
3574
- rawDocumentRemoveEventListener.call(rawDocument, type, listener);
3575
- }
3576
- });
3577
- documentAppListenersMap.clear();
3578
- }
3579
3670
  };
3580
3671
  return {
3581
3672
  reset,
@@ -4788,6 +4879,7 @@ const { createMicroEventSource, clearMicroEventSource } = useMicroEventSource();
4788
4879
  const globalPropertyList$1 = ['window', 'self', 'globalThis'];
4789
4880
  class WithSandBox {
4790
4881
  constructor(appName, url) {
4882
+ this.active = false;
4791
4883
  /**
4792
4884
  * Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
4793
4885
  * Fix https://github.com/micro-zoe/micro-app/issues/234
@@ -4795,24 +4887,20 @@ class WithSandBox {
4795
4887
  this.scopeProperties = [];
4796
4888
  // Properties that can be escape to rawWindow
4797
4889
  this.escapeProperties = [];
4798
- // Properties newly added to microAppWindow
4799
- this.injectedKeys = new Set();
4800
4890
  // Properties escape to rawWindow, cleared when unmount
4801
4891
  this.escapeKeys = new Set();
4802
- // record injected values before the first execution of umdHookMount and rebuild before remount umd app
4803
- // private recordUmdInjectedValues?: Map<PropertyKey, unknown>
4804
- // sandbox state
4805
- this.active = false;
4892
+ // Properties newly added to microAppWindow
4893
+ this.injectedKeys = new Set();
4806
4894
  this.microAppWindow = {}; // Proxy target
4807
4895
  this.adapter = new Adapter();
4808
4896
  // get scopeProperties and escapeProperties from plugins
4809
4897
  this.getSpecialProperties(appName);
4810
- // create proxyWindow with Proxy(microAppWindow)
4811
- this.proxyWindow = this.createProxyWindow(appName);
4812
- // Rewrite global event listener & timeout
4813
- this.effectController = effect(appName, this.microAppWindow);
4898
+ // rewrite window of child app
4899
+ this.windowEffect = this.patchWithWindow(appName, this.microAppWindow);
4900
+ // rewrite document of child app
4901
+ this.documentEffect = this.patchWithDocument(appName, this.microAppWindow);
4814
4902
  // inject global properties
4815
- this.initStaticGlobalKeys(this.microAppWindow, appName, url);
4903
+ this.initStaticGlobalKeys(appName, url, this.microAppWindow);
4816
4904
  }
4817
4905
  /**
4818
4906
  * open sandbox and perform some initial actions
@@ -4823,38 +4911,38 @@ class WithSandBox {
4823
4911
  * @param disablePatchRequest prevent patchRequestApi
4824
4912
  */
4825
4913
  start({ umdMode, baseroute, useMemoryRouter, defaultPage, disablePatchRequest, }) {
4826
- if (!this.active) {
4827
- this.active = true;
4828
- if (useMemoryRouter) {
4829
- if (isUndefined(this.microAppWindow.location)) {
4830
- this.setMicroAppRouter(this.microAppWindow, this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__);
4831
- }
4832
- this.initRouteState(defaultPage);
4833
- // unique listener of popstate event for sub app
4834
- this.removeHistoryListener = addHistoryListener(this.microAppWindow.__MICRO_APP_NAME__);
4835
- }
4836
- else {
4837
- this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute;
4838
- }
4839
- /**
4840
- * Target: Ensure default mode action exactly same to first time when render again
4841
- * 1. The following globalKey maybe modified when render, reset them when render again in default mode
4842
- * 2. Umd mode will not delete any keys during sandBox.stop, ignore umd mode
4843
- * 3. When sandbox.start called for the first time, it must be the default mode
4844
- */
4845
- if (!umdMode) {
4846
- this.initGlobalKeysWhenStart(this.microAppWindow, this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__, disablePatchRequest);
4847
- }
4848
- if (++globalEnv.activeSandbox === 1) {
4849
- patchElementAndDocument();
4850
- patchHistory();
4851
- }
4852
- if (++WithSandBox.activeCount === 1) {
4853
- effectDocumentEvent();
4854
- initEnvOfNestedApp();
4914
+ if (this.active)
4915
+ return;
4916
+ this.active = true;
4917
+ if (useMemoryRouter) {
4918
+ if (isUndefined(this.microAppWindow.location)) {
4919
+ this.setMicroAppRouter(this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__, this.microAppWindow);
4855
4920
  }
4856
- fixBabelPolyfill6();
4921
+ this.initRouteState(defaultPage);
4922
+ // unique listener of popstate event for sub app
4923
+ this.removeHistoryListener = addHistoryListener(this.microAppWindow.__MICRO_APP_NAME__);
4857
4924
  }
4925
+ else {
4926
+ this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute;
4927
+ }
4928
+ /**
4929
+ * Target: Ensure default mode action exactly same to first time when render again
4930
+ * 1. The following globalKey maybe modified when render, reset them when render again in default mode
4931
+ * 2. Umd mode will not delete any keys during sandBox.stop, ignore umd mode
4932
+ * 3. When sandbox.start called for the first time, it must be the default mode
4933
+ */
4934
+ if (!umdMode) {
4935
+ this.initGlobalKeysWhenStart(this.microAppWindow.__MICRO_APP_NAME__, this.microAppWindow.__MICRO_APP_URL__, this.microAppWindow, disablePatchRequest);
4936
+ }
4937
+ if (++globalEnv.activeSandbox === 1) {
4938
+ patchElementAndDocument();
4939
+ patchHistory();
4940
+ }
4941
+ if (++WithSandBox.activeCount === 1) {
4942
+ // effectDocumentEvent()
4943
+ initEnvOfNestedApp();
4944
+ }
4945
+ fixBabelPolyfill6();
4858
4946
  }
4859
4947
  /**
4860
4948
  * close sandbox and perform some clean up actions
@@ -4864,39 +4952,61 @@ class WithSandBox {
4864
4952
  * @param clearData clear data from base app
4865
4953
  */
4866
4954
  stop({ umdMode, keepRouteState, destroy, clearData, }) {
4867
- if (this.active) {
4868
- this.recordAndReleaseEffect({ clearData, destroy }, !umdMode || destroy);
4869
- if (this.removeHistoryListener) {
4870
- this.clearRouteState(keepRouteState);
4871
- // release listener of popstate
4872
- this.removeHistoryListener();
4873
- }
4874
- /**
4875
- * NOTE:
4876
- * 1. injectedKeys and escapeKeys must be placed at the back
4877
- * 2. if key in initial microAppWindow, and then rewrite, this key will be delete from microAppWindow when stop, and lost when restart
4878
- * 3. umd mode will not delete global keys
4879
- */
4880
- if (!umdMode || destroy) {
4881
- clearMicroEventSource(this.microAppWindow.__MICRO_APP_NAME__);
4882
- this.injectedKeys.forEach((key) => {
4883
- Reflect.deleteProperty(this.microAppWindow, key);
4884
- });
4885
- this.injectedKeys.clear();
4886
- this.escapeKeys.forEach((key) => {
4887
- Reflect.deleteProperty(globalEnv.rawWindow, key);
4888
- });
4889
- this.escapeKeys.clear();
4890
- }
4891
- if (--globalEnv.activeSandbox === 0) {
4892
- releasePatchElementAndDocument();
4893
- releasePatchHistory();
4894
- }
4895
- if (--WithSandBox.activeCount === 0) {
4896
- releaseEffectDocumentEvent();
4897
- }
4898
- this.active = false;
4955
+ if (!this.active)
4956
+ return;
4957
+ this.recordAndReleaseEffect({ umdMode, clearData, destroy }, !umdMode || destroy);
4958
+ if (this.removeHistoryListener) {
4959
+ this.clearRouteState(keepRouteState);
4960
+ // release listener of popstate
4961
+ this.removeHistoryListener();
4962
+ }
4963
+ /**
4964
+ * NOTE:
4965
+ * 1. injectedKeys and escapeKeys must be placed at the back
4966
+ * 2. if key in initial microAppWindow, and then rewrite, this key will be delete from microAppWindow when stop, and lost when restart
4967
+ * 3. umd mode will not delete global keys
4968
+ */
4969
+ if (!umdMode || destroy) {
4970
+ clearMicroEventSource(this.microAppWindow.__MICRO_APP_NAME__);
4971
+ this.injectedKeys.forEach((key) => {
4972
+ Reflect.deleteProperty(this.microAppWindow, key);
4973
+ });
4974
+ this.injectedKeys.clear();
4975
+ this.escapeKeys.forEach((key) => {
4976
+ Reflect.deleteProperty(globalEnv.rawWindow, key);
4977
+ });
4978
+ this.escapeKeys.clear();
4979
+ }
4980
+ if (--globalEnv.activeSandbox === 0) {
4981
+ releasePatchElementAndDocument();
4982
+ releasePatchHistory();
4899
4983
  }
4984
+ if (--WithSandBox.activeCount === 0) ;
4985
+ this.active = false;
4986
+ }
4987
+ /**
4988
+ * inject global properties to microAppWindow
4989
+ * @param appName app name
4990
+ * @param url app url
4991
+ * @param microAppWindow micro window
4992
+ */
4993
+ initStaticGlobalKeys(appName, url, microAppWindow) {
4994
+ microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
4995
+ microAppWindow.__MICRO_APP_NAME__ = appName;
4996
+ microAppWindow.__MICRO_APP_URL__ = url;
4997
+ microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
4998
+ microAppWindow.__MICRO_APP_BASE_ROUTE__ = '';
4999
+ microAppWindow.__MICRO_APP_WINDOW__ = microAppWindow;
5000
+ microAppWindow.__MICRO_APP_PRE_RENDER__ = false;
5001
+ microAppWindow.__MICRO_APP_UMD_MODE__ = false;
5002
+ microAppWindow.rawWindow = globalEnv.rawWindow;
5003
+ microAppWindow.rawDocument = globalEnv.rawDocument;
5004
+ microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
5005
+ removeDomScope,
5006
+ pureCreateElement,
5007
+ router,
5008
+ });
5009
+ this.setMappingPropertiesWithRawDescriptor(microAppWindow);
4900
5010
  }
4901
5011
  /**
4902
5012
  * Record global effect and then release (effect: global event, timeout, data listener)
@@ -4927,7 +5037,8 @@ class WithSandBox {
4927
5037
  * 2. unmount prerender app manually
4928
5038
  */
4929
5039
  resetEffectSnapshot() {
4930
- this.effectController.reset();
5040
+ this.windowEffect.reset();
5041
+ this.documentEffect.reset();
4931
5042
  resetDataCenterSnapshot(this.microAppWindow.microApp);
4932
5043
  }
4933
5044
  /**
@@ -4938,20 +5049,14 @@ class WithSandBox {
4938
5049
  * 3. after init prerender app
4939
5050
  */
4940
5051
  recordEffectSnapshot() {
4941
- // this.microAppWindow.__MICRO_APP_UMD_MODE__ = true
4942
- this.effectController.record();
5052
+ this.windowEffect.record();
5053
+ this.documentEffect.record();
4943
5054
  recordDataCenterSnapshot(this.microAppWindow.microApp);
4944
- // this.recordUmdInjectedValues = new Map<PropertyKey, unknown>()
4945
- // this.injectedKeys.forEach((key: PropertyKey) => {
4946
- // this.recordUmdInjectedValues!.set(key, Reflect.get(this.microAppWindow, key))
4947
- // })
4948
5055
  }
4949
5056
  // rebuild umd snapshot before remount umd app
4950
5057
  rebuildEffectSnapshot() {
4951
- // this.recordUmdInjectedValues!.forEach((value: unknown, key: PropertyKey) => {
4952
- // Reflect.set(this.proxyWindow, key, value)
4953
- // })
4954
- this.effectController.rebuild();
5058
+ this.windowEffect.rebuild();
5059
+ this.documentEffect.rebuild();
4955
5060
  rebuildDataCenterSnapshot(this.microAppWindow.microApp);
4956
5061
  }
4957
5062
  /**
@@ -4960,19 +5065,17 @@ class WithSandBox {
4960
5065
  * 1. unmount of default/umd app
4961
5066
  * 2. hidden keep-alive app
4962
5067
  * 3. after init prerender app
5068
+ * @param umdMode is umd mode
4963
5069
  * @param clearData clear data from base app
4964
5070
  * @param isPrerender is prerender app
4965
5071
  * @param keepAlive is keep-alive app
4966
5072
  * @param destroy completely destroy
4967
5073
  */
4968
- releaseGlobalEffect({ clearData = false, isPrerender = false, keepAlive = false, destroy = false, }) {
5074
+ releaseGlobalEffect({ umdMode = false, clearData = false, isPrerender = false, keepAlive = false, destroy = false, }) {
4969
5075
  var _a, _b, _c;
4970
- this.effectController.release({
4971
- umdMode: this.proxyWindow.__MICRO_APP_UMD_MODE__,
4972
- isPrerender,
4973
- keepAlive,
4974
- destroy,
4975
- });
5076
+ // default mode(not keep-alive or isPrerender)
5077
+ this.windowEffect.release((!umdMode && !keepAlive && !isPrerender) || destroy);
5078
+ this.documentEffect.release();
4976
5079
  (_a = this.microAppWindow.microApp) === null || _a === void 0 ? void 0 : _a.clearDataListener();
4977
5080
  (_b = this.microAppWindow.microApp) === null || _b === void 0 ? void 0 : _b.clearGlobalDataListener();
4978
5081
  if (clearData) {
@@ -5008,10 +5111,10 @@ class WithSandBox {
5008
5111
  }
5009
5112
  }
5010
5113
  // create proxyWindow with Proxy(microAppWindow)
5011
- createProxyWindow(appName) {
5114
+ createProxyWindow(appName, microAppWindow) {
5012
5115
  const rawWindow = globalEnv.rawWindow;
5013
5116
  const descriptorTargetMap = new Map();
5014
- return new Proxy(this.microAppWindow, {
5117
+ return new Proxy(microAppWindow, {
5015
5118
  get: (target, key) => {
5016
5119
  throttleDeferForSetAppName(appName);
5017
5120
  if (Reflect.has(target, key) ||
@@ -5021,41 +5124,39 @@ class WithSandBox {
5021
5124
  return bindFunctionToRawTarget(Reflect.get(rawWindow, key), rawWindow);
5022
5125
  },
5023
5126
  set: (target, key, value) => {
5024
- if (this.active) {
5025
- /**
5026
- * TODO:
5027
- * 1、location域名相同,子应用内部跳转时的处理
5028
- */
5029
- if (this.adapter.escapeSetterKeyList.includes(key)) {
5030
- Reflect.set(rawWindow, key, value);
5031
- }
5032
- else if (
5033
- // target.hasOwnProperty has been rewritten
5034
- !rawHasOwnProperty.call(target, key) &&
5035
- rawHasOwnProperty.call(rawWindow, key) &&
5036
- !this.scopeProperties.includes(key)) {
5037
- const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
5038
- const { configurable, enumerable, writable, set } = descriptor;
5039
- // set value because it can be set
5040
- rawDefineProperty(target, key, {
5041
- value,
5042
- configurable,
5043
- enumerable,
5044
- writable: writable !== null && writable !== void 0 ? writable : !!set,
5045
- });
5046
- this.injectedKeys.add(key);
5047
- }
5048
- else {
5049
- !Reflect.has(target, key) && this.injectedKeys.add(key);
5050
- Reflect.set(target, key, value);
5051
- }
5052
- if ((this.escapeProperties.includes(key) ||
5053
- (this.adapter.staticEscapeProperties.includes(key) &&
5054
- !Reflect.has(rawWindow, key))) &&
5055
- !this.scopeProperties.includes(key)) {
5056
- !Reflect.has(rawWindow, key) && this.escapeKeys.add(key);
5057
- Reflect.set(rawWindow, key, value);
5058
- }
5127
+ /**
5128
+ * TODO:
5129
+ * 1、location域名相同,子应用内部跳转时的处理
5130
+ */
5131
+ if (this.adapter.escapeSetterKeyList.includes(key)) {
5132
+ Reflect.set(rawWindow, key, value);
5133
+ }
5134
+ else if (
5135
+ // target.hasOwnProperty has been rewritten
5136
+ !rawHasOwnProperty.call(target, key) &&
5137
+ rawHasOwnProperty.call(rawWindow, key) &&
5138
+ !this.scopeProperties.includes(key)) {
5139
+ const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
5140
+ const { configurable, enumerable, writable, set } = descriptor;
5141
+ // set value because it can be set
5142
+ rawDefineProperty(target, key, {
5143
+ value,
5144
+ configurable,
5145
+ enumerable,
5146
+ writable: writable !== null && writable !== void 0 ? writable : !!set,
5147
+ });
5148
+ this.injectedKeys.add(key);
5149
+ }
5150
+ else {
5151
+ !Reflect.has(target, key) && this.injectedKeys.add(key);
5152
+ Reflect.set(target, key, value);
5153
+ }
5154
+ if ((this.escapeProperties.includes(key) ||
5155
+ (this.adapter.staticEscapeProperties.includes(key) &&
5156
+ !Reflect.has(rawWindow, key))) &&
5157
+ !this.scopeProperties.includes(key)) {
5158
+ !Reflect.has(rawWindow, key) && this.escapeKeys.add(key);
5159
+ Reflect.set(rawWindow, key, value);
5059
5160
  }
5060
5161
  return true;
5061
5162
  },
@@ -5102,40 +5203,24 @@ class WithSandBox {
5102
5203
  },
5103
5204
  });
5104
5205
  }
5105
- // set __MICRO_APP_PRE_RENDER__ state
5106
- setPreRenderState(state) {
5107
- this.microAppWindow.__MICRO_APP_PRE_RENDER__ = state;
5108
- }
5109
- markUmdMode(state) {
5110
- this.microAppWindow.__MICRO_APP_UMD_MODE__ = state;
5111
- }
5112
5206
  /**
5113
- * inject global properties to microAppWindow
5114
- * @param microAppWindow micro window
5207
+ * create proxyWindow, rewrite window event & timer of child app
5115
5208
  * @param appName app name
5116
- * @param url app url
5117
- * @param useMemoryRouter whether use memory router
5209
+ * @param microAppWindow Proxy target
5118
5210
  */
5119
- initStaticGlobalKeys(microAppWindow, appName, url) {
5120
- microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
5121
- microAppWindow.__MICRO_APP_NAME__ = appName;
5122
- microAppWindow.__MICRO_APP_URL__ = url;
5123
- microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
5124
- microAppWindow.__MICRO_APP_WINDOW__ = microAppWindow;
5125
- microAppWindow.__MICRO_APP_PRE_RENDER__ = false;
5126
- microAppWindow.__MICRO_APP_UMD_MODE__ = false;
5127
- microAppWindow.rawWindow = globalEnv.rawWindow;
5128
- microAppWindow.rawDocument = globalEnv.rawDocument;
5129
- microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
5130
- removeDomScope,
5131
- pureCreateElement,
5132
- router,
5133
- });
5134
- this.setProxyDocument(microAppWindow, appName);
5135
- this.setMappingPropertiesWithRawDescriptor(microAppWindow);
5211
+ patchWithWindow(appName, microAppWindow) {
5212
+ // create proxyWindow with Proxy(microAppWindow)
5213
+ this.proxyWindow = this.createProxyWindow(appName, microAppWindow);
5214
+ // rewrite global event & timeout of window
5215
+ return patchWindowEffect(appName, microAppWindow);
5136
5216
  }
5137
- setProxyDocument(microAppWindow, appName) {
5138
- const { proxyDocument, MicroDocument } = this.createProxyDocument(appName);
5217
+ /**
5218
+ * create proxyDocument and MicroDocument, rewrite document of child app
5219
+ * @param appName app name
5220
+ * @param microAppWindow Proxy target
5221
+ */
5222
+ patchWithDocument(appName, microAppWindow) {
5223
+ const { proxyDocument, MicroDocument, documentEffect } = createProxyDocument(appName, this);
5139
5224
  rawDefineProperties(microAppWindow, {
5140
5225
  document: {
5141
5226
  configurable: false,
@@ -5154,6 +5239,14 @@ class WithSandBox {
5154
5239
  },
5155
5240
  }
5156
5241
  });
5242
+ return documentEffect;
5243
+ }
5244
+ // set __MICRO_APP_PRE_RENDER__ state
5245
+ setPreRenderState(state) {
5246
+ this.microAppWindow.__MICRO_APP_PRE_RENDER__ = state;
5247
+ }
5248
+ markUmdMode(state) {
5249
+ this.microAppWindow.__MICRO_APP_UMD_MODE__ = state;
5157
5250
  }
5158
5251
  // properties associated with the native window
5159
5252
  setMappingPropertiesWithRawDescriptor(microAppWindow) {
@@ -5166,6 +5259,7 @@ class WithSandBox {
5166
5259
  topValue = rawWindow.top;
5167
5260
  parentValue = rawWindow.parent;
5168
5261
  }
5262
+ // TODO: 用rawDefineProperties
5169
5263
  rawDefineProperty(microAppWindow, 'top', this.createDescriptorForMicroAppWindow('top', topValue));
5170
5264
  rawDefineProperty(microAppWindow, 'parent', this.createDescriptorForMicroAppWindow('parent', parentValue));
5171
5265
  globalPropertyList$1.forEach((key) => {
@@ -5189,15 +5283,15 @@ class WithSandBox {
5189
5283
  * @param url app url
5190
5284
  * @param disablePatchRequest prevent rewrite request method of child app
5191
5285
  */
5192
- initGlobalKeysWhenStart(microAppWindow, appName, url, disablePatchRequest) {
5286
+ initGlobalKeysWhenStart(appName, url, microAppWindow, disablePatchRequest) {
5193
5287
  microAppWindow.hasOwnProperty = (key) => rawHasOwnProperty.call(microAppWindow, key) || rawHasOwnProperty.call(globalEnv.rawWindow, key);
5194
- this.setHijackProperty(microAppWindow, appName);
5288
+ this.setHijackProperty(appName, microAppWindow);
5195
5289
  if (!disablePatchRequest)
5196
- this.patchRequestApi(microAppWindow, appName, url);
5290
+ this.patchRequestApi(appName, url, microAppWindow);
5197
5291
  this.setScopeProperties(microAppWindow);
5198
5292
  }
5199
5293
  // set hijack Properties to microAppWindow
5200
- setHijackProperty(microAppWindow, appName) {
5294
+ setHijackProperty(appName, microAppWindow) {
5201
5295
  let modifiedEval, modifiedImage;
5202
5296
  rawDefineProperties(microAppWindow, {
5203
5297
  eval: {
@@ -5225,7 +5319,7 @@ class WithSandBox {
5225
5319
  });
5226
5320
  }
5227
5321
  // rewrite fetch, XMLHttpRequest, EventSource
5228
- patchRequestApi(microAppWindow, appName, url) {
5322
+ patchRequestApi(appName, url, microAppWindow) {
5229
5323
  let microFetch = createMicroFetch(url);
5230
5324
  let microXMLHttpRequest = createMicroXMLHttpRequest(url);
5231
5325
  let microEventSource = createMicroEventSource(appName, url);
@@ -5275,7 +5369,7 @@ class WithSandBox {
5275
5369
  });
5276
5370
  }
5277
5371
  // set location & history for memory router
5278
- setMicroAppRouter(microAppWindow, appName, url) {
5372
+ setMicroAppRouter(appName, url, microAppWindow) {
5279
5373
  const { microLocation, microHistory } = createMicroRouter(appName, url);
5280
5374
  rawDefineProperties(microAppWindow, {
5281
5375
  location: {
@@ -5325,85 +5419,26 @@ class WithSandBox {
5325
5419
  actionBeforeExecScripts(container) {
5326
5420
  this.patchStaticElement(container);
5327
5421
  }
5328
- /**
5329
- * Create new document and Document
5330
- */
5331
- createProxyDocument(appName) {
5332
- const rawDocument = globalEnv.rawDocument;
5333
- const rawRootDocument = globalEnv.rawRootDocument;
5334
- const createElement = function (tagName, options) {
5335
- const element = globalEnv.rawCreateElement.call(rawDocument, tagName, options);
5336
- element.__MICRO_APP_NAME__ = appName;
5337
- return element;
5338
- };
5339
- const proxyDocument = new Proxy(rawDocument, {
5340
- get: (target, key) => {
5341
- throttleDeferForSetAppName(appName);
5342
- throttleDeferForParentNode(proxyDocument);
5343
- if (key === 'createElement')
5344
- return createElement;
5345
- if (key === Symbol.toStringTag)
5346
- return 'ProxyDocument';
5347
- if (key === 'defaultView')
5348
- return this.proxyWindow;
5349
- return bindFunctionToRawTarget(Reflect.get(target, key), rawDocument, 'DOCUMENT');
5350
- },
5351
- set: (target, key, value) => {
5352
- /**
5353
- * 1. Fix TypeError: Illegal invocation when set document.title
5354
- * 2. If the set method returns false, and the assignment happened in strict-mode code, a TypeError will be thrown.
5355
- */
5356
- Reflect.set(target, key, value);
5357
- return true;
5358
- }
5359
- });
5360
- class MicroDocument {
5361
- static [Symbol.hasInstance](target) {
5362
- let proto = target;
5363
- while (proto = Object.getPrototypeOf(proto)) {
5364
- if (proto === MicroDocument.prototype) {
5365
- return true;
5366
- }
5367
- }
5368
- return (target === proxyDocument ||
5369
- target instanceof rawRootDocument);
5370
- }
5371
- }
5372
- /**
5373
- * TIP:
5374
- * 1. child class __proto__, which represents the inherit of the constructor, always points to the parent class
5375
- * 2. child class prototype.__proto__, which represents the inherit of methods, always points to parent class prototype
5376
- * e.g.
5377
- * class B extends A {}
5378
- * B.__proto__ === A // true
5379
- * B.prototype.__proto__ === A.prototype // true
5380
- */
5381
- Object.setPrototypeOf(MicroDocument, rawRootDocument);
5382
- // Object.create(rawRootDocument.prototype) will cause MicroDocument and proxyDocument methods not same when exec Document.prototype.xxx = xxx in child app
5383
- Object.setPrototypeOf(MicroDocument.prototype, new Proxy(rawRootDocument.prototype, {
5384
- get(target, key) {
5385
- throttleDeferForSetAppName(appName);
5386
- return bindFunctionToRawTarget(Reflect.get(target, key), rawDocument, 'DOCUMENT');
5387
- },
5388
- set(target, key, value) {
5389
- Reflect.set(target, key, value);
5390
- return true;
5391
- }
5392
- }));
5393
- return {
5394
- proxyDocument,
5395
- MicroDocument,
5396
- };
5397
- }
5398
5422
  }
5399
5423
  WithSandBox.activeCount = 0; // number of active sandbox
5400
5424
 
5401
- function patchIframeRoute(appName, microAppWindow, childFullPath) {
5425
+ function patchIframeRoute(appName, url, microAppWindow, browserHost) {
5426
+ const childStaticLocation = new URL(url);
5427
+ const childHost = childStaticLocation.protocol + '//' + childStaticLocation.host;
5428
+ const childFullPath = childStaticLocation.pathname + childStaticLocation.search + childStaticLocation.hash;
5429
+ // rewrite microAppWindow.history
5402
5430
  const microHistory = microAppWindow.history;
5403
5431
  microAppWindow.rawReplaceState = microHistory.replaceState;
5404
5432
  assign(microHistory, createMicroHistory(appName, microAppWindow.location));
5405
- // exec updateMicroLocation after patch microHistory
5433
+ /**
5434
+ * Init microLocation before exec sandbox.start (sandbox.start will sync microLocation info to browser url)
5435
+ * NOTE:
5436
+ * 1. exec updateMicroLocation after patch microHistory
5437
+ * 2.
5438
+ */
5406
5439
  updateMicroLocation(appName, childFullPath, microAppWindow.location, 'prevent');
5440
+ // create proxyLocation
5441
+ return createMicroLocation(appName, url, microAppWindow, childStaticLocation, browserHost, childHost);
5407
5442
  }
5408
5443
 
5409
5444
  function patchIframeWindow(appName, microAppWindow) {
@@ -5469,15 +5504,16 @@ function patchIframeWindow(appName, microAppWindow) {
5469
5504
  logWarn(e, appName);
5470
5505
  }
5471
5506
  });
5472
- return windowEffect(microAppWindow);
5507
+ return patchWindowEffect$1(microAppWindow);
5473
5508
  }
5474
- function windowEffect(microAppWindow) {
5475
- const { rawWindow, rawAddEventListener, rawRemoveEventListener, } = globalEnv;
5509
+ function patchWindowEffect$1(microAppWindow) {
5510
+ const { rawWindow, rawAddEventListener, rawRemoveEventListener } = globalEnv;
5476
5511
  const eventListenerMap = new Map();
5477
- const sstWindowListenerMap = new Map();
5512
+ const sstEventListenerMap = new Map();
5478
5513
  function getEventTarget(type) {
5479
5514
  return scopeIframeWindowEvent.includes(type) ? microAppWindow : rawWindow;
5480
5515
  }
5516
+ // TODO: listener 是否需要绑定microAppWindow,否则函数中的this指向原生window
5481
5517
  microAppWindow.addEventListener = function (type, listener, options) {
5482
5518
  const listenerList = eventListenerMap.get(type);
5483
5519
  if (listenerList) {
@@ -5497,7 +5533,7 @@ function windowEffect(microAppWindow) {
5497
5533
  rawRemoveEventListener.call(getEventTarget(type), type, listener, options);
5498
5534
  };
5499
5535
  const reset = () => {
5500
- sstWindowListenerMap.clear();
5536
+ sstEventListenerMap.clear();
5501
5537
  };
5502
5538
  /**
5503
5539
  * NOTE:
@@ -5515,14 +5551,14 @@ function windowEffect(microAppWindow) {
5515
5551
  // record window event
5516
5552
  eventListenerMap.forEach((listenerList, type) => {
5517
5553
  if (listenerList.size) {
5518
- sstWindowListenerMap.set(type, new Set(listenerList));
5554
+ sstEventListenerMap.set(type, new Set(listenerList));
5519
5555
  }
5520
5556
  });
5521
5557
  };
5522
5558
  // rebuild event and timer before remount app
5523
5559
  const rebuild = () => {
5524
5560
  // rebuild window event
5525
- sstWindowListenerMap.forEach((listenerList, type) => {
5561
+ sstEventListenerMap.forEach((listenerList, type) => {
5526
5562
  for (const listener of listenerList) {
5527
5563
  microAppWindow.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
5528
5564
  }
@@ -5556,18 +5592,26 @@ function windowEffect(microAppWindow) {
5556
5592
  function patchIframeDocument(appName, microAppWindow, proxyLocation) {
5557
5593
  patchDocumentPrototype(appName, microAppWindow);
5558
5594
  patchDocumentProperties(appName, microAppWindow, proxyLocation);
5559
- return documentEffect(appName, microAppWindow);
5595
+ return patchDocumentEffect(appName, microAppWindow);
5560
5596
  }
5561
5597
  function patchDocumentPrototype(appName, microAppWindow) {
5562
5598
  const rawDocument = globalEnv.rawDocument;
5563
5599
  const microRootDocument = microAppWindow.Document;
5564
5600
  const microDocument = microAppWindow.document;
5601
+ const rawMicroCreateElement = microRootDocument.prototype.createElement;
5602
+ const rawMicroCreateTextNode = microRootDocument.prototype.createTextNode;
5603
+ const rawMicroQuerySelector = microRootDocument.prototype.querySelector;
5604
+ const rawMicroQuerySelectorAll = microRootDocument.prototype.querySelectorAll;
5605
+ const rawMicroGetElementById = microRootDocument.prototype.getElementById;
5606
+ const rawMicroGetElementsByClassName = microRootDocument.prototype.getElementsByClassName;
5607
+ const rawMicroGetElementsByTagName = microRootDocument.prototype.getElementsByTagName;
5608
+ const rawMicroGetElementsByName = microRootDocument.prototype.getElementsByName;
5565
5609
  microRootDocument.prototype.createElement = function createElement(tagName, options) {
5566
- const element = globalEnv.rawCreateElement.call(this, tagName, options);
5610
+ const element = rawMicroCreateElement.call(this, tagName, options);
5567
5611
  return updateElementInfo(element, appName);
5568
5612
  };
5569
5613
  microRootDocument.prototype.createTextNode = function createTextNode(data) {
5570
- const element = globalEnv.rawCreateTextNode.call(this, data);
5614
+ const element = rawMicroCreateTextNode.call(this, data);
5571
5615
  return updateElementInfo(element, appName);
5572
5616
  };
5573
5617
  function getDefaultRawTarget(target) {
@@ -5576,19 +5620,21 @@ function patchDocumentPrototype(appName, microAppWindow) {
5576
5620
  // query element👇
5577
5621
  function querySelector(selectors) {
5578
5622
  var _a, _b;
5579
- if (isUniqueElement(selectors) ||
5623
+ if (!selectors ||
5624
+ isUniqueElement(selectors) ||
5580
5625
  microDocument !== this) {
5581
5626
  const _this = getDefaultRawTarget(this);
5582
- return globalEnv.rawQuerySelector.call(_this, selectors);
5627
+ return rawMicroQuerySelector.call(_this, selectors);
5583
5628
  }
5584
5629
  return (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.querySelector(selectors)) !== null && _b !== void 0 ? _b : null;
5585
5630
  }
5586
5631
  function querySelectorAll(selectors) {
5587
5632
  var _a, _b;
5588
- if (isUniqueElement(selectors) ||
5633
+ if (!selectors ||
5634
+ isUniqueElement(selectors) ||
5589
5635
  microDocument !== this) {
5590
5636
  const _this = getDefaultRawTarget(this);
5591
- return globalEnv.rawQuerySelectorAll.call(_this, selectors);
5637
+ return rawMicroQuerySelectorAll.call(_this, selectors);
5592
5638
  }
5593
5639
  return (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.querySelectorAll(selectors)) !== null && _b !== void 0 ? _b : [];
5594
5640
  }
@@ -5597,25 +5643,25 @@ function patchDocumentPrototype(appName, microAppWindow) {
5597
5643
  microRootDocument.prototype.getElementById = function getElementById(key) {
5598
5644
  const _this = getDefaultRawTarget(this);
5599
5645
  if (isInvalidQuerySelectorKey(key)) {
5600
- return globalEnv.rawGetElementById.call(_this, key);
5646
+ return rawMicroGetElementById.call(_this, key);
5601
5647
  }
5602
5648
  try {
5603
5649
  return querySelector.call(this, `#${key}`);
5604
5650
  }
5605
5651
  catch (_a) {
5606
- return globalEnv.rawGetElementById.call(_this, key);
5652
+ return rawMicroGetElementById.call(_this, key);
5607
5653
  }
5608
5654
  };
5609
5655
  microRootDocument.prototype.getElementsByClassName = function getElementsByClassName(key) {
5610
5656
  const _this = getDefaultRawTarget(this);
5611
5657
  if (isInvalidQuerySelectorKey(key)) {
5612
- return globalEnv.rawGetElementsByClassName.call(_this, key);
5658
+ return rawMicroGetElementsByClassName.call(_this, key);
5613
5659
  }
5614
5660
  try {
5615
5661
  return querySelectorAll.call(this, `.${key}`);
5616
5662
  }
5617
5663
  catch (_a) {
5618
- return globalEnv.rawGetElementsByClassName.call(_this, key);
5664
+ return rawMicroGetElementsByClassName.call(_this, key);
5619
5665
  }
5620
5666
  };
5621
5667
  microRootDocument.prototype.getElementsByTagName = function getElementsByTagName(key) {
@@ -5624,25 +5670,25 @@ function patchDocumentPrototype(appName, microAppWindow) {
5624
5670
  if (isUniqueElement(key) ||
5625
5671
  isInvalidQuerySelectorKey(key) ||
5626
5672
  (!((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.inline) && /^script$/i.test(key))) {
5627
- return globalEnv.rawGetElementsByTagName.call(_this, key);
5673
+ return rawMicroGetElementsByTagName.call(_this, key);
5628
5674
  }
5629
5675
  try {
5630
5676
  return querySelectorAll.call(this, key);
5631
5677
  }
5632
5678
  catch (_b) {
5633
- return globalEnv.rawGetElementsByTagName.call(_this, key);
5679
+ return rawMicroGetElementsByTagName.call(_this, key);
5634
5680
  }
5635
5681
  };
5636
5682
  microRootDocument.prototype.getElementsByName = function getElementsByName(key) {
5637
5683
  const _this = getDefaultRawTarget(this);
5638
5684
  if (isInvalidQuerySelectorKey(key)) {
5639
- return globalEnv.rawGetElementsByName.call(_this, key);
5685
+ return rawMicroGetElementsByName.call(_this, key);
5640
5686
  }
5641
5687
  try {
5642
5688
  return querySelectorAll.call(this, `[name=${key}]`);
5643
5689
  }
5644
5690
  catch (_a) {
5645
- return globalEnv.rawGetElementsByName.call(_this, key);
5691
+ return rawMicroGetElementsByName.call(_this, key);
5646
5692
  }
5647
5693
  };
5648
5694
  }
@@ -5698,46 +5744,37 @@ function patchDocumentProperties(appName, microAppWindow, proxyLocation) {
5698
5744
  enumerable: true,
5699
5745
  configurable: true,
5700
5746
  get: () => rawDocument[tagName],
5701
- set: undefined,
5747
+ set: (value) => { rawDocument[tagName] = value; },
5702
5748
  });
5703
5749
  });
5704
5750
  }
5705
- function documentEffect(appName, microAppWindow) {
5706
- const documentEventListenerMap = new Map();
5707
- const sstDocumentListenerMap = new Map();
5751
+ function patchDocumentEffect(appName, microAppWindow) {
5752
+ const { rawDocument, rawAddEventListener, rawRemoveEventListener } = globalEnv;
5753
+ const eventListenerMap = new Map();
5754
+ const sstEventListenerMap = new Map();
5708
5755
  let onClickHandler = null;
5709
5756
  let sstOnClickHandler = null;
5710
5757
  const microRootDocument = microAppWindow.Document;
5711
5758
  const microDocument = microAppWindow.document;
5712
- const { rawDocument, rawAddEventListener, rawRemoveEventListener, } = globalEnv;
5713
5759
  function getEventTarget(type, bindTarget) {
5714
5760
  return scopeIframeDocumentEvent.includes(type) ? bindTarget : rawDocument;
5715
5761
  }
5716
5762
  microRootDocument.prototype.addEventListener = function (type, listener, options) {
5717
5763
  const handler = isFunction(listener) ? (listener.__MICRO_APP_BOUND_FUNCTION__ = listener.__MICRO_APP_BOUND_FUNCTION__ || listener.bind(this)) : listener;
5718
- const appListenersMap = documentEventListenerMap.get(appName);
5719
- if (appListenersMap) {
5720
- const appListenerList = appListenersMap.get(type);
5721
- if (appListenerList) {
5722
- appListenerList.add(listener);
5723
- }
5724
- else {
5725
- appListenersMap.set(type, new Set([listener]));
5726
- }
5764
+ const listenerList = eventListenerMap.get(type);
5765
+ if (listenerList) {
5766
+ listenerList.add(listener);
5727
5767
  }
5728
5768
  else {
5729
- documentEventListenerMap.set(appName, new Map([[type, new Set([listener])]]));
5769
+ eventListenerMap.set(type, new Set([listener]));
5730
5770
  }
5731
5771
  listener && (listener.__MICRO_APP_MARK_OPTIONS__ = options);
5732
5772
  rawAddEventListener.call(getEventTarget(type, this), type, handler, options);
5733
5773
  };
5734
5774
  microRootDocument.prototype.removeEventListener = function (type, listener, options) {
5735
- const appListenersMap = documentEventListenerMap.get(appName);
5736
- if (appListenersMap) {
5737
- const appListenerList = appListenersMap.get(type);
5738
- if ((appListenerList === null || appListenerList === void 0 ? void 0 : appListenerList.size) && appListenerList.has(listener)) {
5739
- appListenerList.delete(listener);
5740
- }
5775
+ const listenerList = eventListenerMap.get(type);
5776
+ if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
5777
+ listenerList.delete(listener);
5741
5778
  }
5742
5779
  const handler = (listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_BOUND_FUNCTION__) || listener;
5743
5780
  rawRemoveEventListener.call(getEventTarget(type, this), type, handler, options);
@@ -5789,7 +5826,7 @@ function documentEffect(appName, microAppWindow) {
5789
5826
  }
5790
5827
  });
5791
5828
  const reset = () => {
5792
- sstDocumentListenerMap.clear();
5829
+ sstEventListenerMap.clear();
5793
5830
  sstOnClickHandler = null;
5794
5831
  };
5795
5832
  /**
@@ -5805,21 +5842,18 @@ function documentEffect(appName, microAppWindow) {
5805
5842
  // record onclick handler
5806
5843
  sstOnClickHandler = sstOnClickHandler || onClickHandler;
5807
5844
  // record document event
5808
- const documentAppListenersMap = documentEventListenerMap.get(appName);
5809
- if (documentAppListenersMap) {
5810
- documentAppListenersMap.forEach((listenerList, type) => {
5811
- if (listenerList.size) {
5812
- sstDocumentListenerMap.set(type, new Set(listenerList));
5813
- }
5814
- });
5815
- }
5845
+ eventListenerMap.forEach((listenerList, type) => {
5846
+ if (listenerList.size) {
5847
+ sstEventListenerMap.set(type, new Set(listenerList));
5848
+ }
5849
+ });
5816
5850
  };
5817
5851
  // rebuild event and timer before remount app
5818
5852
  const rebuild = () => {
5819
5853
  // rebuild onclick event
5820
5854
  if (sstOnClickHandler)
5821
5855
  microDocument.onclick = sstOnClickHandler;
5822
- sstDocumentListenerMap.forEach((listenerList, type) => {
5856
+ sstEventListenerMap.forEach((listenerList, type) => {
5823
5857
  for (const listener of listenerList) {
5824
5858
  microDocument.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_MARK_OPTIONS__);
5825
5859
  }
@@ -5827,20 +5861,19 @@ function documentEffect(appName, microAppWindow) {
5827
5861
  reset();
5828
5862
  };
5829
5863
  const release = () => {
5830
- // Clear the function bound by micro application through document.onclick
5864
+ // Clear the function bound by micro app through document.onclick
5831
5865
  if (isFunction(onClickHandler)) {
5832
5866
  rawRemoveEventListener.call(rawDocument, 'click', onClickHandler);
5833
5867
  onClickHandler = null;
5834
5868
  }
5835
5869
  // Clear document binding event
5836
- const documentAppListenersMap = documentEventListenerMap.get(appName);
5837
- if (documentAppListenersMap) {
5838
- documentAppListenersMap.forEach((listenerList, type) => {
5870
+ if (eventListenerMap.size) {
5871
+ eventListenerMap.forEach((listenerList, type) => {
5839
5872
  for (const listener of listenerList) {
5840
5873
  rawRemoveEventListener.call(getEventTarget(type, microDocument), type, (listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_BOUND_FUNCTION__) || listener);
5841
5874
  }
5842
5875
  });
5843
- documentAppListenersMap.clear();
5876
+ eventListenerMap.clear();
5844
5877
  }
5845
5878
  };
5846
5879
  return {
@@ -5851,33 +5884,38 @@ function documentEffect(appName, microAppWindow) {
5851
5884
  };
5852
5885
  }
5853
5886
 
5854
- function patchIframeElement(appName, url, microAppWindow, iframeSandbox) {
5855
- patchIframeNode(appName, microAppWindow, iframeSandbox);
5887
+ function patchIframeElement(appName, url, microAppWindow, sandbox) {
5888
+ patchIframeNode(appName, microAppWindow, sandbox);
5856
5889
  patchIframeAttribute(appName, url, microAppWindow);
5857
5890
  }
5858
- function patchIframeNode(appName, microAppWindow, iframeSandbox) {
5859
- const microDocument = microAppWindow.document;
5891
+ function patchIframeNode(appName, microAppWindow, sandbox) {
5892
+ const rawRootElement = globalEnv.rawRootElement; // native root Element
5860
5893
  const rawDocument = globalEnv.rawDocument;
5894
+ const microDocument = microAppWindow.document;
5861
5895
  const microRootNode = microAppWindow.Node;
5862
5896
  const microRootElement = microAppWindow.Element;
5863
5897
  // const rawMicroGetRootNode = microRootNode.prototype.getRootNode
5864
5898
  const rawMicroAppendChild = microRootNode.prototype.appendChild;
5865
5899
  const rawMicroInsertBefore = microRootNode.prototype.insertBefore;
5866
5900
  const rawMicroReplaceChild = microRootNode.prototype.replaceChild;
5901
+ const rawMicroRemoveChild = microRootNode.prototype.removeChild;
5902
+ const rawMicroAppend = microRootElement.prototype.append;
5903
+ const rawMicroPrepend = microRootElement.prototype.prepend;
5904
+ const rawMicroInsertAdjacentElement = microRootElement.prototype.insertAdjacentElement;
5867
5905
  const rawMicroCloneNode = microRootNode.prototype.cloneNode;
5868
5906
  const rawInnerHTMLDesc = Object.getOwnPropertyDescriptor(microRootElement.prototype, 'innerHTML');
5869
5907
  const rawParentNodeLDesc = Object.getOwnPropertyDescriptor(microRootNode.prototype, 'parentNode');
5870
5908
  const isPureNode = (target) => {
5871
5909
  return (isScriptElement(target) || isBaseElement(target)) && target.__PURE_ELEMENT__;
5872
5910
  };
5873
- const getRawTarget = (target) => {
5874
- if (target === iframeSandbox.microHead) {
5911
+ const getRawTarget = (parent) => {
5912
+ if (parent === sandbox.microHead) {
5875
5913
  return rawDocument.head;
5876
5914
  }
5877
- else if (target === iframeSandbox.microBody) {
5915
+ else if (parent === sandbox.microBody) {
5878
5916
  return rawDocument.body;
5879
5917
  }
5880
- return target;
5918
+ return parent;
5881
5919
  };
5882
5920
  microRootNode.prototype.getRootNode = function getRootNode() {
5883
5921
  return microDocument;
@@ -5888,46 +5926,70 @@ function patchIframeNode(appName, microAppWindow, iframeSandbox) {
5888
5926
  };
5889
5927
  microRootNode.prototype.appendChild = function appendChild(node) {
5890
5928
  updateElementInfo(node, appName);
5891
- // TODO:只有script才可以这样拦截,link、style不应该拦截
5892
5929
  if (isPureNode(node)) {
5893
5930
  return rawMicroAppendChild.call(this, node);
5894
5931
  }
5895
- const _this = getRawTarget(this);
5896
- if (_this !== this) {
5897
- return _this.appendChild(node);
5898
- }
5899
- return rawMicroAppendChild.call(_this, node);
5932
+ return rawRootElement.prototype.appendChild.call(getRawTarget(this), node);
5900
5933
  };
5901
- // TODO: 更多场景适配
5902
5934
  microRootNode.prototype.insertBefore = function insertBefore(node, child) {
5903
5935
  updateElementInfo(node, appName);
5904
5936
  if (isPureNode(node)) {
5905
5937
  return rawMicroInsertBefore.call(this, node, child);
5906
5938
  }
5907
- const _this = getRawTarget(this);
5908
- if (_this !== this) {
5909
- if (child && !_this.contains(child)) {
5910
- return _this.appendChild(node);
5911
- }
5912
- return _this.insertBefore(node, child);
5913
- }
5914
- return rawMicroInsertBefore.call(_this, node, child);
5939
+ return rawRootElement.prototype.insertBefore.call(getRawTarget(this), node, child);
5915
5940
  };
5916
- // TODO: 更多场景适配
5917
5941
  microRootNode.prototype.replaceChild = function replaceChild(node, child) {
5918
5942
  updateElementInfo(node, appName);
5919
5943
  if (isPureNode(node)) {
5920
5944
  return rawMicroReplaceChild.call(this, node, child);
5921
5945
  }
5922
- const _this = getRawTarget(this);
5923
- if (_this !== this) {
5924
- if (child && !_this.contains(child)) {
5925
- _this.appendChild(node);
5926
- return child;
5927
- }
5928
- return _this.replaceChild(node, child);
5946
+ return rawRootElement.prototype.replaceChild.call(getRawTarget(this), node, child);
5947
+ };
5948
+ microRootNode.prototype.removeChild = function removeChild(oldChild) {
5949
+ if (isPureNode(oldChild) || this.contains(oldChild)) {
5950
+ return rawMicroRemoveChild.call(this, oldChild);
5951
+ }
5952
+ return rawRootElement.prototype.removeChild.call(getRawTarget(this), oldChild);
5953
+ };
5954
+ microRootElement.prototype.append = function append(...nodes) {
5955
+ let i = 0;
5956
+ let hasPureNode = false;
5957
+ while (i < nodes.length) {
5958
+ nodes[i] = isNode(nodes[i]) ? nodes[i] : microDocument.createTextNode(nodes[i]);
5959
+ if (isPureNode(nodes[i]))
5960
+ hasPureNode = true;
5961
+ i++;
5962
+ }
5963
+ if (hasPureNode) {
5964
+ return rawMicroAppend.call(this, ...nodes);
5965
+ }
5966
+ return rawRootElement.prototype.append.call(getRawTarget(this), ...nodes);
5967
+ };
5968
+ microRootElement.prototype.prepend = function prepend(...nodes) {
5969
+ let i = 0;
5970
+ let hasPureNode = false;
5971
+ while (i < nodes.length) {
5972
+ nodes[i] = isNode(nodes[i]) ? nodes[i] : microDocument.createTextNode(nodes[i]);
5973
+ if (isPureNode(nodes[i]))
5974
+ hasPureNode = true;
5975
+ i++;
5976
+ }
5977
+ if (hasPureNode) {
5978
+ return rawMicroPrepend.call(this, ...nodes);
5979
+ }
5980
+ return rawRootElement.prototype.prepend.call(getRawTarget(this), ...nodes);
5981
+ };
5982
+ /**
5983
+ * The insertAdjacentElement method of the Element interface inserts a given element node at a given position relative to the element it is invoked upon.
5984
+ * Scenes:
5985
+ * 1. vite4 development env for style
5986
+ */
5987
+ microRootElement.prototype.insertAdjacentElement = function insertAdjacentElement(where, element) {
5988
+ updateElementInfo(element, appName);
5989
+ if (isPureNode(element)) {
5990
+ return rawMicroInsertAdjacentElement.call(this, where, element);
5929
5991
  }
5930
- return rawMicroReplaceChild.call(_this, node, child);
5992
+ return rawRootElement.prototype.insertAdjacentElement.call(getRawTarget(this), where, element);
5931
5993
  };
5932
5994
  // patch cloneNode
5933
5995
  microRootNode.prototype.cloneNode = function cloneNode(deep) {
@@ -5954,7 +6016,7 @@ function patchIframeNode(appName, microAppWindow, iframeSandbox) {
5954
6016
  configurable: true,
5955
6017
  enumerable: true,
5956
6018
  get() {
5957
- var _a;
6019
+ var _a, _b, _c;
5958
6020
  // set html.parentNode to microDocument
5959
6021
  throttleDeferForParentNode(microDocument);
5960
6022
  const result = rawParentNodeLDesc.get.call(this);
@@ -5967,12 +6029,11 @@ function patchIframeNode(appName, microAppWindow, iframeSandbox) {
5967
6029
  * Will it cause other problems ?
5968
6030
  * e.g. target.parentNode.remove(target)
5969
6031
  */
5970
- if ((result === null || result === void 0 ? void 0 : result.tagName) === 'MICRO-APP-BODY' && ((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container)) {
5971
- return rawDocument.body;
6032
+ if (isMicroAppBody(result) && ((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container)) {
6033
+ return ((_c = (_b = microApp.options).getRootElementParentNode) === null || _c === void 0 ? void 0 : _c.call(_b, this, appName)) || rawDocument.body;
5972
6034
  }
5973
6035
  return result;
5974
6036
  },
5975
- set: undefined,
5976
6037
  });
5977
6038
  // Adapt to new image(...) scene
5978
6039
  const ImageProxy = new Proxy(microAppWindow.Image, {
@@ -6012,6 +6073,12 @@ function patchIframeAttribute(appName, url, microAppWindow) {
6012
6073
  [microAppWindow.HTMLScriptElement.prototype, 'src'],
6013
6074
  [microAppWindow.HTMLLinkElement.prototype, 'href'],
6014
6075
  ];
6076
+ /**
6077
+ * element.setAttribute does not trigger this actions:
6078
+ * 1. img.src = xxx
6079
+ * 2. script.src = xxx
6080
+ * 3. link.href = xxx
6081
+ */
6015
6082
  protoAttrList.forEach(([target, attr]) => {
6016
6083
  const { enumerable, configurable, get, set } = Object.getOwnPropertyDescriptor(target, attr) || {
6017
6084
  enumerable: true,
@@ -6044,23 +6111,30 @@ class IframeSandbox {
6044
6111
  };
6045
6112
  const rawLocation = globalEnv.rawWindow.location;
6046
6113
  const browserHost = rawLocation.protocol + '//' + rawLocation.host;
6047
- const childStaticLocation = new URL(url);
6048
- const childHost = childStaticLocation.protocol + '//' + childStaticLocation.host;
6049
- const childFullPath = childStaticLocation.pathname + childStaticLocation.search + childStaticLocation.hash;
6050
6114
  this.deleteIframeElement = this.createIframeElement(appName, browserHost);
6051
6115
  this.microAppWindow = this.iframe.contentWindow;
6052
- // TODO: 优化代码
6053
- // exec before initStaticGlobalKeys
6054
- this.createProxyLocation(appName, url, this.microAppWindow, childStaticLocation, browserHost, childHost);
6055
- this.createProxyWindow(appName, this.microAppWindow);
6056
- this.initStaticGlobalKeys(appName, url);
6057
- // get escapeProperties from plugins
6058
- this.getSpecialProperties(appName);
6059
6116
  this.patchIframe(this.microAppWindow, (resolve) => {
6117
+ // TODO: 优化代码
6118
+ // create new html to iframe
6060
6119
  this.createIframeTemplate(this.microAppWindow);
6061
- patchIframeRoute(appName, this.microAppWindow, childFullPath);
6120
+ // get escapeProperties from plugins
6121
+ this.getSpecialProperties(appName);
6122
+ // rewrite location & history of child app
6123
+ this.proxyLocation = patchIframeRoute(appName, url, this.microAppWindow, browserHost);
6124
+ // create proxy window
6125
+ this.proxyWindow = this.createProxyWindow(this.microAppWindow);
6126
+ /**
6127
+ * create static properties
6128
+ * NOTE:
6129
+ * 1. execute as early as possible
6130
+ * 2. run after patchIframeRoute & createProxyWindow
6131
+ */
6132
+ this.initStaticGlobalKeys(appName, url);
6133
+ // rewrite window of child app
6062
6134
  this.windowEffect = patchIframeWindow(appName, this.microAppWindow);
6135
+ // rewrite document of child app
6063
6136
  this.documentEffect = patchIframeDocument(appName, this.microAppWindow, this.proxyLocation);
6137
+ // rewrite Node & Element of child app
6064
6138
  patchIframeElement(appName, url, this.microAppWindow, this);
6065
6139
  resolve();
6066
6140
  });
@@ -6097,53 +6171,80 @@ class IframeSandbox {
6097
6171
  });
6098
6172
  }
6099
6173
  start({ baseroute, useMemoryRouter, defaultPage, disablePatchRequest, }) {
6100
- if (!this.active) {
6101
- this.active = true;
6102
- // TODO: 虚拟路由升级
6103
- if (useMemoryRouter) {
6104
- this.initRouteState(defaultPage);
6105
- // unique listener of popstate event for sub app
6106
- this.removeHistoryListener = addHistoryListener(this.microAppWindow.__MICRO_APP_NAME__);
6107
- }
6108
- else {
6109
- this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute;
6110
- }
6111
- /**
6112
- * create base element to iframe
6113
- * WARNING: This will also affect a, image, link and script
6114
- */
6115
- if (!disablePatchRequest) {
6116
- this.createIframeBase();
6117
- }
6118
- if (++globalEnv.activeSandbox === 1) {
6119
- patchElementAndDocument();
6120
- patchHistory();
6121
- }
6122
- if (++IframeSandbox.activeCount === 1) ;
6174
+ if (this.active)
6175
+ return;
6176
+ this.active = true;
6177
+ // TODO: 虚拟路由升级
6178
+ // eslint-disable-next-line
6179
+ if (useMemoryRouter || true) {
6180
+ this.initRouteState(defaultPage);
6181
+ // unique listener of popstate event for sub app
6182
+ this.removeHistoryListener = addHistoryListener(this.microAppWindow.__MICRO_APP_NAME__);
6123
6183
  }
6184
+ else {
6185
+ this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute;
6186
+ }
6187
+ /**
6188
+ * create base element to iframe
6189
+ * WARNING: This will also affect a, image, link and script
6190
+ */
6191
+ if (!disablePatchRequest) {
6192
+ this.createIframeBase();
6193
+ }
6194
+ if (++globalEnv.activeSandbox === 1) {
6195
+ patchElementAndDocument();
6196
+ patchHistory();
6197
+ }
6198
+ if (++IframeSandbox.activeCount === 1) ;
6124
6199
  }
6125
6200
  stop({ umdMode, keepRouteState, destroy, clearData, }) {
6126
- if (this.active) {
6127
- this.recordAndReleaseEffect({ clearData }, !umdMode || destroy);
6128
- if (this.removeHistoryListener) {
6129
- this.clearRouteState(keepRouteState);
6130
- // release listener of popstate
6131
- this.removeHistoryListener();
6132
- }
6133
- if (!umdMode || destroy) {
6134
- this.deleteIframeElement();
6135
- this.escapeKeys.forEach((key) => {
6136
- Reflect.deleteProperty(globalEnv.rawWindow, key);
6137
- });
6138
- this.escapeKeys.clear();
6139
- }
6140
- if (--globalEnv.activeSandbox === 0) {
6141
- releasePatchElementAndDocument();
6142
- releasePatchHistory();
6143
- }
6144
- if (--IframeSandbox.activeCount === 0) ;
6145
- this.active = false;
6201
+ if (!this.active)
6202
+ return;
6203
+ this.recordAndReleaseEffect({ clearData }, !umdMode || destroy);
6204
+ if (this.removeHistoryListener) {
6205
+ this.clearRouteState(keepRouteState);
6206
+ // release listener of popstate
6207
+ this.removeHistoryListener();
6208
+ }
6209
+ if (!umdMode || destroy) {
6210
+ this.deleteIframeElement();
6211
+ this.escapeKeys.forEach((key) => {
6212
+ Reflect.deleteProperty(globalEnv.rawWindow, key);
6213
+ });
6214
+ this.escapeKeys.clear();
6215
+ }
6216
+ if (--globalEnv.activeSandbox === 0) {
6217
+ releasePatchElementAndDocument();
6218
+ releasePatchHistory();
6146
6219
  }
6220
+ if (--IframeSandbox.activeCount === 0) ;
6221
+ this.active = false;
6222
+ }
6223
+ /**
6224
+ * create static properties
6225
+ * NOTE:
6226
+ * 1. execute as early as possible
6227
+ * 2. run after patchIframeRoute & createProxyWindow
6228
+ */
6229
+ initStaticGlobalKeys(appName, url) {
6230
+ this.microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
6231
+ this.microAppWindow.__MICRO_APP_NAME__ = appName;
6232
+ this.microAppWindow.__MICRO_APP_URL__ = url;
6233
+ this.microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
6234
+ this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = '';
6235
+ this.microAppWindow.__MICRO_APP_WINDOW__ = this.microAppWindow;
6236
+ this.microAppWindow.__MICRO_APP_PRE_RENDER__ = false;
6237
+ this.microAppWindow.__MICRO_APP_UMD_MODE__ = false;
6238
+ this.microAppWindow.__MICRO_APP_SANDBOX__ = this;
6239
+ this.microAppWindow.__MICRO_APP_PROXY_WINDOW__ = this.proxyWindow;
6240
+ this.microAppWindow.rawWindow = globalEnv.rawWindow;
6241
+ this.microAppWindow.rawDocument = globalEnv.rawDocument;
6242
+ this.microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
6243
+ removeDomScope,
6244
+ pureCreateElement,
6245
+ location: this.proxyLocation,
6246
+ router,
6247
+ });
6147
6248
  }
6148
6249
  /**
6149
6250
  * Record global effect and then release (effect: global event, timeout, data listener)
@@ -6221,42 +6322,33 @@ class IframeSandbox {
6221
6322
  setPreRenderState(state) {
6222
6323
  this.microAppWindow.__MICRO_APP_PRE_RENDER__ = state;
6223
6324
  }
6325
+ // record umdMode
6224
6326
  markUmdMode(state) {
6225
6327
  this.microAppWindow.__MICRO_APP_UMD_MODE__ = state;
6226
6328
  }
6227
- initStaticGlobalKeys(appName, url) {
6228
- this.microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
6229
- this.microAppWindow.__MICRO_APP_NAME__ = appName;
6230
- this.microAppWindow.__MICRO_APP_URL__ = url;
6231
- this.microAppWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
6232
- this.microAppWindow.__MICRO_APP_WINDOW__ = this.microAppWindow;
6233
- this.microAppWindow.__MICRO_APP_PRE_RENDER__ = false;
6234
- this.microAppWindow.__MICRO_APP_UMD_MODE__ = false;
6235
- this.microAppWindow.__MICRO_APP_SANDBOX__ = this;
6236
- this.microAppWindow.__MICRO_APP_PROXY_WINDOW__ = this.proxyWindow;
6237
- this.microAppWindow.rawWindow = globalEnv.rawWindow;
6238
- this.microAppWindow.rawDocument = globalEnv.rawDocument;
6239
- this.microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
6240
- removeDomScope,
6241
- pureCreateElement,
6242
- location: this.proxyLocation,
6243
- router,
6244
- });
6245
- }
6246
6329
  // TODO: RESTRUCTURE
6247
6330
  patchIframe(microAppWindow, cb) {
6331
+ const oldMicroDocument = microAppWindow.document;
6248
6332
  this.sandboxReady = new Promise((resolve) => {
6249
6333
  (function iframeLocationReady() {
6250
6334
  setTimeout(() => {
6251
- if (microAppWindow.location.href === 'about:blank') {
6252
- iframeLocationReady();
6335
+ try {
6336
+ if (microAppWindow.document === oldMicroDocument) {
6337
+ iframeLocationReady();
6338
+ }
6339
+ else {
6340
+ /**
6341
+ * NOTE:
6342
+ * 1. microAppWindow will not be recreated
6343
+ * 2. the properties of microAppWindow may be recreated, such as document
6344
+ * 3. the variables added to microAppWindow may be cleared
6345
+ */
6346
+ microAppWindow.stop();
6347
+ cb(resolve);
6348
+ }
6253
6349
  }
6254
- else {
6255
- /**
6256
- * microAppWindow.document rebuild
6257
- */
6258
- microAppWindow.stop();
6259
- cb(resolve);
6350
+ catch (e) {
6351
+ iframeLocationReady();
6260
6352
  }
6261
6353
  }, 0);
6262
6354
  })();
@@ -6282,12 +6374,9 @@ class IframeSandbox {
6282
6374
  this.updateIframeBase();
6283
6375
  this.microHead.appendChild(this.baseElement);
6284
6376
  }
6285
- createProxyLocation(appName, url, microAppWindow, childStaticLocation, browserHost, childHost) {
6286
- this.proxyLocation = createMicroLocation(appName, url, microAppWindow, childStaticLocation, browserHost, childHost);
6287
- }
6288
- createProxyWindow(appName, microAppWindow) {
6377
+ createProxyWindow(microAppWindow) {
6289
6378
  const rawWindow = globalEnv.rawWindow;
6290
- this.proxyWindow = new Proxy(microAppWindow, {
6379
+ return new Proxy(microAppWindow, {
6291
6380
  get: (target, key) => {
6292
6381
  if (key === 'location') {
6293
6382
  return this.proxyLocation;
@@ -6298,20 +6387,18 @@ class IframeSandbox {
6298
6387
  return bindFunctionToRawTarget(Reflect.get(target, key), target);
6299
6388
  },
6300
6389
  set: (target, key, value) => {
6301
- if (this.active) {
6302
- /**
6303
- * TODO:
6304
- * 1、location域名相同,子应用内部跳转时的处理
6305
- * 2、和with沙箱的变量相同,提取成公共数组
6306
- */
6307
- if (key === 'location') {
6308
- return Reflect.set(rawWindow, key, value);
6309
- }
6310
- Reflect.set(target, key, value);
6311
- if (this.escapeProperties.includes(key)) {
6312
- !Reflect.has(rawWindow, key) && this.escapeKeys.add(key);
6313
- Reflect.set(rawWindow, key, value);
6314
- }
6390
+ /**
6391
+ * TODO:
6392
+ * 1、location域名相同,子应用内部跳转时的处理
6393
+ * 2、和with沙箱的变量相同,提取成公共数组
6394
+ */
6395
+ if (key === 'location') {
6396
+ return Reflect.set(rawWindow, key, value);
6397
+ }
6398
+ Reflect.set(target, key, value);
6399
+ if (this.escapeProperties.includes(key)) {
6400
+ !Reflect.has(rawWindow, key) && this.escapeKeys.add(key);
6401
+ Reflect.set(rawWindow, key, value);
6315
6402
  }
6316
6403
  return true;
6317
6404
  },
@@ -6640,7 +6727,7 @@ class CreateApp {
6640
6727
  this.handleMounted(this.umdHookMount(microApp.getData(this.name, true)));
6641
6728
  }
6642
6729
  catch (e) {
6643
- logError('An error occurred in function mount \n', this.name, e);
6730
+ logError('An error occurred in window.mount \n', this.name, e);
6644
6731
  }
6645
6732
  }
6646
6733
  else if (isFinished === true) {
@@ -6655,7 +6742,7 @@ class CreateApp {
6655
6742
  this.handleMounted(this.umdHookMount(microApp.getData(this.name, true)));
6656
6743
  }
6657
6744
  catch (e) {
6658
- logError('An error occurred in function mount \n', this.name, e);
6745
+ logError('An error occurred in window.mount \n', this.name, e);
6659
6746
  }
6660
6747
  }
6661
6748
  }
@@ -6673,7 +6760,7 @@ class CreateApp {
6673
6760
  if (isPromise(umdHookMountResult)) {
6674
6761
  umdHookMountResult
6675
6762
  .then(() => this.dispatchMountedEvent())
6676
- .catch((e) => this.onerror(e));
6763
+ .catch(() => this.dispatchMountedEvent());
6677
6764
  }
6678
6765
  else {
6679
6766
  this.dispatchMountedEvent();
@@ -6701,7 +6788,8 @@ class CreateApp {
6701
6788
  }
6702
6789
  /**
6703
6790
  * unmount app
6704
- * NOTE: Do not add any params on account of unmountApp
6791
+ * NOTE:
6792
+ * 1. do not add any params on account of unmountApp
6705
6793
  * @param destroy completely destroy, delete cache resources
6706
6794
  * @param clearData clear data of dateCenter
6707
6795
  * @param keepRouteState keep route state when unmount, default is false
@@ -6711,33 +6799,35 @@ class CreateApp {
6711
6799
  var _a;
6712
6800
  destroy = destroy || this.state === appStates.LOAD_FAILED;
6713
6801
  this.setAppState(appStates.UNMOUNT);
6714
- // result of unmount function
6715
6802
  let umdHookUnmountResult = null;
6716
- /**
6717
- * send an unmount event to the micro app or call umd unmount hook
6718
- * before the sandbox is cleared
6719
- */
6720
6803
  try {
6804
+ // call umd unmount hook before the sandbox is cleared
6721
6805
  umdHookUnmountResult = (_a = this.umdHookUnmount) === null || _a === void 0 ? void 0 : _a.call(this, microApp.getData(this.name, true));
6722
6806
  }
6723
6807
  catch (e) {
6724
- logError('An error occurred in function unmount \n', this.name, e);
6808
+ logError('An error occurred in window.unmount \n', this.name, e);
6725
6809
  }
6726
6810
  // dispatch unmount event to micro app
6727
6811
  dispatchCustomEventToMicroApp(this, 'unmount');
6728
6812
  // call window.onunmount of child app
6729
6813
  execMicroAppGlobalHook(this.getMicroAppGlobalHook(microGlobalEvent.ONUNMOUNT), this.name, microGlobalEvent.ONUNMOUNT);
6730
- this.handleUnmounted(destroy, clearData, keepRouteState, umdHookUnmountResult, unmountcb);
6814
+ this.handleUnmounted({
6815
+ destroy,
6816
+ clearData,
6817
+ keepRouteState,
6818
+ unmountcb,
6819
+ umdHookUnmountResult,
6820
+ });
6731
6821
  }
6732
6822
  /**
6733
6823
  * handle for promise umdHookUnmount
6734
6824
  * @param destroy completely destroy, delete cache resources
6735
6825
  * @param clearData clear data of dateCenter
6736
6826
  * @param keepRouteState keep route state when unmount, default is false
6737
- * @param umdHookUnmountResult result of umdHookUnmount
6738
6827
  * @param unmountcb callback of unmount
6828
+ * @param umdHookUnmountResult result of umdHookUnmount
6739
6829
  */
6740
- handleUnmounted(destroy, clearData, keepRouteState, umdHookUnmountResult, unmountcb) {
6830
+ handleUnmounted({ destroy, clearData, keepRouteState, unmountcb, umdHookUnmountResult, }) {
6741
6831
  const nextAction = () => this.actionsForUnmount({
6742
6832
  destroy,
6743
6833
  clearData,
@@ -6745,6 +6835,8 @@ class CreateApp {
6745
6835
  unmountcb,
6746
6836
  });
6747
6837
  if (isPromise(umdHookUnmountResult)) {
6838
+ // async window.unmount will cause appName bind error in nest app
6839
+ removeDomScope();
6748
6840
  umdHookUnmountResult.then(nextAction).catch(nextAction);
6749
6841
  }
6750
6842
  else {
@@ -6758,7 +6850,7 @@ class CreateApp {
6758
6850
  * @param keepRouteState keep route state when unmount, default is false
6759
6851
  * @param unmountcb callback of unmount
6760
6852
  */
6761
- actionsForUnmount({ destroy, clearData, keepRouteState, unmountcb }) {
6853
+ actionsForUnmount({ destroy, clearData, keepRouteState, unmountcb, }) {
6762
6854
  var _a;
6763
6855
  if (this.umdMode && this.container && !destroy) {
6764
6856
  cloneContainer(this.container, this.source.html, false);
@@ -6775,9 +6867,6 @@ class CreateApp {
6775
6867
  destroy,
6776
6868
  clearData: clearData || destroy,
6777
6869
  });
6778
- if (!getActiveApps().length) {
6779
- releasePatchSetAttribute();
6780
- }
6781
6870
  // dispatch unmount event to base app
6782
6871
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.UNMOUNT);
6783
6872
  this.clearOptions(destroy);
@@ -6794,6 +6883,7 @@ class CreateApp {
6794
6883
  this.sandBox = null;
6795
6884
  if (destroy)
6796
6885
  this.actionsForCompletelyDestroy();
6886
+ removeDomScope();
6797
6887
  }
6798
6888
  // actions for completely destroy
6799
6889
  actionsForCompletelyDestroy() {
@@ -6903,8 +6993,8 @@ class CreateApp {
6903
6993
  return {};
6904
6994
  }
6905
6995
  getMicroAppGlobalHook(eventName) {
6906
- var _a;
6907
- const listener = ((_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow)[eventName];
6996
+ var _a, _b;
6997
+ const listener = (_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow) === null || _b === void 0 ? void 0 : _b[eventName];
6908
6998
  return isFunction(listener) ? listener : null;
6909
6999
  }
6910
7000
  querySelector(selectors) {
@@ -6927,7 +7017,7 @@ function isIframeSandbox(appName) {
6927
7017
  function defineElement(tagName) {
6928
7018
  class MicroAppElement extends HTMLElement {
6929
7019
  constructor() {
6930
- super();
7020
+ super(...arguments);
6931
7021
  this.isWaiting = false;
6932
7022
  this.cacheData = null;
6933
7023
  this.connectedCount = 0;
@@ -6979,8 +7069,6 @@ function defineElement(tagName) {
6979
7069
  this.setAttribute('name', this.appName);
6980
7070
  }
6981
7071
  };
6982
- // patchSetAttribute hijiack data attribute, it needs exec first
6983
- patchSetAttribute();
6984
7072
  }
6985
7073
  static get observedAttributes() {
6986
7074
  return ['name', 'url'];
@@ -7249,6 +7337,7 @@ function defineElement(tagName) {
7249
7337
  */
7250
7338
  handleAppMount(app) {
7251
7339
  app.isPrefetch = false;
7340
+ // TODO: Can defer be removed?
7252
7341
  defer(() => this.mount(app));
7253
7342
  }
7254
7343
  /**
@@ -7390,6 +7479,30 @@ function defineElement(tagName) {
7390
7479
  this.getAttribute('defaultPage') ||
7391
7480
  '');
7392
7481
  }
7482
+ /**
7483
+ * Rewrite micro-app.setAttribute, process attr data
7484
+ * @param key attr name
7485
+ * @param value attr value
7486
+ */
7487
+ setAttribute(key, value) {
7488
+ if (key === 'data') {
7489
+ if (isPlainObject(value)) {
7490
+ const cloneValue = {};
7491
+ Object.getOwnPropertyNames(value).forEach((ownKey) => {
7492
+ if (!(isString(ownKey) && ownKey.indexOf('__') === 0)) {
7493
+ cloneValue[ownKey] = value[ownKey];
7494
+ }
7495
+ });
7496
+ this.data = cloneValue;
7497
+ }
7498
+ else if (value !== '[object Object]') {
7499
+ logWarn('property data must be an object', this.appName);
7500
+ }
7501
+ }
7502
+ else {
7503
+ globalEnv.rawSetAttribute.call(this, key, value);
7504
+ }
7505
+ }
7393
7506
  /**
7394
7507
  * Data from the base application
7395
7508
  */