@micro-zoe/micro-app 0.8.4 → 0.8.7

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.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- const version = '0.8.4';
1
+ const version = '0.8.7';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -359,6 +359,66 @@ function fetchSource(url, appName = null, options = {}) {
359
359
  });
360
360
  }
361
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
+
362
422
  // common reg
363
423
  const rootSelectorREG = /(^|\s+)(html|:root)(?=[\s>~[.#:]+|$)/;
364
424
  const bodySelectorREG = /(^|\s+)((html[\s>~]+body)|body)(?=[\s>~[.#:]+|$)/;
@@ -372,7 +432,7 @@ function parseError(msg, linkPath) {
372
432
  throw err;
373
433
  }
374
434
  /**
375
- * Reference resources https://github.com/reworkcss/css
435
+ * Reference https://github.com/reworkcss/css
376
436
  * CSSParser mainly deals with 3 scenes: styleRule, @, and comment
377
437
  * And scopecss deals with 2 scenes: selector & url
378
438
  * And can also disable scopecss with inline comments
@@ -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