@micro-zoe/micro-app 0.8.3 → 0.8.6

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/README.md CHANGED
@@ -97,7 +97,7 @@ yarn bootstrap
97
97
  yarn start
98
98
  ```
99
99
 
100
- For more commands, see [DEVELP](https://github.com/micro-zoe/micro-app/blob/master/DEVELOP.md)
100
+ For more commands, see [DEVELOP](https://github.com/micro-zoe/micro-app/blob/master/DEVELOP.md)
101
101
 
102
102
  # FAQ
103
103
  <details>
@@ -152,7 +152,7 @@ For more commands, see [DEVELP](https://github.com/micro-zoe/micro-app/blob/mast
152
152
  </details>
153
153
 
154
154
  # Contributors
155
- <a href="https://github.com/micro-zoe/micro-app/graphs/contributors"><img src="https://micro-zoe.com/contributors.svg?height=55&people=11" /></a>
155
+ <a href="https://github.com/micro-zoe/micro-app/graphs/contributors"><img src="https://micro-zoe.com/contributors.svg?height=55&people=13" /></a>
156
156
  <!-- opencollective is inaccurate -->
157
157
  <!-- <a href="https://github.com/micro-zoe/micro-app/graphs/contributors"><img src="https://opencollective.com/micro-app/contributors.svg?width=890&button=false" /></a> -->
158
158
 
package/README.zh-cn.md CHANGED
@@ -153,7 +153,7 @@ yarn start # 访问 http://localhost:3000
153
153
  </details>
154
154
 
155
155
  # 贡献者们
156
- <a href="https://github.com/micro-zoe/micro-app/graphs/contributors"><img src="https://micro-zoe.com/contributors.svg?height=55&people=11" /></a>
156
+ <a href="https://github.com/micro-zoe/micro-app/graphs/contributors"><img src="https://micro-zoe.com/contributors.svg?height=55&people=13" /></a>
157
157
  <!-- opencollective is inaccurate -->
158
158
  <!-- <a href="https://github.com/micro-zoe/micro-app/graphs/contributors"><img src="https://opencollective.com/micro-app/contributors.svg?width=890&button=false" /></a> -->
159
159
 
package/lib/index.d.ts CHANGED
@@ -190,6 +190,7 @@ declare module '@micro-zoe/micro-app/libs/utils' {
190
190
  * trim start & end
191
191
  */
192
192
  export function trim(str: string): string;
193
+ export function isFireFox(): boolean;
193
194
  }
194
195
 
195
196
  declare module '@micro-zoe/micro-app/interact' {
package/lib/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- const version = '0.8.3';
1
+ const version = '0.8.6';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -303,6 +303,9 @@ function getRootContainer(target) {
303
303
  function trim(str) {
304
304
  return str ? str.replace(/^\s+|\s+$/g, '') : '';
305
305
  }
306
+ function isFireFox() {
307
+ return navigator.userAgent.indexOf('Firefox') > -1;
308
+ }
306
309
 
307
310
  var ObservedAttrName;
308
311
  (function (ObservedAttrName) {
@@ -356,6 +359,66 @@ function fetchSource(url, appName = null, options = {}) {
356
359
  });
357
360
  }
358
361
 
362
+ class HTMLLoader {
363
+ static getInstance() {
364
+ if (!this.instance) {
365
+ this.instance = new HTMLLoader();
366
+ }
367
+ return this.instance;
368
+ }
369
+ /**
370
+ * run logic of load and format html
371
+ * @param successCb success callback
372
+ * @param errorCb error callback, type: (err: Error, meetFetchErr: boolean) => void
373
+ */
374
+ run(app, successCb) {
375
+ const appName = app.name;
376
+ const htmlUrl = app.ssrUrl || app.url;
377
+ fetchSource(htmlUrl, appName, { cache: 'no-cache' }).then((htmlStr) => {
378
+ if (!htmlStr) {
379
+ const msg = 'html is empty, please check in detail';
380
+ app.onerror(new Error(msg));
381
+ return logError(msg, appName);
382
+ }
383
+ htmlStr = this.formatHTML(htmlUrl, htmlStr, appName);
384
+ successCb(htmlStr, app);
385
+ }).catch((e) => {
386
+ logError(`Failed to fetch data from ${app.url}, micro-app stop rendering`, appName, e);
387
+ app.onLoadError(e);
388
+ });
389
+ }
390
+ formatHTML(htmlUrl, htmlStr, appName) {
391
+ return this.processHtml(htmlUrl, htmlStr, appName, microApp.plugins)
392
+ .replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
393
+ return match
394
+ .replace(/<head/i, '<micro-app-head')
395
+ .replace(/<\/head>/i, '</micro-app-head>');
396
+ })
397
+ .replace(/<body[^>]*>[\s\S]*?<\/body>/i, (match) => {
398
+ return match
399
+ .replace(/<body/i, '<micro-app-body')
400
+ .replace(/<\/body>/i, '</micro-app-body>');
401
+ });
402
+ }
403
+ processHtml(url, code, appName, plugins) {
404
+ var _a;
405
+ if (!plugins)
406
+ return code;
407
+ const mergedPlugins = [];
408
+ plugins.global && mergedPlugins.push(...plugins.global);
409
+ ((_a = plugins.modules) === null || _a === void 0 ? void 0 : _a[appName]) && mergedPlugins.push(...plugins.modules[appName]);
410
+ if (mergedPlugins.length > 0) {
411
+ return mergedPlugins.reduce((preCode, plugin) => {
412
+ if (isPlainObject(plugin) && isFunction(plugin.processHtml)) {
413
+ return plugin.processHtml(preCode, url, plugin.options);
414
+ }
415
+ return preCode;
416
+ }, code);
417
+ }
418
+ return code;
419
+ }
420
+ }
421
+
359
422
  // common reg
360
423
  const rootSelectorREG = /(^|\s+)(html|:root)(?=[\s>~[.#:]+|$)/;
361
424
  const bodySelectorREG = /(^|\s+)((html[\s>~]+body)|body)(?=[\s>~[.#:]+|$)/;
@@ -369,7 +432,7 @@ function parseError(msg, linkPath) {
369
432
  throw err;
370
433
  }
371
434
  /**
372
- * Reference resources https://github.com/reworkcss/css
435
+ * Reference https://github.com/reworkcss/css
373
436
  * CSSParser mainly deals with 3 scenes: styleRule, @, and comment
374
437
  * And scopecss deals with 2 scenes: selector & url
375
438
  * And can also disable scopecss with inline comments
@@ -403,7 +466,7 @@ class CSSParser {
403
466
  this.baseURI = baseURI;
404
467
  this.linkPath = linkPath || '';
405
468
  this.matchRules();
406
- return this.result;
469
+ return isFireFox() ? decodeURIComponent(this.result) : this.result;
407
470
  }
408
471
  reset() {
409
472
  this.cssText = this.prefix = this.baseURI = this.linkPath = this.result = '';
@@ -422,17 +485,37 @@ class CSSParser {
422
485
  }
423
486
  // https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleRule
424
487
  matchStyleRule() {
425
- const selectorList = this.formatSelector();
488
+ const selectors = this.formatSelector(true);
426
489
  // reset scopecssDisableNextLine
427
490
  this.scopecssDisableNextLine = false;
428
- if (!selectorList)
491
+ if (!selectors)
429
492
  return parseError('selector missing', this.linkPath);
430
- this.result += selectorList.join(', ');
493
+ this.recordResult(selectors);
431
494
  this.matchComments();
432
495
  this.styleDeclarations();
433
496
  this.matchLeadingSpaces();
434
497
  return true;
435
498
  }
499
+ formatSelector(skip) {
500
+ const m = this.commonMatch(/^([^{]+)/, skip);
501
+ if (!m)
502
+ return false;
503
+ return m[0].replace(/(^|,[\n\s]*)([^,]+)/g, (_, separator, selector) => {
504
+ selector = trim(selector);
505
+ if (!(this.scopecssDisableNextLine ||
506
+ (this.scopecssDisable && (!this.scopecssDisableSelectors.length ||
507
+ this.scopecssDisableSelectors.includes(selector))) ||
508
+ rootSelectorREG.test(selector))) {
509
+ if (bodySelectorREG.test(selector)) {
510
+ selector = selector.replace(bodySelectorREG, this.prefix + ' micro-app-body');
511
+ }
512
+ else {
513
+ selector = this.prefix + ' ' + selector;
514
+ }
515
+ }
516
+ return separator + selector;
517
+ });
518
+ }
436
519
  // https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration
437
520
  styleDeclarations() {
438
521
  if (!this.matchOpenBrace())
@@ -458,7 +541,7 @@ class CSSParser {
458
541
  return `url("${CompletionPath($1, this.baseURI)}")`;
459
542
  });
460
543
  }
461
- this.result += cssValue;
544
+ this.recordResult(cssValue);
462
545
  }
463
546
  // reset scopecssDisableNextLine
464
547
  this.scopecssDisableNextLine = false;
@@ -473,39 +556,6 @@ class CSSParser {
473
556
  }
474
557
  return this.matchAllDeclarations();
475
558
  }
476
- formatSelector() {
477
- const m = this.commonMatch(/^([^{]+)/, true);
478
- if (!m)
479
- return false;
480
- return trim(m[0])
481
- .replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, '')
482
- .replace(/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'/g, (r) => {
483
- return r.replace(/,/g, '\u200C');
484
- })
485
- .split(/\s*(?![^(]*\)),\s*/)
486
- .map((s) => {
487
- const selectorText = s.replace(/\u200C/g, ',');
488
- if (this.scopecssDisableNextLine) {
489
- return selectorText;
490
- }
491
- else if (this.scopecssDisable) {
492
- if (!this.scopecssDisableSelectors.length ||
493
- this.scopecssDisableSelectors.includes(selectorText)) {
494
- return selectorText;
495
- }
496
- }
497
- if (selectorText === '*') {
498
- return this.prefix + ' *';
499
- }
500
- else if (bodySelectorREG.test(selectorText)) {
501
- return selectorText.replace(bodySelectorREG, this.prefix + ' micro-app-body');
502
- }
503
- else if (rootSelectorREG.test(selectorText)) { // ignore root selector
504
- return selectorText;
505
- }
506
- return this.prefix + ' ' + selectorText;
507
- });
508
- }
509
559
  matchAtRule() {
510
560
  if (this.cssText[0] !== '@')
511
561
  return false;
@@ -565,7 +615,7 @@ class CSSParser {
565
615
  pageRule() {
566
616
  if (!this.commonMatch(/^@page */))
567
617
  return false;
568
- this.formatSelector();
618
+ this.formatSelector(false);
569
619
  // reset scopecssDisableNextLine
570
620
  this.scopecssDisableNextLine = false;
571
621
  return this.commonHandlerForAtRuleWithSelfRule('page');
@@ -598,7 +648,7 @@ class CSSParser {
598
648
  if (!this.commonMatch(reg))
599
649
  return false;
600
650
  this.matchLeadingSpaces();
601
- return false;
651
+ return true;
602
652
  };
603
653
  }
604
654
  // common handler for @font-face, @page
@@ -631,7 +681,7 @@ class CSSParser {
631
681
  }
632
682
  // get comment content
633
683
  let commentText = this.cssText.slice(2, i - 2);
634
- this.result += `/*${commentText}*/`;
684
+ this.recordResult(`/*${commentText}*/`);
635
685
  commentText = trim(commentText.replace(/^\s*!/, ''));
636
686
  // set ignore config
637
687
  if (commentText === 'scopecss-disable-next-line') {
@@ -664,7 +714,7 @@ class CSSParser {
664
714
  const matchStr = matchArray[0];
665
715
  this.cssText = this.cssText.slice(matchStr.length);
666
716
  if (!skip)
667
- this.result += matchStr;
717
+ this.recordResult(matchStr);
668
718
  return matchArray;
669
719
  }
670
720
  matchOpenBrace() {
@@ -677,6 +727,16 @@ class CSSParser {
677
727
  matchLeadingSpaces() {
678
728
  this.commonMatch(/^\s*/);
679
729
  }
730
+ // splice string
731
+ recordResult(strFragment) {
732
+ // Firefox is slow when string contain special characters, see https://github.com/micro-zoe/micro-app/issues/256
733
+ if (isFireFox()) {
734
+ this.result += encodeURIComponent(strFragment);
735
+ }
736
+ else {
737
+ this.result += strFragment;
738
+ }
739
+ }
680
740
  }
681
741
  /**
682
742
  * common method of bind CSS
@@ -921,12 +981,12 @@ function handleNewNode(parent, child, app) {
921
981
  return child;
922
982
  }
923
983
  else if (child instanceof HTMLLinkElement) {
924
- if (child.hasAttribute('exclude')) {
984
+ if (child.hasAttribute('exclude') || checkExcludeUrl(child.getAttribute('href'), app.name)) {
925
985
  const linkReplaceComment = document.createComment('link element with exclude attribute ignored by micro-app');
926
986
  dynamicElementInMicroAppMap.set(child, linkReplaceComment);
927
987
  return linkReplaceComment;
928
988
  }
929
- else if (child.hasAttribute('ignore')) {
989
+ else if (child.hasAttribute('ignore') || checkIgnoreUrl(child.getAttribute('href'), app.name)) {
930
990
  return child;
931
991
  }
932
992
  const { url, info, replaceComment } = extractLinkFromHtml(child, parent, app, true);
@@ -974,51 +1034,48 @@ function handleNewNode(parent, child, app) {
974
1034
  * @param passiveChild second param of insertBefore and replaceChild
975
1035
  */
976
1036
  function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
1037
+ const container = getContainer(parent, app);
977
1038
  /**
978
1039
  * If passiveChild is not the child node, insertBefore replaceChild will have a problem, at this time, it will be degraded to appendChild
979
1040
  * E.g: document.head.insertBefore(targetChild, document.head.childNodes[0])
980
1041
  */
981
- if (parent === document.head) {
982
- const microAppHead = app.container.querySelector('micro-app-head');
1042
+ if (container) {
983
1043
  /**
984
1044
  * 1. If passiveChild exists, it must be insertBefore or replaceChild
985
1045
  * 2. When removeChild, targetChild may not be in microAppHead or head
986
1046
  */
987
- if (passiveChild && !microAppHead.contains(passiveChild)) {
988
- return globalEnv.rawAppendChild.call(microAppHead, targetChild);
1047
+ if (passiveChild && !container.contains(passiveChild)) {
1048
+ return globalEnv.rawAppendChild.call(container, targetChild);
989
1049
  }
990
- else if (rawMethod === globalEnv.rawRemoveChild && !microAppHead.contains(targetChild)) {
1050
+ else if (rawMethod === globalEnv.rawRemoveChild && !container.contains(targetChild)) {
991
1051
  if (parent.contains(targetChild)) {
992
1052
  return rawMethod.call(parent, targetChild);
993
1053
  }
994
1054
  return targetChild;
995
1055
  }
996
- else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
997
- return rawMethod.call(microAppHead, targetChild);
998
- }
999
- return rawMethod.call(microAppHead, targetChild, passiveChild);
1056
+ return invokeRawMethod(rawMethod, container, targetChild, passiveChild);
1000
1057
  }
1001
- else if (parent === document.body) {
1002
- const microAppBody = app.container.querySelector('micro-app-body');
1003
- if (passiveChild && !microAppBody.contains(passiveChild)) {
1004
- return globalEnv.rawAppendChild.call(microAppBody, targetChild);
1005
- }
1006
- else if (rawMethod === globalEnv.rawRemoveChild && !microAppBody.contains(targetChild)) {
1007
- if (parent.contains(targetChild)) {
1008
- return rawMethod.call(parent, targetChild);
1009
- }
1010
- return targetChild;
1011
- }
1012
- else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
1013
- return rawMethod.call(microAppBody, targetChild);
1014
- }
1015
- return rawMethod.call(microAppBody, targetChild, passiveChild);
1016
- }
1017
- else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
1058
+ return invokeRawMethod(rawMethod, parent, targetChild, passiveChild);
1059
+ }
1060
+ function invokeRawMethod(rawMethod, parent, targetChild, passiveChild) {
1061
+ if (isPendMethod(rawMethod)) {
1018
1062
  return rawMethod.call(parent, targetChild);
1019
1063
  }
1020
1064
  return rawMethod.call(parent, targetChild, passiveChild);
1021
1065
  }
1066
+ function isPendMethod(method) {
1067
+ return method === globalEnv.rawAppend || method === globalEnv.rawPrepend;
1068
+ }
1069
+ function getContainer(node, app) {
1070
+ var _a, _b;
1071
+ if (node === document.head) {
1072
+ return (_a = app === null || app === void 0 ? void 0 : app.container) === null || _a === void 0 ? void 0 : _a.querySelector('micro-app-head');
1073
+ }
1074
+ if (node === document.body) {
1075
+ return (_b = app === null || app === void 0 ? void 0 : app.container) === null || _b === void 0 ? void 0 : _b.querySelector('micro-app-body');
1076
+ }
1077
+ return null;
1078
+ }
1022
1079
  // Get the map element
1023
1080
  function getMappingNode(node) {
1024
1081
  var _a;
@@ -1134,7 +1191,8 @@ function patchElementPrototypeMethods() {
1134
1191
  */
1135
1192
  function markElement(element) {
1136
1193
  const appName = getCurrentAppName();
1137
- appName && (element.__MICRO_APP_NAME__ = appName);
1194
+ if (appName)
1195
+ element.__MICRO_APP_NAME__ = appName;
1138
1196
  return element;
1139
1197
  }
1140
1198
  // methods of document
@@ -1307,13 +1365,39 @@ function rejectMicroAppStyle() {
1307
1365
  }
1308
1366
  }
1309
1367
 
1368
+ // 管理 app 的单例
1369
+ class AppManager {
1370
+ constructor() {
1371
+ // Todo: appInstanceMap 由 AppManager 来创建,不再由 create_app 管理
1372
+ this.appInstanceMap = appInstanceMap;
1373
+ }
1374
+ static getInstance() {
1375
+ if (!this.instance) {
1376
+ this.instance = new AppManager();
1377
+ }
1378
+ return this.instance;
1379
+ }
1380
+ get(appName) {
1381
+ return this.appInstanceMap.get(appName);
1382
+ }
1383
+ set(appName, app) {
1384
+ this.appInstanceMap.set(appName, app);
1385
+ }
1386
+ getAll() {
1387
+ return Array.from(this.appInstanceMap.values());
1388
+ }
1389
+ clear() {
1390
+ this.appInstanceMap.clear();
1391
+ }
1392
+ }
1393
+
1310
1394
  function unmountNestedApp() {
1311
1395
  releaseUnmountOfNestedApp();
1312
- appInstanceMap.forEach(app => {
1396
+ AppManager.getInstance().getAll().forEach(app => {
1313
1397
  // @ts-ignore
1314
1398
  app.container && getRootContainer(app.container).disconnectedCallback();
1315
1399
  });
1316
- !window.__MICRO_APP_UMD_MODE__ && appInstanceMap.clear();
1400
+ !window.__MICRO_APP_UMD_MODE__ && AppManager.getInstance().clear();
1317
1401
  }
1318
1402
  // if micro-app run in micro application, delete all next generation application when unmount event received
1319
1403
  function listenUmountOfNestedApp() {
@@ -1435,11 +1519,14 @@ const globalScripts = new Map();
1435
1519
  function extractScriptElement(script, parent, app, isDynamic = false) {
1436
1520
  let replaceComment = null;
1437
1521
  let src = script.getAttribute('src');
1438
- if (script.hasAttribute('exclude')) {
1522
+ if (src) {
1523
+ src = CompletionPath(src, app.url);
1524
+ }
1525
+ if (script.hasAttribute('exclude') || checkExcludeUrl(src, app.name)) {
1439
1526
  replaceComment = document.createComment('script element with exclude attribute removed by micro-app');
1440
1527
  }
1441
1528
  else if ((script.type && !['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module'].includes(script.type)) ||
1442
- script.hasAttribute('ignore')) {
1529
+ script.hasAttribute('ignore') || checkIgnoreUrl(src, app.name)) {
1443
1530
  return null;
1444
1531
  }
1445
1532
  else if ((globalEnv.supportModuleScript && script.noModule) ||
@@ -1447,7 +1534,6 @@ function extractScriptElement(script, parent, app, isDynamic = false) {
1447
1534
  replaceComment = document.createComment(`${script.noModule ? 'noModule' : 'module'} script ignored by micro-app`);
1448
1535
  }
1449
1536
  else if (src) { // remote script
1450
- src = CompletionPath(src, app.url);
1451
1537
  const info = {
1452
1538
  code: '',
1453
1539
  isExternal: true,
@@ -1497,6 +1583,46 @@ function extractScriptElement(script, parent, app, isDynamic = false) {
1497
1583
  return parent.replaceChild(replaceComment, script);
1498
1584
  }
1499
1585
  }
1586
+ /**
1587
+ * get assets plugins
1588
+ * @param appName app name
1589
+ */
1590
+ function getAssetsPlugins(appName) {
1591
+ var _a, _b, _c;
1592
+ const globalPlugins = ((_a = microApp.plugins) === null || _a === void 0 ? void 0 : _a.global) || [];
1593
+ const modulePlugins = ((_c = (_b = microApp.plugins) === null || _b === void 0 ? void 0 : _b.modules) === null || _c === void 0 ? void 0 : _c[appName]) || [];
1594
+ return [...globalPlugins, ...modulePlugins];
1595
+ }
1596
+ /**
1597
+ * whether the url needs to be excluded
1598
+ * @param url css or js link
1599
+ * @param plugins microApp plugins
1600
+ */
1601
+ function checkExcludeUrl(url, appName) {
1602
+ if (!url)
1603
+ return false;
1604
+ const plugins = getAssetsPlugins(appName) || [];
1605
+ return plugins.some(plugin => {
1606
+ if (!plugin.excludeChecker)
1607
+ return false;
1608
+ return plugin.excludeChecker(url);
1609
+ });
1610
+ }
1611
+ /**
1612
+ * whether the url needs to be ignore
1613
+ * @param url css or js link
1614
+ * @param plugins microApp plugins
1615
+ */
1616
+ function checkIgnoreUrl(url, appName) {
1617
+ if (!url)
1618
+ return false;
1619
+ const plugins = getAssetsPlugins(appName) || [];
1620
+ return plugins.some(plugin => {
1621
+ if (!plugin.ignoreChecker)
1622
+ return false;
1623
+ return plugin.ignoreChecker(url);
1624
+ });
1625
+ }
1500
1626
  /**
1501
1627
  * Get remote resources of script
1502
1628
  * @param wrapElement htmlDom
@@ -1605,7 +1731,7 @@ function execScripts(scriptList, app, initHook) {
1605
1731
  function runScript(url, app, info, isDynamic, callback) {
1606
1732
  var _a;
1607
1733
  try {
1608
- const code = bindScope(url, app, info.code, info.module);
1734
+ const code = bindScope(url, app, info.code, info);
1609
1735
  if (app.inline || info.module) {
1610
1736
  const scriptElement = pureCreateElement('script');
1611
1737
  runCode2InlineScript(url, code, info.module, scriptElement, callback);
@@ -1658,7 +1784,7 @@ function runDynamicRemoteScript(url, info, app, originScript) {
1658
1784
  app.source.scripts.set(url, info);
1659
1785
  info.isGlobal && globalScripts.set(url, code);
1660
1786
  try {
1661
- code = bindScope(url, app, code, info.module);
1787
+ code = bindScope(url, app, code, info);
1662
1788
  if (app.inline || info.module) {
1663
1789
  runCode2InlineScript(url, code, info.module, replaceElement, dispatchScriptOnLoadEvent);
1664
1790
  }
@@ -1714,13 +1840,13 @@ function runCode2Function(code, info) {
1714
1840
  * @param url script address
1715
1841
  * @param app app
1716
1842
  * @param code code
1717
- * @param module type='module' of script
1843
+ * @param info source script info
1718
1844
  */
1719
- function bindScope(url, app, code, module) {
1845
+ function bindScope(url, app, code, info) {
1720
1846
  if (isPlainObject(microApp.plugins)) {
1721
- code = usePlugins(url, code, app.name, microApp.plugins);
1847
+ code = usePlugins(url, code, app.name, microApp.plugins, info);
1722
1848
  }
1723
- if (app.sandBox && !module) {
1849
+ if (app.sandBox && !info.module) {
1724
1850
  globalEnv.rawWindow.__MICRO_APP_PROXY_WINDOW__ = app.sandBox.proxyWindow;
1725
1851
  return `;(function(proxyWindow){with(proxyWindow.__MICRO_APP_WINDOW__){(function(${globalKeyToBeCached}){;${code}\n}).call(proxyWindow,${globalKeyToBeCached})}})(window.__MICRO_APP_PROXY_WINDOW__);`;
1726
1852
  }
@@ -1732,19 +1858,20 @@ function bindScope(url, app, code, module) {
1732
1858
  * @param code code
1733
1859
  * @param appName app name
1734
1860
  * @param plugins plugin list
1861
+ * @param info source script info
1735
1862
  */
1736
- function usePlugins(url, code, appName, plugins) {
1863
+ function usePlugins(url, code, appName, plugins, info) {
1737
1864
  var _a;
1738
- const newCode = processCode(plugins.global, code, url);
1739
- return processCode((_a = plugins.modules) === null || _a === void 0 ? void 0 : _a[appName], newCode, url);
1865
+ const newCode = processCode(plugins.global, code, url, info);
1866
+ return processCode((_a = plugins.modules) === null || _a === void 0 ? void 0 : _a[appName], newCode, url, info);
1740
1867
  }
1741
- function processCode(configs, code, url) {
1868
+ function processCode(configs, code, url, info) {
1742
1869
  if (!isArray(configs)) {
1743
1870
  return code;
1744
1871
  }
1745
1872
  return configs.reduce((preCode, config) => {
1746
1873
  if (isPlainObject(config) && isFunction(config.loader)) {
1747
- return config.loader(preCode, url, config.options);
1874
+ return config.loader(preCode, url, config.options, info);
1748
1875
  }
1749
1876
  return preCode;
1750
1877
  }, code);
@@ -1772,10 +1899,10 @@ function flatChildren(parent, app, microAppHead) {
1772
1899
  });
1773
1900
  for (const dom of children) {
1774
1901
  if (dom instanceof HTMLLinkElement) {
1775
- if (dom.hasAttribute('exclude')) {
1902
+ if (dom.hasAttribute('exclude') || checkExcludeUrl(dom.getAttribute('href'), app.name)) {
1776
1903
  parent.replaceChild(document.createComment('link element with exclude attribute ignored by micro-app'), dom);
1777
1904
  }
1778
- else if (!dom.hasAttribute('ignore')) {
1905
+ else if (!(dom.hasAttribute('ignore') || checkIgnoreUrl(dom.getAttribute('href'), app.name))) {
1779
1906
  extractLinkFromHtml(dom, parent, app);
1780
1907
  }
1781
1908
  else if (dom.hasAttribute('href')) {
@@ -1829,34 +1956,6 @@ function extractSourceDom(htmlStr, app) {
1829
1956
  app.onLoad(wrapElement);
1830
1957
  }
1831
1958
  }
1832
- /**
1833
- * Get and format html
1834
- * @param app app
1835
- */
1836
- function extractHtml(app) {
1837
- fetchSource(app.ssrUrl || app.url, app.name, { cache: 'no-cache' }).then((htmlStr) => {
1838
- if (!htmlStr) {
1839
- const msg = 'html is empty, please check in detail';
1840
- app.onerror(new Error(msg));
1841
- return logError(msg, app.name);
1842
- }
1843
- htmlStr = htmlStr
1844
- .replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
1845
- return match
1846
- .replace(/<head/i, '<micro-app-head')
1847
- .replace(/<\/head>/i, '</micro-app-head>');
1848
- })
1849
- .replace(/<body[^>]*>[\s\S]*?<\/body>/i, (match) => {
1850
- return match
1851
- .replace(/<body/i, '<micro-app-body')
1852
- .replace(/<\/body>/i, '</micro-app-body>');
1853
- });
1854
- extractSourceDom(htmlStr, app);
1855
- }).catch((e) => {
1856
- logError(`Failed to fetch data from ${app.url}, micro-app stop rendering`, app.name, e);
1857
- app.onLoadError(e);
1858
- });
1859
- }
1860
1959
 
1861
1960
  class EventCenter {
1862
1961
  constructor() {
@@ -2822,7 +2921,7 @@ class CreateApp {
2822
2921
  // Load resources
2823
2922
  loadSourceCode() {
2824
2923
  this.state = appStates.LOADING_SOURCE_CODE;
2825
- extractHtml(this);
2924
+ HTMLLoader.getInstance().run(this, extractSourceDom);
2826
2925
  }
2827
2926
  /**
2828
2927
  * When resource is loaded, mount app if it is not prefetch or unmount
@@ -3503,6 +3602,7 @@ function getGlobalAssets(assets) {
3503
3602
  });
3504
3603
  }
3505
3604
  }
3605
+ // TODO: requestIdleCallback for every file
3506
3606
  function fetchGlobalResources(resources, suffix, cache) {
3507
3607
  if (isArray(resources)) {
3508
3608
  const effectiveResource = resources.filter((path) => isString(path) && path.includes(`.${suffix}`) && !cache.has(path));