@micro-zoe/micro-app 0.4.2 → 0.5.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 = '0.4.2';
1
+ const version = '0.5.2';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -37,6 +37,10 @@ function isPromise(target) {
37
37
  function isBoundFunction(target) {
38
38
  return isFunction(target) && target.name.indexOf('bound ') === 0 && !target.hasOwnProperty('prototype');
39
39
  }
40
+ // is ShadowRoot
41
+ function isShadowRoot(target) {
42
+ return typeof ShadowRoot !== 'undefined' && target instanceof ShadowRoot;
43
+ }
40
44
  /**
41
45
  * format error log
42
46
  * @param msg message
@@ -81,10 +85,12 @@ function addProtocol(url) {
81
85
  return url.startsWith('//') ? `${location.protocol}${url}` : url;
82
86
  }
83
87
  /**
84
- * Format URL address
85
- * @param url address
88
+ * format URL address
89
+ * note the scenes:
90
+ * 1. micro-app -> attributeChangedCallback
91
+ * 2. preFetch
86
92
  */
87
- function formatURL(url, appName = null) {
93
+ function formatAppURL(url, appName = null) {
88
94
  if (!isString(url) || !url)
89
95
  return '';
90
96
  try {
@@ -101,6 +107,20 @@ function formatURL(url, appName = null) {
101
107
  return '';
102
108
  }
103
109
  }
110
+ /**
111
+ * format name
112
+ * note the scenes:
113
+ * 1. micro-app -> attributeChangedCallback
114
+ * 2. event_center -> EventCenterForMicroApp -> constructor
115
+ * 3. event_center -> EventCenterForBaseApp -> all methods
116
+ * 4. preFetch
117
+ * 5. plugins
118
+ */
119
+ function formatAppName(name) {
120
+ if (!isString(name) || !name)
121
+ return '';
122
+ return name.replace(/(^\d+)|([^\w\d-_])/gi, '');
123
+ }
104
124
  /**
105
125
  * Get valid address, such as https://xxx/xx/xx.html to https://xxx/xx/
106
126
  * @param url app.url
@@ -237,7 +257,7 @@ function pureCreateElement(tagName, options) {
237
257
  * @param target Accept cloned elements
238
258
  * @param deep deep clone or transfer dom
239
259
  */
240
- function cloneNode(origin, target, deep) {
260
+ function cloneContainer(origin, target, deep) {
241
261
  target.innerHTML = '';
242
262
  if (deep) {
243
263
  const clonedNode = origin.cloneNode(true);
@@ -253,6 +273,23 @@ function cloneNode(origin, target, deep) {
253
273
  });
254
274
  }
255
275
  }
276
+ // is invalid key of querySelector
277
+ function isInvalidQuerySelectorKey(key) {
278
+ return !key || /(^\d)|([^\w\d-_\u4e00-\u9fa5])/gi.test(key);
279
+ }
280
+ // unique element
281
+ function isUniqueElement(key) {
282
+ return (/^body$/i.test(key) ||
283
+ /^head$/i.test(key) ||
284
+ /^html$/i.test(key));
285
+ }
286
+ /**
287
+ * get micro-app element
288
+ * @param target app container
289
+ */
290
+ function getRootContainer(target) {
291
+ return (isShadowRoot(target) ? target.host : target);
292
+ }
256
293
 
257
294
  var ObservedAttrName;
258
295
  (function (ObservedAttrName) {
@@ -280,6 +317,21 @@ var lifeCycles;
280
317
  lifeCycles["ERROR"] = "error";
281
318
  })(lifeCycles || (lifeCycles = {}));
282
319
 
320
+ /**
321
+ * fetch source of html, js, css
322
+ * @param url source path
323
+ * @param appName app name
324
+ * @param config config of fetch
325
+ */
326
+ function fetchSource(url, appName = null, options = {}) {
327
+ if (isFunction(microApp.fetch)) {
328
+ return microApp.fetch(url, options, appName);
329
+ }
330
+ return fetch(url, options).then((res) => {
331
+ return res.text();
332
+ });
333
+ }
334
+
283
335
  const globalEnv = {};
284
336
  function initGlobalEnv() {
285
337
  if (isBrowser) {
@@ -466,33 +518,35 @@ function scopedRule(rules, prefix) {
466
518
  */
467
519
  function commonAction(templateStyle, styleElement, originContent, prefix, baseURI, linkpath) {
468
520
  var _a, _b;
469
- const rules = Array.from((_b = (_a = templateStyle.sheet) === null || _a === void 0 ? void 0 : _a.cssRules) !== null && _b !== void 0 ? _b : []);
470
- let result = scopedHost(scopedRule(rules, prefix), baseURI, originContent, linkpath);
471
- /**
472
- * Solve the problem of missing content quotes in some Safari browsers
473
- * docs: https://developer.mozilla.org/zh-CN/docs/Web/CSS/content
474
- * If there are still problems, it is recommended to use the attr()
475
- */
476
- if (isSafari()) {
477
- result = result.replace(/([;{]\s*content:\s*)([^\s"][^";}]*)/gm, (all, $1, $2) => {
478
- if ($2 === 'none' ||
479
- /^(url\()|(counter\()|(attr\()|(open-quote)|(close-quote)/.test($2)) {
480
- return all;
481
- }
482
- return `${$1}"${$2}"`;
483
- });
521
+ if (!styleElement.__MICRO_APP_HAS_SCOPED__) {
522
+ const rules = Array.from((_b = (_a = templateStyle.sheet) === null || _a === void 0 ? void 0 : _a.cssRules) !== null && _b !== void 0 ? _b : []);
523
+ let result = scopedHost(scopedRule(rules, prefix), baseURI, originContent, linkpath);
524
+ /**
525
+ * Solve the problem of missing content quotes in some Safari browsers
526
+ * docs: https://developer.mozilla.org/zh-CN/docs/Web/CSS/content
527
+ * If there are still problems, it is recommended to use the attr()
528
+ */
529
+ if (isSafari()) {
530
+ result = result.replace(/([;{]\s*content:\s*)([^\s"][^";}]*)/gm, (all, $1, $2) => {
531
+ if ($2 === 'none' ||
532
+ /^(url\()|(counter\()|(attr\()|(open-quote)|(close-quote)/.test($2)) {
533
+ return all;
534
+ }
535
+ return `${$1}"${$2}"`;
536
+ });
537
+ }
538
+ styleElement.textContent = result;
539
+ styleElement.__MICRO_APP_HAS_SCOPED__ = true;
484
540
  }
485
- styleElement.textContent = result;
486
541
  }
487
542
  /**
488
543
  * scopedCSS
489
544
  * @param styleElement target style element
490
545
  * @param appName app name
491
546
  */
492
- function scopedCSS(styleElement, appName) {
493
- const app = appInstanceMap.get(appName);
494
- if (app === null || app === void 0 ? void 0 : app.scopecss) {
495
- const prefix = `${microApp.tagName}[name=${appName}]`;
547
+ function scopedCSS(styleElement, app) {
548
+ if (app.scopecss) {
549
+ const prefix = `${microApp.tagName}[name=${app.name}]`;
496
550
  let templateStyle = globalEnv.templateStyle;
497
551
  if (!templateStyle) {
498
552
  globalEnv.templateStyle = templateStyle = pureCreateElement('style');
@@ -502,7 +556,7 @@ function scopedCSS(styleElement, appName) {
502
556
  }
503
557
  if (styleElement.textContent) {
504
558
  templateStyle.textContent = styleElement.textContent;
505
- commonAction(templateStyle, styleElement, styleElement.textContent, prefix, app.url, styleElement.linkpath);
559
+ commonAction(templateStyle, styleElement, styleElement.textContent, prefix, app.url, styleElement.__MICRO_APP_LINK_PATH__);
506
560
  templateStyle.textContent = '';
507
561
  }
508
562
  else {
@@ -513,7 +567,7 @@ function scopedCSS(styleElement, appName) {
513
567
  if ((!styleElement.textContent && ((_b = (_a = styleElement.sheet) === null || _a === void 0 ? void 0 : _a.cssRules) === null || _b === void 0 ? void 0 : _b.length)) ||
514
568
  styleElement.hasAttribute('data-styled'))
515
569
  return;
516
- commonAction(styleElement, styleElement, styleElement.textContent, prefix, app.url, styleElement.linkpath);
570
+ commonAction(styleElement, styleElement, styleElement.textContent, prefix, app.url, styleElement.__MICRO_APP_LINK_PATH__);
517
571
  });
518
572
  observer.observe(styleElement, { childList: true });
519
573
  }
@@ -571,7 +625,7 @@ const globalLinks = new Map();
571
625
  * @param microAppHead micro-app-head element
572
626
  * @param isDynamic dynamic insert
573
627
  */
574
- function extractLinkFromHtml(link, parent, app, microAppHead, isDynamic = false) {
628
+ function extractLinkFromHtml(link, parent, app, isDynamic = false) {
575
629
  const rel = link.getAttribute('rel');
576
630
  let href = link.getAttribute('href');
577
631
  let replaceComment = null;
@@ -579,12 +633,9 @@ function extractLinkFromHtml(link, parent, app, microAppHead, isDynamic = false)
579
633
  href = CompletionPath(href, app.url);
580
634
  if (!isDynamic) {
581
635
  replaceComment = document.createComment(`link element with href=${href} move to micro-app-head as style element`);
582
- const placeholderComment = document.createComment(`placeholder for link with href=${href}`);
583
- // all style elements insert into microAppHead
584
- microAppHead.appendChild(placeholderComment);
585
636
  app.source.links.set(href, {
586
637
  code: '',
587
- placeholder: placeholderComment,
638
+ placeholder: replaceComment,
588
639
  isGlobal: link.hasAttribute('global'),
589
640
  });
590
641
  }
@@ -599,7 +650,7 @@ function extractLinkFromHtml(link, parent, app, microAppHead, isDynamic = false)
599
650
  }
600
651
  }
601
652
  else if (rel && ['prefetch', 'preload', 'prerender', 'icon', 'apple-touch-icon'].includes(rel)) {
602
- // preload prefetch icon ....
653
+ // preload prefetch icon ....
603
654
  if (isDynamic) {
604
655
  replaceComment = document.createComment(`link element with rel=${rel}${href ? ' & href=' + href : ''} removed by micro-app`);
605
656
  }
@@ -653,8 +704,9 @@ function fetchLinkSuccess(url, info, data, microAppHead, app) {
653
704
  }
654
705
  const styleLink = pureCreateElement('style');
655
706
  styleLink.textContent = data;
656
- styleLink.linkpath = url;
657
- microAppHead.replaceChild(scopedCSS(styleLink, app.name), info.placeholder);
707
+ styleLink.__MICRO_APP_LINK_PATH__ = url;
708
+ styleLink.setAttribute('data-origin-href', url);
709
+ microAppHead.replaceChild(scopedCSS(styleLink, app), info.placeholder);
658
710
  info.placeholder = null;
659
711
  info.code = data;
660
712
  }
@@ -669,7 +721,7 @@ function fetchLinkSuccess(url, info, data, microAppHead, app) {
669
721
  function foramtDynamicLink(url, info, app, originLink, replaceStyle) {
670
722
  if (app.source.links.has(url)) {
671
723
  replaceStyle.textContent = app.source.links.get(url).code;
672
- scopedCSS(replaceStyle, app.name);
724
+ scopedCSS(replaceStyle, app);
673
725
  defer(() => dispatchOnLoadEvent(originLink));
674
726
  return;
675
727
  }
@@ -678,17 +730,16 @@ function foramtDynamicLink(url, info, app, originLink, replaceStyle) {
678
730
  info.code = code;
679
731
  app.source.links.set(url, info);
680
732
  replaceStyle.textContent = code;
681
- scopedCSS(replaceStyle, app.name);
733
+ scopedCSS(replaceStyle, app);
682
734
  defer(() => dispatchOnLoadEvent(originLink));
683
735
  return;
684
736
  }
685
737
  fetchSource(url, app.name).then((data) => {
686
738
  info.code = data;
687
739
  app.source.links.set(url, info);
688
- if (info.isGlobal)
689
- globalLinks.set(url, data);
740
+ info.isGlobal && globalLinks.set(url, data);
690
741
  replaceStyle.textContent = data;
691
- scopedCSS(replaceStyle, app.name);
742
+ scopedCSS(replaceStyle, app);
692
743
  dispatchOnLoadEvent(originLink);
693
744
  }).catch((err) => {
694
745
  logError(err, app.name);
@@ -828,6 +879,7 @@ function execScripts(scriptList, app, initedHook) {
828
879
  const deferScriptInfo = [];
829
880
  for (const [url, info] of scriptListEntries) {
830
881
  if (!info.isDynamic) {
882
+ // Notice the second render
831
883
  if (info.defer || info.async) {
832
884
  if (info.isExternal && !info.code) {
833
885
  deferScriptPromise.push(fetchSource(url, app.name));
@@ -836,11 +888,10 @@ function execScripts(scriptList, app, initedHook) {
836
888
  deferScriptPromise.push(info.code);
837
889
  }
838
890
  deferScriptInfo.push([url, info]);
839
- if (info.module)
840
- initedHook.moduleCount = initedHook.moduleCount ? ++initedHook.moduleCount : 1;
891
+ info.module && (initedHook.moduleCount = initedHook.moduleCount ? ++initedHook.moduleCount : 1);
841
892
  }
842
893
  else {
843
- runScript(url, info.code, app, info.module, false);
894
+ runScript(url, app, info, false);
844
895
  initedHook(false);
845
896
  }
846
897
  }
@@ -849,9 +900,9 @@ function execScripts(scriptList, app, initedHook) {
849
900
  Promise.all(deferScriptPromise).then((res) => {
850
901
  res.forEach((code, index) => {
851
902
  const [url, info] = deferScriptInfo[index];
852
- runScript(url, info.code = info.code || code, app, info.module, false, initedHook);
853
- if (!info.module)
854
- initedHook(false);
903
+ info.code = info.code || code;
904
+ runScript(url, app, info, false, initedHook);
905
+ !info.module && initedHook(false);
855
906
  });
856
907
  initedHook(isUndefined(initedHook.moduleCount));
857
908
  }).catch((err) => {
@@ -866,26 +917,25 @@ function execScripts(scriptList, app, initedHook) {
866
917
  /**
867
918
  * run code
868
919
  * @param url script address
869
- * @param code js code
870
920
  * @param app app
871
- * @param module type='module' of script
921
+ * @param info script info
872
922
  * @param isDynamic dynamically created script
873
923
  * @param callback callback of module script
874
924
  */
875
- function runScript(url, code, app, module, isDynamic, callback) {
925
+ function runScript(url, app, info, isDynamic, callback) {
876
926
  var _a;
877
927
  try {
878
- code = bindScope(url, code, app, module);
879
- if (app.inline || module) {
928
+ const code = bindScope(url, app, info.code, info.module);
929
+ if (app.inline || info.module) {
880
930
  const scriptElement = pureCreateElement('script');
881
- setInlinScriptContent(url, code, module, scriptElement, callback);
931
+ runCode2InlineScript(url, code, info.module, scriptElement, callback);
882
932
  if (isDynamic)
883
933
  return scriptElement;
884
934
  // TEST IGNORE
885
935
  (_a = app.container) === null || _a === void 0 ? void 0 : _a.querySelector('micro-app-body').appendChild(scriptElement);
886
936
  }
887
937
  else {
888
- Function(code)();
938
+ runCode2Function(code, info);
889
939
  if (isDynamic)
890
940
  return document.createComment('dynamic script extract by micro-app');
891
941
  }
@@ -903,19 +953,18 @@ function runScript(url, code, app, module, isDynamic, callback) {
903
953
  */
904
954
  function runDynamicRemoteScript(url, info, app, originScript) {
905
955
  const dispatchScriptOnLoadEvent = () => dispatchOnLoadEvent(originScript);
956
+ // url is unique
906
957
  if (app.source.scripts.has(url)) {
907
958
  const existInfo = app.source.scripts.get(url);
908
- if (!info.module)
909
- defer(dispatchScriptOnLoadEvent);
910
- return runScript(url, existInfo.code, app, info.module, true, dispatchScriptOnLoadEvent);
959
+ !existInfo.module && defer(dispatchScriptOnLoadEvent);
960
+ return runScript(url, app, existInfo, true, dispatchScriptOnLoadEvent);
911
961
  }
912
962
  if (globalScripts.has(url)) {
913
963
  const code = globalScripts.get(url);
914
964
  info.code = code;
915
965
  app.source.scripts.set(url, info);
916
- if (!info.module)
917
- defer(dispatchScriptOnLoadEvent);
918
- return runScript(url, code, app, info.module, true, dispatchScriptOnLoadEvent);
966
+ !info.module && defer(dispatchScriptOnLoadEvent);
967
+ return runScript(url, app, info, true, dispatchScriptOnLoadEvent);
919
968
  }
920
969
  let replaceElement;
921
970
  if (app.inline || info.module) {
@@ -927,22 +976,20 @@ function runDynamicRemoteScript(url, info, app, originScript) {
927
976
  fetchSource(url, app.name).then((code) => {
928
977
  info.code = code;
929
978
  app.source.scripts.set(url, info);
930
- if (info.isGlobal)
931
- globalScripts.set(url, code);
979
+ info.isGlobal && globalScripts.set(url, code);
932
980
  try {
933
- code = bindScope(url, code, app, info.module);
981
+ code = bindScope(url, app, code, info.module);
934
982
  if (app.inline || info.module) {
935
- setInlinScriptContent(url, code, info.module, replaceElement, dispatchScriptOnLoadEvent);
983
+ runCode2InlineScript(url, code, info.module, replaceElement, dispatchScriptOnLoadEvent);
936
984
  }
937
985
  else {
938
- Function(code)();
986
+ runCode2Function(code, info);
939
987
  }
940
988
  }
941
989
  catch (e) {
942
990
  console.error(`[micro-app from runDynamicScript] app ${app.name}: `, e, url);
943
991
  }
944
- if (!info.module)
945
- dispatchOnLoadEvent(originScript);
992
+ !info.module && dispatchOnLoadEvent(originScript);
946
993
  }).catch((err) => {
947
994
  logError(err, app.name);
948
995
  dispatchOnErrorEvent(originScript);
@@ -952,20 +999,17 @@ function runDynamicRemoteScript(url, info, app, originScript) {
952
999
  /**
953
1000
  * common handle for inline script
954
1001
  * @param url script address
955
- * @param code js code
1002
+ * @param code bound code
956
1003
  * @param module type='module' of script
957
1004
  * @param scriptElement target script element
958
1005
  * @param callback callback of module script
959
1006
  */
960
- function setInlinScriptContent(url, code, module, scriptElement, callback) {
1007
+ function runCode2InlineScript(url, code, module, scriptElement, callback) {
961
1008
  if (module) {
962
1009
  // module script is async, transform it to a blob for subsequent operations
963
1010
  const blob = new Blob([code], { type: 'text/javascript' });
964
1011
  scriptElement.src = URL.createObjectURL(blob);
965
1012
  scriptElement.setAttribute('type', 'module');
966
- if (!url.startsWith('inline-')) {
967
- scriptElement.setAttribute('originSrc', url);
968
- }
969
1013
  if (callback) {
970
1014
  callback.moduleCount && callback.moduleCount--;
971
1015
  scriptElement.onload = callback.bind(scriptElement, callback.moduleCount === 0);
@@ -974,15 +1018,25 @@ function setInlinScriptContent(url, code, module, scriptElement, callback) {
974
1018
  else {
975
1019
  scriptElement.textContent = code;
976
1020
  }
1021
+ if (!url.startsWith('inline-')) {
1022
+ scriptElement.setAttribute('data-origin-src', url);
1023
+ }
1024
+ }
1025
+ // init & run code2Function
1026
+ function runCode2Function(code, info) {
1027
+ if (!info.code2Function) {
1028
+ info.code2Function = new Function(code);
1029
+ }
1030
+ info.code2Function.call(window);
977
1031
  }
978
1032
  /**
979
1033
  * bind js scope
980
1034
  * @param url script address
981
- * @param code code
982
1035
  * @param app app
1036
+ * @param code code
983
1037
  * @param module type='module' of script
984
1038
  */
985
- function bindScope(url, code, app, module) {
1039
+ function bindScope(url, app, code, module) {
986
1040
  if (isPlainObject(microApp.plugins)) {
987
1041
  code = usePlugins(url, code, app.name, microApp.plugins);
988
1042
  }
@@ -1018,713 +1072,411 @@ function usePlugins(url, code, appName, plugins) {
1018
1072
  return code;
1019
1073
  }
1020
1074
 
1021
- // Record element and map element
1022
- const dynamicElementInMicroAppMap = new WeakMap();
1023
1075
  /**
1024
- * Process the new node and format the style, link and script element
1025
- * @param parent parent node
1026
- * @param child new node
1027
- * @param app app
1076
+ * transform html string to dom
1077
+ * @param str string dom
1028
1078
  */
1029
- function handleNewNode(parent, child, app) {
1030
- if (child instanceof HTMLStyleElement) {
1031
- if (child.hasAttribute('exclude')) {
1032
- const replaceComment = document.createComment('style element with exclude attribute ignored by micro-app');
1033
- dynamicElementInMicroAppMap.set(child, replaceComment);
1034
- return replaceComment;
1035
- }
1036
- else if (app.scopecss && !child.hasAttribute('ignore')) {
1037
- return scopedCSS(child, app.name);
1038
- }
1039
- return child;
1040
- }
1041
- else if (child instanceof HTMLLinkElement) {
1042
- if (child.hasAttribute('exclude')) {
1043
- const linkReplaceComment = document.createComment('link element with exclude attribute ignored by micro-app');
1044
- dynamicElementInMicroAppMap.set(child, linkReplaceComment);
1045
- return linkReplaceComment;
1046
- }
1047
- else if (!app.scopecss || child.hasAttribute('ignore')) {
1048
- return child;
1049
- }
1050
- const { url, info, replaceComment } = extractLinkFromHtml(child, parent, app, null, true);
1051
- if (url && info) {
1052
- const replaceStyle = pureCreateElement('style');
1053
- replaceStyle.linkpath = url;
1054
- foramtDynamicLink(url, info, app, child, replaceStyle);
1055
- dynamicElementInMicroAppMap.set(child, replaceStyle);
1056
- return replaceStyle;
1057
- }
1058
- else if (replaceComment) {
1059
- dynamicElementInMicroAppMap.set(child, replaceComment);
1060
- return replaceComment;
1061
- }
1062
- return child;
1063
- }
1064
- else if (child instanceof HTMLScriptElement) {
1065
- const { replaceComment, url, info } = extractScriptElement(child, parent, app, true) || {};
1066
- if (url && info) {
1067
- if (info.code) { // inline script
1068
- const replaceElement = runScript(url, info.code, app, info.module, true);
1069
- dynamicElementInMicroAppMap.set(child, replaceElement);
1070
- return replaceElement;
1071
- }
1072
- else { // remote script
1073
- const replaceElement = runDynamicRemoteScript(url, info, app, child);
1074
- dynamicElementInMicroAppMap.set(child, replaceElement);
1075
- return replaceElement;
1076
- }
1077
- }
1078
- else if (replaceComment) {
1079
- dynamicElementInMicroAppMap.set(child, replaceComment);
1080
- return replaceComment;
1081
- }
1082
- return child;
1083
- }
1084
- return child;
1079
+ function getWrapElement(str) {
1080
+ const wrapDiv = pureCreateElement('div');
1081
+ wrapDiv.innerHTML = str;
1082
+ return wrapDiv;
1085
1083
  }
1086
1084
  /**
1087
- * Handle the elements inserted into head and body, and execute normally in other cases
1085
+ * Recursively process each child element
1086
+ * @param parent parent element
1088
1087
  * @param app app
1089
- * @param method raw method
1090
- * @param parent parent node
1091
- * @param targetChild target node
1092
- * @param passiveChild second param of insertBefore and replaceChild
1088
+ * @param microAppHead micro-app-head element
1093
1089
  */
1094
- function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
1095
- /**
1096
- * If passiveChild is not the child node, insertBefore replaceChild will have a problem, at this time, it will be degraded to appendChild
1097
- * E.g: document.head.insertBefore(targetChild, document.head.childNodes[0])
1098
- */
1099
- if (parent === document.head) {
1100
- const microAppHead = app.container.querySelector('micro-app-head');
1101
- /**
1102
- * 1. If passivechild exists, it must be insertBefore or replacechild
1103
- * 2. When removeChild, targetChild may not be in microAppHead or head
1104
- */
1105
- if (passiveChild && !microAppHead.contains(passiveChild)) {
1106
- return globalEnv.rawAppendChild.call(microAppHead, targetChild);
1107
- }
1108
- else if (rawMethod === globalEnv.rawRemoveChild && !microAppHead.contains(targetChild)) {
1109
- if (parent.contains(targetChild)) {
1110
- return rawMethod.call(parent, targetChild);
1090
+ function flatChildren(parent, app, microAppHead) {
1091
+ const children = Array.from(parent.children);
1092
+ children.length && children.forEach((child) => {
1093
+ flatChildren(child, app);
1094
+ });
1095
+ for (const dom of children) {
1096
+ if (dom instanceof HTMLLinkElement) {
1097
+ if (dom.hasAttribute('exclude')) {
1098
+ parent.replaceChild(document.createComment('link element with exclude attribute ignored by micro-app'), dom);
1099
+ }
1100
+ else if (!dom.hasAttribute('ignore')) {
1101
+ extractLinkFromHtml(dom, parent, app);
1102
+ }
1103
+ else if (dom.hasAttribute('href')) {
1104
+ dom.setAttribute('href', CompletionPath(dom.getAttribute('href'), app.url));
1111
1105
  }
1112
- return targetChild;
1113
1106
  }
1114
- else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
1115
- return rawMethod.call(microAppHead, targetChild);
1107
+ else if (dom instanceof HTMLStyleElement) {
1108
+ if (dom.hasAttribute('exclude')) {
1109
+ parent.replaceChild(document.createComment('style element with exclude attribute ignored by micro-app'), dom);
1110
+ }
1111
+ else if (app.scopecss && !dom.hasAttribute('ignore')) {
1112
+ scopedCSS(dom, app);
1113
+ }
1116
1114
  }
1117
- return rawMethod.call(microAppHead, targetChild, passiveChild);
1118
- }
1119
- else if (parent === document.body) {
1120
- const microAppBody = app.container.querySelector('micro-app-body');
1121
- if (passiveChild && !microAppBody.contains(passiveChild)) {
1122
- return globalEnv.rawAppendChild.call(microAppBody, targetChild);
1115
+ else if (dom instanceof HTMLScriptElement) {
1116
+ extractScriptElement(dom, parent, app);
1123
1117
  }
1124
- else if (rawMethod === globalEnv.rawRemoveChild && !microAppBody.contains(targetChild)) {
1125
- if (parent.contains(targetChild)) {
1126
- return rawMethod.call(parent, targetChild);
1127
- }
1128
- return targetChild;
1118
+ else if (dom instanceof HTMLMetaElement || dom instanceof HTMLTitleElement) {
1119
+ parent.removeChild(dom);
1129
1120
  }
1130
- else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
1131
- return rawMethod.call(microAppBody, targetChild);
1121
+ else if (dom instanceof HTMLImageElement && dom.hasAttribute('src')) {
1122
+ dom.setAttribute('src', CompletionPath(dom.getAttribute('src'), app.url));
1132
1123
  }
1133
- return rawMethod.call(microAppBody, targetChild, passiveChild);
1134
1124
  }
1135
- else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
1136
- return rawMethod.call(parent, targetChild);
1137
- }
1138
- return rawMethod.call(parent, targetChild, passiveChild);
1139
- }
1140
- // Get the map element
1141
- function getMappingNode(node) {
1142
- var _a;
1143
- return (_a = dynamicElementInMicroAppMap.get(node)) !== null && _a !== void 0 ? _a : node;
1144
1125
  }
1145
1126
  /**
1146
- * method of handle new node
1147
- * @param parent parent node
1148
- * @param newChild new node
1149
- * @param passiveChild passive node
1150
- * @param rawMethodraw method
1127
+ * Extract link and script, bind style scope
1128
+ * @param htmlStr html string
1129
+ * @param app app
1151
1130
  */
1152
- function commonElementHander(parent, newChild, passiveChild, rawMethod) {
1153
- if (newChild === null || newChild === void 0 ? void 0 : newChild.__MICRO_APP_NAME__) {
1154
- const app = appInstanceMap.get(newChild.__MICRO_APP_NAME__);
1155
- if (app === null || app === void 0 ? void 0 : app.container) {
1156
- return invokePrototypeMethod(app, rawMethod, parent, handleNewNode(parent, newChild, app), passiveChild && getMappingNode(passiveChild));
1157
- }
1158
- else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
1159
- return rawMethod.call(parent, newChild);
1160
- }
1161
- return rawMethod.call(parent, newChild, passiveChild);
1131
+ function extractSourceDom(htmlStr, app) {
1132
+ const wrapElement = getWrapElement(htmlStr);
1133
+ const microAppHead = wrapElement.querySelector('micro-app-head');
1134
+ const microAppBody = wrapElement.querySelector('micro-app-body');
1135
+ if (!microAppHead || !microAppBody) {
1136
+ const msg = `element ${microAppHead ? 'body' : 'head'} is missing`;
1137
+ app.onerror(new Error(msg));
1138
+ return logError(msg, app.name);
1162
1139
  }
1163
- else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
1164
- const appName = getCurrentAppName();
1165
- if (!(newChild instanceof Node) && appName) {
1166
- const app = appInstanceMap.get(appName);
1167
- if (app === null || app === void 0 ? void 0 : app.container) {
1168
- if (parent === document.head) {
1169
- return rawMethod.call(app.container.querySelector('micro-app-head'), newChild);
1170
- }
1171
- else if (parent === document.body) {
1172
- return rawMethod.call(app.container.querySelector('micro-app-body'), newChild);
1173
- }
1174
- }
1175
- }
1176
- return rawMethod.call(parent, newChild);
1140
+ flatChildren(wrapElement, app);
1141
+ if (app.source.links.size) {
1142
+ fetchLinksFromHtml(wrapElement, app, microAppHead);
1143
+ }
1144
+ else {
1145
+ app.onLoad(wrapElement);
1146
+ }
1147
+ if (app.source.scripts.size) {
1148
+ fetchScriptsFromHtml(wrapElement, app);
1149
+ }
1150
+ else {
1151
+ app.onLoad(wrapElement);
1177
1152
  }
1178
- return rawMethod.call(parent, newChild, passiveChild);
1179
1153
  }
1180
1154
  /**
1181
- * Rewrite element prototype method
1155
+ * Get and format html
1156
+ * @param app app
1182
1157
  */
1183
- function patchElementPrototypeMethods() {
1184
- patchDocument();
1185
- // Rewrite setAttribute
1186
- Element.prototype.setAttribute = function setAttribute(key, value) {
1187
- if (/^micro-app(-\S+)?/i.test(this.tagName) && key === 'data') {
1188
- if (isPlainObject(value)) {
1189
- const cloneValue = {};
1190
- Object.getOwnPropertyNames(value).forEach((propertyKey) => {
1191
- if (!(isString(propertyKey) && propertyKey.indexOf('__') === 0)) {
1192
- // @ts-ignore
1193
- cloneValue[propertyKey] = value[propertyKey];
1194
- }
1195
- });
1196
- this.data = cloneValue;
1197
- }
1198
- else if (value !== '[object Object]') {
1199
- logWarn('property data must be an object', this.getAttribute('name'));
1200
- }
1201
- }
1202
- else if (((key === 'src' && /^(img|script)$/i.test(this.tagName)) ||
1203
- (key === 'href' && /^link$/i.test(this.tagName))) &&
1204
- this.__MICRO_APP_NAME__ &&
1205
- appInstanceMap.has(this.__MICRO_APP_NAME__)) {
1206
- const app = appInstanceMap.get(this.__MICRO_APP_NAME__);
1207
- globalEnv.rawSetAttribute.call(this, key, CompletionPath(value, app.url));
1208
- }
1209
- else {
1210
- globalEnv.rawSetAttribute.call(this, key, value);
1211
- }
1212
- };
1213
- // prototype methods of add element👇
1214
- Node.prototype.appendChild = function appendChild(newChild) {
1215
- return commonElementHander(this, newChild, null, globalEnv.rawAppendChild);
1216
- };
1217
- Node.prototype.insertBefore = function insertBefore(newChild, refChild) {
1218
- return commonElementHander(this, newChild, refChild, globalEnv.rawInsertBefore);
1219
- };
1220
- Node.prototype.replaceChild = function replaceChild(newChild, oldChild) {
1221
- return commonElementHander(this, newChild, oldChild, globalEnv.rawReplaceChild);
1222
- };
1223
- Element.prototype.append = function append(...nodes) {
1224
- let i = 0;
1225
- const length = nodes.length;
1226
- while (i < length) {
1227
- commonElementHander(this, nodes[i], null, globalEnv.rawAppend);
1228
- i++;
1229
- }
1230
- };
1231
- Element.prototype.prepend = function prepend(...nodes) {
1232
- let i = nodes.length;
1233
- while (i > 0) {
1234
- commonElementHander(this, nodes[i - 1], null, globalEnv.rawPrepend);
1235
- i--;
1236
- }
1237
- };
1238
- // prototype methods of delete element👇
1239
- Node.prototype.removeChild = function removeChild(oldChild) {
1240
- if (oldChild === null || oldChild === void 0 ? void 0 : oldChild.__MICRO_APP_NAME__) {
1241
- const app = appInstanceMap.get(oldChild.__MICRO_APP_NAME__);
1242
- if (app === null || app === void 0 ? void 0 : app.container) {
1243
- return invokePrototypeMethod(app, globalEnv.rawRemoveChild, this, getMappingNode(oldChild));
1244
- }
1245
- return globalEnv.rawRemoveChild.call(this, oldChild);
1158
+ function extractHtml(app) {
1159
+ fetchSource(app.ssrUrl || app.url, app.name, { cache: 'no-cache' }).then((htmlStr) => {
1160
+ if (!htmlStr) {
1161
+ const msg = 'html is empty, please check in detail';
1162
+ app.onerror(new Error(msg));
1163
+ return logError(msg, app.name);
1246
1164
  }
1247
- return globalEnv.rawRemoveChild.call(this, oldChild);
1248
- };
1165
+ htmlStr = htmlStr
1166
+ .replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
1167
+ return match
1168
+ .replace(/<head/i, '<micro-app-head')
1169
+ .replace(/<\/head>/i, '</micro-app-head>');
1170
+ })
1171
+ .replace(/<body[^>]*>[\s\S]*?<\/body>/i, (match) => {
1172
+ return match
1173
+ .replace(/<body/i, '<micro-app-body')
1174
+ .replace(/<\/body>/i, '</micro-app-body>');
1175
+ });
1176
+ extractSourceDom(htmlStr, app);
1177
+ }).catch((e) => {
1178
+ logError(`Failed to fetch data from ${app.url}, micro-app stop rendering`, app.name, e);
1179
+ app.onLoadError(e);
1180
+ });
1249
1181
  }
1250
- /**
1251
- * Mark the newly created element in the micro application
1252
- * @param element new element
1253
- */
1254
- function markElement(element) {
1255
- const appName = getCurrentAppName();
1256
- if (appName) {
1257
- element.__MICRO_APP_NAME__ = appName;
1182
+
1183
+ const boundedMap = new WeakMap();
1184
+ function isBoundedFunction(value) {
1185
+ if (boundedMap.has(value)) {
1186
+ return boundedMap.get(value);
1258
1187
  }
1259
- return element;
1188
+ // bind function
1189
+ const boundFunction = isBoundFunction(value);
1190
+ boundedMap.set(value, boundFunction);
1191
+ return boundFunction;
1260
1192
  }
1261
- // methods of document
1262
- function patchDocument() {
1263
- const rawDocument = globalEnv.rawDocument;
1264
- // create element 👇
1265
- Document.prototype.createElement = function createElement(tagName, options) {
1266
- const _this = this && rawDocument !== this ? this : rawDocument;
1267
- const element = globalEnv.rawCreateElement.call(_this, tagName, options);
1268
- return markElement(element);
1269
- };
1270
- Document.prototype.createElementNS = function createElementNS(namespaceURI, name, options) {
1271
- const _this = this && rawDocument !== this ? this : rawDocument;
1272
- const element = globalEnv.rawCreateElementNS.call(_this, namespaceURI, name, options);
1273
- return markElement(element);
1274
- };
1275
- Document.prototype.createDocumentFragment = function createDocumentFragment() {
1276
- const _this = this && rawDocument !== this ? this : rawDocument;
1277
- const element = globalEnv.rawCreateDocumentFragment.call(_this);
1278
- return markElement(element);
1279
- };
1280
- // query element👇
1281
- function querySelector(selectors) {
1282
- var _a, _b, _c;
1283
- /**
1284
- * The hijacking of queryselector should only occur on rawDocument, we should ignore document created by new DOMParser().parseFromString()
1285
- * see https://github.com/micro-zoe/micro-app/issues/56
1286
- */
1287
- if (this && rawDocument !== this)
1288
- return globalEnv.rawQuerySelector.call(this, selectors);
1289
- const appName = getCurrentAppName();
1290
- if (!appName || selectors === 'head' || selectors === 'body' || selectors === 'html') {
1291
- return globalEnv.rawQuerySelector.call(rawDocument, selectors);
1292
- }
1293
- return (_c = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) === null || _b === void 0 ? void 0 : _b.querySelector(selectors)) !== null && _c !== void 0 ? _c : null;
1193
+ const constructorMap = new WeakMap();
1194
+ function isConstructor(value) {
1195
+ if (constructorMap.has(value)) {
1196
+ return constructorMap.get(value);
1294
1197
  }
1295
- function querySelectorAll(selectors) {
1296
- var _a, _b, _c;
1297
- if (this && rawDocument !== this)
1298
- return globalEnv.rawQuerySelectorAll.call(this, selectors);
1299
- const appName = getCurrentAppName();
1300
- if (!appName || selectors === 'head' || selectors === 'body' || selectors === 'html') {
1301
- return globalEnv.rawQuerySelectorAll.call(rawDocument, selectors);
1302
- }
1303
- return (_c = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) === null || _b === void 0 ? void 0 : _b.querySelectorAll(selectors)) !== null && _c !== void 0 ? _c : [];
1198
+ const valueStr = value.toString();
1199
+ const result = (value.prototype &&
1200
+ value.prototype.constructor === value &&
1201
+ Object.getOwnPropertyNames(value.prototype).length > 1) ||
1202
+ /^function\s+[A-Z]/.test(valueStr) ||
1203
+ /^class\s+/.test(valueStr);
1204
+ constructorMap.set(value, result);
1205
+ return result;
1206
+ }
1207
+ const rawWindowMethodMap = new WeakMap();
1208
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
1209
+ function bindFunctionToRawWidow(rawWindow, value) {
1210
+ if (rawWindowMethodMap.has(value)) {
1211
+ return rawWindowMethodMap.get(value);
1304
1212
  }
1305
- Document.prototype.querySelector = querySelector;
1306
- Document.prototype.querySelectorAll = querySelectorAll;
1307
- // querySelector does not support the beginning of a number
1308
- Document.prototype.getElementById = function getElementById(key) {
1309
- const appName = getCurrentAppName();
1310
- if (!appName || /^\d/.test(key)) {
1311
- const _this = this && rawDocument !== this ? this : rawDocument;
1312
- return globalEnv.rawGetElementById.call(_this, key);
1313
- }
1314
- return querySelector.call(this, `#${key}`);
1315
- };
1316
- Document.prototype.getElementsByClassName = function getElementsByClassName(key) {
1317
- const appName = getCurrentAppName();
1318
- if (!appName || /^\d/.test(key)) {
1319
- const _this = this && rawDocument !== this ? this : rawDocument;
1320
- return globalEnv.rawGetElementsByClassName.call(_this, key);
1321
- }
1322
- return querySelectorAll.call(this, `.${key}`);
1323
- };
1324
- Document.prototype.getElementsByTagName = function getElementsByTagName(key) {
1325
- var _a;
1326
- const appName = getCurrentAppName();
1327
- if (!appName ||
1328
- /^body$/i.test(key) ||
1329
- /^head$/i.test(key) ||
1330
- /^html$/i.test(key) ||
1331
- (!((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.inline) && /^script$/i.test(key))) {
1332
- const _this = this && rawDocument !== this ? this : rawDocument;
1333
- return globalEnv.rawGetElementsByTagName.call(_this, key);
1213
+ if (isFunction(value) && !isConstructor(value) && !isBoundedFunction(value)) {
1214
+ const bindRawWindowValue = value.bind(rawWindow);
1215
+ for (const key in value) {
1216
+ bindRawWindowValue[key] = value[key];
1334
1217
  }
1335
- return querySelectorAll.call(this, key);
1336
- };
1337
- Document.prototype.getElementsByName = function getElementsByName(key) {
1338
- const appName = getCurrentAppName();
1339
- if (!appName || /^\d/.test(key)) {
1340
- const _this = this && rawDocument !== this ? this : rawDocument;
1341
- return globalEnv.rawGetElementsByName.call(_this, key);
1218
+ if (value.hasOwnProperty('prototype') && !bindRawWindowValue.hasOwnProperty('prototype')) {
1219
+ bindRawWindowValue.prototype = value.prototype;
1342
1220
  }
1343
- return querySelectorAll.call(this, `[name=${key}]`);
1344
- };
1345
- }
1346
- function releasePatchDocument() {
1347
- Document.prototype.createElement = globalEnv.rawCreateElement;
1348
- Document.prototype.createElementNS = globalEnv.rawCreateElementNS;
1349
- Document.prototype.createDocumentFragment = globalEnv.rawCreateDocumentFragment;
1350
- Document.prototype.querySelector = globalEnv.rawQuerySelector;
1351
- Document.prototype.querySelectorAll = globalEnv.rawQuerySelectorAll;
1352
- Document.prototype.getElementById = globalEnv.rawGetElementById;
1353
- Document.prototype.getElementsByClassName = globalEnv.rawGetElementsByClassName;
1354
- Document.prototype.getElementsByTagName = globalEnv.rawGetElementsByTagName;
1355
- Document.prototype.getElementsByName = globalEnv.rawGetElementsByName;
1356
- }
1357
- // release patch
1358
- function releasePatches() {
1359
- setCurrentAppName(null);
1360
- releasePatchDocument();
1361
- Element.prototype.setAttribute = globalEnv.rawSetAttribute;
1362
- Node.prototype.appendChild = globalEnv.rawAppendChild;
1363
- Node.prototype.insertBefore = globalEnv.rawInsertBefore;
1364
- Node.prototype.replaceChild = globalEnv.rawReplaceChild;
1365
- Node.prototype.removeChild = globalEnv.rawRemoveChild;
1366
- Element.prototype.append = globalEnv.rawAppend;
1367
- Element.prototype.prepend = globalEnv.rawPrepend;
1368
- }
1369
- // Set the style of micro-app-head and micro-app-body
1370
- let hasRejectMicroAppStyle = false;
1371
- function rejectMicroAppStyle() {
1372
- if (!hasRejectMicroAppStyle) {
1373
- hasRejectMicroAppStyle = true;
1374
- const style = pureCreateElement('style');
1375
- style.setAttribute('type', 'text/css');
1376
- style.textContent = `\n${microApp.tagName}, micro-app-body { display: block; } \nmicro-app-head { display: none; }`;
1377
- globalEnv.rawDocument.head.appendChild(style);
1221
+ rawWindowMethodMap.set(value, bindRawWindowValue);
1222
+ return bindRawWindowValue;
1378
1223
  }
1224
+ return value;
1379
1225
  }
1380
1226
 
1381
- function unmountNestedApp() {
1382
- replaseUnmountOfNestedApp();
1383
- appInstanceMap.forEach(app => {
1384
- let element = app.container;
1385
- if (element) {
1386
- if (element instanceof ShadowRoot) {
1387
- element = element.host;
1388
- }
1389
- // @ts-ignore
1390
- element.disconnectedCallback();
1391
- }
1392
- });
1393
- if (!window.__MICRO_APP_UMD_MODE__)
1394
- appInstanceMap.clear();
1395
- if (elementInstanceMap.size) {
1396
- elementInstanceMap.clear();
1397
- releasePatches();
1398
- }
1399
- }
1400
- // if micro-app run in micro application, delete all next generation application when unmount event received
1401
- function listenUmountOfNestedApp() {
1402
- if (window.__MICRO_APP_ENVIRONMENT__) {
1403
- window.addEventListener('unmount', unmountNestedApp, false);
1227
+ // document.onclick binding list, the binding function of each application is unique
1228
+ const documentClickListMap = new Map();
1229
+ let hasRewriteDocumentOnClick = false;
1230
+ /**
1231
+ * Rewrite document.onclick and execute it only once
1232
+ */
1233
+ function overwriteDocumentOnClick() {
1234
+ hasRewriteDocumentOnClick = true;
1235
+ if (Object.getOwnPropertyDescriptor(document, 'onclick')) {
1236
+ return logWarn('Cannot redefine document property onclick');
1404
1237
  }
1405
- }
1406
- // release listener
1407
- function replaseUnmountOfNestedApp() {
1408
- if (window.__MICRO_APP_ENVIRONMENT__) {
1409
- window.removeEventListener('unmount', unmountNestedApp, false);
1238
+ const rawOnClick = document.onclick;
1239
+ document.onclick = null;
1240
+ let hasDocumentClickInited = false;
1241
+ function onClickHandler(e) {
1242
+ documentClickListMap.forEach((f) => {
1243
+ isFunction(f) && f.call(document, e);
1244
+ });
1410
1245
  }
1411
- }
1412
-
1413
- function eventHandler$1(event, element) {
1414
- Object.defineProperties(event, {
1415
- currentTarget: {
1416
- get() {
1417
- return element;
1418
- }
1246
+ Object.defineProperty(document, 'onclick', {
1247
+ configurable: true,
1248
+ enumerable: true,
1249
+ get() {
1250
+ const appName = getCurrentAppName();
1251
+ return appName ? documentClickListMap.get(appName) : documentClickListMap.get('base');
1419
1252
  },
1420
- target: {
1421
- get() {
1422
- return element;
1253
+ set(f) {
1254
+ const appName = getCurrentAppName();
1255
+ if (appName) {
1256
+ documentClickListMap.set(appName, f);
1423
1257
  }
1424
- },
1258
+ else {
1259
+ documentClickListMap.set('base', f);
1260
+ }
1261
+ if (!hasDocumentClickInited && isFunction(f)) {
1262
+ hasDocumentClickInited = true;
1263
+ globalEnv.rawDocumentAddEventListener.call(globalEnv.rawDocument, 'click', onClickHandler, false);
1264
+ }
1265
+ }
1425
1266
  });
1267
+ rawOnClick && (document.onclick = rawOnClick);
1426
1268
  }
1427
1269
  /**
1428
- * dispatch lifeCycles event to base app
1429
- * created, beforemount, mounted, unmount, error
1430
- * @param element container
1431
- * @param appName app.name
1432
- * @param lifecycleName lifeCycle name
1433
- * @param error param from error hook
1270
+ * The document event is globally, we need to clear these event bindings when micro application unmounted
1434
1271
  */
1435
- function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
1436
- var _a;
1437
- if (!element) {
1438
- return logError(`element does not exist in lifecycle ${lifecycleName}`, appName);
1439
- }
1440
- else if (element instanceof ShadowRoot) {
1441
- element = element.host;
1442
- }
1443
- // clear dom scope before dispatch lifeCycles event to base app, especially mounted & unmount
1444
- removeDomScope();
1445
- const detail = Object.assign({
1446
- name: appName,
1447
- container: element,
1448
- }, error && {
1449
- error
1450
- });
1451
- const event = new CustomEvent(lifecycleName, {
1452
- detail,
1453
- });
1454
- eventHandler$1(event, element);
1455
- // global hooks
1456
- // @ts-ignore
1457
- if (isFunction((_a = microApp.lifeCycles) === null || _a === void 0 ? void 0 : _a[lifecycleName])) {
1458
- // @ts-ignore
1459
- microApp.lifeCycles[lifecycleName](event);
1460
- }
1461
- element.dispatchEvent(event);
1272
+ const documentEventListenerMap = new Map();
1273
+ function effectDocumentEvent() {
1274
+ const { rawDocument, rawDocumentAddEventListener, rawDocumentRemoveEventListener, } = globalEnv;
1275
+ !hasRewriteDocumentOnClick && overwriteDocumentOnClick();
1276
+ document.addEventListener = function (type, listener, options) {
1277
+ var _a;
1278
+ const appName = getCurrentAppName();
1279
+ /**
1280
+ * ignore bound function of document event in umd mode, used to solve problem of react global events
1281
+ */
1282
+ if (appName && !(((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.umdMode) && isBoundFunction(listener))) {
1283
+ const appListenersMap = documentEventListenerMap.get(appName);
1284
+ if (appListenersMap) {
1285
+ const appListenerList = appListenersMap.get(type);
1286
+ if (appListenerList) {
1287
+ appListenerList.add(listener);
1288
+ }
1289
+ else {
1290
+ appListenersMap.set(type, new Set([listener]));
1291
+ }
1292
+ }
1293
+ else {
1294
+ documentEventListenerMap.set(appName, new Map([[type, new Set([listener])]]));
1295
+ }
1296
+ listener && (listener.__MICRO_MARK_OPTIONS__ = options);
1297
+ }
1298
+ rawDocumentAddEventListener.call(rawDocument, type, listener, options);
1299
+ };
1300
+ document.removeEventListener = function (type, listener, options) {
1301
+ var _a;
1302
+ const appName = getCurrentAppName();
1303
+ if (appName && !(((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.umdMode) && isBoundFunction(listener))) {
1304
+ const appListenersMap = documentEventListenerMap.get(appName);
1305
+ if (appListenersMap) {
1306
+ const appListenerList = appListenersMap.get(type);
1307
+ if ((appListenerList === null || appListenerList === void 0 ? void 0 : appListenerList.size) && appListenerList.has(listener)) {
1308
+ appListenerList.delete(listener);
1309
+ }
1310
+ }
1311
+ }
1312
+ rawDocumentRemoveEventListener.call(rawDocument, type, listener, options);
1313
+ };
1314
+ }
1315
+ // Clear the document event agent
1316
+ function releaseEffectDocumentEvent() {
1317
+ document.addEventListener = globalEnv.rawDocumentAddEventListener;
1318
+ document.removeEventListener = globalEnv.rawDocumentRemoveEventListener;
1462
1319
  }
1463
1320
  /**
1464
- * Dispatch unmount event to micro app
1465
- * @param appName app.name
1321
+ * Format event name
1322
+ * @param type event name
1323
+ * @param microWindow micro window
1466
1324
  */
1467
- function dispatchUnmountToMicroApp(appName) {
1468
- const event = new CustomEvent(`unmount-${appName}`);
1469
- window.dispatchEvent(event);
1325
+ function formatEventType(type, microWindow) {
1326
+ if (type === 'unmount') {
1327
+ return `unmount-${microWindow.__MICRO_APP_NAME__}`;
1328
+ }
1329
+ return type;
1470
1330
  }
1471
-
1472
- // record all micro-app elements
1473
- const elementInstanceMap = new Map();
1474
1331
  /**
1475
- * define element
1476
- * @param tagName element name
1332
+ * Rewrite side-effect events
1333
+ * @param microWindow micro window
1477
1334
  */
1478
- function defineElement(tagName) {
1479
- class MicroAppElement extends HTMLElement {
1480
- constructor() {
1481
- super();
1482
- this.isWating = false;
1483
- this.cacheData = null;
1484
- this.hasConnected = false;
1485
- this.appName = '';
1486
- this.appUrl = '';
1487
- this.version = version;
1488
- /**
1489
- * handle for change of name an url after element inited
1490
- */
1491
- this.handleAttributeUpdate = () => {
1492
- var _a;
1493
- this.isWating = false;
1494
- const attrName = this.getAttribute('name');
1495
- const attrUrl = formatURL(this.getAttribute('url'), this.appName);
1496
- if (this.legalAttribute('name', attrName) && this.legalAttribute('url', attrUrl)) {
1497
- const existApp = appInstanceMap.get(attrName);
1498
- if (attrName !== this.appName && existApp) {
1499
- // handling of cached and non-prefetch apps
1500
- if (appStatus.UNMOUNT !== existApp.getAppStatus() && !existApp.isPrefetch) {
1501
- this.setAttribute('name', this.appName);
1502
- return logError(`an app named ${attrName} already exists`, this.appName);
1503
- }
1504
- }
1505
- if (attrName !== this.appName || attrUrl !== this.appUrl) {
1506
- this.handleUnmount(attrName === this.appName);
1507
- this.appName = attrName;
1508
- this.appUrl = attrUrl;
1509
- ((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this).innerHTML = '';
1510
- /**
1511
- * when existApp not undefined
1512
- * if attrName and this.appName are equal, existApp has been unmounted
1513
- * if attrName and this.appName are not equal, existApp is prefetch or unmounted
1514
- */
1515
- if (existApp && existApp.url === attrUrl) {
1516
- // mount app
1517
- this.handleAppMount(existApp);
1518
- }
1519
- else {
1520
- this.handleCreateApp();
1521
- }
1522
- }
1523
- }
1524
- else if (attrName !== this.appName) {
1525
- this.setAttribute('name', this.appName);
1526
- }
1527
- };
1528
- // cloned node of umd container also trigger constructor, we should skip
1529
- if (!this.querySelector('micro-app-head')) {
1530
- this.performWhenFirstCreated();
1531
- }
1335
+ function effect(microWindow) {
1336
+ const appName = microWindow.__MICRO_APP_NAME__;
1337
+ const eventListenerMap = new Map();
1338
+ const intervalIdMap = new Map();
1339
+ const timeoutIdMap = new Map();
1340
+ const { rawWindow, rawDocument, rawWindowAddEventListener, rawWindowRemoveEventListener, rawSetInterval, rawSetTimeout, rawClearInterval, rawClearTimeout, rawDocumentRemoveEventListener, } = globalEnv;
1341
+ // listener may be null, e.g test-passive
1342
+ microWindow.addEventListener = function (type, listener, options) {
1343
+ type = formatEventType(type, microWindow);
1344
+ const listenerList = eventListenerMap.get(type);
1345
+ if (listenerList) {
1346
+ listenerList.add(listener);
1532
1347
  }
1533
- static get observedAttributes() {
1534
- return ['name', 'url'];
1348
+ else {
1349
+ eventListenerMap.set(type, new Set([listener]));
1535
1350
  }
1536
- // 👇 Configuration
1537
- // name: app name
1538
- // url: html address
1539
- // shadowDom: use shadowDOM, default is false
1540
- // destroy: whether delete cache resources when unmount, default is false
1541
- // inline: whether js runs in inline script mode, default is false
1542
- // disableScopecss: whether disable css scoped, default is false
1543
- // disableSandbox: whether disable sandbox, default is false
1544
- // macro: used to solve the async render problem of vue3, default is false
1545
- // baseRoute: route prefix, default is ''
1546
- connectedCallback() {
1547
- this.hasConnected = true;
1548
- if (!elementInstanceMap.has(this)) {
1549
- this.performWhenFirstCreated();
1550
- }
1551
- defer(() => dispatchLifecyclesEvent(this, this.appName, lifeCycles.CREATED));
1552
- this.initialMount();
1351
+ listener && (listener.__MICRO_MARK_OPTIONS__ = options);
1352
+ rawWindowAddEventListener.call(rawWindow, type, listener, options);
1353
+ };
1354
+ microWindow.removeEventListener = function (type, listener, options) {
1355
+ type = formatEventType(type, microWindow);
1356
+ const listenerList = eventListenerMap.get(type);
1357
+ if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
1358
+ listenerList.delete(listener);
1553
1359
  }
1554
- disconnectedCallback() {
1555
- this.hasConnected = false;
1556
- elementInstanceMap.delete(this);
1557
- this.handleUnmount(this.getDisposeResult('destroy') || this.getDisposeResult('destory'));
1558
- if (elementInstanceMap.size === 0) {
1559
- releasePatches();
1360
+ rawWindowRemoveEventListener.call(rawWindow, type, listener, options);
1361
+ };
1362
+ microWindow.setInterval = function (handler, timeout, ...args) {
1363
+ const intervalId = rawSetInterval.call(rawWindow, handler, timeout, ...args);
1364
+ intervalIdMap.set(intervalId, { handler, timeout, args });
1365
+ return intervalId;
1366
+ };
1367
+ microWindow.setTimeout = function (handler, timeout, ...args) {
1368
+ const timeoutId = rawSetTimeout.call(rawWindow, handler, timeout, ...args);
1369
+ timeoutIdMap.set(timeoutId, { handler, timeout, args });
1370
+ return timeoutId;
1371
+ };
1372
+ microWindow.clearInterval = function (intervalId) {
1373
+ intervalIdMap.delete(intervalId);
1374
+ rawClearInterval.call(rawWindow, intervalId);
1375
+ };
1376
+ microWindow.clearTimeout = function (timeoutId) {
1377
+ timeoutIdMap.delete(timeoutId);
1378
+ rawClearTimeout.call(rawWindow, timeoutId);
1379
+ };
1380
+ const umdWindowListenerMap = new Map();
1381
+ const umdDocumentListenerMap = new Map();
1382
+ let umdIntervalIdMap = new Map();
1383
+ let umdTimeoutIdMap = new Map();
1384
+ let umdOnClickHandler;
1385
+ // record event and timer before exec umdMountHook
1386
+ const recordUmdEffect = () => {
1387
+ // record window event
1388
+ eventListenerMap.forEach((listenerList, type) => {
1389
+ if (listenerList.size) {
1390
+ umdWindowListenerMap.set(type, new Set(listenerList));
1560
1391
  }
1392
+ });
1393
+ // record timers
1394
+ if (intervalIdMap.size) {
1395
+ umdIntervalIdMap = new Map(intervalIdMap);
1561
1396
  }
1562
- attributeChangedCallback(attr, _oldVal, newVal) {
1563
- if (this.legalAttribute(attr, newVal) &&
1564
- this[attr === ObservedAttrName.NAME ? 'appName' : 'appUrl'] !== newVal) {
1565
- if (attr === ObservedAttrName.URL && !this.appUrl) {
1566
- newVal = formatURL(newVal, this.appName);
1567
- if (!newVal) {
1568
- return logError('Invalid attribute url', this.appName);
1569
- }
1570
- this.appUrl = newVal;
1571
- this.handleInitialNameAndUrl();
1572
- }
1573
- else if (attr === ObservedAttrName.NAME && !this.appName) {
1574
- if (this.cacheData) {
1575
- microApp.setData(newVal, this.cacheData);
1576
- this.cacheData = null;
1577
- }
1578
- this.appName = newVal;
1579
- this.handleInitialNameAndUrl();
1580
- }
1581
- else if (!this.isWating) {
1582
- this.isWating = true;
1583
- defer(this.handleAttributeUpdate);
1584
- }
1585
- }
1397
+ if (timeoutIdMap.size) {
1398
+ umdTimeoutIdMap = new Map(timeoutIdMap);
1586
1399
  }
1587
- // handle for connectedCallback run before attributeChangedCallback
1588
- handleInitialNameAndUrl() {
1589
- if (this.hasConnected) {
1590
- this.initialMount();
1591
- }
1400
+ // record onclick handler
1401
+ umdOnClickHandler = documentClickListMap.get(appName);
1402
+ // record document event
1403
+ const documentAppListenersMap = documentEventListenerMap.get(appName);
1404
+ if (documentAppListenersMap) {
1405
+ documentAppListenersMap.forEach((listenerList, type) => {
1406
+ if (listenerList.size) {
1407
+ umdDocumentListenerMap.set(type, new Set(listenerList));
1408
+ }
1409
+ });
1592
1410
  }
1593
- // Perform global initialization when the element count is 1
1594
- performWhenFirstCreated() {
1595
- if (elementInstanceMap.set(this, true).size === 1) {
1596
- patchElementPrototypeMethods();
1597
- rejectMicroAppStyle();
1598
- replaseUnmountOfNestedApp();
1599
- listenUmountOfNestedApp();
1411
+ };
1412
+ // rebuild event and timer before remount umd app
1413
+ const rebuildUmdEffect = () => {
1414
+ // rebuild window event
1415
+ umdWindowListenerMap.forEach((listenerList, type) => {
1416
+ for (const listener of listenerList) {
1417
+ microWindow.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_MARK_OPTIONS__);
1600
1418
  }
1601
- }
1602
- /**
1603
- * first mount of this app
1604
- */
1605
- initialMount() {
1606
- if (!this.appName || !this.appUrl)
1607
- return;
1608
- if (this.getDisposeResult('shadowDOM') && !this.shadowRoot) {
1609
- this.attachShadow({ mode: 'open' });
1419
+ });
1420
+ // rebuild timer
1421
+ umdIntervalIdMap.forEach((info) => {
1422
+ microWindow.setInterval(info.handler, info.timeout, ...info.args);
1423
+ });
1424
+ umdTimeoutIdMap.forEach((info) => {
1425
+ microWindow.setTimeout(info.handler, info.timeout, ...info.args);
1426
+ });
1427
+ // rebuild onclick event
1428
+ umdOnClickHandler && documentClickListMap.set(appName, umdOnClickHandler);
1429
+ // rebuild document event
1430
+ setCurrentAppName(appName);
1431
+ umdDocumentListenerMap.forEach((listenerList, type) => {
1432
+ for (const listener of listenerList) {
1433
+ document.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_MARK_OPTIONS__);
1610
1434
  }
1611
- const app = appInstanceMap.get(this.appName);
1612
- if (app) {
1613
- if (app.url === this.appUrl && (app.isPrefetch ||
1614
- app.getAppStatus() === appStatus.UNMOUNT)) {
1615
- this.handleAppMount(app);
1616
- }
1617
- else if (app.isPrefetch) {
1618
- logError(`the url ${this.appUrl} is different from prefetch url ${app.url}`, this.appName);
1619
- }
1620
- else if (app.getAppStatus() === appStatus.UNMOUNT) {
1621
- /**
1622
- * add this logic for SSR
1623
- * if old app has unmounted and url is different, create new app to replace old one
1624
- */
1625
- this.handleCreateApp();
1626
- }
1627
- else {
1628
- logError(`an app named ${this.appName} already exists`, this.appName);
1435
+ });
1436
+ setCurrentAppName(null);
1437
+ };
1438
+ // release all event listener & interval & timeout when unmount app
1439
+ const releaseEffect = () => {
1440
+ // Clear window binding events
1441
+ if (eventListenerMap.size) {
1442
+ eventListenerMap.forEach((listenerList, type) => {
1443
+ for (const listener of listenerList) {
1444
+ rawWindowRemoveEventListener.call(rawWindow, type, listener);
1629
1445
  }
1630
- }
1631
- else {
1632
- this.handleCreateApp();
1633
- }
1634
- }
1635
- /**
1636
- * judge the attribute is legal
1637
- * @param name attribute name
1638
- * @param val attribute value
1639
- */
1640
- legalAttribute(name, val) {
1641
- if (!isString(val) || !val) {
1642
- logError(`unexpected attribute ${name}, please check again`, this.appName);
1643
- return false;
1644
- }
1645
- return true;
1646
- }
1647
- /**
1648
- * mount app
1649
- * some serious note before mount:
1650
- * 1. is prefetch ?
1651
- * 2. is remount in another container ?
1652
- * 3. is remount with change properties of the container ?
1653
- */
1654
- handleAppMount(app) {
1655
- app.isPrefetch = false;
1656
- defer(() => {
1657
- var _a;
1658
- return app.mount((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this, this.getDisposeResult('inline'), this.getBaseRouteCompatible());
1659
1446
  });
1447
+ eventListenerMap.clear();
1660
1448
  }
1661
- // create app instance
1662
- handleCreateApp() {
1663
- var _a;
1664
- const instance = new CreateApp({
1665
- name: this.appName,
1666
- url: this.appUrl,
1667
- container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
1668
- inline: this.getDisposeResult('inline'),
1669
- scopecss: !(this.getDisposeResult('disableScopecss') || this.getDisposeResult('shadowDOM')),
1670
- useSandbox: !this.getDisposeResult('disableSandbox'),
1671
- macro: this.getDisposeResult('macro'),
1672
- baseroute: this.getBaseRouteCompatible(),
1449
+ // Clear timers
1450
+ if (intervalIdMap.size) {
1451
+ intervalIdMap.forEach((_, intervalId) => {
1452
+ rawClearInterval.call(rawWindow, intervalId);
1673
1453
  });
1674
- appInstanceMap.set(this.appName, instance);
1675
- }
1676
- /**
1677
- * unmount app
1678
- * @param destroy delete cache resources when unmount
1679
- */
1680
- handleUnmount(destroy) {
1681
- const app = appInstanceMap.get(this.appName);
1682
- if (app && appStatus.UNMOUNT !== app.getAppStatus())
1683
- app.unmount(destroy);
1684
- }
1685
- /**
1686
- * Get configuration
1687
- * Global setting is lowest priority
1688
- * @param name Configuration item name
1689
- */
1690
- getDisposeResult(name) {
1691
- // @ts-ignore
1692
- return (this.hasAttribute(name) || microApp[name]) && this.getAttribute(name) !== 'false';
1693
- }
1694
- /**
1695
- * 2021-09-08
1696
- * get baseRoute
1697
- * getAttribute('baseurl') is compatible writing of versions below 0.3.1
1698
- */
1699
- getBaseRouteCompatible() {
1700
- var _a, _b;
1701
- return (_b = (_a = this.getAttribute('baseroute')) !== null && _a !== void 0 ? _a : this.getAttribute('baseurl')) !== null && _b !== void 0 ? _b : '';
1454
+ intervalIdMap.clear();
1702
1455
  }
1703
- /**
1704
- * Data from the base application
1705
- */
1706
- set data(value) {
1707
- if (this.appName) {
1708
- microApp.setData(this.appName, value);
1709
- }
1710
- else {
1711
- this.cacheData = value;
1712
- }
1456
+ if (timeoutIdMap.size) {
1457
+ timeoutIdMap.forEach((_, timeoutId) => {
1458
+ rawClearTimeout.call(rawWindow, timeoutId);
1459
+ });
1460
+ timeoutIdMap.clear();
1713
1461
  }
1714
- /**
1715
- * get data only used in jsx-custom-event once
1716
- */
1717
- get data() {
1718
- if (this.appName) {
1719
- return microApp.getData(this.appName, true);
1720
- }
1721
- else if (this.cacheData) {
1722
- return this.cacheData;
1723
- }
1724
- return null;
1462
+ // Clear the function bound by micro application through document.onclick
1463
+ documentClickListMap.delete(appName);
1464
+ // Clear document binding event
1465
+ const documentAppListenersMap = documentEventListenerMap.get(appName);
1466
+ if (documentAppListenersMap) {
1467
+ documentAppListenersMap.forEach((listenerList, type) => {
1468
+ for (const listener of listenerList) {
1469
+ rawDocumentRemoveEventListener.call(rawDocument, type, listener);
1470
+ }
1471
+ });
1472
+ documentAppListenersMap.clear();
1725
1473
  }
1726
- }
1727
- window.customElements.define(tagName, MicroAppElement);
1474
+ };
1475
+ return {
1476
+ recordUmdEffect,
1477
+ rebuildUmdEffect,
1478
+ releaseEffect,
1479
+ };
1728
1480
  }
1729
1481
 
1730
1482
  class EventCenter {
@@ -1844,9 +1596,7 @@ class EventCenterForGlobal {
1844
1596
  * @param cb listener
1845
1597
  */
1846
1598
  removeGlobalDataListener(cb) {
1847
- if (isFunction(cb)) {
1848
- eventCenter.off('global', cb);
1849
- }
1599
+ isFunction(cb) && eventCenter.off('global', cb);
1850
1600
  }
1851
1601
  /**
1852
1602
  * dispatch global data
@@ -1890,7 +1640,7 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
1890
1640
  * @param autoTrigger If there is cached data when first bind listener, whether it needs to trigger, default is false
1891
1641
  */
1892
1642
  addDataListener(appName, cb, autoTrigger) {
1893
- eventCenter.on(formatEventName(appName, false), cb, autoTrigger);
1643
+ eventCenter.on(formatEventName(formatAppName(appName), false), cb, autoTrigger);
1894
1644
  }
1895
1645
  /**
1896
1646
  * remove listener
@@ -1898,9 +1648,7 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
1898
1648
  * @param cb listener
1899
1649
  */
1900
1650
  removeDataListener(appName, cb) {
1901
- if (isFunction(cb)) {
1902
- eventCenter.off(formatEventName(appName, false), cb);
1903
- }
1651
+ isFunction(cb) && eventCenter.off(formatEventName(formatAppName(appName), false), cb);
1904
1652
  }
1905
1653
  /**
1906
1654
  * get data from micro app or base app
@@ -1908,7 +1656,7 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
1908
1656
  * @param fromBaseApp whether get data from base app, default is false
1909
1657
  */
1910
1658
  getData(appName, fromBaseApp = false) {
1911
- return eventCenter.getData(formatEventName(appName, fromBaseApp));
1659
+ return eventCenter.getData(formatEventName(formatAppName(appName), fromBaseApp));
1912
1660
  }
1913
1661
  /**
1914
1662
  * Dispatch data to the specified micro app
@@ -1916,21 +1664,22 @@ class EventCenterForBaseApp extends EventCenterForGlobal {
1916
1664
  * @param data data
1917
1665
  */
1918
1666
  setData(appName, data) {
1919
- eventCenter.dispatch(formatEventName(appName, true), data);
1667
+ eventCenter.dispatch(formatEventName(formatAppName(appName), true), data);
1920
1668
  }
1921
1669
  /**
1922
1670
  * clear all listener for specified micro app
1923
1671
  * @param appName app.name
1924
1672
  */
1925
1673
  clearDataListener(appName) {
1926
- eventCenter.off(formatEventName(appName, false));
1674
+ eventCenter.off(formatEventName(formatAppName(appName), false));
1927
1675
  }
1928
1676
  }
1929
1677
  // Event center for sub app
1930
1678
  class EventCenterForMicroApp extends EventCenterForGlobal {
1931
1679
  constructor(appName) {
1932
1680
  super();
1933
- this.appName = appName;
1681
+ this.appName = formatAppName(appName);
1682
+ !this.appName && logError(`Invalid appName ${appName}`);
1934
1683
  }
1935
1684
  /**
1936
1685
  * add listener, monitor the data sent by the base app
@@ -1946,9 +1695,7 @@ class EventCenterForMicroApp extends EventCenterForGlobal {
1946
1695
  * @param cb listener
1947
1696
  */
1948
1697
  removeDataListener(cb) {
1949
- if (isFunction(cb)) {
1950
- eventCenter.off(formatEventName(this.appName, true), cb);
1951
- }
1698
+ isFunction(cb) && eventCenter.off(formatEventName(this.appName, true), cb);
1952
1699
  }
1953
1700
  /**
1954
1701
  * get data from base app
@@ -1970,11 +1717,7 @@ class EventCenterForMicroApp extends EventCenterForGlobal {
1970
1717
  data,
1971
1718
  }
1972
1719
  });
1973
- let element = app.container;
1974
- if (element instanceof ShadowRoot) {
1975
- element = element.host;
1976
- }
1977
- element.dispatchEvent(event);
1720
+ getRootContainer(app.container).dispatchEvent(event);
1978
1721
  }
1979
1722
  }
1980
1723
  /**
@@ -2017,1001 +1760,1268 @@ function rebuildDataCenterSnapshot(microAppEventCneter) {
2017
1760
  }
2018
1761
  }
2019
1762
 
2020
- class MicroApp extends EventCenterForBaseApp {
2021
- constructor() {
2022
- super(...arguments);
2023
- this.tagName = 'micro-app';
2024
- this.preFetch = preFetch;
2025
- }
2026
- start(options) {
2027
- if (!isBrowser || !window.customElements) {
2028
- return logError('micro-app is not supported in this environment');
2029
- }
2030
- if (options === null || options === void 0 ? void 0 : options.tagName) {
2031
- if (/^micro-app(-\S+)?/.test(options.tagName)) {
2032
- this.tagName = options.tagName;
2033
- }
2034
- else {
2035
- return logError(`${options.tagName} is invalid tagName`);
2036
- }
2037
- }
2038
- if (window.customElements.get(this.tagName)) {
2039
- return logWarn(`element ${this.tagName} is already defined`);
2040
- }
2041
- initGlobalEnv();
2042
- if (options && isPlainObject(options)) {
2043
- this.shadowDOM = options.shadowDOM;
2044
- this.destroy = options.destroy;
2045
- /**
2046
- * compatible with versions below 0.4.2 of destroy
2047
- * Do not merge with the previous line of code
2048
- */
2049
- // @ts-ignore
2050
- this.destory = options.destory;
2051
- this.inline = options.inline;
2052
- this.disableScopecss = options.disableScopecss;
2053
- this.disableSandbox = options.disableSandbox;
2054
- this.macro = options.macro;
2055
- if (isFunction(options.fetch))
2056
- this.fetch = options.fetch;
2057
- if (isPlainObject(options.lifeCycles)) {
2058
- this.lifeCycles = options.lifeCycles;
2059
- }
2060
- if (isPlainObject(options.plugins)) {
2061
- this.plugins = options.plugins;
2062
- }
2063
- // load app assets when browser is idle
2064
- if (options.preFetchApps) {
2065
- preFetch(options.preFetchApps);
2066
- }
2067
- // load global assets when browser is idle
2068
- if (options.globalAssets) {
2069
- getGlobalAssets(options.globalAssets);
2070
- }
2071
- }
2072
- defineElement(this.tagName);
2073
- }
2074
- }
2075
- var microApp = new MicroApp();
2076
-
1763
+ // Variables that can escape to rawWindow
1764
+ const staticEscapeProperties = [
1765
+ 'System',
1766
+ '__cjsWrapper',
1767
+ '__REACT_ERROR_OVERLAY_GLOBAL_HOOK__',
1768
+ ];
1769
+ // Variables that can only assigned to rawWindow
1770
+ const escapeSetterKeyList = [
1771
+ 'location',
1772
+ ];
1773
+ const unscopables = {
1774
+ undefined: true,
1775
+ Array: true,
1776
+ Object: true,
1777
+ String: true,
1778
+ Boolean: true,
1779
+ Math: true,
1780
+ Number: true,
1781
+ Symbol: true,
1782
+ parseFloat: true,
1783
+ Float32Array: true,
1784
+ };
2077
1785
  /**
2078
- * fetch source of html, js, css
2079
- * @param url source path
2080
- * @param appName app name
2081
- * @param config config of fetch
1786
+ * macro task to solve the rendering problem of vue3
2082
1787
  */
2083
- function fetchSource(url, appName = null, options = {}) {
2084
- if (isFunction(microApp.fetch)) {
2085
- return microApp.fetch(url, options, appName);
2086
- }
2087
- return fetch(url, options).then((res) => {
2088
- return res.text();
2089
- });
2090
- }
2091
-
2092
- /**
2093
- * transform html string to dom
2094
- * @param str string dom
2095
- */
2096
- function getWrapElement(str) {
2097
- const wrapDiv = pureCreateElement('div');
2098
- wrapDiv.innerHTML = str;
2099
- return wrapDiv;
1788
+ let macroTimer;
1789
+ function macroTask(fn) {
1790
+ macroTimer && clearTimeout(macroTimer);
1791
+ macroTimer = setTimeout(fn, 0);
2100
1792
  }
2101
- /**
2102
- * Recursively process each child element
2103
- * @param parent parent element
2104
- * @param app app
2105
- * @param microAppHead micro-app-head element
2106
- */
2107
- function flatChildren(parent, app, microAppHead) {
2108
- const children = Array.from(parent.children);
2109
- children.length && children.forEach((child) => {
2110
- flatChildren(child, app, microAppHead);
2111
- });
2112
- for (const dom of children) {
2113
- if (dom instanceof HTMLLinkElement) {
2114
- if (dom.hasAttribute('exclude')) {
2115
- parent.replaceChild(document.createComment('link element with exclude attribute ignored by micro-app'), dom);
2116
- }
2117
- else if (app.scopecss && !dom.hasAttribute('ignore')) {
2118
- extractLinkFromHtml(dom, parent, app, microAppHead);
2119
- }
2120
- else if (dom.hasAttribute('href')) {
2121
- dom.setAttribute('href', CompletionPath(dom.getAttribute('href'), app.url));
1793
+ class SandBox {
1794
+ constructor(appName, url, macro) {
1795
+ // Scoped global Properties(Properties that can only get and set in microWindow, will not escape to rawWindow)
1796
+ this.scopeProperties = ['webpackJsonp'];
1797
+ // Properties that can be escape to rawWindow
1798
+ this.escapeProperties = [];
1799
+ // Properties newly added to microWindow
1800
+ this.injectedKeys = new Set();
1801
+ // Properties escape to rawWindow, cleared when unmount
1802
+ this.escapeKeys = new Set();
1803
+ // sandbox state
1804
+ this.active = false;
1805
+ this.microWindow = {}; // Proxy target
1806
+ const rawWindow = globalEnv.rawWindow;
1807
+ const rawDocument = globalEnv.rawDocument;
1808
+ const descriptorTargetMap = new Map();
1809
+ const hasOwnProperty = (key) => this.microWindow.hasOwnProperty(key) || rawWindow.hasOwnProperty(key);
1810
+ // get scopeProperties and escapeProperties from plugins
1811
+ this.getScopeProperties(appName);
1812
+ // inject global properties
1813
+ this.inject(this.microWindow, appName, url);
1814
+ // Rewrite global event listener & timeout
1815
+ Object.assign(this, effect(this.microWindow));
1816
+ this.proxyWindow = new Proxy(this.microWindow, {
1817
+ get: (target, key) => {
1818
+ if (key === Symbol.unscopables)
1819
+ return unscopables;
1820
+ if (['window', 'self', 'globalThis'].includes(key)) {
1821
+ return this.proxyWindow;
1822
+ }
1823
+ if (key === 'top' || key === 'parent') {
1824
+ if (rawWindow === rawWindow.parent) { // not in iframe
1825
+ return this.proxyWindow;
1826
+ }
1827
+ return Reflect.get(rawWindow, key); // iframe
1828
+ }
1829
+ if (key === 'hasOwnProperty')
1830
+ return hasOwnProperty;
1831
+ if (key === 'document' || key === 'eval') {
1832
+ if (this.active) {
1833
+ setCurrentAppName(appName);
1834
+ (macro ? macroTask : defer)(() => setCurrentAppName(null));
1835
+ }
1836
+ switch (key) {
1837
+ case 'document':
1838
+ return rawDocument;
1839
+ case 'eval':
1840
+ return eval;
1841
+ }
1842
+ }
1843
+ if (Reflect.has(target, key)) {
1844
+ return Reflect.get(target, key);
1845
+ }
1846
+ if (this.scopeProperties.includes(key) ||
1847
+ (isString(key) && /^__MICRO_APP_/.test(key))) {
1848
+ return Reflect.get(target, key);
1849
+ }
1850
+ const rawValue = Reflect.get(rawWindow, key);
1851
+ return bindFunctionToRawWidow(rawWindow, rawValue);
1852
+ },
1853
+ set: (target, key, value) => {
1854
+ if (this.active) {
1855
+ if (escapeSetterKeyList.includes(key)) {
1856
+ Reflect.set(rawWindow, key, value);
1857
+ }
1858
+ else if (!target.hasOwnProperty(key) &&
1859
+ rawWindow.hasOwnProperty(key) &&
1860
+ !this.scopeProperties.includes(key)) {
1861
+ const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
1862
+ const { writable, configurable, enumerable } = descriptor;
1863
+ if (writable) {
1864
+ Object.defineProperty(target, key, {
1865
+ configurable,
1866
+ enumerable,
1867
+ writable,
1868
+ value,
1869
+ });
1870
+ this.injectedKeys.add(key);
1871
+ }
1872
+ }
1873
+ else {
1874
+ Reflect.set(target, key, value);
1875
+ this.injectedKeys.add(key);
1876
+ }
1877
+ if ((this.escapeProperties.includes(key) ||
1878
+ (staticEscapeProperties.includes(key) && !Reflect.has(rawWindow, key))) &&
1879
+ !this.scopeProperties.includes(key)) {
1880
+ Reflect.set(rawWindow, key, value);
1881
+ this.escapeKeys.add(key);
1882
+ }
1883
+ }
1884
+ return true;
1885
+ },
1886
+ has: (target, key) => {
1887
+ if (this.scopeProperties.includes(key))
1888
+ return key in target;
1889
+ return key in unscopables || key in target || key in rawWindow;
1890
+ },
1891
+ getOwnPropertyDescriptor: (target, key) => {
1892
+ if (target.hasOwnProperty(key)) {
1893
+ descriptorTargetMap.set(key, 'target');
1894
+ return Object.getOwnPropertyDescriptor(target, key);
1895
+ }
1896
+ if (rawWindow.hasOwnProperty(key)) {
1897
+ descriptorTargetMap.set(key, 'rawWindow');
1898
+ const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
1899
+ if (descriptor && !descriptor.configurable) {
1900
+ descriptor.configurable = true;
1901
+ }
1902
+ return descriptor;
1903
+ }
1904
+ return undefined;
1905
+ },
1906
+ defineProperty: (target, key, value) => {
1907
+ const from = descriptorTargetMap.get(key);
1908
+ if (from === 'rawWindow') {
1909
+ return Reflect.defineProperty(rawWindow, key, value);
1910
+ }
1911
+ return Reflect.defineProperty(target, key, value);
1912
+ },
1913
+ ownKeys: (target) => {
1914
+ return unique(Reflect.ownKeys(rawWindow).concat(Reflect.ownKeys(target)));
1915
+ },
1916
+ deleteProperty: (target, key) => {
1917
+ if (target.hasOwnProperty(key)) {
1918
+ this.escapeKeys.has(key) && Reflect.deleteProperty(rawWindow, key);
1919
+ return Reflect.deleteProperty(target, key);
1920
+ }
1921
+ return true;
1922
+ },
1923
+ });
1924
+ }
1925
+ start(baseroute) {
1926
+ if (!this.active) {
1927
+ this.active = true;
1928
+ this.microWindow.__MICRO_APP_BASE_ROUTE__ = this.microWindow.__MICRO_APP_BASE_URL__ = baseroute;
1929
+ // BUG FIX: bable-polyfill@6.x
1930
+ globalEnv.rawWindow._babelPolyfill && (globalEnv.rawWindow._babelPolyfill = false);
1931
+ if (++SandBox.activeCount === 1) {
1932
+ effectDocumentEvent();
2122
1933
  }
2123
1934
  }
2124
- else if (dom instanceof HTMLStyleElement) {
2125
- if (dom.hasAttribute('exclude')) {
2126
- parent.replaceChild(document.createComment('style element with exclude attribute ignored by micro-app'), dom);
2127
- }
2128
- else if (app.scopecss && !dom.hasAttribute('ignore')) {
2129
- microAppHead.appendChild(scopedCSS(dom, app.name));
1935
+ }
1936
+ stop() {
1937
+ if (this.active) {
1938
+ this.active = false;
1939
+ this.releaseEffect();
1940
+ this.microWindow.microApp.clearDataListener();
1941
+ this.microWindow.microApp.clearGlobalDataListener();
1942
+ this.injectedKeys.forEach((key) => {
1943
+ Reflect.deleteProperty(this.microWindow, key);
1944
+ });
1945
+ this.injectedKeys.clear();
1946
+ this.escapeKeys.forEach((key) => {
1947
+ Reflect.deleteProperty(globalEnv.rawWindow, key);
1948
+ });
1949
+ this.escapeKeys.clear();
1950
+ if (--SandBox.activeCount === 0) {
1951
+ releaseEffectDocumentEvent();
2130
1952
  }
2131
1953
  }
2132
- else if (dom instanceof HTMLScriptElement) {
2133
- extractScriptElement(dom, parent, app);
2134
- }
2135
- else if (dom instanceof HTMLMetaElement || dom instanceof HTMLTitleElement) {
2136
- parent.removeChild(dom);
1954
+ }
1955
+ // record umd snapshot before the first execution of umdHookMount
1956
+ recordUmdSnapshot() {
1957
+ this.microWindow.__MICRO_APP_UMD_MODE__ = true;
1958
+ this.recordUmdEffect();
1959
+ recordDataCenterSnapshot(this.microWindow.microApp);
1960
+ this.recordUmdinjectedValues = new Map();
1961
+ this.injectedKeys.forEach((key) => {
1962
+ this.recordUmdinjectedValues.set(key, Reflect.get(this.microWindow, key));
1963
+ });
1964
+ }
1965
+ // rebuild umd snapshot before remount umd app
1966
+ rebuildUmdSnapshot() {
1967
+ this.recordUmdinjectedValues.forEach((value, key) => {
1968
+ Reflect.set(this.proxyWindow, key, value);
1969
+ });
1970
+ this.rebuildUmdEffect();
1971
+ rebuildDataCenterSnapshot(this.microWindow.microApp);
1972
+ }
1973
+ /**
1974
+ * get scopeProperties and escapeProperties from plugins
1975
+ * @param appName app name
1976
+ */
1977
+ getScopeProperties(appName) {
1978
+ var _a;
1979
+ if (!isPlainObject(microApp.plugins))
1980
+ return;
1981
+ if (isArray(microApp.plugins.global)) {
1982
+ for (const plugin of microApp.plugins.global) {
1983
+ if (isPlainObject(plugin)) {
1984
+ if (isArray(plugin.scopeProperties)) {
1985
+ this.scopeProperties = this.scopeProperties.concat(plugin.scopeProperties);
1986
+ }
1987
+ if (isArray(plugin.escapeProperties)) {
1988
+ this.escapeProperties = this.escapeProperties.concat(plugin.escapeProperties);
1989
+ }
1990
+ }
1991
+ }
2137
1992
  }
2138
- else if (dom instanceof HTMLImageElement && dom.hasAttribute('src')) {
2139
- dom.setAttribute('src', CompletionPath(dom.getAttribute('src'), app.url));
1993
+ if (isArray((_a = microApp.plugins.modules) === null || _a === void 0 ? void 0 : _a[appName])) {
1994
+ for (const plugin of microApp.plugins.modules[appName]) {
1995
+ if (isPlainObject(plugin)) {
1996
+ if (isArray(plugin.scopeProperties)) {
1997
+ this.scopeProperties = this.scopeProperties.concat(plugin.scopeProperties);
1998
+ }
1999
+ if (isArray(plugin.escapeProperties)) {
2000
+ this.escapeProperties = this.escapeProperties.concat(plugin.escapeProperties);
2001
+ }
2002
+ }
2003
+ }
2140
2004
  }
2141
2005
  }
2006
+ /**
2007
+ * inject global properties to microWindow
2008
+ * @param microWindow micro window
2009
+ * @param appName app name
2010
+ * @param url app url
2011
+ */
2012
+ inject(microWindow, appName, url) {
2013
+ microWindow.__MICRO_APP_ENVIRONMENT__ = true;
2014
+ microWindow.__MICRO_APP_NAME__ = appName;
2015
+ microWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
2016
+ microWindow.microApp = new EventCenterForMicroApp(appName);
2017
+ microWindow.rawWindow = globalEnv.rawWindow;
2018
+ microWindow.rawDocument = globalEnv.rawDocument;
2019
+ microWindow.removeDomScope = removeDomScope;
2020
+ }
2021
+ }
2022
+ SandBox.activeCount = 0; // number of active sandbox
2023
+
2024
+ function eventHandler$1(event, element) {
2025
+ Object.defineProperties(event, {
2026
+ currentTarget: {
2027
+ get() {
2028
+ return element;
2029
+ }
2030
+ },
2031
+ target: {
2032
+ get() {
2033
+ return element;
2034
+ }
2035
+ },
2036
+ });
2037
+ }
2038
+ /**
2039
+ * dispatch lifeCycles event to base app
2040
+ * created, beforemount, mounted, unmount, error
2041
+ * @param element container
2042
+ * @param appName app.name
2043
+ * @param lifecycleName lifeCycle name
2044
+ * @param error param from error hook
2045
+ */
2046
+ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
2047
+ var _a;
2048
+ if (!element) {
2049
+ return logError(`element does not exist in lifecycle ${lifecycleName}`, appName);
2050
+ }
2051
+ element = getRootContainer(element);
2052
+ // clear dom scope before dispatch lifeCycles event to base app, especially mounted & unmount
2053
+ removeDomScope();
2054
+ const detail = Object.assign({
2055
+ name: appName,
2056
+ container: element,
2057
+ }, error && {
2058
+ error
2059
+ });
2060
+ const event = new CustomEvent(lifecycleName, {
2061
+ detail,
2062
+ });
2063
+ eventHandler$1(event, element);
2064
+ // global hooks
2065
+ // @ts-ignore
2066
+ if (isFunction((_a = microApp.lifeCycles) === null || _a === void 0 ? void 0 : _a[lifecycleName])) {
2067
+ // @ts-ignore
2068
+ microApp.lifeCycles[lifecycleName](event);
2069
+ }
2070
+ element.dispatchEvent(event);
2142
2071
  }
2143
2072
  /**
2144
- * Extract link and script, bind style scope
2145
- * @param htmlStr html string
2146
- * @param app app
2073
+ * Dispatch unmount event to micro app
2074
+ * @param appName app.name
2147
2075
  */
2148
- function extractSourceDom(htmlStr, app) {
2149
- const wrapElement = getWrapElement(htmlStr);
2150
- const microAppHead = wrapElement.querySelector('micro-app-head');
2151
- const microAppBody = wrapElement.querySelector('micro-app-body');
2152
- if (!microAppHead || !microAppBody) {
2153
- const msg = `element ${microAppHead ? 'body' : 'head'} is missing`;
2154
- app.onerror(new Error(msg));
2155
- return logError(msg, app.name);
2076
+ function dispatchUnmountToMicroApp(appName) {
2077
+ const event = new CustomEvent(`unmount-${appName}`);
2078
+ window.dispatchEvent(event);
2079
+ }
2080
+
2081
+ // micro app instances
2082
+ const appInstanceMap = new Map();
2083
+ class CreateApp {
2084
+ constructor({ name, url, ssrUrl, container, inline, scopecss, useSandbox, macro, baseroute, }) {
2085
+ this.status = appStatus.NOT_LOADED;
2086
+ this.loadSourceLevel = 0;
2087
+ this.umdHookMount = null;
2088
+ this.umdHookUnmount = null;
2089
+ this.libraryName = null;
2090
+ this.umdMode = false;
2091
+ this.isPrefetch = false;
2092
+ this.container = null;
2093
+ this.macro = false;
2094
+ this.baseroute = '';
2095
+ this.sandBox = null;
2096
+ this.container = container !== null && container !== void 0 ? container : null;
2097
+ this.inline = inline !== null && inline !== void 0 ? inline : false;
2098
+ this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : '';
2099
+ this.ssrUrl = ssrUrl !== null && ssrUrl !== void 0 ? ssrUrl : '';
2100
+ // optional during init👆
2101
+ this.name = name;
2102
+ this.url = url;
2103
+ this.useSandbox = useSandbox;
2104
+ this.scopecss = this.useSandbox && scopecss;
2105
+ this.macro = macro !== null && macro !== void 0 ? macro : false;
2106
+ this.source = {
2107
+ links: new Map(),
2108
+ scripts: new Map(),
2109
+ };
2110
+ this.loadSourceCode();
2111
+ this.useSandbox && (this.sandBox = new SandBox(name, url, this.macro));
2156
2112
  }
2157
- flatChildren(wrapElement, app, microAppHead);
2158
- if (app.source.links.size) {
2159
- fetchLinksFromHtml(wrapElement, app, microAppHead);
2113
+ // Load resources
2114
+ loadSourceCode() {
2115
+ this.status = appStatus.LOADING_SOURCE_CODE;
2116
+ extractHtml(this);
2160
2117
  }
2161
- else {
2162
- app.onLoad(wrapElement);
2118
+ /**
2119
+ * When resource is loaded, mount app if it is not prefetch or unmount
2120
+ */
2121
+ onLoad(html) {
2122
+ if (++this.loadSourceLevel === 2) {
2123
+ this.source.html = html;
2124
+ if (this.isPrefetch || appStatus.UNMOUNT === this.status)
2125
+ return;
2126
+ this.status = appStatus.LOAD_SOURCE_FINISHED;
2127
+ this.mount();
2128
+ }
2163
2129
  }
2164
- if (app.source.scripts.size) {
2165
- fetchScriptsFromHtml(wrapElement, app);
2130
+ /**
2131
+ * Error loading HTML
2132
+ * @param e Error
2133
+ */
2134
+ onLoadError(e) {
2135
+ this.loadSourceLevel = -1;
2136
+ if (appStatus.UNMOUNT !== this.status) {
2137
+ this.onerror(e);
2138
+ this.status = appStatus.LOAD_SOURCE_ERROR;
2139
+ }
2166
2140
  }
2167
- else {
2168
- app.onLoad(wrapElement);
2141
+ /**
2142
+ * mount app
2143
+ * @param container app container
2144
+ * @param inline js runs in inline mode
2145
+ * @param baseroute route prefix, default is ''
2146
+ */
2147
+ mount(container, inline, baseroute) {
2148
+ var _a, _b, _c;
2149
+ if (isBoolean(inline) && inline !== this.inline) {
2150
+ this.inline = inline;
2151
+ }
2152
+ this.container = (_a = this.container) !== null && _a !== void 0 ? _a : container;
2153
+ this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : this.baseroute;
2154
+ if (this.loadSourceLevel !== 2) {
2155
+ this.status = appStatus.LOADING_SOURCE_CODE;
2156
+ return;
2157
+ }
2158
+ dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
2159
+ this.status = appStatus.MOUNTING;
2160
+ cloneContainer(this.source.html, this.container, !this.umdMode);
2161
+ (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.start(this.baseroute);
2162
+ let umdHookMountResult; // result of mount function
2163
+ if (!this.umdMode) {
2164
+ let hasDispatchMountedEvent = false;
2165
+ // if all js are executed, param isFinished will be true
2166
+ execScripts(this.source.scripts, this, (isFinished) => {
2167
+ var _a;
2168
+ if (!this.umdMode) {
2169
+ const { mount, unmount } = this.getUmdLibraryHooks();
2170
+ // if mount & unmount is function, the sub app is umd mode
2171
+ if (isFunction(mount) && isFunction(unmount)) {
2172
+ this.umdHookMount = mount;
2173
+ this.umdHookUnmount = unmount;
2174
+ this.umdMode = true;
2175
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordUmdSnapshot();
2176
+ try {
2177
+ umdHookMountResult = this.umdHookMount();
2178
+ }
2179
+ catch (e) {
2180
+ logError('an error occurred in the mount function \n', this.name, e);
2181
+ }
2182
+ }
2183
+ }
2184
+ if (!hasDispatchMountedEvent && (isFinished === true || this.umdMode)) {
2185
+ hasDispatchMountedEvent = true;
2186
+ this.handleMounted(umdHookMountResult);
2187
+ }
2188
+ });
2189
+ }
2190
+ else {
2191
+ (_c = this.sandBox) === null || _c === void 0 ? void 0 : _c.rebuildUmdSnapshot();
2192
+ try {
2193
+ umdHookMountResult = this.umdHookMount();
2194
+ }
2195
+ catch (e) {
2196
+ logError('an error occurred in the mount function \n', this.name, e);
2197
+ }
2198
+ this.handleMounted(umdHookMountResult);
2199
+ }
2169
2200
  }
2170
- }
2171
- /**
2172
- * Get and format html
2173
- * @param app app
2174
- */
2175
- function extractHtml(app) {
2176
- fetchSource(app.url, app.name, { cache: 'no-cache' }).then((htmlStr) => {
2177
- if (!htmlStr) {
2178
- const msg = 'html is empty, please check in detail';
2179
- app.onerror(new Error(msg));
2180
- return logError(msg, app.name);
2201
+ /**
2202
+ * handle for promise umdHookMount
2203
+ * @param umdHookMountResult result of umdHookMount
2204
+ */
2205
+ handleMounted(umdHookMountResult) {
2206
+ if (isPromise(umdHookMountResult)) {
2207
+ umdHookMountResult
2208
+ .then(() => this.dispatchMountedEvent())
2209
+ .catch((e) => this.onerror(e));
2210
+ }
2211
+ else {
2212
+ this.dispatchMountedEvent();
2181
2213
  }
2182
- htmlStr = htmlStr
2183
- .replace(/<head[^>]*>[\s\S]*?<\/head>/i, (match) => {
2184
- return match
2185
- .replace(/<head/i, '<micro-app-head')
2186
- .replace(/<\/head>/i, '</micro-app-head>');
2187
- })
2188
- .replace(/<body[^>]*>[\s\S]*?<\/body>/i, (match) => {
2189
- return match
2190
- .replace(/<body/i, '<micro-app-body')
2191
- .replace(/<\/body>/i, '</micro-app-body>');
2192
- });
2193
- extractSourceDom(htmlStr, app);
2194
- }).catch((e) => {
2195
- logError(`Failed to fetch data from ${app.url}, micro-app stop rendering`, app.name, e);
2196
- app.onLoadError(e);
2197
- });
2198
- }
2199
-
2200
- const boundedMap = new WeakMap();
2201
- function isBoundedFunction(value) {
2202
- if (boundedMap.has(value)) {
2203
- return boundedMap.get(value);
2204
2214
  }
2205
- // bind function
2206
- const boundFunction = isBoundFunction(value);
2207
- boundedMap.set(value, boundFunction);
2208
- return boundFunction;
2209
- }
2210
- const constructorMap = new WeakMap();
2211
- function isConstructor(value) {
2212
- if (constructorMap.has(value)) {
2213
- return constructorMap.get(value);
2215
+ /**
2216
+ * dispatch mounted event when app run finished
2217
+ */
2218
+ dispatchMountedEvent() {
2219
+ if (appStatus.UNMOUNT !== this.status) {
2220
+ this.status = appStatus.MOUNTED;
2221
+ dispatchLifecyclesEvent(this.container, this.name, lifeCycles.MOUNTED);
2222
+ }
2223
+ }
2224
+ /**
2225
+ * unmount app
2226
+ * @param destroy completely destroy, delete cache resources
2227
+ */
2228
+ unmount(destroy) {
2229
+ if (this.status === appStatus.LOAD_SOURCE_ERROR) {
2230
+ destroy = true;
2231
+ }
2232
+ this.status = appStatus.UNMOUNT;
2233
+ // result of unmount function
2234
+ let umdHookUnmountResult;
2235
+ /**
2236
+ * send an unmount event to the micro app or call umd unmount hook
2237
+ * before the sandbox is cleared
2238
+ */
2239
+ if (this.umdHookUnmount) {
2240
+ try {
2241
+ umdHookUnmountResult = this.umdHookUnmount();
2242
+ }
2243
+ catch (e) {
2244
+ logError('an error occurred in the unmount function \n', this.name, e);
2245
+ }
2246
+ }
2247
+ // dispatch unmount event to micro app
2248
+ dispatchUnmountToMicroApp(this.name);
2249
+ this.handleUnmounted(destroy, umdHookUnmountResult);
2250
+ }
2251
+ /**
2252
+ * handle for promise umdHookUnmount
2253
+ * @param umdHookUnmountResult result of umdHookUnmount
2254
+ */
2255
+ handleUnmounted(destroy, umdHookUnmountResult) {
2256
+ if (isPromise(umdHookUnmountResult)) {
2257
+ umdHookUnmountResult
2258
+ .then(() => this.actionsForUnmount(destroy))
2259
+ .catch(() => this.actionsForUnmount(destroy));
2260
+ }
2261
+ else {
2262
+ this.actionsForUnmount(destroy);
2263
+ }
2214
2264
  }
2215
- const valueStr = value.toString();
2216
- const result = (value.prototype &&
2217
- value.prototype.constructor === value &&
2218
- Object.getOwnPropertyNames(value.prototype).length > 1) ||
2219
- /^function\s+[A-Z]/.test(valueStr) ||
2220
- /^class\s+/.test(valueStr);
2221
- constructorMap.set(value, result);
2222
- return result;
2223
- }
2224
- const rawWindowMethodMap = new WeakMap();
2225
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
2226
- function bindFunctionToRawWidow(rawWindow, value) {
2227
- if (rawWindowMethodMap.has(value)) {
2228
- return rawWindowMethodMap.get(value);
2265
+ /**
2266
+ * actions for unmount app
2267
+ * @param destroy completely destroy, delete cache resources
2268
+ */
2269
+ actionsForUnmount(destroy) {
2270
+ var _a;
2271
+ // dispatch unmount event to base app
2272
+ dispatchLifecyclesEvent(this.container, this.name, lifeCycles.UNMOUNT);
2273
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.stop();
2274
+ if (destroy) {
2275
+ this.actionsForCompletelyDestory();
2276
+ }
2277
+ else if (this.umdMode && this.container.childElementCount) {
2278
+ /**
2279
+ * In umd mode, ui frameworks will no longer create style elements to head in lazy load page when render again, so we should save container to keep these elements
2280
+ */
2281
+ cloneContainer(this.container, this.source.html, false);
2282
+ }
2283
+ this.container = null;
2229
2284
  }
2230
- if (isFunction(value) && !isConstructor(value) && !isBoundedFunction(value)) {
2231
- const bindRawWindowValue = value.bind(rawWindow);
2232
- for (const key in value) {
2233
- bindRawWindowValue[key] = value[key];
2285
+ // actions for completely destroy
2286
+ actionsForCompletelyDestory() {
2287
+ if (!this.useSandbox && this.umdMode) {
2288
+ delete window[this.libraryName];
2234
2289
  }
2235
- if (value.hasOwnProperty('prototype') && !bindRawWindowValue.hasOwnProperty('prototype')) {
2236
- bindRawWindowValue.prototype = value.prototype;
2290
+ appInstanceMap.delete(this.name);
2291
+ }
2292
+ /**
2293
+ * app rendering error
2294
+ * @param e Error
2295
+ */
2296
+ onerror(e) {
2297
+ dispatchLifecyclesEvent(this.container, this.name, lifeCycles.ERROR, e);
2298
+ }
2299
+ // get app status
2300
+ getAppStatus() {
2301
+ return this.status;
2302
+ }
2303
+ // get umd library, if it not exist, return empty object
2304
+ getUmdLibraryHooks() {
2305
+ var _a, _b;
2306
+ // after execScripts, the app maybe unmounted
2307
+ if (appStatus.UNMOUNT !== this.status) {
2308
+ const global = ((_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow) !== null && _b !== void 0 ? _b : globalEnv.rawWindow);
2309
+ this.libraryName = getRootContainer(this.container).getAttribute('library') || `micro-app-${this.name}`;
2310
+ // do not use isObject
2311
+ return typeof global[this.libraryName] === 'object' ? global[this.libraryName] : {};
2237
2312
  }
2238
- rawWindowMethodMap.set(value, bindRawWindowValue);
2239
- return bindRawWindowValue;
2313
+ return {};
2240
2314
  }
2241
- return value;
2315
+ }
2316
+ // if app not prefetch & not unmount, then app is active
2317
+ function getActiveApps() {
2318
+ const activeApps = [];
2319
+ appInstanceMap.forEach((app, appName) => {
2320
+ if (appStatus.UNMOUNT !== app.getAppStatus() && !app.isPrefetch) {
2321
+ activeApps.push(appName);
2322
+ }
2323
+ });
2324
+ return activeApps;
2325
+ }
2326
+ // get all registered apps
2327
+ function getAllApps() {
2328
+ return Array.from(appInstanceMap.keys());
2242
2329
  }
2243
2330
 
2244
- // document.onclick binding list, the binding function of each application is unique
2245
- const documentClickListMap = new Map();
2246
- let hasRewriteDocumentOnClick = false;
2331
+ // Record element and map element
2332
+ const dynamicElementInMicroAppMap = new WeakMap();
2247
2333
  /**
2248
- * Rewrite document.onclick and execute it only once
2334
+ * Process the new node and format the style, link and script element
2335
+ * @param parent parent node
2336
+ * @param child new node
2337
+ * @param app app
2249
2338
  */
2250
- function overwriteDocumentOnClick() {
2251
- hasRewriteDocumentOnClick = true;
2252
- if (Object.getOwnPropertyDescriptor(document, 'onclick')) {
2253
- return logWarn('Cannot redefine document property onclick');
2339
+ function handleNewNode(parent, child, app) {
2340
+ if (child instanceof HTMLStyleElement) {
2341
+ if (child.hasAttribute('exclude')) {
2342
+ const replaceComment = document.createComment('style element with exclude attribute ignored by micro-app');
2343
+ dynamicElementInMicroAppMap.set(child, replaceComment);
2344
+ return replaceComment;
2345
+ }
2346
+ else if (app.scopecss && !child.hasAttribute('ignore')) {
2347
+ return scopedCSS(child, app);
2348
+ }
2349
+ return child;
2254
2350
  }
2255
- const rawOnClick = document.onclick;
2256
- document.onclick = null;
2257
- let hasDocumentClickInited = false;
2258
- function onClickHandler(e) {
2259
- documentClickListMap.forEach((f) => {
2260
- isFunction(f) && f.call(document, e);
2261
- });
2351
+ else if (child instanceof HTMLLinkElement) {
2352
+ if (child.hasAttribute('exclude')) {
2353
+ const linkReplaceComment = document.createComment('link element with exclude attribute ignored by micro-app');
2354
+ dynamicElementInMicroAppMap.set(child, linkReplaceComment);
2355
+ return linkReplaceComment;
2356
+ }
2357
+ else if (child.hasAttribute('ignore')) {
2358
+ return child;
2359
+ }
2360
+ const { url, info, replaceComment } = extractLinkFromHtml(child, parent, app, true);
2361
+ if (url && info) {
2362
+ const replaceStyle = pureCreateElement('style');
2363
+ replaceStyle.__MICRO_APP_LINK_PATH__ = url;
2364
+ foramtDynamicLink(url, info, app, child, replaceStyle);
2365
+ dynamicElementInMicroAppMap.set(child, replaceStyle);
2366
+ return replaceStyle;
2367
+ }
2368
+ else if (replaceComment) {
2369
+ dynamicElementInMicroAppMap.set(child, replaceComment);
2370
+ return replaceComment;
2371
+ }
2372
+ return child;
2262
2373
  }
2263
- Object.defineProperty(document, 'onclick', {
2264
- configurable: true,
2265
- enumerable: true,
2266
- get() {
2267
- const appName = getCurrentAppName();
2268
- return appName ? documentClickListMap.get(appName) : documentClickListMap.get('base');
2269
- },
2270
- set(f) {
2271
- const appName = getCurrentAppName();
2272
- if (appName) {
2273
- documentClickListMap.set(appName, f);
2274
- }
2275
- else {
2276
- documentClickListMap.set('base', f);
2374
+ else if (child instanceof HTMLScriptElement) {
2375
+ const { replaceComment, url, info } = extractScriptElement(child, parent, app, true) || {};
2376
+ if (url && info) {
2377
+ if (!info.isExternal) { // inline script
2378
+ const replaceElement = runScript(url, app, info, true);
2379
+ dynamicElementInMicroAppMap.set(child, replaceElement);
2380
+ return replaceElement;
2277
2381
  }
2278
- if (!hasDocumentClickInited && isFunction(f)) {
2279
- hasDocumentClickInited = true;
2280
- globalEnv.rawDocumentAddEventListener.call(globalEnv.rawDocument, 'click', onClickHandler, false);
2382
+ else { // remote script
2383
+ const replaceElement = runDynamicRemoteScript(url, info, app, child);
2384
+ dynamicElementInMicroAppMap.set(child, replaceElement);
2385
+ return replaceElement;
2281
2386
  }
2282
2387
  }
2283
- });
2284
- if (rawOnClick) {
2285
- document.onclick = rawOnClick;
2388
+ else if (replaceComment) {
2389
+ dynamicElementInMicroAppMap.set(child, replaceComment);
2390
+ return replaceComment;
2391
+ }
2392
+ return child;
2286
2393
  }
2394
+ return child;
2287
2395
  }
2288
2396
  /**
2289
- * The document event is globally, we need to clear these event bindings when micro application unmounted
2397
+ * Handle the elements inserted into head and body, and execute normally in other cases
2398
+ * @param app app
2399
+ * @param method raw method
2400
+ * @param parent parent node
2401
+ * @param targetChild target node
2402
+ * @param passiveChild second param of insertBefore and replaceChild
2290
2403
  */
2291
- const documentEventListenerMap = new Map();
2292
- function effectDocumentEvent() {
2293
- const { rawDocument, rawDocumentAddEventListener, rawDocumentRemoveEventListener, } = globalEnv;
2294
- if (!hasRewriteDocumentOnClick) {
2295
- overwriteDocumentOnClick();
2296
- }
2297
- document.addEventListener = function (type, listener, options) {
2298
- var _a;
2299
- const appName = getCurrentAppName();
2404
+ function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
2405
+ /**
2406
+ * If passiveChild is not the child node, insertBefore replaceChild will have a problem, at this time, it will be degraded to appendChild
2407
+ * E.g: document.head.insertBefore(targetChild, document.head.childNodes[0])
2408
+ */
2409
+ if (parent === document.head) {
2410
+ const microAppHead = app.container.querySelector('micro-app-head');
2300
2411
  /**
2301
- * ignore bound function of document event in umd mode, used to solve problem of react global events
2412
+ * 1. If passivechild exists, it must be insertBefore or replacechild
2413
+ * 2. When removeChild, targetChild may not be in microAppHead or head
2302
2414
  */
2303
- if (appName && !(((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.umdMode) && isBoundFunction(listener))) {
2304
- const appListenersMap = documentEventListenerMap.get(appName);
2305
- if (appListenersMap) {
2306
- const appListenerList = appListenersMap.get(type);
2307
- if (appListenerList) {
2308
- appListenerList.add(listener);
2309
- }
2310
- else {
2311
- appListenersMap.set(type, new Set([listener]));
2312
- }
2415
+ if (passiveChild && !microAppHead.contains(passiveChild)) {
2416
+ return globalEnv.rawAppendChild.call(microAppHead, targetChild);
2417
+ }
2418
+ else if (rawMethod === globalEnv.rawRemoveChild && !microAppHead.contains(targetChild)) {
2419
+ if (parent.contains(targetChild)) {
2420
+ return rawMethod.call(parent, targetChild);
2313
2421
  }
2314
- else {
2315
- documentEventListenerMap.set(appName, new Map([[type, new Set([listener])]]));
2422
+ return targetChild;
2423
+ }
2424
+ else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
2425
+ return rawMethod.call(microAppHead, targetChild);
2426
+ }
2427
+ return rawMethod.call(microAppHead, targetChild, passiveChild);
2428
+ }
2429
+ else if (parent === document.body) {
2430
+ const microAppBody = app.container.querySelector('micro-app-body');
2431
+ if (passiveChild && !microAppBody.contains(passiveChild)) {
2432
+ return globalEnv.rawAppendChild.call(microAppBody, targetChild);
2433
+ }
2434
+ else if (rawMethod === globalEnv.rawRemoveChild && !microAppBody.contains(targetChild)) {
2435
+ if (parent.contains(targetChild)) {
2436
+ return rawMethod.call(parent, targetChild);
2316
2437
  }
2317
- listener && (listener.__MICRO_MARK_OPTIONS__ = options);
2438
+ return targetChild;
2318
2439
  }
2319
- rawDocumentAddEventListener.call(rawDocument, type, listener, options);
2320
- };
2321
- document.removeEventListener = function (type, listener, options) {
2322
- const appName = getCurrentAppName();
2323
- if (appName) {
2324
- const appListenersMap = documentEventListenerMap.get(appName);
2325
- if (appListenersMap) {
2326
- const appListenerList = appListenersMap.get(type);
2327
- if ((appListenerList === null || appListenerList === void 0 ? void 0 : appListenerList.size) && appListenerList.has(listener)) {
2328
- appListenerList.delete(listener);
2440
+ else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
2441
+ return rawMethod.call(microAppBody, targetChild);
2442
+ }
2443
+ return rawMethod.call(microAppBody, targetChild, passiveChild);
2444
+ }
2445
+ else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
2446
+ return rawMethod.call(parent, targetChild);
2447
+ }
2448
+ return rawMethod.call(parent, targetChild, passiveChild);
2449
+ }
2450
+ // Get the map element
2451
+ function getMappingNode(node) {
2452
+ var _a;
2453
+ return (_a = dynamicElementInMicroAppMap.get(node)) !== null && _a !== void 0 ? _a : node;
2454
+ }
2455
+ /**
2456
+ * method of handle new node
2457
+ * @param parent parent node
2458
+ * @param newChild new node
2459
+ * @param passiveChild passive node
2460
+ * @param rawMethodraw method
2461
+ */
2462
+ function commonElementHander(parent, newChild, passiveChild, rawMethod) {
2463
+ if (newChild === null || newChild === void 0 ? void 0 : newChild.__MICRO_APP_NAME__) {
2464
+ const app = appInstanceMap.get(newChild.__MICRO_APP_NAME__);
2465
+ if (app === null || app === void 0 ? void 0 : app.container) {
2466
+ return invokePrototypeMethod(app, rawMethod, parent, handleNewNode(parent, newChild, app), passiveChild && getMappingNode(passiveChild));
2467
+ }
2468
+ else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
2469
+ return rawMethod.call(parent, newChild);
2470
+ }
2471
+ return rawMethod.call(parent, newChild, passiveChild);
2472
+ }
2473
+ else if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
2474
+ const appName = getCurrentAppName();
2475
+ if (!(newChild instanceof Node) && appName) {
2476
+ const app = appInstanceMap.get(appName);
2477
+ if (app === null || app === void 0 ? void 0 : app.container) {
2478
+ if (parent === document.head) {
2479
+ return rawMethod.call(app.container.querySelector('micro-app-head'), newChild);
2480
+ }
2481
+ else if (parent === document.body) {
2482
+ return rawMethod.call(app.container.querySelector('micro-app-body'), newChild);
2329
2483
  }
2330
2484
  }
2331
2485
  }
2332
- rawDocumentRemoveEventListener.call(rawDocument, type, listener, options);
2333
- };
2334
- }
2335
- // Clear the document event agent
2336
- function releaseEffectDocumentEvent() {
2337
- document.addEventListener = globalEnv.rawDocumentAddEventListener;
2338
- document.removeEventListener = globalEnv.rawDocumentRemoveEventListener;
2339
- }
2340
- /**
2341
- * Format event name
2342
- * @param type event name
2343
- * @param microWindow micro window
2344
- */
2345
- function formatEventType(type, microWindow) {
2346
- if (type === 'unmount') {
2347
- return `unmount-${microWindow.__MICRO_APP_NAME__}`;
2486
+ return rawMethod.call(parent, newChild);
2348
2487
  }
2349
- return type;
2488
+ return rawMethod.call(parent, newChild, passiveChild);
2350
2489
  }
2351
2490
  /**
2352
- * Rewrite side-effect events
2353
- * @param microWindow micro window
2491
+ * Rewrite element prototype method
2354
2492
  */
2355
- function effect(microWindow) {
2356
- const appName = microWindow.__MICRO_APP_NAME__;
2357
- const eventListenerMap = new Map();
2358
- const intervalIdMap = new Map();
2359
- const timeoutIdMap = new Map();
2360
- const { rawWindow, rawDocument, rawWindowAddEventListener, rawWindowRemoveEventListener, rawSetInterval, rawSetTimeout, rawClearInterval, rawClearTimeout, rawDocumentRemoveEventListener, } = globalEnv;
2361
- // listener may be null, e.g test-passive
2362
- microWindow.addEventListener = function (type, listener, options) {
2363
- type = formatEventType(type, microWindow);
2364
- const listenerList = eventListenerMap.get(type);
2365
- if (listenerList) {
2366
- listenerList.add(listener);
2493
+ function patchElementPrototypeMethods() {
2494
+ patchDocument();
2495
+ // Rewrite setAttribute
2496
+ Element.prototype.setAttribute = function setAttribute(key, value) {
2497
+ if (/^micro-app(-\S+)?/i.test(this.tagName) && key === 'data') {
2498
+ if (isPlainObject(value)) {
2499
+ const cloneValue = {};
2500
+ Object.getOwnPropertyNames(value).forEach((propertyKey) => {
2501
+ if (!(isString(propertyKey) && propertyKey.indexOf('__') === 0)) {
2502
+ // @ts-ignore
2503
+ cloneValue[propertyKey] = value[propertyKey];
2504
+ }
2505
+ });
2506
+ this.data = cloneValue;
2507
+ }
2508
+ else if (value !== '[object Object]') {
2509
+ logWarn('property data must be an object', this.getAttribute('name'));
2510
+ }
2511
+ }
2512
+ else if ((((key === 'src' || key === 'srcset') && /^(img|script)$/i.test(this.tagName)) ||
2513
+ (key === 'href' && /^link$/i.test(this.tagName))) &&
2514
+ this.__MICRO_APP_NAME__ &&
2515
+ appInstanceMap.has(this.__MICRO_APP_NAME__)) {
2516
+ const app = appInstanceMap.get(this.__MICRO_APP_NAME__);
2517
+ globalEnv.rawSetAttribute.call(this, key, CompletionPath(value, app.url));
2367
2518
  }
2368
2519
  else {
2369
- eventListenerMap.set(type, new Set([listener]));
2520
+ globalEnv.rawSetAttribute.call(this, key, value);
2370
2521
  }
2371
- listener && (listener.__MICRO_MARK_OPTIONS__ = options);
2372
- rawWindowAddEventListener.call(rawWindow, type, listener, options);
2373
2522
  };
2374
- microWindow.removeEventListener = function (type, listener, options) {
2375
- type = formatEventType(type, microWindow);
2376
- const listenerList = eventListenerMap.get(type);
2377
- if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
2378
- listenerList.delete(listener);
2379
- }
2380
- rawWindowRemoveEventListener.call(rawWindow, type, listener, options);
2523
+ // prototype methods of add element👇
2524
+ Node.prototype.appendChild = function appendChild(newChild) {
2525
+ return commonElementHander(this, newChild, null, globalEnv.rawAppendChild);
2381
2526
  };
2382
- microWindow.setInterval = function (handler, timeout, ...args) {
2383
- const intervalId = rawSetInterval.call(rawWindow, handler, timeout, ...args);
2384
- intervalIdMap.set(intervalId, { handler, timeout, args });
2385
- return intervalId;
2527
+ Node.prototype.insertBefore = function insertBefore(newChild, refChild) {
2528
+ return commonElementHander(this, newChild, refChild, globalEnv.rawInsertBefore);
2386
2529
  };
2387
- microWindow.setTimeout = function (handler, timeout, ...args) {
2388
- const timeoutId = rawSetTimeout.call(rawWindow, handler, timeout, ...args);
2389
- timeoutIdMap.set(timeoutId, { handler, timeout, args });
2390
- return timeoutId;
2530
+ Node.prototype.replaceChild = function replaceChild(newChild, oldChild) {
2531
+ return commonElementHander(this, newChild, oldChild, globalEnv.rawReplaceChild);
2391
2532
  };
2392
- microWindow.clearInterval = function (intervalId) {
2393
- intervalIdMap.delete(intervalId);
2394
- rawClearInterval.call(rawWindow, intervalId);
2533
+ Element.prototype.append = function append(...nodes) {
2534
+ let i = 0;
2535
+ const length = nodes.length;
2536
+ while (i < length) {
2537
+ commonElementHander(this, nodes[i], null, globalEnv.rawAppend);
2538
+ i++;
2539
+ }
2395
2540
  };
2396
- microWindow.clearTimeout = function (timeoutId) {
2397
- timeoutIdMap.delete(timeoutId);
2398
- rawClearTimeout.call(rawWindow, timeoutId);
2541
+ Element.prototype.prepend = function prepend(...nodes) {
2542
+ let i = nodes.length;
2543
+ while (i > 0) {
2544
+ commonElementHander(this, nodes[i - 1], null, globalEnv.rawPrepend);
2545
+ i--;
2546
+ }
2399
2547
  };
2400
- const umdWindowListenerMap = new Map();
2401
- const umdDocumentListenerMap = new Map();
2402
- let umdIntervalIdMap = new Map();
2403
- let umdTimeoutIdMap = new Map();
2404
- let umdOnClickHandler;
2405
- // record event and timer before exec umdMountHook
2406
- const recordUmdEffect = () => {
2407
- // record window event
2408
- eventListenerMap.forEach((listenerList, type) => {
2409
- if (listenerList.size) {
2410
- umdWindowListenerMap.set(type, new Set(listenerList));
2548
+ // prototype methods of delete element👇
2549
+ Node.prototype.removeChild = function removeChild(oldChild) {
2550
+ if (oldChild === null || oldChild === void 0 ? void 0 : oldChild.__MICRO_APP_NAME__) {
2551
+ const app = appInstanceMap.get(oldChild.__MICRO_APP_NAME__);
2552
+ if (app === null || app === void 0 ? void 0 : app.container) {
2553
+ return invokePrototypeMethod(app, globalEnv.rawRemoveChild, this, getMappingNode(oldChild));
2411
2554
  }
2412
- });
2413
- // record timers
2414
- if (intervalIdMap.size) {
2415
- umdIntervalIdMap = new Map(intervalIdMap);
2555
+ return globalEnv.rawRemoveChild.call(this, oldChild);
2416
2556
  }
2417
- if (timeoutIdMap.size) {
2418
- umdTimeoutIdMap = new Map(timeoutIdMap);
2557
+ return globalEnv.rawRemoveChild.call(this, oldChild);
2558
+ };
2559
+ }
2560
+ /**
2561
+ * Mark the newly created element in the micro application
2562
+ * @param element new element
2563
+ */
2564
+ function markElement(element) {
2565
+ const appName = getCurrentAppName();
2566
+ appName && (element.__MICRO_APP_NAME__ = appName);
2567
+ return element;
2568
+ }
2569
+ // methods of document
2570
+ function patchDocument() {
2571
+ const rawDocument = globalEnv.rawDocument;
2572
+ // create element 👇
2573
+ Document.prototype.createElement = function createElement(tagName, options) {
2574
+ const element = globalEnv.rawCreateElement.call(this, tagName, options);
2575
+ return markElement(element);
2576
+ };
2577
+ Document.prototype.createElementNS = function createElementNS(namespaceURI, name, options) {
2578
+ const element = globalEnv.rawCreateElementNS.call(this, namespaceURI, name, options);
2579
+ return markElement(element);
2580
+ };
2581
+ Document.prototype.createDocumentFragment = function createDocumentFragment() {
2582
+ const element = globalEnv.rawCreateDocumentFragment.call(this);
2583
+ return markElement(element);
2584
+ };
2585
+ // query element👇
2586
+ function querySelector(selectors) {
2587
+ var _a, _b, _c;
2588
+ const appName = getCurrentAppName();
2589
+ if (!appName ||
2590
+ !selectors ||
2591
+ isUniqueElement(selectors) ||
2592
+ // see https://github.com/micro-zoe/micro-app/issues/56
2593
+ rawDocument !== this) {
2594
+ return globalEnv.rawQuerySelector.call(this, selectors);
2419
2595
  }
2420
- // record onclick handler
2421
- umdOnClickHandler = documentClickListMap.get(appName);
2422
- // record document event
2423
- const documentAppListenersMap = documentEventListenerMap.get(appName);
2424
- if (documentAppListenersMap) {
2425
- documentAppListenersMap.forEach((listenerList, type) => {
2426
- if (listenerList.size) {
2427
- umdDocumentListenerMap.set(type, new Set(listenerList));
2428
- }
2429
- });
2596
+ return (_c = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) === null || _b === void 0 ? void 0 : _b.querySelector(selectors)) !== null && _c !== void 0 ? _c : null;
2597
+ }
2598
+ function querySelectorAll(selectors) {
2599
+ var _a, _b, _c;
2600
+ const appName = getCurrentAppName();
2601
+ if (!appName ||
2602
+ !selectors ||
2603
+ isUniqueElement(selectors) ||
2604
+ rawDocument !== this) {
2605
+ return globalEnv.rawQuerySelectorAll.call(this, selectors);
2606
+ }
2607
+ return (_c = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container) === null || _b === void 0 ? void 0 : _b.querySelectorAll(selectors)) !== null && _c !== void 0 ? _c : [];
2608
+ }
2609
+ Document.prototype.querySelector = querySelector;
2610
+ Document.prototype.querySelectorAll = querySelectorAll;
2611
+ Document.prototype.getElementById = function getElementById(key) {
2612
+ if (!getCurrentAppName() || isInvalidQuerySelectorKey(key)) {
2613
+ return globalEnv.rawGetElementById.call(this, key);
2614
+ }
2615
+ try {
2616
+ return querySelector.call(this, `#${key}`);
2617
+ }
2618
+ catch (_a) {
2619
+ return globalEnv.rawGetElementById.call(this, key);
2430
2620
  }
2431
2621
  };
2432
- // rebuild event and timer before remount umd app
2433
- const rebuildUmdEffect = () => {
2434
- // rebuild window event
2435
- umdWindowListenerMap.forEach((listenerList, type) => {
2436
- for (const listener of listenerList) {
2437
- microWindow.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_MARK_OPTIONS__);
2438
- }
2439
- });
2440
- // rebuild timer
2441
- umdIntervalIdMap.forEach((info) => {
2442
- microWindow.setInterval(info.handler, info.timeout, ...info.args);
2443
- });
2444
- umdTimeoutIdMap.forEach((info) => {
2445
- microWindow.setTimeout(info.handler, info.timeout, ...info.args);
2446
- });
2447
- // rebuild onclick event
2448
- umdOnClickHandler && documentClickListMap.set(appName, umdOnClickHandler);
2449
- // rebuild document event
2450
- setCurrentAppName(appName);
2451
- umdDocumentListenerMap.forEach((listenerList, type) => {
2452
- for (const listener of listenerList) {
2453
- document.addEventListener(type, listener, listener === null || listener === void 0 ? void 0 : listener.__MICRO_MARK_OPTIONS__);
2454
- }
2455
- });
2456
- setCurrentAppName(null);
2457
- };
2458
- // release all event listener & interval & timeout when unmount app
2459
- const releaseEffect = () => {
2460
- // Clear window binding events
2461
- if (eventListenerMap.size) {
2462
- eventListenerMap.forEach((listenerList, type) => {
2463
- for (const listener of listenerList) {
2464
- rawWindowRemoveEventListener.call(rawWindow, type, listener);
2465
- }
2466
- });
2467
- eventListenerMap.clear();
2622
+ Document.prototype.getElementsByClassName = function getElementsByClassName(key) {
2623
+ if (!getCurrentAppName() || isInvalidQuerySelectorKey(key)) {
2624
+ return globalEnv.rawGetElementsByClassName.call(this, key);
2468
2625
  }
2469
- // Clear timers
2470
- if (intervalIdMap.size) {
2471
- intervalIdMap.forEach((_, intervalId) => {
2472
- rawClearInterval.call(rawWindow, intervalId);
2473
- });
2474
- intervalIdMap.clear();
2626
+ try {
2627
+ return querySelectorAll.call(this, `.${key}`);
2475
2628
  }
2476
- if (timeoutIdMap.size) {
2477
- timeoutIdMap.forEach((_, timeoutId) => {
2478
- rawClearTimeout.call(rawWindow, timeoutId);
2479
- });
2480
- timeoutIdMap.clear();
2629
+ catch (_a) {
2630
+ return globalEnv.rawGetElementsByClassName.call(this, key);
2481
2631
  }
2482
- // Clear the function bound by micro application through document.onclick
2483
- documentClickListMap.delete(appName);
2484
- // Clear document binding event
2485
- const documentAppListenersMap = documentEventListenerMap.get(appName);
2486
- if (documentAppListenersMap) {
2487
- documentAppListenersMap.forEach((listenerList, type) => {
2488
- for (const listener of listenerList) {
2489
- rawDocumentRemoveEventListener.call(rawDocument, type, listener);
2490
- }
2491
- });
2492
- documentAppListenersMap.clear();
2632
+ };
2633
+ Document.prototype.getElementsByTagName = function getElementsByTagName(key) {
2634
+ var _a;
2635
+ const appName = getCurrentAppName();
2636
+ if (!appName ||
2637
+ isUniqueElement(key) ||
2638
+ isInvalidQuerySelectorKey(key) ||
2639
+ (!((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.inline) && /^script$/i.test(key))) {
2640
+ return globalEnv.rawGetElementsByTagName.call(this, key);
2641
+ }
2642
+ try {
2643
+ return querySelectorAll.call(this, key);
2644
+ }
2645
+ catch (_b) {
2646
+ return globalEnv.rawGetElementsByTagName.call(this, key);
2493
2647
  }
2494
2648
  };
2495
- return {
2496
- recordUmdEffect,
2497
- rebuildUmdEffect,
2498
- releaseEffect,
2649
+ Document.prototype.getElementsByName = function getElementsByName(key) {
2650
+ if (!getCurrentAppName() || isInvalidQuerySelectorKey(key)) {
2651
+ return globalEnv.rawGetElementsByName.call(this, key);
2652
+ }
2653
+ try {
2654
+ return querySelectorAll.call(this, `[name=${key}]`);
2655
+ }
2656
+ catch (_a) {
2657
+ return globalEnv.rawGetElementsByName.call(this, key);
2658
+ }
2499
2659
  };
2500
2660
  }
2661
+ function releasePatchDocument() {
2662
+ Document.prototype.createElement = globalEnv.rawCreateElement;
2663
+ Document.prototype.createElementNS = globalEnv.rawCreateElementNS;
2664
+ Document.prototype.createDocumentFragment = globalEnv.rawCreateDocumentFragment;
2665
+ Document.prototype.querySelector = globalEnv.rawQuerySelector;
2666
+ Document.prototype.querySelectorAll = globalEnv.rawQuerySelectorAll;
2667
+ Document.prototype.getElementById = globalEnv.rawGetElementById;
2668
+ Document.prototype.getElementsByClassName = globalEnv.rawGetElementsByClassName;
2669
+ Document.prototype.getElementsByTagName = globalEnv.rawGetElementsByTagName;
2670
+ Document.prototype.getElementsByName = globalEnv.rawGetElementsByName;
2671
+ }
2672
+ // release patch
2673
+ function releasePatches() {
2674
+ setCurrentAppName(null);
2675
+ releasePatchDocument();
2676
+ Element.prototype.setAttribute = globalEnv.rawSetAttribute;
2677
+ Node.prototype.appendChild = globalEnv.rawAppendChild;
2678
+ Node.prototype.insertBefore = globalEnv.rawInsertBefore;
2679
+ Node.prototype.replaceChild = globalEnv.rawReplaceChild;
2680
+ Node.prototype.removeChild = globalEnv.rawRemoveChild;
2681
+ Element.prototype.append = globalEnv.rawAppend;
2682
+ Element.prototype.prepend = globalEnv.rawPrepend;
2683
+ }
2684
+ // Set the style of micro-app-head and micro-app-body
2685
+ let hasRejectMicroAppStyle = false;
2686
+ function rejectMicroAppStyle() {
2687
+ if (!hasRejectMicroAppStyle) {
2688
+ hasRejectMicroAppStyle = true;
2689
+ const style = pureCreateElement('style');
2690
+ style.setAttribute('type', 'text/css');
2691
+ style.textContent = `\n${microApp.tagName}, micro-app-body { display: block; } \nmicro-app-head { display: none; }`;
2692
+ globalEnv.rawDocument.head.appendChild(style);
2693
+ }
2694
+ }
2501
2695
 
2502
- // Variables that can escape to rawWindow
2503
- const staticEscapeProperties = [
2504
- 'System',
2505
- '__cjsWrapper',
2506
- '__REACT_ERROR_OVERLAY_GLOBAL_HOOK__',
2507
- ];
2508
- // Variables that can only assigned to rawWindow
2509
- const escapeSetterKeyList = [
2510
- 'location',
2511
- ];
2512
- const unscopables = {
2513
- undefined: true,
2514
- Array: true,
2515
- Object: true,
2516
- String: true,
2517
- Boolean: true,
2518
- Math: true,
2519
- Number: true,
2520
- Symbol: true,
2521
- parseFloat: true,
2522
- Float32Array: true,
2523
- };
2696
+ function unmountNestedApp() {
2697
+ replaseUnmountOfNestedApp();
2698
+ appInstanceMap.forEach(app => {
2699
+ // @ts-ignore
2700
+ app.container && getRootContainer(app.container).disconnectedCallback();
2701
+ });
2702
+ !window.__MICRO_APP_UMD_MODE__ && appInstanceMap.clear();
2703
+ if (elementInstanceMap.size) {
2704
+ elementInstanceMap.clear();
2705
+ releasePatches();
2706
+ }
2707
+ }
2708
+ // if micro-app run in micro application, delete all next generation application when unmount event received
2709
+ function listenUmountOfNestedApp() {
2710
+ if (window.__MICRO_APP_ENVIRONMENT__) {
2711
+ window.addEventListener('unmount', unmountNestedApp, false);
2712
+ }
2713
+ }
2714
+ // release listener
2715
+ function replaseUnmountOfNestedApp() {
2716
+ if (window.__MICRO_APP_ENVIRONMENT__) {
2717
+ window.removeEventListener('unmount', unmountNestedApp, false);
2718
+ }
2719
+ }
2720
+
2721
+ // record all micro-app elements
2722
+ const elementInstanceMap = new Map();
2524
2723
  /**
2525
- * macro task to solve the rendering problem of vue3
2724
+ * define element
2725
+ * @param tagName element name
2526
2726
  */
2527
- let macroTimer;
2528
- function macroTask(fn) {
2529
- if (macroTimer)
2530
- clearTimeout(macroTimer);
2531
- macroTimer = setTimeout(fn, 0);
2532
- }
2533
- class SandBox {
2534
- constructor(appName, url, macro) {
2535
- // Scoped global Properties(Properties that can only get and set in microWindow, will not escape to rawWindow)
2536
- this.scopeProperties = ['webpackJsonp'];
2537
- // Properties that can be escape to rawWindow
2538
- this.escapeProperties = [];
2539
- // Properties newly added to microWindow
2540
- this.injectedKeys = new Set();
2541
- // Properties escape to rawWindow, cleared when unmount
2542
- this.escapeKeys = new Set();
2543
- // sandbox state
2544
- this.active = false;
2545
- this.microWindow = {}; // Proxy target
2546
- const rawWindow = globalEnv.rawWindow;
2547
- const rawDocument = globalEnv.rawDocument;
2548
- const descriptorTargetMap = new Map();
2549
- const hasOwnProperty = (key) => this.microWindow.hasOwnProperty(key) || rawWindow.hasOwnProperty(key);
2550
- // get scopeProperties and escapeProperties from plugins
2551
- this.getScopeProperties(appName);
2552
- // inject global properties
2553
- this.inject(this.microWindow, appName, url);
2554
- // Rewrite global event listener & timeout
2555
- Object.assign(this, effect(this.microWindow));
2556
- this.proxyWindow = new Proxy(this.microWindow, {
2557
- get: (target, key) => {
2558
- if (key === Symbol.unscopables)
2559
- return unscopables;
2560
- if (['window', 'self', 'globalThis'].includes(key)) {
2561
- return this.proxyWindow;
2562
- }
2563
- if (key === 'top' || key === 'parent') {
2564
- if (rawWindow === rawWindow.parent) { // not in iframe
2565
- return this.proxyWindow;
2566
- }
2567
- return Reflect.get(rawWindow, key); // iframe
2568
- }
2569
- if (key === 'hasOwnProperty')
2570
- return hasOwnProperty;
2571
- if (key === 'document' || key === 'eval') {
2572
- if (this.active) {
2573
- setCurrentAppName(appName);
2574
- (macro ? macroTask : defer)(() => setCurrentAppName(null));
2575
- }
2576
- switch (key) {
2577
- case 'document':
2578
- return rawDocument;
2579
- case 'eval':
2580
- return eval;
2581
- }
2582
- }
2583
- if (Reflect.has(target, key)) {
2584
- return Reflect.get(target, key);
2585
- }
2586
- if (this.scopeProperties.includes(key) ||
2587
- (isString(key) && /^__MICRO_APP_/.test(key))) {
2588
- return Reflect.get(target, key);
2589
- }
2590
- const rawValue = Reflect.get(rawWindow, key);
2591
- return bindFunctionToRawWidow(rawWindow, rawValue);
2592
- },
2593
- set: (target, key, value) => {
2594
- if (this.active) {
2595
- if (escapeSetterKeyList.includes(key)) {
2596
- Reflect.set(rawWindow, key, value);
2597
- }
2598
- else if (!target.hasOwnProperty(key) &&
2599
- rawWindow.hasOwnProperty(key) &&
2600
- !this.scopeProperties.includes(key)) {
2601
- const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
2602
- const { writable, configurable, enumerable } = descriptor;
2603
- if (writable) {
2604
- Object.defineProperty(target, key, {
2605
- configurable,
2606
- enumerable,
2607
- writable,
2608
- value,
2609
- });
2610
- this.injectedKeys.add(key);
2727
+ function defineElement(tagName) {
2728
+ class MicroAppElement extends HTMLElement {
2729
+ constructor() {
2730
+ super();
2731
+ this.isWating = false;
2732
+ this.cacheData = null;
2733
+ this.hasConnected = false;
2734
+ this.appName = ''; // app name
2735
+ this.appUrl = ''; // app url
2736
+ this.ssrUrl = ''; // html path in ssr mode
2737
+ this.version = version;
2738
+ /**
2739
+ * handle for change of name an url after element inited
2740
+ */
2741
+ this.handleAttributeUpdate = () => {
2742
+ var _a;
2743
+ this.isWating = false;
2744
+ const formatAttrName = formatAppName(this.getAttribute('name'));
2745
+ const formatAttrUrl = formatAppURL(this.getAttribute('url'), this.appName);
2746
+ if (this.legalAttribute('name', formatAttrName) && this.legalAttribute('url', formatAttrUrl)) {
2747
+ const existApp = appInstanceMap.get(formatAttrName);
2748
+ if (formatAttrName !== this.appName && existApp) {
2749
+ // handling of cached and non-prefetch apps
2750
+ if (appStatus.UNMOUNT !== existApp.getAppStatus() && !existApp.isPrefetch) {
2751
+ this.setAttribute('name', this.appName);
2752
+ return logError(`an app named ${formatAttrName} already exists`, this.appName);
2611
2753
  }
2612
2754
  }
2613
- else {
2614
- Reflect.set(target, key, value);
2615
- this.injectedKeys.add(key);
2616
- }
2617
- if ((this.escapeProperties.includes(key) ||
2618
- (staticEscapeProperties.includes(key) && !Reflect.has(rawWindow, key))) &&
2619
- !this.scopeProperties.includes(key)) {
2620
- Reflect.set(rawWindow, key, value);
2621
- this.escapeKeys.add(key);
2622
- }
2623
- }
2624
- return true;
2625
- },
2626
- has: (target, key) => {
2627
- if (this.scopeProperties.includes(key))
2628
- return key in target;
2629
- return key in unscopables || key in target || key in rawWindow;
2630
- },
2631
- getOwnPropertyDescriptor: (target, key) => {
2632
- if (target.hasOwnProperty(key)) {
2633
- descriptorTargetMap.set(key, 'target');
2634
- return Object.getOwnPropertyDescriptor(target, key);
2635
- }
2636
- if (rawWindow.hasOwnProperty(key)) {
2637
- descriptorTargetMap.set(key, 'rawWindow');
2638
- const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
2639
- if (descriptor && !descriptor.configurable) {
2640
- descriptor.configurable = true;
2641
- }
2642
- return descriptor;
2643
- }
2644
- return undefined;
2645
- },
2646
- defineProperty: (target, key, value) => {
2647
- const from = descriptorTargetMap.get(key);
2648
- if (from === 'rawWindow') {
2649
- return Reflect.defineProperty(rawWindow, key, value);
2650
- }
2651
- return Reflect.defineProperty(target, key, value);
2652
- },
2653
- ownKeys: (target) => {
2654
- return unique(Reflect.ownKeys(rawWindow).concat(Reflect.ownKeys(target)));
2655
- },
2656
- deleteProperty: (target, key) => {
2657
- if (target.hasOwnProperty(key)) {
2658
- if (this.escapeKeys.has(key)) {
2659
- Reflect.deleteProperty(rawWindow, key);
2755
+ if (formatAttrName !== this.appName || formatAttrUrl !== this.appUrl) {
2756
+ this.handleUnmount(formatAttrName === this.appName);
2757
+ /**
2758
+ * change ssrUrl in ssr mode
2759
+ * do not add judgment of formatAttrUrl === this.appUrl
2760
+ */
2761
+ if (this.getDisposeResult('ssr')) {
2762
+ this.ssrUrl = CompletionPath(globalEnv.rawWindow.location.pathname, formatAttrUrl);
2763
+ }
2764
+ else if (this.ssrUrl) {
2765
+ this.ssrUrl = '';
2766
+ }
2767
+ this.appName = formatAttrName;
2768
+ this.appUrl = formatAttrUrl;
2769
+ ((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this).innerHTML = '';
2770
+ if (formatAttrName !== this.getAttribute('name')) {
2771
+ this.setAttribute('name', this.appName);
2772
+ }
2773
+ /**
2774
+ * when existApp not null:
2775
+ * scene1: if formatAttrName and this.appName are equal: exitApp is the current app, the url must be different, existApp has been unmounted
2776
+ * scene2: if formatAttrName and this.appName are different: existApp must be prefetch or unmounted, if url is equal, then just mount, if url is different, then create new app to replace existApp
2777
+ * scene3: url is different but ssrUrl is equal
2778
+ * scene4: url is equal but ssrUrl is different, if url is equal, name must different
2779
+ */
2780
+ if (existApp &&
2781
+ existApp.url === this.appUrl &&
2782
+ existApp.ssrUrl === this.ssrUrl) {
2783
+ // mount app
2784
+ this.handleAppMount(existApp);
2785
+ }
2786
+ else {
2787
+ this.handleCreateApp();
2788
+ }
2660
2789
  }
2661
- return Reflect.deleteProperty(target, key);
2662
2790
  }
2663
- return true;
2664
- },
2665
- });
2666
- }
2667
- start(baseroute) {
2668
- if (!this.active) {
2669
- this.active = true;
2670
- this.microWindow.__MICRO_APP_BASE_ROUTE__ = this.microWindow.__MICRO_APP_BASE_URL__ = baseroute;
2671
- if (globalEnv.rawWindow._babelPolyfill)
2672
- globalEnv.rawWindow._babelPolyfill = false;
2673
- if (++SandBox.activeCount === 1) {
2674
- effectDocumentEvent();
2791
+ else if (formatAttrName !== this.appName) {
2792
+ this.setAttribute('name', this.appName);
2793
+ }
2794
+ };
2795
+ // cloned node of umd container also trigger constructor, we should skip
2796
+ if (!this.querySelector('micro-app-head')) {
2797
+ this.performWhenFirstCreated();
2675
2798
  }
2676
2799
  }
2677
- }
2678
- stop() {
2679
- if (this.active) {
2680
- this.active = false;
2681
- this.releaseEffect();
2682
- this.microWindow.microApp.clearDataListener();
2683
- this.microWindow.microApp.clearGlobalDataListener();
2684
- this.injectedKeys.forEach((key) => {
2685
- Reflect.deleteProperty(this.microWindow, key);
2686
- });
2687
- this.injectedKeys.clear();
2688
- this.escapeKeys.forEach((key) => {
2689
- Reflect.deleteProperty(globalEnv.rawWindow, key);
2690
- });
2691
- this.escapeKeys.clear();
2692
- if (--SandBox.activeCount === 0) {
2693
- releaseEffectDocumentEvent();
2800
+ static get observedAttributes() {
2801
+ return ['name', 'url'];
2802
+ }
2803
+ // 👇 Configuration
2804
+ // name: app name
2805
+ // url: html address
2806
+ // shadowDom: use shadowDOM, default is false
2807
+ // destroy: whether delete cache resources when unmount, default is false
2808
+ // inline: whether js runs in inline script mode, default is false
2809
+ // disableScopecss: whether disable css scoped, default is false
2810
+ // disableSandbox: whether disable sandbox, default is false
2811
+ // macro: used to solve the async render problem of vue3, default is false
2812
+ // baseRoute: route prefix, default is ''
2813
+ connectedCallback() {
2814
+ this.hasConnected = true;
2815
+ if (!elementInstanceMap.has(this)) {
2816
+ this.performWhenFirstCreated();
2694
2817
  }
2818
+ defer(() => dispatchLifecyclesEvent(this, this.appName, lifeCycles.CREATED));
2819
+ this.initialMount();
2695
2820
  }
2696
- }
2697
- // record umd snapshot before the first execution of umdHookMount
2698
- recordUmdSnapshot() {
2699
- this.microWindow.__MICRO_APP_UMD_MODE__ = true;
2700
- this.recordUmdEffect();
2701
- recordDataCenterSnapshot(this.microWindow.microApp);
2702
- this.recordUmdinjectedValues = new Map();
2703
- this.injectedKeys.forEach((key) => {
2704
- this.recordUmdinjectedValues.set(key, Reflect.get(this.microWindow, key));
2705
- });
2706
- }
2707
- // rebuild umd snapshot before remount umd app
2708
- rebuildUmdSnapshot() {
2709
- this.recordUmdinjectedValues.forEach((value, key) => {
2710
- Reflect.set(this.proxyWindow, key, value);
2711
- });
2712
- this.rebuildUmdEffect();
2713
- rebuildDataCenterSnapshot(this.microWindow.microApp);
2714
- }
2715
- /**
2716
- * get scopeProperties and escapeProperties from plugins
2717
- * @param appName app name
2718
- */
2719
- getScopeProperties(appName) {
2720
- var _a;
2721
- if (!isPlainObject(microApp.plugins))
2722
- return;
2723
- if (isArray(microApp.plugins.global)) {
2724
- for (const plugin of microApp.plugins.global) {
2725
- if (isPlainObject(plugin)) {
2726
- if (isArray(plugin.scopeProperties)) {
2727
- this.scopeProperties = this.scopeProperties.concat(plugin.scopeProperties);
2728
- }
2729
- if (isArray(plugin.escapeProperties)) {
2730
- this.escapeProperties = this.escapeProperties.concat(plugin.escapeProperties);
2731
- }
2732
- }
2821
+ disconnectedCallback() {
2822
+ this.hasConnected = false;
2823
+ elementInstanceMap.delete(this);
2824
+ this.handleUnmount(this.getDisposeResult('destroy') || this.getDisposeResult('destory'));
2825
+ if (elementInstanceMap.size === 0) {
2826
+ releasePatches();
2733
2827
  }
2734
2828
  }
2735
- if (isArray((_a = microApp.plugins.modules) === null || _a === void 0 ? void 0 : _a[appName])) {
2736
- for (const plugin of microApp.plugins.modules[appName]) {
2737
- if (isPlainObject(plugin)) {
2738
- if (isArray(plugin.scopeProperties)) {
2739
- this.scopeProperties = this.scopeProperties.concat(plugin.scopeProperties);
2829
+ attributeChangedCallback(attr, _oldVal, newVal) {
2830
+ if (this.legalAttribute(attr, newVal) &&
2831
+ this[attr === ObservedAttrName.NAME ? 'appName' : 'appUrl'] !== newVal) {
2832
+ if (attr === ObservedAttrName.URL && !this.appUrl) {
2833
+ newVal = formatAppURL(newVal, this.appName);
2834
+ if (!newVal) {
2835
+ return logError(`Invalid attribute url ${newVal}`, this.appName);
2740
2836
  }
2741
- if (isArray(plugin.escapeProperties)) {
2742
- this.escapeProperties = this.escapeProperties.concat(plugin.escapeProperties);
2837
+ this.appUrl = newVal;
2838
+ this.handleInitialNameAndUrl();
2839
+ }
2840
+ else if (attr === ObservedAttrName.NAME && !this.appName) {
2841
+ const formatNewName = formatAppName(newVal);
2842
+ if (!formatNewName) {
2843
+ return logError(`Invalid attribute name ${newVal}`, this.appName);
2844
+ }
2845
+ if (this.cacheData) {
2846
+ microApp.setData(formatNewName, this.cacheData);
2847
+ this.cacheData = null;
2848
+ }
2849
+ this.appName = formatNewName;
2850
+ if (formatNewName !== newVal) {
2851
+ this.setAttribute('name', this.appName);
2743
2852
  }
2853
+ this.handleInitialNameAndUrl();
2854
+ }
2855
+ else if (!this.isWating) {
2856
+ this.isWating = true;
2857
+ defer(this.handleAttributeUpdate);
2744
2858
  }
2745
2859
  }
2746
2860
  }
2747
- }
2748
- /**
2749
- * inject global properties to microWindow
2750
- * @param microWindow micro window
2751
- * @param appName app name
2752
- * @param url app url
2753
- */
2754
- inject(microWindow, appName, url) {
2755
- microWindow.__MICRO_APP_ENVIRONMENT__ = true;
2756
- microWindow.__MICRO_APP_NAME__ = appName;
2757
- microWindow.__MICRO_APP_PUBLIC_PATH__ = getEffectivePath(url);
2758
- microWindow.microApp = new EventCenterForMicroApp(appName);
2759
- microWindow.rawWindow = globalEnv.rawWindow;
2760
- microWindow.rawDocument = globalEnv.rawDocument;
2761
- microWindow.removeDomScope = removeDomScope;
2762
- }
2763
- }
2764
- SandBox.activeCount = 0; // number of active sandbox
2765
-
2766
- // micro app instances
2767
- const appInstanceMap = new Map();
2768
- class CreateApp {
2769
- constructor({ name, url, container, inline, scopecss, useSandbox, macro, baseroute }) {
2770
- this.status = appStatus.NOT_LOADED;
2771
- this.loadSourceLevel = 0;
2772
- this.umdHookMount = null;
2773
- this.umdHookUnmount = null;
2774
- this.libraryName = null;
2775
- this.umdMode = false;
2776
- this.isPrefetch = false;
2777
- this.container = null;
2778
- this.macro = false;
2779
- this.baseroute = '';
2780
- this.sandBox = null;
2781
- this.container = container !== null && container !== void 0 ? container : null;
2782
- this.inline = inline !== null && inline !== void 0 ? inline : false;
2783
- this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : '';
2784
- // optional during init👆
2785
- this.name = name;
2786
- this.url = url;
2787
- this.useSandbox = useSandbox;
2788
- this.scopecss = this.useSandbox && scopecss;
2789
- this.macro = macro !== null && macro !== void 0 ? macro : false;
2790
- this.source = {
2791
- links: new Map(),
2792
- scripts: new Map(),
2793
- };
2794
- this.loadSourceCode();
2795
- if (this.useSandbox) {
2796
- this.sandBox = new SandBox(name, url, this.macro);
2797
- }
2798
- }
2799
- // Load resources
2800
- loadSourceCode() {
2801
- this.status = appStatus.LOADING_SOURCE_CODE;
2802
- extractHtml(this);
2803
- }
2804
- /**
2805
- * When resource is loaded, mount app if it is not prefetch or unmount
2806
- */
2807
- onLoad(html) {
2808
- if (++this.loadSourceLevel === 2) {
2809
- this.source.html = html;
2810
- if (this.isPrefetch || appStatus.UNMOUNT === this.status)
2811
- return;
2812
- this.status = appStatus.LOAD_SOURCE_FINISHED;
2813
- this.mount();
2814
- }
2815
- }
2816
- /**
2817
- * Error loading HTML
2818
- * @param e Error
2819
- */
2820
- onLoadError(e) {
2821
- this.loadSourceLevel = -1;
2822
- if (appStatus.UNMOUNT !== this.status) {
2823
- this.onerror(e);
2824
- this.status = appStatus.LOAD_SOURCE_ERROR;
2825
- }
2826
- }
2827
- /**
2828
- * mount app
2829
- * @param container app container
2830
- * @param inline js runs in inline mode
2831
- * @param baseroute route prefix, default is ''
2832
- */
2833
- mount(container, inline, baseroute) {
2834
- var _a, _b, _c;
2835
- if (isBoolean(inline) && inline !== this.inline) {
2836
- this.inline = inline;
2861
+ // handle for connectedCallback run before attributeChangedCallback
2862
+ handleInitialNameAndUrl() {
2863
+ this.hasConnected && this.initialMount();
2837
2864
  }
2838
- this.container = (_a = this.container) !== null && _a !== void 0 ? _a : container;
2839
- this.baseroute = baseroute !== null && baseroute !== void 0 ? baseroute : this.baseroute;
2840
- if (this.loadSourceLevel !== 2) {
2841
- this.status = appStatus.LOADING_SOURCE_CODE;
2842
- return;
2865
+ // Perform global initialization when the element count is 1
2866
+ performWhenFirstCreated() {
2867
+ if (elementInstanceMap.set(this, true).size === 1) {
2868
+ patchElementPrototypeMethods();
2869
+ rejectMicroAppStyle();
2870
+ replaseUnmountOfNestedApp();
2871
+ listenUmountOfNestedApp();
2872
+ }
2843
2873
  }
2844
- dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
2845
- this.status = appStatus.MOUNTING;
2846
- cloneNode(this.source.html, this.container, !this.umdMode);
2847
- (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.start(this.baseroute);
2848
- let umdHookMountResult; // result of mount function
2849
- if (!this.umdMode) {
2850
- let hasDispatchMountedEvent = false;
2851
- // if all js are executed, param isFinished will be true
2852
- execScripts(this.source.scripts, this, (isFinished) => {
2853
- var _a;
2854
- if (!this.umdMode) {
2855
- const { mount, unmount } = this.getUmdLibraryHooks();
2856
- // if mount & unmount is function, the sub app is umd mode
2857
- if (isFunction(mount) && isFunction(unmount)) {
2858
- this.umdHookMount = mount;
2859
- this.umdHookUnmount = unmount;
2860
- this.umdMode = true;
2861
- (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.recordUmdSnapshot();
2862
- try {
2863
- umdHookMountResult = this.umdHookMount();
2864
- }
2865
- catch (e) {
2866
- logError('an error occurred in the mount function \n', this.name, e);
2867
- }
2868
- }
2874
+ /**
2875
+ * first mount of this app
2876
+ */
2877
+ initialMount() {
2878
+ if (!this.appName || !this.appUrl)
2879
+ return;
2880
+ if (this.getDisposeResult('shadowDOM') && !this.shadowRoot && isFunction(this.attachShadow)) {
2881
+ this.attachShadow({ mode: 'open' });
2882
+ }
2883
+ if (this.getDisposeResult('ssr')) {
2884
+ this.ssrUrl = CompletionPath(globalEnv.rawWindow.location.pathname, this.appUrl);
2885
+ }
2886
+ else if (this.ssrUrl) {
2887
+ this.ssrUrl = '';
2888
+ }
2889
+ const app = appInstanceMap.get(this.appName);
2890
+ if (app) {
2891
+ const existAppUrl = app.ssrUrl || app.url;
2892
+ const activeAppUrl = this.ssrUrl || this.appUrl;
2893
+ if (existAppUrl === activeAppUrl && (app.isPrefetch ||
2894
+ app.getAppStatus() === appStatus.UNMOUNT)) {
2895
+ this.handleAppMount(app);
2869
2896
  }
2870
- if (!hasDispatchMountedEvent && (isFinished === true || this.umdMode)) {
2871
- hasDispatchMountedEvent = true;
2872
- this.handleMounted(umdHookMountResult);
2897
+ else if (app.isPrefetch || app.getAppStatus() === appStatus.UNMOUNT) {
2898
+ /**
2899
+ * url is different & old app is unmounted or prefetch, create new app to replace old one
2900
+ */
2901
+ logWarn(`the ${app.isPrefetch ? 'prefetch' : 'unmounted'} app with url: ${existAppUrl} is replaced by a new app`, this.appName);
2902
+ this.handleCreateApp();
2903
+ }
2904
+ else {
2905
+ logError(`an app named ${this.appName} already exists`, this.appName);
2873
2906
  }
2874
- });
2875
- }
2876
- else {
2877
- (_c = this.sandBox) === null || _c === void 0 ? void 0 : _c.rebuildUmdSnapshot();
2878
- try {
2879
- umdHookMountResult = this.umdHookMount();
2880
2907
  }
2881
- catch (e) {
2882
- logError('an error occurred in the mount function \n', this.name, e);
2908
+ else {
2909
+ this.handleCreateApp();
2883
2910
  }
2884
- this.handleMounted(umdHookMountResult);
2885
- }
2886
- }
2887
- /**
2888
- * handle for promise umdHookMount
2889
- * @param umdHookMountResult result of umdHookMount
2890
- */
2891
- handleMounted(umdHookMountResult) {
2892
- if (isPromise(umdHookMountResult)) {
2893
- umdHookMountResult
2894
- .then(() => this.dispatchMountedEvent())
2895
- .catch((e) => this.onerror(e));
2896
2911
  }
2897
- else {
2898
- this.dispatchMountedEvent();
2912
+ /**
2913
+ * judge the attribute is legal
2914
+ * @param name attribute name
2915
+ * @param val attribute value
2916
+ */
2917
+ legalAttribute(name, val) {
2918
+ if (!isString(val) || !val) {
2919
+ logError(`unexpected attribute ${name}, please check again`, this.appName);
2920
+ return false;
2921
+ }
2922
+ return true;
2899
2923
  }
2900
- }
2901
- /**
2902
- * dispatch mounted event when app run finished
2903
- */
2904
- dispatchMountedEvent() {
2905
- if (appStatus.UNMOUNT !== this.status) {
2906
- this.status = appStatus.MOUNTED;
2907
- // remove defer in v0.4.2
2908
- // defer(() => {
2909
- // if (appStatus.UNMOUNT !== this.status) {
2910
- dispatchLifecyclesEvent(this.container, this.name, lifeCycles.MOUNTED);
2911
- // }
2912
- // })
2924
+ /**
2925
+ * mount app
2926
+ * some serious note before mount:
2927
+ * 1. is prefetch ?
2928
+ * 2. is remount in another container ?
2929
+ * 3. is remount with change properties of the container ?
2930
+ */
2931
+ handleAppMount(app) {
2932
+ app.isPrefetch = false;
2933
+ defer(() => {
2934
+ var _a;
2935
+ return app.mount((_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this, this.getDisposeResult('inline'), this.getBaseRouteCompatible());
2936
+ });
2913
2937
  }
2914
- }
2915
- /**
2916
- * unmount app
2917
- * @param destroy completely destroy, delete cache resources
2918
- */
2919
- unmount(destroy) {
2920
- if (this.status === appStatus.LOAD_SOURCE_ERROR) {
2921
- destroy = true;
2938
+ // create app instance
2939
+ handleCreateApp() {
2940
+ var _a;
2941
+ /**
2942
+ * actions for destory old app
2943
+ * fix of unmounted umd app with disableSandbox
2944
+ */
2945
+ if (appInstanceMap.has(this.appName)) {
2946
+ appInstanceMap.get(this.appName).actionsForCompletelyDestory();
2947
+ }
2948
+ const instance = new CreateApp({
2949
+ name: this.appName,
2950
+ url: this.appUrl,
2951
+ ssrUrl: this.ssrUrl,
2952
+ container: (_a = this.shadowRoot) !== null && _a !== void 0 ? _a : this,
2953
+ inline: this.getDisposeResult('inline'),
2954
+ scopecss: !(this.getDisposeResult('disableScopecss') || this.getDisposeResult('shadowDOM')),
2955
+ useSandbox: !this.getDisposeResult('disableSandbox'),
2956
+ macro: this.getDisposeResult('macro'),
2957
+ baseroute: this.getBaseRouteCompatible(),
2958
+ });
2959
+ appInstanceMap.set(this.appName, instance);
2922
2960
  }
2923
- this.status = appStatus.UNMOUNT;
2924
- // result of unmount function
2925
- let umdHookUnmountResult;
2926
2961
  /**
2927
- * send an unmount event to the micro app or call umd unmount hook
2928
- * before the sandbox is cleared
2962
+ * unmount app
2963
+ * @param destroy delete cache resources when unmount
2929
2964
  */
2930
- if (this.umdHookUnmount) {
2931
- try {
2932
- umdHookUnmountResult = this.umdHookUnmount();
2933
- }
2934
- catch (e) {
2935
- logError('an error occurred in the unmount function \n', this.name, e);
2936
- }
2965
+ handleUnmount(destroy) {
2966
+ const app = appInstanceMap.get(this.appName);
2967
+ if (app && appStatus.UNMOUNT !== app.getAppStatus())
2968
+ app.unmount(destroy);
2937
2969
  }
2938
- // dispatch unmount event to micro app
2939
- dispatchUnmountToMicroApp(this.name);
2940
- this.handleUnmounted(destroy, umdHookUnmountResult);
2941
- }
2942
- /**
2943
- * handle for promise umdHookUnmount
2944
- * @param umdHookUnmountResult result of umdHookUnmount
2945
- */
2946
- handleUnmounted(destroy, umdHookUnmountResult) {
2947
- if (isPromise(umdHookUnmountResult)) {
2948
- umdHookUnmountResult
2949
- .then(() => this.actionsForUnmount(destroy))
2950
- .catch(() => this.actionsForUnmount(destroy));
2970
+ /**
2971
+ * Get configuration
2972
+ * Global setting is lowest priority
2973
+ * @param name Configuration item name
2974
+ */
2975
+ getDisposeResult(name) {
2976
+ // @ts-ignore
2977
+ return (this.hasAttribute(name) || microApp[name]) && this.getAttribute(name) !== 'false';
2951
2978
  }
2952
- else {
2953
- this.actionsForUnmount(destroy);
2979
+ /**
2980
+ * 2021-09-08
2981
+ * get baseRoute
2982
+ * getAttribute('baseurl') is compatible writing of versions below 0.3.1
2983
+ */
2984
+ getBaseRouteCompatible() {
2985
+ var _a, _b;
2986
+ return (_b = (_a = this.getAttribute('baseroute')) !== null && _a !== void 0 ? _a : this.getAttribute('baseurl')) !== null && _b !== void 0 ? _b : '';
2954
2987
  }
2955
- }
2956
- /**
2957
- * actions for unmount app
2958
- * @param destroy completely destroy, delete cache resources
2959
- */
2960
- actionsForUnmount(destroy) {
2961
- var _a;
2962
- // dispatch unmount event to base app
2963
- dispatchLifecyclesEvent(this.container, this.name, lifeCycles.UNMOUNT);
2964
- (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.stop();
2965
- // actions for completely destroy
2966
- if (destroy) {
2967
- if (!this.useSandbox && this.umdMode) {
2968
- delete window[this.libraryName];
2988
+ /**
2989
+ * Data from the base application
2990
+ */
2991
+ set data(value) {
2992
+ if (this.appName) {
2993
+ microApp.setData(this.appName, value);
2994
+ }
2995
+ else {
2996
+ this.cacheData = value;
2969
2997
  }
2970
- appInstanceMap.delete(this.name);
2971
- }
2972
- else if (this.umdMode && this.container.childElementCount) {
2973
- /**
2974
- * In umd mode, ui frameworks will no longer create style elements to head in lazy load page when render again, so we should save container to keep these elements
2975
- */
2976
- cloneNode(this.container, this.source.html, false);
2977
2998
  }
2978
- this.container = null;
2979
- }
2980
- /**
2981
- * app rendering error
2982
- * @param e Error
2983
- */
2984
- onerror(e) {
2985
- dispatchLifecyclesEvent(this.container, this.name, lifeCycles.ERROR, e);
2986
- }
2987
- // get app status
2988
- getAppStatus() {
2989
- return this.status;
2990
- }
2991
- // get umd library, if it not exist, return empty object
2992
- getUmdLibraryHooks() {
2993
- var _a, _b;
2994
- // after execScripts, the app maybe unmounted
2995
- if (appStatus.UNMOUNT !== this.status) {
2996
- const global = ((_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow) !== null && _b !== void 0 ? _b : globalEnv.rawWindow);
2997
- this.libraryName = (this.container instanceof ShadowRoot ? this.container.host : this.container).getAttribute('library') || `micro-app-${this.name}`;
2998
- // do not use isObject
2999
- return typeof global[this.libraryName] === 'object' ? global[this.libraryName] : {};
2999
+ /**
3000
+ * get data only used in jsx-custom-event once
3001
+ */
3002
+ get data() {
3003
+ if (this.appName) {
3004
+ return microApp.getData(this.appName, true);
3005
+ }
3006
+ else if (this.cacheData) {
3007
+ return this.cacheData;
3008
+ }
3009
+ return null;
3000
3010
  }
3001
- return {};
3002
3011
  }
3012
+ window.customElements.define(tagName, MicroAppElement);
3003
3013
  }
3004
3014
 
3005
3015
  function filterPreFetchTarget(apps) {
3006
3016
  const validApps = [];
3007
3017
  if (isArray(apps)) {
3008
3018
  apps.forEach((item) => {
3009
- item.url = formatURL(item.url, item.name);
3010
- if (isPlainObject(item) &&
3011
- isString(item.name) &&
3012
- item.url &&
3013
- !appInstanceMap.has(item.name)) {
3014
- validApps.push(item);
3019
+ if (isPlainObject(item)) {
3020
+ item.name = formatAppName(item.name);
3021
+ item.url = formatAppURL(item.url, item.name);
3022
+ if (item.name && item.url && !appInstanceMap.has(item.name)) {
3023
+ validApps.push(item);
3024
+ }
3015
3025
  }
3016
3026
  });
3017
3027
  }
@@ -3038,8 +3048,7 @@ function preFetch(apps) {
3038
3048
  return logError('preFetch is only supported in browser environment');
3039
3049
  }
3040
3050
  requestIdleCallback(() => {
3041
- if (isFunction(apps))
3042
- apps = apps();
3051
+ isFunction(apps) && (apps = apps());
3043
3052
  filterPreFetchTarget(apps).forEach((item) => {
3044
3053
  var _a, _b, _c;
3045
3054
  const app = new CreateApp({
@@ -3097,6 +3106,67 @@ function getGlobalAssets(assets) {
3097
3106
  }
3098
3107
  }
3099
3108
 
3109
+ class MicroApp extends EventCenterForBaseApp {
3110
+ constructor() {
3111
+ super(...arguments);
3112
+ this.tagName = 'micro-app';
3113
+ this.preFetch = preFetch;
3114
+ }
3115
+ start(options) {
3116
+ if (!isBrowser || !window.customElements) {
3117
+ return logError('micro-app is not supported in this environment');
3118
+ }
3119
+ if (options === null || options === void 0 ? void 0 : options.tagName) {
3120
+ if (/^micro-app(-\S+)?/.test(options.tagName)) {
3121
+ this.tagName = options.tagName;
3122
+ }
3123
+ else {
3124
+ return logError(`${options.tagName} is invalid tagName`);
3125
+ }
3126
+ }
3127
+ if (window.customElements.get(this.tagName)) {
3128
+ return logWarn(`element ${this.tagName} is already defined`);
3129
+ }
3130
+ initGlobalEnv();
3131
+ if (options && isPlainObject(options)) {
3132
+ this.shadowDOM = options.shadowDOM;
3133
+ this.destroy = options.destroy;
3134
+ /**
3135
+ * compatible with versions below 0.4.2 of destroy
3136
+ * do not merge with the previous line
3137
+ */
3138
+ // @ts-ignore
3139
+ this.destory = options.destory;
3140
+ this.inline = options.inline;
3141
+ this.disableScopecss = options.disableScopecss;
3142
+ this.disableSandbox = options.disableSandbox;
3143
+ this.macro = options.macro;
3144
+ isFunction(options.fetch) && (this.fetch = options.fetch);
3145
+ isPlainObject(options.lifeCycles) && (this.lifeCycles = options.lifeCycles);
3146
+ // load app assets when browser is idle
3147
+ options.preFetchApps && preFetch(options.preFetchApps);
3148
+ // load global assets when browser is idle
3149
+ options.globalAssets && getGlobalAssets(options.globalAssets);
3150
+ if (isPlainObject(options.plugins)) {
3151
+ const modules = options.plugins.modules;
3152
+ if (isPlainObject(modules)) {
3153
+ for (const appName in modules) {
3154
+ const formattedAppName = formatAppName(appName);
3155
+ if (formattedAppName && appName !== formattedAppName) {
3156
+ modules[formattedAppName] = modules[appName];
3157
+ delete modules[appName];
3158
+ }
3159
+ }
3160
+ }
3161
+ this.plugins = options.plugins;
3162
+ }
3163
+ }
3164
+ // define customElement after init
3165
+ defineElement(this.tagName);
3166
+ }
3167
+ }
3168
+ var microApp = new MicroApp();
3169
+
3100
3170
  export default microApp;
3101
- export { preFetch, pureCreateElement, removeDomScope, setCurrentAppName, version };
3171
+ export { EventCenterForMicroApp, getActiveApps, getAllApps, preFetch, pureCreateElement, removeDomScope, version };
3102
3172
  //# sourceMappingURL=index.esm.js.map