@micro-zoe/micro-app 0.8.5 → 0.8.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.5';
1
+ const version = '0.8.8';
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>~[.#:]+|$)/;
@@ -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
- else if ((script.type && !['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module'].includes(script.type)) ||
1442
- script.hasAttribute('ignore')) {
1528
+ else if ((script.type && !['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module', 'systemjs-module', 'systemjs-importmap'].includes(script.type)) ||
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
@@ -1773,10 +1899,10 @@ function flatChildren(parent, app, microAppHead) {
1773
1899
  });
1774
1900
  for (const dom of children) {
1775
1901
  if (dom instanceof HTMLLinkElement) {
1776
- if (dom.hasAttribute('exclude')) {
1902
+ if (dom.hasAttribute('exclude') || checkExcludeUrl(dom.getAttribute('href'), app.name)) {
1777
1903
  parent.replaceChild(document.createComment('link element with exclude attribute ignored by micro-app'), dom);
1778
1904
  }
1779
- else if (!dom.hasAttribute('ignore')) {
1905
+ else if (!(dom.hasAttribute('ignore') || checkIgnoreUrl(dom.getAttribute('href'), app.name))) {
1780
1906
  extractLinkFromHtml(dom, parent, app);
1781
1907
  }
1782
1908
  else if (dom.hasAttribute('href')) {
@@ -1830,34 +1956,6 @@ function extractSourceDom(htmlStr, app) {
1830
1956
  app.onLoad(wrapElement);
1831
1957
  }
1832
1958
  }
1833
- /**
1834
- * Get and format html
1835
- * @param app app
1836
- */
1837
- function extractHtml(app) {
1838
- fetchSource(app.ssrUrl || app.url, app.name, { cache: 'no-cache' }).then((htmlStr) => {
1839
- if (!htmlStr) {
1840
- const msg = 'html is empty, please check in detail';
1841
- app.onerror(new Error(msg));
1842
- return logError(msg, app.name);
1843
- }
1844
- htmlStr = htmlStr
1845
- .replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
1846
- return match
1847
- .replace(/<head/i, '<micro-app-head')
1848
- .replace(/<\/head>/i, '</micro-app-head>');
1849
- })
1850
- .replace(/<body[^>]*>[\s\S]*?<\/body>/i, (match) => {
1851
- return match
1852
- .replace(/<body/i, '<micro-app-body')
1853
- .replace(/<\/body>/i, '</micro-app-body>');
1854
- });
1855
- extractSourceDom(htmlStr, app);
1856
- }).catch((e) => {
1857
- logError(`Failed to fetch data from ${app.url}, micro-app stop rendering`, app.name, e);
1858
- app.onLoadError(e);
1859
- });
1860
- }
1861
1959
 
1862
1960
  class EventCenter {
1863
1961
  constructor() {
@@ -2823,7 +2921,7 @@ class CreateApp {
2823
2921
  // Load resources
2824
2922
  loadSourceCode() {
2825
2923
  this.state = appStates.LOADING_SOURCE_CODE;
2826
- extractHtml(this);
2924
+ HTMLLoader.getInstance().run(this, extractSourceDom);
2827
2925
  }
2828
2926
  /**
2829
2927
  * When resource is loaded, mount app if it is not prefetch or unmount