@micro-zoe/micro-app 0.4.1 → 0.5.1

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