@micro-zoe/micro-app 1.0.0-rc.0 → 1.0.0-rc.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- const version = '1.0.0-rc.0';
1
+ const version = '1.0.0-rc.10';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -15,7 +15,9 @@ const assign = Object.assign;
15
15
  // Object prototype methods
16
16
  const rawDefineProperty = Object.defineProperty;
17
17
  const rawDefineProperties = Object.defineProperties;
18
+ const rawToString = Object.prototype.toString;
18
19
  const rawHasOwnProperty = Object.prototype.hasOwnProperty;
20
+ const toTypeString = (value) => rawToString.call(value);
19
21
  // is Undefined
20
22
  function isUndefined(target) {
21
23
  return target === undefined;
@@ -42,19 +44,20 @@ function isFunction(target) {
42
44
  }
43
45
  // is PlainObject
44
46
  function isPlainObject(target) {
45
- return toString.call(target) === '[object Object]';
47
+ return toTypeString(target) === '[object Object]';
46
48
  }
47
49
  // is Object
48
50
  function isObject(target) {
49
- return typeof target === 'object';
51
+ return !isNull(target) && typeof target === 'object';
50
52
  }
51
53
  // is Promise
52
54
  function isPromise(target) {
53
- return toString.call(target) === '[object Promise]';
55
+ return toTypeString(target) === '[object Promise]';
54
56
  }
55
57
  // is bind function
56
58
  function isBoundFunction(target) {
57
- return isFunction(target) && target.name.indexOf('bound ') === 0 && !target.hasOwnProperty('prototype');
59
+ var _a;
60
+ return isFunction(target) && ((_a = target.name) === null || _a === void 0 ? void 0 : _a.indexOf('bound ')) === 0 && !target.hasOwnProperty('prototype');
58
61
  }
59
62
  // is constructor function
60
63
  function isConstructor(target) {
@@ -86,41 +89,76 @@ function isNode(target) {
86
89
  var _a;
87
90
  return target instanceof Node || isNumber((_a = target) === null || _a === void 0 ? void 0 : _a.nodeType);
88
91
  }
92
+ function isAnchorElement(target) {
93
+ return toTypeString(target) === '[object HTMLAnchorElement]';
94
+ }
95
+ function isAudioElement(target) {
96
+ return toTypeString(target) === '[object HTMLAudioElement]';
97
+ }
98
+ function isVideoElement(target) {
99
+ return toTypeString(target) === '[object HTMLVideoElement]';
100
+ }
89
101
  function isLinkElement(target) {
90
- var _a, _b;
91
- return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'LINK';
102
+ return toTypeString(target) === '[object HTMLLinkElement]';
92
103
  }
93
104
  function isStyleElement(target) {
94
- var _a, _b;
95
- return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'STYLE';
105
+ return toTypeString(target) === '[object HTMLStyleElement]';
96
106
  }
97
107
  function isScriptElement(target) {
98
- var _a, _b;
99
- return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'SCRIPT';
108
+ return toTypeString(target) === '[object HTMLScriptElement]';
100
109
  }
101
110
  function isIFrameElement(target) {
102
- var _a, _b;
103
- return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'IFRAME';
111
+ return toTypeString(target) === '[object HTMLIFrameElement]';
104
112
  }
105
113
  function isDivElement(target) {
106
- var _a, _b;
107
- return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'DIV';
114
+ return toTypeString(target) === '[object HTMLDivElement]';
108
115
  }
109
116
  function isImageElement(target) {
110
- var _a, _b;
111
- return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'IMG';
117
+ return toTypeString(target) === '[object HTMLImageElement]';
112
118
  }
113
119
  function isBaseElement(target) {
114
- var _a, _b;
115
- return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'BASE';
120
+ return toTypeString(target) === '[object HTMLBaseElement]';
121
+ }
122
+ function isDocumentFragment(target) {
123
+ return toTypeString(target) === '[object DocumentFragment]';
116
124
  }
117
125
  function isMicroAppBody(target) {
118
- var _a, _b;
119
- return ((_b = (_a = target) === null || _a === void 0 ? void 0 : _a.tagName) === null || _b === void 0 ? void 0 : _b.toUpperCase()) === 'MICRO-APP-BODY';
126
+ return isElement(target) && target.tagName.toUpperCase() === 'MICRO-APP-BODY';
127
+ }
128
+ function isMicroAppHead(target) {
129
+ return isElement(target) && target.tagName.toUpperCase() === 'MICRO-APP-HEAD';
120
130
  }
121
131
  // is ProxyDocument
122
132
  function isProxyDocument(target) {
123
- return toString.call(target) === '[object ProxyDocument]';
133
+ return toTypeString(target) === '[object ProxyDocument]';
134
+ }
135
+ function isTargetExtension(path, suffix) {
136
+ try {
137
+ return createURL(path).pathname.split('.').pop() === suffix;
138
+ }
139
+ catch (_a) {
140
+ return false;
141
+ }
142
+ }
143
+ function includes(target, searchElement, fromIndex) {
144
+ if (target == null) {
145
+ throw new TypeError('includes target is null or undefined');
146
+ }
147
+ const O = Object(target);
148
+ const len = parseInt(O.length, 10) || 0;
149
+ if (len === 0)
150
+ return false;
151
+ // @ts-ignore
152
+ fromIndex = parseInt(fromIndex, 10) || 0;
153
+ let i = Math.max(fromIndex >= 0 ? fromIndex : len + fromIndex, 0);
154
+ while (i < len) {
155
+ // NaN !== NaN
156
+ if (searchElement === O[i] || (searchElement !== searchElement && O[i] !== O[i])) {
157
+ return true;
158
+ }
159
+ i++;
160
+ }
161
+ return false;
124
162
  }
125
163
  /**
126
164
  * format error log
@@ -158,6 +196,14 @@ function logWarn(msg, appName = null, ...rest) {
158
196
  function defer(fn, ...args) {
159
197
  Promise.resolve().then(fn.bind(null, ...args));
160
198
  }
199
+ /**
200
+ * async execution with macro task
201
+ * @param fn callback
202
+ * @param args params
203
+ */
204
+ function macro(fn, delay = 0, ...args) {
205
+ setTimeout(fn.bind(null, ...args), delay);
206
+ }
161
207
  /**
162
208
  * create URL as MicroLocation
163
209
  */
@@ -185,13 +231,13 @@ function formatAppURL(url, appName = null) {
185
231
  if (!isString(url) || !url)
186
232
  return '';
187
233
  try {
188
- const { origin, pathname, search } = createURL(addProtocol(url));
189
- // If it ends with .html/.node/.php/.net/.etc, don’t need to add /
190
- if (/\.(\w+)$/.test(pathname)) {
191
- return `${origin}${pathname}${search}`;
192
- }
193
- const fullPath = `${origin}${pathname}/`.replace(/\/\/$/, '/');
194
- return /^https?:\/\//.test(fullPath) ? `${fullPath}${search}` : '';
234
+ const { origin, pathname, search } = createURL(addProtocol(url), (window.rawWindow || window).location.href);
235
+ /**
236
+ * keep the original url unchanged, such as .html .node .php .net .etc, search, except hash
237
+ * BUG FIX: Never using '/' to complete url, refer to https://github.com/micro-zoe/micro-app/issues/1147
238
+ */
239
+ const fullPath = `${origin}${pathname}${search}`;
240
+ return /^https?:\/\//.test(fullPath) ? fullPath : '';
195
241
  }
196
242
  catch (e) {
197
243
  logError(e, appName);
@@ -214,14 +260,15 @@ function formatAppName(name) {
214
260
  return name.replace(/(^\d+)|([^\w\d-_])/gi, '');
215
261
  }
216
262
  /**
217
- * Get valid address, such as https://xxx/xx/xx.html to https://xxx/xx/
263
+ * Get valid address, such as
264
+ * 1. https://domain/xx/xx.html to https://domain/xx/
265
+ * 2. https://domain/xx to https://domain/xx/
218
266
  * @param url app.url
219
267
  */
220
268
  function getEffectivePath(url) {
221
269
  const { origin, pathname } = createURL(url);
222
270
  if (/\.(\w+)$/.test(pathname)) {
223
- const fullPath = `${origin}${pathname}`;
224
- const pathArr = fullPath.split('/');
271
+ const pathArr = `${origin}${pathname}`.split('/');
225
272
  pathArr.pop();
226
273
  return pathArr.join('/') + '/';
227
274
  }
@@ -320,63 +367,75 @@ function promiseRequestIdle(callback) {
320
367
  /**
321
368
  * Record the currently running app.name
322
369
  */
323
- let currentMicroAppName = null;
370
+ let currentAppName = null;
324
371
  function setCurrentAppName(appName) {
325
- currentMicroAppName = appName;
372
+ currentAppName = appName;
373
+ }
374
+ // get the currently running app.name
375
+ function getCurrentAppName() {
376
+ return currentAppName;
326
377
  }
327
378
  function throttleDeferForSetAppName(appName) {
328
- if (currentMicroAppName !== appName) {
379
+ if (currentAppName !== appName && !getPreventSetState()) {
329
380
  setCurrentAppName(appName);
330
381
  defer(() => {
331
382
  setCurrentAppName(null);
332
383
  });
333
384
  }
334
385
  }
335
- // get the currently running app.name
336
- function getCurrentAppName() {
337
- return currentMicroAppName;
386
+ // only for iframe document.body(head).querySelector(querySelectorAll)
387
+ let iframeCurrentAppName = null;
388
+ function setIframeCurrentAppName(appName) {
389
+ iframeCurrentAppName = appName;
338
390
  }
339
- // Clear appName
340
- function removeDomScope() {
341
- setCurrentAppName(null);
391
+ function getIframeCurrentAppName() {
392
+ return iframeCurrentAppName;
393
+ }
394
+ function throttleDeferForIframeAppName(appName) {
395
+ if (iframeCurrentAppName !== appName && !getPreventSetState()) {
396
+ setIframeCurrentAppName(appName);
397
+ defer(() => {
398
+ setIframeCurrentAppName(null);
399
+ });
400
+ }
401
+ }
402
+ // prevent set app name
403
+ let preventSetState = false;
404
+ function getPreventSetState() {
405
+ return preventSetState;
406
+ }
407
+ /**
408
+ * prevent set appName
409
+ * usage:
410
+ * removeDomScope(true)
411
+ * -----> element scope point to base app <-----
412
+ * removeDomScope(false)
413
+ */
414
+ function removeDomScope(force) {
415
+ if (force !== false) {
416
+ setCurrentAppName(null);
417
+ setIframeCurrentAppName(null);
418
+ if (force && !preventSetState) {
419
+ preventSetState = true;
420
+ defer(() => {
421
+ preventSetState = false;
422
+ });
423
+ }
424
+ }
425
+ else {
426
+ preventSetState = false;
427
+ }
342
428
  }
343
429
  /**
344
430
  * Create pure elements
345
431
  */
346
432
  function pureCreateElement(tagName, options) {
347
- const element = document.createElement(tagName, options);
433
+ const element = (window.rawDocument || document).createElement(tagName, options);
348
434
  if (element.__MICRO_APP_NAME__)
349
435
  delete element.__MICRO_APP_NAME__;
350
436
  element.__PURE_ELEMENT__ = true;
351
437
  return element;
352
438
  }
353
- /**
354
- * clone origin elements to target
355
- * @param origin Cloned element
356
- * @param target Accept cloned elements
357
- * @param deep deep clone or transfer dom
358
- */
359
- function cloneContainer(target, origin, deep) {
360
- // 在基座接受到afterhidden方法后立即执行unmount,彻底destroy应用时,因为unmount时同步执行,所以this.container为null后才执行cloneContainer
361
- if (origin) {
362
- target.innerHTML = '';
363
- if (deep) {
364
- // TODO: ShadowRoot兼容,ShadowRoot不能直接使用cloneNode
365
- const clonedNode = origin.cloneNode(true);
366
- const fragment = document.createDocumentFragment();
367
- Array.from(clonedNode.childNodes).forEach((node) => {
368
- fragment.appendChild(node);
369
- });
370
- target.appendChild(fragment);
371
- }
372
- else {
373
- Array.from(origin.childNodes).forEach((node) => {
374
- target.appendChild(node);
375
- });
376
- }
377
- }
378
- return target;
379
- }
380
439
  // is invalid key of querySelector
381
440
  function isInvalidQuerySelectorKey(key) {
382
441
  return !key || /(^\d)|([^\w\d-_\u4e00-\u9fa5])/gi.test(key);
@@ -550,11 +609,49 @@ function execMicroAppGlobalHook(fn, appName, hookName, ...args) {
550
609
  logError(`An error occurred in app ${appName} window.${hookName} \n`, null, e);
551
610
  }
552
611
  }
612
+ /**
613
+ * remove all childNode from target node
614
+ * @param $dom target node
615
+ */
553
616
  function clearDOM($dom) {
554
617
  while ($dom === null || $dom === void 0 ? void 0 : $dom.firstChild) {
555
618
  $dom.removeChild($dom.firstChild);
556
619
  }
557
620
  }
621
+ function instanceOf(instance, constructor) {
622
+ if (instance === null || instance === undefined) {
623
+ return false;
624
+ }
625
+ else if (!isFunction(constructor)) {
626
+ throw new TypeError("Right-hand side of 'instanceof' is not callable");
627
+ }
628
+ let proto = Object.getPrototypeOf(instance);
629
+ while (proto) {
630
+ if (proto === constructor.prototype) {
631
+ return true;
632
+ }
633
+ proto = Object.getPrototypeOf(proto);
634
+ }
635
+ return false;
636
+ }
637
+ /**
638
+ * Format event name
639
+ * In with sandbox, child event and lifeCycles bind to microAppElement, there are two events with same name - mounted unmount, it should be handled specifically to prevent conflicts
640
+ * Issue: https://github.com/micro-zoe/micro-app/issues/1161
641
+ * @param type event name
642
+ * @param appName app name
643
+ */
644
+ const formatEventList = ['mounted', 'unmount'];
645
+ function formatEventType(type, appName) {
646
+ return formatEventList.includes(type) ? `${type}-${appName}` : type;
647
+ }
648
+ /**
649
+ * Is the object empty
650
+ * target maybe number, string, array ...
651
+ */
652
+ function isEmptyObject(target) {
653
+ return isPlainObject(target) ? !Object.keys(target).length : true;
654
+ }
558
655
 
559
656
  function formatEventInfo(event, element) {
560
657
  Object.defineProperties(event, {
@@ -581,7 +678,7 @@ function formatEventInfo(event, element) {
581
678
  function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
582
679
  var _a;
583
680
  if (!element) {
584
- return logError(`element does not exist in lifecycle ${lifecycleName}`, appName);
681
+ return logWarn(`element does not exist in lifecycle ${lifecycleName}`, appName);
585
682
  }
586
683
  element = getRootContainer(element);
587
684
  // clear dom scope before dispatch lifeCycles event to base app, especially mounted & unmount
@@ -598,19 +695,19 @@ function dispatchLifecyclesEvent(element, appName, lifecycleName, error) {
598
695
  formatEventInfo(event, element);
599
696
  // global hooks
600
697
  if (isFunction((_a = microApp.options.lifeCycles) === null || _a === void 0 ? void 0 : _a[lifecycleName])) {
601
- microApp.options.lifeCycles[lifecycleName](event);
698
+ microApp.options.lifeCycles[lifecycleName](event, appName);
602
699
  }
603
700
  element.dispatchEvent(event);
604
701
  }
605
702
  /**
606
703
  * Dispatch custom event to micro app
607
704
  * @param app app
608
- * @param eventName event name ['unmount', 'appstate-change']
705
+ * @param eventName event name ['mounted', 'unmount', 'appstate-change', 'statechange']
609
706
  * @param detail event detail
610
707
  */
611
708
  function dispatchCustomEventToMicroApp(app, eventName, detail = {}) {
612
709
  var _a;
613
- const event = new CustomEvent(eventName, {
710
+ const event = new CustomEvent(formatEventType(eventName, app.name), {
614
711
  detail,
615
712
  });
616
713
  (_a = app.sandBox) === null || _a === void 0 ? void 0 : _a.microAppWindow.dispatchEvent(event);
@@ -654,7 +751,11 @@ class HTMLLoader {
654
751
  run(app, successCb) {
655
752
  const appName = app.name;
656
753
  const htmlUrl = app.ssrUrl || app.url;
657
- fetchSource(htmlUrl, appName, { cache: 'no-cache' }).then((htmlStr) => {
754
+ const isJsResource = isTargetExtension(htmlUrl, 'js');
755
+ const htmlPromise = isJsResource
756
+ ? Promise.resolve(`<micro-app-head><script src='${htmlUrl}'></script></micro-app-head><micro-app-body></micro-app-body>`)
757
+ : fetchSource(htmlUrl, appName, { cache: 'no-cache' });
758
+ htmlPromise.then((htmlStr) => {
658
759
  if (!htmlStr) {
659
760
  const msg = 'html is empty, please check in detail';
660
761
  app.onerror(new Error(msg));
@@ -773,7 +874,7 @@ class CSSParser {
773
874
  // reset scopecssDisableNextLine
774
875
  this.scopecssDisableNextLine = false;
775
876
  if (!selectors)
776
- return parseError('selector missing', this.linkPath);
877
+ return this.printError('selector missing', this.linkPath);
777
878
  this.recordResult(selectors);
778
879
  this.matchComments();
779
880
  this.styleDeclarations();
@@ -784,9 +885,24 @@ class CSSParser {
784
885
  const m = this.commonMatch(/^[^{]+/, skip);
785
886
  if (!m)
786
887
  return false;
888
+ /**
889
+ * NOTE:
890
+ * 1. :is(h1, h2, h3):has(+ h2, + h3, + h4) {}
891
+ * should be ==> micro-app[name=xxx] :is(h1, h2, h3):has(+ h2, + h3, + h4) {}
892
+ * 2. :dir(ltr) {}
893
+ * should be ==> micro-app[name=xxx] :dir(ltr) {}
894
+ * 3. body :not(div, .fancy) {}
895
+ * should be ==> micro-app[name=xxx] micro-app-body :not(div, .fancy) {}
896
+ * 4. .a, .b, li:nth-child(3)
897
+ * should be ==> micro-app[name=xxx] .a, micro-app[name=xxx] .b, micro-app[name=xxx] li:nth-child(3)
898
+ * 5. :is(.a, .b, .c) a {}
899
+ * should be ==> micro-app[name=xxx] :is(.a, .b, .c) a {}
900
+ * 6. :where(.a, .b, .c) a {}
901
+ * should be ==> micro-app[name=xxx] :where(.a, .b, .c) a {}
902
+ */
787
903
  return m[0].replace(/(^|,[\n\s]*)([^,]+)/g, (_, separator, selector) => {
788
904
  selector = trim(selector);
789
- if (!(this.scopecssDisableNextLine ||
905
+ if (selector && !(this.scopecssDisableNextLine ||
790
906
  (this.scopecssDisable && (!this.scopecssDisableSelectors.length ||
791
907
  this.scopecssDisableSelectors.includes(selector))) ||
792
908
  rootSelectorREG.test(selector))) {
@@ -803,19 +919,19 @@ class CSSParser {
803
919
  // https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration
804
920
  styleDeclarations() {
805
921
  if (!this.matchOpenBrace())
806
- return parseError("Declaration missing '{'", this.linkPath);
922
+ return this.printError("Declaration missing '{'", this.linkPath);
807
923
  this.matchAllDeclarations();
808
924
  if (!this.matchCloseBrace())
809
- return parseError("Declaration missing '}'", this.linkPath);
925
+ return this.printError("Declaration missing '}'", this.linkPath);
810
926
  return true;
811
927
  }
812
- matchAllDeclarations() {
813
- let cssValue = this.commonMatch(/^(?:url\(["']?(?:[^)"'}]+)["']?\)|[^}/])*/, true)[0];
928
+ matchAllDeclarations(nesting = 0) {
929
+ let cssValue = this.commonMatch(/^(?:url\(["']?(?:[^)"'}]+)["']?\)|[^{}/])*/, true)[0];
814
930
  if (cssValue) {
815
931
  if (!this.scopecssDisableNextLine &&
816
932
  (!this.scopecssDisable || this.scopecssDisableSelectors.length)) {
817
933
  cssValue = cssValue.replace(/url\(["']?([^)"']+)["']?\)/gm, (all, $1) => {
818
- if (/^((data|blob):|#)/.test($1) || /^(https?:)?\/\//.test($1)) {
934
+ if (/^((data|blob):|#|%23)/.test($1) || /^(https?:)?\/\//.test($1)) {
819
935
  return all;
820
936
  }
821
937
  // ./a/b.png ../a/b.png a/b.png
@@ -829,16 +945,28 @@ class CSSParser {
829
945
  }
830
946
  // reset scopecssDisableNextLine
831
947
  this.scopecssDisableNextLine = false;
832
- if (!this.cssText || this.cssText.charAt(0) === '}')
948
+ if (!this.cssText.length)
833
949
  return;
834
950
  // extract comments in declarations
835
- if (this.cssText.charAt(0) === '/' && this.cssText.charAt(1) === '*') {
836
- this.matchComments();
951
+ if (this.cssText.charAt(0) === '/') {
952
+ if (this.cssText.charAt(1) === '*') {
953
+ this.matchComments();
954
+ }
955
+ else {
956
+ this.commonMatch(/\/+/);
957
+ }
837
958
  }
838
- else {
839
- this.commonMatch(/\/+/);
959
+ else if (this.cssText.charAt(0) === '{') {
960
+ this.matchOpenBrace();
961
+ nesting++;
962
+ }
963
+ else if (this.cssText.charAt(0) === '}') {
964
+ if (nesting < 1)
965
+ return;
966
+ this.matchCloseBrace();
967
+ nesting--;
840
968
  }
841
- return this.matchAllDeclarations();
969
+ return this.matchAllDeclarations(nesting);
842
970
  }
843
971
  matchAtRule() {
844
972
  if (this.cssText[0] !== '@')
@@ -856,7 +984,8 @@ class CSSParser {
856
984
  this.documentRule() ||
857
985
  this.pageRule() ||
858
986
  this.hostRule() ||
859
- this.fontFaceRule();
987
+ this.fontFaceRule() ||
988
+ this.layerRule();
860
989
  }
861
990
  // :global is CSS Modules rule, it will be converted to normal syntax
862
991
  // private matchGlobalRule (): boolean | void {
@@ -870,16 +999,16 @@ class CSSParser {
870
999
  if (!this.commonMatch(/^@([-\w]+)?keyframes\s*/))
871
1000
  return false;
872
1001
  if (!this.commonMatch(/^[^{]+/))
873
- return parseError('@keyframes missing name', this.linkPath);
1002
+ return this.printError('@keyframes missing name', this.linkPath);
874
1003
  this.matchComments();
875
1004
  if (!this.matchOpenBrace())
876
- return parseError("@keyframes missing '{'", this.linkPath);
1005
+ return this.printError("@keyframes missing '{'", this.linkPath);
877
1006
  this.matchComments();
878
1007
  while (this.keyframeRule()) {
879
1008
  this.matchComments();
880
1009
  }
881
1010
  if (!this.matchCloseBrace())
882
- return parseError("@keyframes missing '}'", this.linkPath);
1011
+ return this.printError("@keyframes missing '}'", this.linkPath);
883
1012
  this.matchLeadingSpaces();
884
1013
  return true;
885
1014
  }
@@ -918,17 +1047,30 @@ class CSSParser {
918
1047
  return false;
919
1048
  return this.commonHandlerForAtRuleWithSelfRule('font-face');
920
1049
  }
1050
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/@layer
1051
+ layerRule() {
1052
+ if (!this.commonMatch(/^@layer\s*([^{;]+)/))
1053
+ return false;
1054
+ if (!this.matchOpenBrace())
1055
+ return !!this.commonMatch(/^[;]+/);
1056
+ this.matchComments();
1057
+ this.matchRules();
1058
+ if (!this.matchCloseBrace())
1059
+ return this.printError('@layer missing \'}\'', this.linkPath);
1060
+ this.matchLeadingSpaces();
1061
+ return true;
1062
+ }
921
1063
  // common matcher for @media, @supports, @document, @host, :global, @container
922
1064
  createMatcherForRuleWithChildRule(reg, name) {
923
1065
  return () => {
924
1066
  if (!this.commonMatch(reg))
925
1067
  return false;
926
1068
  if (!this.matchOpenBrace())
927
- return parseError(`${name} missing '{'`, this.linkPath);
1069
+ return this.printError(`${name} missing '{'`, this.linkPath);
928
1070
  this.matchComments();
929
1071
  this.matchRules();
930
1072
  if (!this.matchCloseBrace())
931
- return parseError(`${name} missing '}'`, this.linkPath);
1073
+ return this.printError(`${name} missing '}'`, this.linkPath);
932
1074
  this.matchLeadingSpaces();
933
1075
  return true;
934
1076
  };
@@ -946,10 +1088,10 @@ class CSSParser {
946
1088
  // common handler for @font-face, @page
947
1089
  commonHandlerForAtRuleWithSelfRule(name) {
948
1090
  if (!this.matchOpenBrace())
949
- return parseError(`@${name} missing '{'`, this.linkPath);
1091
+ return this.printError(`@${name} missing '{'`, this.linkPath);
950
1092
  this.matchAllDeclarations();
951
1093
  if (!this.matchCloseBrace())
952
- return parseError(`@${name} missing '}'`, this.linkPath);
1094
+ return this.printError(`@${name} missing '}'`, this.linkPath);
953
1095
  this.matchLeadingSpaces();
954
1096
  return true;
955
1097
  }
@@ -969,7 +1111,7 @@ class CSSParser {
969
1111
  ++i;
970
1112
  i += 2;
971
1113
  if (this.cssText.charAt(i - 1) === '') {
972
- return parseError('End of comment missing', this.linkPath);
1114
+ return this.printError('End of comment missing', this.linkPath);
973
1115
  }
974
1116
  // get comment content
975
1117
  let commentText = this.cssText.slice(2, i - 2);
@@ -1013,7 +1155,7 @@ class CSSParser {
1013
1155
  return this.commonMatch(/^{\s*/);
1014
1156
  }
1015
1157
  matchCloseBrace() {
1016
- return this.commonMatch(/^}/);
1158
+ return this.commonMatch(/^}\s*/);
1017
1159
  }
1018
1160
  // match and slice the leading spaces
1019
1161
  matchLeadingSpaces() {
@@ -1029,6 +1171,11 @@ class CSSParser {
1029
1171
  this.result += strFragment;
1030
1172
  }
1031
1173
  }
1174
+ printError(msg, linkPath) {
1175
+ if (this.cssText.length) {
1176
+ parseError(msg, linkPath);
1177
+ }
1178
+ }
1032
1179
  }
1033
1180
  /**
1034
1181
  * common method of bind CSS
@@ -1227,7 +1374,7 @@ function extractLinkFromHtml(link, parent, app, isDynamic = false) {
1227
1374
  return { address: href, linkInfo };
1228
1375
  }
1229
1376
  }
1230
- else if (rel && ['prefetch', 'preload', 'prerender'].includes(rel)) {
1377
+ else if (rel && ['prefetch', 'preload', 'prerender', 'modulepreload', 'icon'].includes(rel)) {
1231
1378
  // preload prefetch prerender ....
1232
1379
  if (isDynamic) {
1233
1380
  replaceComment = document.createComment(`link element with rel=${rel}${href ? ' & href=' + href : ''} removed by micro-app`);
@@ -1272,12 +1419,12 @@ function fetchLinksFromHtml(wrapElement, app, microAppHead, fiberStyleResult) {
1272
1419
  */
1273
1420
  if (fiberStyleResult) {
1274
1421
  fiberStyleResult.then(() => {
1275
- fiberLinkTasks.push(() => Promise.resolve(app.onLoad(wrapElement)));
1422
+ fiberLinkTasks.push(() => Promise.resolve(app.onLoad({ html: wrapElement })));
1276
1423
  serialExecFiberTasks(fiberLinkTasks);
1277
1424
  });
1278
1425
  }
1279
1426
  else {
1280
- app.onLoad(wrapElement);
1427
+ app.onLoad({ html: wrapElement });
1281
1428
  }
1282
1429
  });
1283
1430
  }
@@ -1441,41 +1588,83 @@ var MicroAppConfig;
1441
1588
  MicroAppConfig["DISABLE_MEMORY_ROUTER"] = "disable-memory-router";
1442
1589
  MicroAppConfig["DISABLE_PATCH_REQUEST"] = "disable-patch-request";
1443
1590
  MicroAppConfig["KEEP_ROUTER_STATE"] = "keep-router-state";
1444
- MicroAppConfig["HIDDEN_ROUTER"] = "hidden-router";
1445
1591
  MicroAppConfig["KEEP_ALIVE"] = "keep-alive";
1446
1592
  MicroAppConfig["CLEAR_DATA"] = "clear-data";
1447
1593
  MicroAppConfig["SSR"] = "ssr";
1448
1594
  MicroAppConfig["FIBER"] = "fiber";
1449
1595
  })(MicroAppConfig || (MicroAppConfig = {}));
1596
+ /**
1597
+ * global key must be static key, they can not rewrite
1598
+ * e.g.
1599
+ * window.Promise = newValue
1600
+ * new Promise ==> still get old value, not newValue, because they are cached by top function
1601
+ * NOTE:
1602
+ * 1. Do not add fetch, XMLHttpRequest, EventSource
1603
+ */
1604
+ const GLOBAL_CACHED_KEY = 'window,self,globalThis,document,Document,Array,Object,String,Boolean,Math,Number,Symbol,Date,Function,Proxy,WeakMap,WeakSet,Set,Map,Reflect,Element,Node,RegExp,Error,TypeError,JSON,isNaN,parseFloat,parseInt,performance,console,decodeURI,encodeURI,decodeURIComponent,encodeURIComponent,navigator,undefined,location,history';
1450
1605
  // prefetch level
1451
1606
  const PREFETCH_LEVEL = [1, 2, 3];
1452
- // memory router constants
1607
+ /**
1608
+ * memory router modes
1609
+ * NOTE:
1610
+ * 1. The only difference between native and native-scope is location.origin, in native-scope mode location.origin point to child app
1611
+ * 2. native mode equal to disable-memory-router
1612
+ */
1613
+ // 临时注释,1.0版本放开,默认模式切换为state
1614
+ // // default mode, sync child app router info to history.state
1615
+ // export const DEFAULT_ROUTER_MODE = 'state'
1616
+ // // sync child app router info to browser url as search
1617
+ // export const ROUTER_MODE_SEARCH = 'search'
1618
+ // 临时放开,1.0版本去除
1619
+ const ROUTER_MODE_STATE = 'state';
1453
1620
  const DEFAULT_ROUTER_MODE = 'search';
1454
- const ROUTER_MODE_HISTORY = 'history';
1455
- const ROUTER_MODE_CUSTOM = 'custom';
1621
+ // render base on browser url, and location.origin location.href point to base app
1622
+ const ROUTER_MODE_NATIVE = 'native';
1623
+ // render base on browser url, but location.origin location.href point to child app
1624
+ const ROUTER_MODE_NATIVE_SCOPE = 'native-scope';
1625
+ // search mode, but child router info will not sync to browser url
1626
+ const ROUTER_MODE_PURE = 'pure';
1456
1627
  const ROUTER_MODE_LIST = [
1457
1628
  DEFAULT_ROUTER_MODE,
1458
- ROUTER_MODE_HISTORY,
1459
- ROUTER_MODE_CUSTOM,
1629
+ ROUTER_MODE_STATE,
1630
+ ROUTER_MODE_NATIVE,
1631
+ ROUTER_MODE_NATIVE_SCOPE,
1632
+ ROUTER_MODE_PURE,
1460
1633
  ];
1461
1634
  // event bound to child app window
1462
- const SCOPE_WINDOW_EVENT = [
1635
+ const BASE_SCOPE_WINDOW_EVENT = [
1463
1636
  'popstate',
1464
1637
  'hashchange',
1465
1638
  'load',
1466
- 'beforeunload',
1467
1639
  'unload',
1468
1640
  'unmount',
1469
1641
  'appstate-change',
1642
+ 'statechange',
1643
+ 'mounted',
1470
1644
  ];
1645
+ // bind event of with sandbox
1646
+ const SCOPE_WINDOW_EVENT_OF_WITH = BASE_SCOPE_WINDOW_EVENT;
1647
+ // bind event of iframe sandbox
1648
+ const SCOPE_WINDOW_EVENT_OF_IFRAME = BASE_SCOPE_WINDOW_EVENT.concat([
1649
+ 'unhandledrejection',
1650
+ 'message'
1651
+ ]);
1471
1652
  // on event bound to child app window
1472
- const SCOPE_WINDOW_ON_EVENT = [
1653
+ // TODO: with和iframe处理方式不同,需修改
1654
+ const BASE_SCOPE_WINDOW_ON_EVENT = [
1473
1655
  'onpopstate',
1474
1656
  'onhashchange',
1475
1657
  'onload',
1476
- 'onbeforeunload',
1477
1658
  'onunload',
1659
+ 'onerror'
1660
+ // 'onbeforeunload', // remove at 2024.5.30 by cangdu
1478
1661
  ];
1662
+ // bind on event of with sandbox
1663
+ const SCOPE_WINDOW_ON_EVENT_OF_WITH = BASE_SCOPE_WINDOW_ON_EVENT;
1664
+ // bind on event of iframe sandbox
1665
+ const SCOPE_WINDOW_ON_EVENT_OF_IFRAME = BASE_SCOPE_WINDOW_ON_EVENT.concat([
1666
+ 'onunhandledrejection',
1667
+ ]);
1479
1668
  // event bound to child app document
1480
1669
  const SCOPE_DOCUMENT_EVENT = [
1481
1670
  'DOMContentLoaded',
@@ -1492,15 +1681,13 @@ const GLOBAL_KEY_TO_WINDOW = [
1492
1681
  'globalThis',
1493
1682
  ];
1494
1683
  const RAW_GLOBAL_TARGET = ['rawWindow', 'rawDocument'];
1495
- /**
1496
- * global key must be static key, they can not rewrite
1497
- * e.g.
1498
- * window.Promise = newValue
1499
- * new Promise ==> still get old value, not newValue, because they are cached by top function
1500
- * NOTE:
1501
- * 1. Do not add fetch, XMLHttpRequest, EventSource
1502
- */
1503
- const GLOBAL_CACHED_KEY = 'window,self,globalThis,document,Document,Array,Object,String,Boolean,Math,Number,Symbol,Date,Function,Proxy,WeakMap,WeakSet,Set,Map,Reflect,Element,Node,RegExp,Error,TypeError,JSON,isNaN,parseFloat,parseInt,performance,console,decodeURI,encodeURI,decodeURIComponent,encodeURIComponent,navigator,undefined,location,history';
1684
+ const HIJACK_LOCATION_KEYS = [
1685
+ 'host',
1686
+ 'hostname',
1687
+ 'port',
1688
+ 'protocol',
1689
+ 'origin',
1690
+ ];
1504
1691
 
1505
1692
  const scriptTypes = ['text/javascript', 'text/ecmascript', 'application/javascript', 'application/ecmascript', 'module', 'systemjs-module', 'systemjs-importmap'];
1506
1693
  // whether use type='module' script
@@ -1524,8 +1711,7 @@ function isInlineMode(app, scriptInfo) {
1524
1711
  return (app.inline ||
1525
1712
  scriptInfo.appSpace[app.name].inline ||
1526
1713
  isTypeModule(app, scriptInfo) ||
1527
- isSpecialScript(app, scriptInfo) ||
1528
- app.iframe);
1714
+ isSpecialScript(app, scriptInfo));
1529
1715
  }
1530
1716
  // TODO: iframe重新插入window前后不一致,通过iframe Function创建的函数无法复用
1531
1717
  function getEffectWindow(app) {
@@ -1765,16 +1951,16 @@ function fetchScriptsFromHtml(wrapElement, app) {
1765
1951
  logError(err, app.name);
1766
1952
  }, () => {
1767
1953
  if (fiberScriptTasks) {
1768
- fiberScriptTasks.push(() => Promise.resolve(app.onLoad(wrapElement)));
1954
+ fiberScriptTasks.push(() => Promise.resolve(app.onLoad({ html: wrapElement })));
1769
1955
  serialExecFiberTasks(fiberScriptTasks);
1770
1956
  }
1771
1957
  else {
1772
- app.onLoad(wrapElement);
1958
+ app.onLoad({ html: wrapElement });
1773
1959
  }
1774
1960
  });
1775
1961
  }
1776
1962
  else {
1777
- app.onLoad(wrapElement);
1963
+ app.onLoad({ html: wrapElement });
1778
1964
  }
1779
1965
  }
1780
1966
  /**
@@ -1901,6 +2087,7 @@ function execScripts(app, initHook) {
1901
2087
  * @param callback callback of module script
1902
2088
  */
1903
2089
  function runScript(address, app, scriptInfo, callback, replaceElement) {
2090
+ var _a;
1904
2091
  try {
1905
2092
  actionsBeforeRunScript(app);
1906
2093
  const appSpaceData = scriptInfo.appSpace[app.name];
@@ -1931,7 +2118,7 @@ function runScript(address, app, scriptInfo, callback, replaceElement) {
1931
2118
  */
1932
2119
  if (!replaceElement) {
1933
2120
  // TEST IGNORE
1934
- const parent = app.iframe ? app.sandBox.microBody : app.querySelector('micro-app-body');
2121
+ const parent = app.iframe ? (_a = app.sandBox) === null || _a === void 0 ? void 0 : _a.microBody : app.querySelector('micro-app-body');
1935
2122
  parent === null || parent === void 0 ? void 0 : parent.appendChild(scriptElement);
1936
2123
  }
1937
2124
  }
@@ -1941,6 +2128,8 @@ function runScript(address, app, scriptInfo, callback, replaceElement) {
1941
2128
  }
1942
2129
  catch (e) {
1943
2130
  console.error(`[micro-app from ${replaceElement ? 'runDynamicScript' : 'runScript'}] app ${app.name}: `, e, address);
2131
+ // throw error in with sandbox to parent app
2132
+ throw e;
1944
2133
  }
1945
2134
  }
1946
2135
  /**
@@ -1951,7 +2140,7 @@ function runScript(address, app, scriptInfo, callback, replaceElement) {
1951
2140
  * @param originScript origin script element
1952
2141
  */
1953
2142
  function runDynamicRemoteScript(address, app, scriptInfo, originScript) {
1954
- const replaceElement = isInlineMode(app, scriptInfo) ? pureCreateElement('script') : document.createComment('dynamic script extract by micro-app');
2143
+ const replaceElement = isInlineMode(app, scriptInfo) ? pureCreateElement('script') : document.createComment(`dynamic script with src='${address}' extract by micro-app`);
1955
2144
  const dispatchScriptOnLoadEvent = () => dispatchOnLoadEvent(originScript);
1956
2145
  const runDynamicScript = () => {
1957
2146
  const descriptor = Object.getOwnPropertyDescriptor(globalEnv.rawDocument, 'currentScript');
@@ -1985,7 +2174,7 @@ function runDynamicRemoteScript(address, app, scriptInfo, originScript) {
1985
2174
  * @param scriptInfo scriptInfo
1986
2175
  */
1987
2176
  function runDynamicInlineScript(address, app, scriptInfo) {
1988
- const replaceElement = isInlineMode(app, scriptInfo) ? pureCreateElement('script') : document.createComment('dynamic script extract by micro-app');
2177
+ const replaceElement = isInlineMode(app, scriptInfo) ? pureCreateElement('script') : document.createComment('dynamic inline script extract by micro-app');
1989
2178
  runScript(address, app, scriptInfo, void 0, replaceElement);
1990
2179
  return replaceElement;
1991
2180
  }
@@ -2096,15 +2285,6 @@ function processCode(configs, code, address) {
2096
2285
  }, code);
2097
2286
  }
2098
2287
 
2099
- /**
2100
- * transform html string to dom
2101
- * @param str string dom
2102
- */
2103
- function getWrapElement(str) {
2104
- const wrapDiv = pureCreateElement('div');
2105
- wrapDiv.innerHTML = str;
2106
- return wrapDiv;
2107
- }
2108
2288
  /**
2109
2289
  * Recursively process each child element
2110
2290
  * @param parent parent element
@@ -2161,7 +2341,7 @@ function flatChildren(parent, app, microAppHead, fiberStyleTasks) {
2161
2341
  * @param app app
2162
2342
  */
2163
2343
  function extractSourceDom(htmlStr, app) {
2164
- const wrapElement = getWrapElement(htmlStr);
2344
+ const wrapElement = app.parseHtmlString(htmlStr);
2165
2345
  const microAppHead = globalEnv.rawElementQuerySelector.call(wrapElement, 'micro-app-head');
2166
2346
  const microAppBody = globalEnv.rawElementQuerySelector.call(wrapElement, 'micro-app-body');
2167
2347
  if (!microAppHead || !microAppBody) {
@@ -2172,23 +2352,23 @@ function extractSourceDom(htmlStr, app) {
2172
2352
  const fiberStyleTasks = app.isPrefetch || app.fiber ? [] : null;
2173
2353
  flatChildren(wrapElement, app, microAppHead, fiberStyleTasks);
2174
2354
  /**
2175
- * Style and link are parallel, because it takes a lot of time for link to request resources. During this period, style processing can be performed to improve efficiency.
2355
+ * Style and link are parallel, as it takes a lot of time for link to request resources. During this period, style processing can be performed to improve efficiency.
2176
2356
  */
2177
2357
  const fiberStyleResult = serialExecFiberTasks(fiberStyleTasks);
2178
2358
  if (app.source.links.size) {
2179
2359
  fetchLinksFromHtml(wrapElement, app, microAppHead, fiberStyleResult);
2180
2360
  }
2181
2361
  else if (fiberStyleResult) {
2182
- fiberStyleResult.then(() => app.onLoad(wrapElement));
2362
+ fiberStyleResult.then(() => app.onLoad({ html: wrapElement }));
2183
2363
  }
2184
2364
  else {
2185
- app.onLoad(wrapElement);
2365
+ app.onLoad({ html: wrapElement });
2186
2366
  }
2187
2367
  if (app.source.scripts.size) {
2188
2368
  fetchScriptsFromHtml(wrapElement, app);
2189
2369
  }
2190
2370
  else {
2191
- app.onLoad(wrapElement);
2371
+ app.onLoad({ html: wrapElement });
2192
2372
  }
2193
2373
  }
2194
2374
 
@@ -2367,7 +2547,7 @@ const eventCenter = new EventCenter();
2367
2547
  function createEventName(appName, fromBaseApp) {
2368
2548
  if (!isString(appName) || !appName)
2369
2549
  return '';
2370
- return fromBaseApp ? `__from_base_app_${appName}__` : `__from_micro_app_${appName}__`;
2550
+ return fromBaseApp ? `__${appName}_from_base_app__` : `__${appName}_from_micro_app__`;
2371
2551
  }
2372
2552
  // Global data
2373
2553
  class EventCenterForGlobal {
@@ -2666,7 +2846,15 @@ function isConstructorFunction(value) {
2666
2846
  }
2667
2847
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
2668
2848
  function bindFunctionToRawTarget(value, rawTarget, key = 'WINDOW') {
2669
- if (isFunction(value) && !isConstructorFunction(value) && !isBoundedFunction(value)) {
2849
+ /**
2850
+ * In safari, nest app like: A -> B -> C
2851
+ * if B is iframe sandbox, and C is with sandbox, same property of document in C is abnormal
2852
+ * e.g:
2853
+ * document.all:
2854
+ * - typeof document.all ==> 'function'
2855
+ * - document.all.bind ==> undefined
2856
+ */
2857
+ if (isFunction(value) && !isConstructorFunction(value) && !isBoundedFunction(value) && value.bind) {
2670
2858
  const cacheKey = `__MICRO_APP_BOUND_${key}_FUNCTION__`;
2671
2859
  if (value[cacheKey])
2672
2860
  return value[cacheKey];
@@ -2687,10 +2875,10 @@ function bindFunctionToRawTarget(value, rawTarget, key = 'WINDOW') {
2687
2875
  return value;
2688
2876
  }
2689
2877
 
2690
- class Adapter {
2691
- constructor() {
2878
+ class BaseSandbox {
2879
+ constructor(appName, url) {
2692
2880
  // keys that can only assigned to rawWindow
2693
- this.escapeSetterKeyList = [
2881
+ this.rawWindowScopeKeyList = [
2694
2882
  'location',
2695
2883
  ];
2696
2884
  // keys that can escape to rawWindow
@@ -2703,7 +2891,20 @@ class Adapter {
2703
2891
  'webpackJsonp',
2704
2892
  'webpackHotUpdate',
2705
2893
  'Vue',
2894
+ // TODO: 是否可以和constants/SCOPE_WINDOW_ON_EVENT合并
2895
+ 'onpopstate',
2896
+ 'onhashchange',
2706
2897
  ];
2898
+ // Properties that can only get and set in microAppWindow, will not escape to rawWindow
2899
+ this.scopeProperties = Array.from(this.staticScopeProperties);
2900
+ // Properties that can be escape to rawWindow
2901
+ this.escapeProperties = [];
2902
+ // Properties newly added to microAppWindow
2903
+ this.injectedKeys = new Set();
2904
+ // Properties escape to rawWindow, cleared when unmount
2905
+ this.escapeKeys = new Set();
2906
+ this.appName = appName;
2907
+ this.url = url;
2707
2908
  this.injectReactHMRProperty();
2708
2909
  }
2709
2910
  // adapter for react
@@ -2721,6 +2922,14 @@ class Adapter {
2721
2922
  }
2722
2923
  }
2723
2924
  }
2925
+ /**
2926
+ * TODO:
2927
+ * 1、将class Adapter去掉,改为CustomWindow,或者让CustomWindow继承Adapter
2928
+ * 2、with沙箱中的常量放入CustomWindow,虽然和iframe沙箱不一致,但更合理
2929
+ * 修改时机:在iframe沙箱支持插件后再修改
2930
+ */
2931
+ class CustomWindow {
2932
+ }
2724
2933
  // Fix conflict of babel-polyfill@6.x
2725
2934
  function fixBabelPolyfill6() {
2726
2935
  if (globalEnv.rawWindow._babelPolyfill)
@@ -2743,50 +2952,17 @@ function fixReactHMRConflict(app) {
2743
2952
  }
2744
2953
  }
2745
2954
  }
2746
- /**
2747
- * reDefine parentNode of html
2748
- * Scenes:
2749
- * 1. element-ui@2/lib/utils/popper.js
2750
- * var parent = element.parentNode;
2751
- * // root is child app window
2752
- * if (parent === root.document) ...
2753
- */
2754
- function throttleDeferForParentNode(microDocument) {
2755
- const html = globalEnv.rawDocument.firstElementChild;
2756
- if ((html === null || html === void 0 ? void 0 : html.parentNode) === globalEnv.rawDocument) {
2757
- setParentNode(html, microDocument);
2758
- defer(() => {
2759
- setParentNode(html, globalEnv.rawDocument);
2760
- });
2761
- }
2762
- }
2763
- /**
2764
- * Modify the point of parentNode
2765
- * @param target target Node
2766
- * @param value parentNode
2767
- */
2768
- function setParentNode(target, value) {
2769
- const descriptor = Object.getOwnPropertyDescriptor(target, 'parentNode');
2770
- if (!descriptor || descriptor.configurable) {
2771
- rawDefineProperty(target, 'parentNode', {
2772
- value,
2773
- configurable: true,
2774
- });
2775
- }
2776
- }
2777
2955
  /**
2778
2956
  * update dom tree of target dom
2779
2957
  * @param container target dom
2780
2958
  * @param appName app name
2781
2959
  */
2782
2960
  function patchElementTree(container, appName) {
2783
- const children = Array.from(container.children);
2961
+ const children = Array.from(container.childNodes);
2784
2962
  children.length && children.forEach((child) => {
2785
2963
  patchElementTree(child, appName);
2786
2964
  });
2787
- for (const child of children) {
2788
- updateElementInfo(child, appName);
2789
- }
2965
+ updateElementInfo(container, appName);
2790
2966
  }
2791
2967
  /**
2792
2968
  * rewrite baseURI, ownerDocument, __MICRO_APP_NAME__ of target node
@@ -2796,38 +2972,119 @@ function patchElementTree(container, appName) {
2796
2972
  */
2797
2973
  function updateElementInfo(node, appName) {
2798
2974
  var _a, _b;
2799
- const proxyWindow = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.sandBox) === null || _b === void 0 ? void 0 : _b.proxyWindow;
2800
- if (isNode(node) &&
2801
- !node.__MICRO_APP_NAME__ &&
2975
+ if (appName &&
2976
+ isNode(node) &&
2977
+ node.__MICRO_APP_NAME__ !== appName &&
2802
2978
  !node.__PURE_ELEMENT__ &&
2803
- proxyWindow) {
2979
+ !getPreventSetState()) {
2804
2980
  /**
2805
2981
  * TODO:
2806
2982
  * 1. 测试baseURI和ownerDocument在with沙箱中是否正确
2807
2983
  * 经过验证with沙箱不能重写ownerDocument,否则react点击事件会触发两次
2808
- * 2. with沙箱所有node设置__MICRO_APP_NAME__都使用updateElementInfo
2809
- * 3. 性能: defineProperty的性能肯定不如直接设置
2810
2984
  */
2811
- rawDefineProperties(node, {
2812
- baseURI: {
2813
- configurable: true,
2814
- get: () => proxyWindow.location.href,
2815
- },
2985
+ const props = {
2816
2986
  __MICRO_APP_NAME__: {
2817
2987
  configurable: true,
2988
+ enumerable: true,
2818
2989
  writable: true,
2819
2990
  value: appName,
2820
2991
  },
2821
- });
2992
+ };
2993
+ if (isAnchorElement(node)) {
2994
+ // a 标签
2995
+ const microApp = AppManager.getInstance().get(appName);
2996
+ if (microApp) {
2997
+ props.href = {
2998
+ get() {
2999
+ return this.getAttribute('href');
3000
+ },
3001
+ set(value) {
3002
+ this.setAttribute('href', value);
3003
+ },
3004
+ };
3005
+ }
3006
+ }
3007
+ if (isImageElement(node) || isVideoElement(node) || isAudioElement(node)) {
3008
+ // @ts-ignore
3009
+ node.crossOrigin = 'anonymous';
3010
+ }
3011
+ rawDefineProperties(node, props);
3012
+ /**
3013
+ * In FireFox, iframe Node.prototype will point to native Node.prototype after insert to document
3014
+ *
3015
+ * Performance:
3016
+ * iframe element.__proto__ === browser HTMLElement.prototype // Chrome: false, FireFox: true
3017
+ * iframe element.__proto__ === iframe HTMLElement.prototype // Chrome: true, FireFox: false
3018
+ *
3019
+ * NOTE:
3020
+ * 1. Node.prototype.baseURI
3021
+ * 2. Node.prototype.ownerDocument
3022
+ * 3. Node.prototype.parentNode
3023
+ * 4. Node.prototype.getRootNode
3024
+ * 5. Node.prototype.cloneNode
3025
+ * 6. Element.prototype.innerHTML
3026
+ * 7. Image
3027
+ */
2822
3028
  if (isIframeSandbox(appName)) {
2823
- rawDefineProperty(node, 'ownerDocument', {
2824
- configurable: true,
2825
- get: () => proxyWindow.document,
2826
- });
3029
+ const proxyWindow = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.sandBox) === null || _b === void 0 ? void 0 : _b.proxyWindow;
3030
+ if (proxyWindow) {
3031
+ rawDefineProperties(node, {
3032
+ baseURI: {
3033
+ configurable: true,
3034
+ enumerable: true,
3035
+ get: () => proxyWindow.location.href,
3036
+ },
3037
+ ownerDocument: {
3038
+ configurable: true,
3039
+ enumerable: true,
3040
+ get: () => node !== proxyWindow.document ? proxyWindow.document : null,
3041
+ },
3042
+ parentNode: getIframeParentNodeDesc(appName, globalEnv.rawParentNodeDesc),
3043
+ getRootNode: {
3044
+ configurable: true,
3045
+ enumerable: true,
3046
+ writable: true,
3047
+ value: function getRootNode() {
3048
+ return proxyWindow.document;
3049
+ }
3050
+ },
3051
+ });
3052
+ }
2827
3053
  }
2828
3054
  }
2829
3055
  return node;
2830
3056
  }
3057
+ /**
3058
+ * get Descriptor of Node.prototype.parentNode for iframe
3059
+ * @param appName app name
3060
+ * @param parentNode parentNode Descriptor of iframe or browser
3061
+ */
3062
+ function getIframeParentNodeDesc(appName, parentNodeDesc) {
3063
+ return {
3064
+ configurable: true,
3065
+ enumerable: true,
3066
+ get() {
3067
+ var _a, _b, _c, _d;
3068
+ throttleDeferForIframeAppName(appName);
3069
+ const result = (_a = parentNodeDesc.get) === null || _a === void 0 ? void 0 : _a.call(this);
3070
+ /**
3071
+ * If parentNode is <micro-app-body>, return rawDocument.body
3072
+ * Scenes:
3073
+ * 1. element-ui@2/lib/utils/vue-popper.js
3074
+ * if (this.popperElm.parentNode === document.body) ...
3075
+ * e.g.:
3076
+ * 1. element-ui@2.x el-dropdown
3077
+ * WARNING:
3078
+ * Will it cause other problems ?
3079
+ * e.g. target.parentNode.remove(target)
3080
+ */
3081
+ if (isMicroAppBody(result) && ((_b = appInstanceMap.get(appName)) === null || _b === void 0 ? void 0 : _b.container)) {
3082
+ return ((_d = (_c = microApp.options).getRootElementParentNode) === null || _d === void 0 ? void 0 : _d.call(_c, this, appName)) || globalEnv.rawDocument.body;
3083
+ }
3084
+ return result;
3085
+ }
3086
+ };
3087
+ }
2831
3088
 
2832
3089
  /**
2833
3090
  * create proxyDocument and MicroDocument, rewrite document of child app
@@ -2866,11 +3123,14 @@ function createProxyDocument(appName, sandbox) {
2866
3123
  const sstEventListenerMap = new Map();
2867
3124
  let onClickHandler = null;
2868
3125
  let sstOnClickHandler = null;
2869
- const { rawDocument, rawCreateElement, rawAddEventListener, rawRemoveEventListener, } = globalEnv;
3126
+ const { rawDocument, rawCreateElement, rawCreateElementNS, rawAddEventListener, rawRemoveEventListener, } = globalEnv;
2870
3127
  function createElement(tagName, options) {
2871
3128
  const element = rawCreateElement.call(rawDocument, tagName, options);
2872
- element.__MICRO_APP_NAME__ = appName;
2873
- return element;
3129
+ return updateElementInfo(element, appName);
3130
+ }
3131
+ function createElementNS(namespaceURI, name, options) {
3132
+ const element = rawCreateElementNS.call(rawDocument, namespaceURI, name, options);
3133
+ return updateElementInfo(element, appName);
2874
3134
  }
2875
3135
  /**
2876
3136
  * TODO:
@@ -2955,13 +3215,40 @@ function createProxyDocument(appName, sandbox) {
2955
3215
  eventListenerMap.clear();
2956
3216
  }
2957
3217
  };
3218
+ const genProxyDocumentProps = () => {
3219
+ var _a;
3220
+ // microApp framework built-in Proxy
3221
+ const builtInProxyProps = new Map([
3222
+ ['onclick', (value) => {
3223
+ if (isFunction(onClickHandler)) {
3224
+ rawRemoveEventListener.call(rawDocument, 'click', onClickHandler, false);
3225
+ }
3226
+ // TODO: listener 是否需要绑定proxyDocument,否则函数中的this指向原生window
3227
+ if (isFunction(value)) {
3228
+ rawAddEventListener.call(rawDocument, 'click', value, false);
3229
+ }
3230
+ onClickHandler = value;
3231
+ }]
3232
+ ]);
3233
+ // external custom proxy
3234
+ const customProxyDocumentProps = ((_a = microApp.options) === null || _a === void 0 ? void 0 : _a.customProxyDocumentProps) || new Map();
3235
+ // External has higher priority than built-in
3236
+ const mergedProxyDocumentProps = new Map([
3237
+ ...builtInProxyProps,
3238
+ ...customProxyDocumentProps,
3239
+ ]);
3240
+ return mergedProxyDocumentProps;
3241
+ };
3242
+ const mergedProxyDocumentProps = genProxyDocumentProps();
2958
3243
  const proxyDocument = new Proxy(rawDocument, {
2959
3244
  get: (target, key) => {
2960
3245
  var _a;
2961
3246
  throttleDeferForSetAppName(appName);
2962
- throttleDeferForParentNode(proxyDocument);
3247
+ // TODO: 转换成数据形式,类似iframe的方式
2963
3248
  if (key === 'createElement')
2964
3249
  return createElement;
3250
+ if (key === 'createElementNS')
3251
+ return createElementNS;
2965
3252
  if (key === Symbol.toStringTag)
2966
3253
  return 'ProxyDocument';
2967
3254
  if (key === 'defaultView')
@@ -2974,18 +3261,14 @@ function createProxyDocument(appName, sandbox) {
2974
3261
  return removeEventListener;
2975
3262
  if (key === 'microAppElement')
2976
3263
  return (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container;
3264
+ if (key === '__MICRO_APP_NAME__')
3265
+ return appName;
2977
3266
  return bindFunctionToRawTarget(Reflect.get(target, key), rawDocument, 'DOCUMENT');
2978
3267
  },
2979
3268
  set: (target, key, value) => {
2980
- if (key === 'onclick') {
2981
- if (isFunction(onClickHandler)) {
2982
- rawRemoveEventListener.call(rawDocument, 'click', onClickHandler, false);
2983
- }
2984
- // TODO: listener 是否需要绑定proxyDocument,否则函数中的this指向原生window
2985
- if (isFunction(value)) {
2986
- rawAddEventListener.call(rawDocument, 'click', value, false);
2987
- }
2988
- onClickHandler = value;
3269
+ if (mergedProxyDocumentProps.has(key)) {
3270
+ const proxyCallback = mergedProxyDocumentProps.get(key);
3271
+ proxyCallback(value);
2989
3272
  }
2990
3273
  else if (key !== 'microAppElement') {
2991
3274
  /**
@@ -3018,7 +3301,8 @@ function createMicroDocument(appName, proxyDocument) {
3018
3301
  class MicroDocument {
3019
3302
  static [Symbol.hasInstance](target) {
3020
3303
  let proto = target;
3021
- while (proto = Object.getPrototypeOf(proto)) {
3304
+ while (proto) {
3305
+ proto = Object.getPrototypeOf(proto);
3022
3306
  if (proto === MicroDocument.prototype) {
3023
3307
  return true;
3024
3308
  }
@@ -3061,7 +3345,7 @@ function createMicroDocument(appName, proxyDocument) {
3061
3345
  function patchWindow(appName, microAppWindow, sandbox) {
3062
3346
  patchWindowProperty(microAppWindow);
3063
3347
  createProxyWindow(appName, microAppWindow, sandbox);
3064
- return patchWindowEffect(microAppWindow);
3348
+ return patchWindowEffect(microAppWindow, appName);
3065
3349
  }
3066
3350
  /**
3067
3351
  * rewrite special properties of window
@@ -3072,7 +3356,7 @@ function patchWindowProperty(microAppWindow) {
3072
3356
  const rawWindow = globalEnv.rawWindow;
3073
3357
  Object.getOwnPropertyNames(rawWindow)
3074
3358
  .filter((key) => {
3075
- return /^on/.test(key) && !SCOPE_WINDOW_ON_EVENT.includes(key);
3359
+ return /^on/.test(key) && !SCOPE_WINDOW_ON_EVENT_OF_WITH.includes(key);
3076
3360
  })
3077
3361
  .forEach((eventName) => {
3078
3362
  const { enumerable, writable, set } = Object.getOwnPropertyDescriptor(rawWindow, eventName) || {
@@ -3102,22 +3386,22 @@ function createProxyWindow(appName, microAppWindow, sandbox) {
3102
3386
  throttleDeferForSetAppName(appName);
3103
3387
  if (Reflect.has(target, key) ||
3104
3388
  (isString(key) && /^__MICRO_APP_/.test(key)) ||
3105
- sandbox.scopeProperties.includes(key)) {
3106
- if (RAW_GLOBAL_TARGET.includes(key))
3389
+ includes(sandbox.scopeProperties, key)) {
3390
+ if (includes(RAW_GLOBAL_TARGET, key))
3107
3391
  removeDomScope();
3108
3392
  return Reflect.get(target, key);
3109
3393
  }
3110
3394
  return bindFunctionToRawTarget(Reflect.get(rawWindow, key), rawWindow);
3111
3395
  },
3112
3396
  set: (target, key, value) => {
3113
- if (sandbox.adapter.escapeSetterKeyList.includes(key)) {
3397
+ if (includes(sandbox.rawWindowScopeKeyList, key)) {
3114
3398
  Reflect.set(rawWindow, key, value);
3115
3399
  }
3116
3400
  else if (
3117
3401
  // target.hasOwnProperty has been rewritten
3118
3402
  !rawHasOwnProperty.call(target, key) &&
3119
3403
  rawHasOwnProperty.call(rawWindow, key) &&
3120
- !sandbox.scopeProperties.includes(key)) {
3404
+ !includes(sandbox.scopeProperties, key)) {
3121
3405
  const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
3122
3406
  const { configurable, enumerable, writable, set } = descriptor;
3123
3407
  // set value because it can be set
@@ -3130,32 +3414,37 @@ function createProxyWindow(appName, microAppWindow, sandbox) {
3130
3414
  sandbox.injectedKeys.add(key);
3131
3415
  }
3132
3416
  else {
3133
- !Reflect.has(target, key) && sandbox.injectedKeys.add(key);
3417
+ // all scopeProperties will add to injectedKeys, use for key in window (Proxy.has)
3418
+ if (!Reflect.has(target, key) || includes(sandbox.scopeProperties, key)) {
3419
+ sandbox.injectedKeys.add(key);
3420
+ }
3134
3421
  Reflect.set(target, key, value);
3135
3422
  }
3136
- if ((sandbox.escapeProperties.includes(key) ||
3137
- (sandbox.adapter.staticEscapeProperties.includes(key) &&
3423
+ if ((includes(sandbox.escapeProperties, key) ||
3424
+ (
3425
+ // TODO: staticEscapeProperties 合并到 escapeProperties
3426
+ includes(sandbox.staticEscapeProperties, key) &&
3138
3427
  !Reflect.has(rawWindow, key))) &&
3139
- !sandbox.scopeProperties.includes(key)) {
3428
+ !includes(sandbox.scopeProperties, key)) {
3140
3429
  !Reflect.has(rawWindow, key) && sandbox.escapeKeys.add(key);
3141
3430
  Reflect.set(rawWindow, key, value);
3142
3431
  }
3143
3432
  return true;
3144
3433
  },
3145
3434
  has: (target, key) => {
3146
- if (sandbox.scopeProperties.includes(key)) {
3147
- /**
3148
- * Some keywords, such as Vue, need to meet two conditions at the same time:
3149
- * 1. 'Vue' in window --> false
3150
- * 2. Vue (top level variable) // undefined
3151
- * Issue https://github.com/micro-zoe/micro-app/issues/686
3152
- */
3153
- if (sandbox.adapter.staticScopeProperties.includes(key)) {
3154
- return !!target[key];
3435
+ /**
3436
+ * Some keywords, such as Vue, need to meet two conditions at the same time:
3437
+ * 1. window.Vue --> undefined
3438
+ * 2. 'Vue' in window --> false
3439
+ * Issue https://github.com/micro-zoe/micro-app/issues/686
3440
+ */
3441
+ if (includes(sandbox.scopeProperties, key)) {
3442
+ if (sandbox.injectedKeys.has(key)) {
3443
+ return Reflect.has(target, key); // true
3155
3444
  }
3156
- return key in target;
3445
+ return !!target[key]; // false
3157
3446
  }
3158
- return key in target || key in rawWindow;
3447
+ return Reflect.has(target, key) || Reflect.has(rawWindow, key);
3159
3448
  },
3160
3449
  // Object.getOwnPropertyDescriptor(window, key)
3161
3450
  getOwnPropertyDescriptor: (target, key) => {
@@ -3200,14 +3489,26 @@ function createProxyWindow(appName, microAppWindow, sandbox) {
3200
3489
  * Rewrite side-effect events
3201
3490
  * @param microAppWindow micro window
3202
3491
  */
3203
- function patchWindowEffect(microAppWindow) {
3492
+ function patchWindowEffect(microAppWindow, appName) {
3204
3493
  const eventListenerMap = new Map();
3205
3494
  const sstEventListenerMap = new Map();
3206
3495
  const intervalIdMap = new Map();
3207
3496
  const timeoutIdMap = new Map();
3208
3497
  const { rawWindow, rawAddEventListener, rawRemoveEventListener, rawDispatchEvent, rawSetInterval, rawSetTimeout, rawClearInterval, rawClearTimeout, } = globalEnv;
3498
+ /**
3499
+ * All events will bind to microAppElement or rawWindow
3500
+ * Some special events, such as popstate、load、unmount、appstate-change、statechange..., bind to microAppElement, others bind to rawWindow
3501
+ * NOTE:
3502
+ * 1、At first, microAppWindow = new EventTarget(), but it can not compatible with iOS 14 or below, so microAppElement was used instead. (2024.1.22)
3503
+ * @param type event name
3504
+ * @returns microAppElement/rawWindow
3505
+ */
3209
3506
  function getEventTarget(type) {
3210
- return SCOPE_WINDOW_EVENT.includes(type) ? microAppWindow : rawWindow;
3507
+ var _a;
3508
+ if (SCOPE_WINDOW_EVENT_OF_WITH.includes(type) && ((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container)) {
3509
+ return getRootContainer(appInstanceMap.get(appName).container);
3510
+ }
3511
+ return rawWindow;
3211
3512
  }
3212
3513
  /**
3213
3514
  * listener may be null, e.g test-passive
@@ -3217,6 +3518,7 @@ function patchWindowEffect(microAppWindow) {
3217
3518
  * window.addEventListener.call(非window, type, listener, options)
3218
3519
  */
3219
3520
  microAppWindow.addEventListener = function (type, listener, options) {
3521
+ type = formatEventType(type, appName);
3220
3522
  const listenerList = eventListenerMap.get(type);
3221
3523
  if (listenerList) {
3222
3524
  listenerList.add(listener);
@@ -3228,6 +3530,7 @@ function patchWindowEffect(microAppWindow) {
3228
3530
  rawAddEventListener.call(getEventTarget(type), type, listener, options);
3229
3531
  };
3230
3532
  microAppWindow.removeEventListener = function (type, listener, options) {
3533
+ type = formatEventType(type, appName);
3231
3534
  const listenerList = eventListenerMap.get(type);
3232
3535
  if ((listenerList === null || listenerList === void 0 ? void 0 : listenerList.size) && listenerList.has(listener)) {
3233
3536
  listenerList.delete(listener);
@@ -3320,42 +3623,44 @@ function patchWindowEffect(microAppWindow) {
3320
3623
  }
3321
3624
 
3322
3625
  // set micro app state to origin state
3323
- function setMicroState(appName, microState) {
3324
- if (!isRouterModeCustom(appName)) {
3325
- const rawState = globalEnv.rawWindow.history.state;
3326
- const additionalState = {
3327
- microAppState: assign({}, rawState === null || rawState === void 0 ? void 0 : rawState.microAppState, {
3328
- [appName]: microState
3329
- })
3330
- };
3331
- // create new state object
3332
- return assign({}, rawState, additionalState);
3333
- }
3334
- return microState;
3626
+ function setMicroState(appName, microState, targetLocation) {
3627
+ // TODO: 验证native模式下修改state nextjs路由是否正常
3628
+ const rawState = globalEnv.rawWindow.history.state;
3629
+ const additionalState = {
3630
+ __MICRO_APP_STATE__: assign({}, rawState === null || rawState === void 0 ? void 0 : rawState.__MICRO_APP_STATE__, {
3631
+ [appName]: {
3632
+ fullPath: targetLocation ? targetLocation.pathname + targetLocation.search + targetLocation.hash : null,
3633
+ state: microState !== null && microState !== void 0 ? microState : null,
3634
+ mode: getRouterMode(appName),
3635
+ }
3636
+ }),
3637
+ };
3638
+ // create new state object
3639
+ return assign({}, rawState, additionalState);
3335
3640
  }
3336
3641
  // delete micro app state form origin state
3337
3642
  function removeMicroState(appName, rawState) {
3338
- if (!isRouterModeCustom(appName)) {
3339
- if (isPlainObject(rawState === null || rawState === void 0 ? void 0 : rawState.microAppState)) {
3340
- if (!isUndefined(rawState.microAppState[appName])) {
3341
- delete rawState.microAppState[appName];
3342
- }
3343
- if (!Object.keys(rawState.microAppState).length) {
3344
- delete rawState.microAppState;
3345
- }
3643
+ if (isPlainObject(rawState === null || rawState === void 0 ? void 0 : rawState.__MICRO_APP_STATE__)) {
3644
+ if (!isUndefined(rawState.__MICRO_APP_STATE__[appName])) {
3645
+ delete rawState.__MICRO_APP_STATE__[appName];
3646
+ }
3647
+ if (!Object.keys(rawState.__MICRO_APP_STATE__).length) {
3648
+ delete rawState.__MICRO_APP_STATE__;
3346
3649
  }
3347
- return assign({}, rawState);
3348
3650
  }
3349
- return rawState;
3651
+ return !isEmptyObject(rawState) ? assign({}, rawState) : null;
3350
3652
  }
3351
3653
  // get micro app state form origin state
3352
3654
  function getMicroState(appName) {
3655
+ var _a, _b;
3656
+ const rawState = globalEnv.rawWindow.history.state;
3657
+ return ((_b = (_a = rawState === null || rawState === void 0 ? void 0 : rawState.__MICRO_APP_STATE__) === null || _a === void 0 ? void 0 : _a[appName]) === null || _b === void 0 ? void 0 : _b.state) || null;
3658
+ }
3659
+ // get micro app router info state form origin state
3660
+ function getMicroRouterInfoState(appName) {
3353
3661
  var _a;
3354
3662
  const rawState = globalEnv.rawWindow.history.state;
3355
- if (!isRouterModeCustom(appName)) {
3356
- return ((_a = rawState === null || rawState === void 0 ? void 0 : rawState.microAppState) === null || _a === void 0 ? void 0 : _a[appName]) || null;
3357
- }
3358
- return rawState;
3663
+ return ((_a = rawState === null || rawState === void 0 ? void 0 : rawState.__MICRO_APP_STATE__) === null || _a === void 0 ? void 0 : _a[appName]) || null;
3359
3664
  }
3360
3665
  const ENC_AD_RE = /&/g; // %M1
3361
3666
  const ENC_EQ_RE = /=/g; // %M2
@@ -3391,14 +3696,35 @@ function formatQueryAppName(appName) {
3391
3696
  * @param appName app.name
3392
3697
  */
3393
3698
  function getMicroPathFromURL(appName) {
3394
- var _a, _b;
3699
+ var _a, _b, _c, _d;
3395
3700
  const rawLocation = globalEnv.rawWindow.location;
3396
- if (!isRouterModeCustom(appName)) {
3701
+ const rawState = globalEnv.rawWindow.history.state;
3702
+ if (isRouterModeSearch(appName)) {
3397
3703
  const queryObject = getQueryObjectFromURL(rawLocation.search, rawLocation.hash);
3398
3704
  const microPath = ((_a = queryObject.hashQuery) === null || _a === void 0 ? void 0 : _a[formatQueryAppName(appName)]) || ((_b = queryObject.searchQuery) === null || _b === void 0 ? void 0 : _b[formatQueryAppName(appName)]);
3399
3705
  return isString(microPath) ? decodeMicroPath(microPath) : null;
3400
3706
  }
3401
- return rawLocation.pathname + rawLocation.search + rawLocation.hash;
3707
+ /**
3708
+ * Get fullPath from __MICRO_APP_STATE__
3709
+ * NOTE:
3710
+ * 1. state mode: all base on __MICRO_APP_STATE__
3711
+ * 2. pure mode: navigate by location.xxx may contain one-time information in __MICRO_APP_STATE__
3712
+ * 3. native mode: vue-router@4 will exec replaceState with history.state before pushState, like:
3713
+ * history.replaceState(
3714
+ * assign({}, history.state, {...}),
3715
+ * title,
3716
+ * history.state.current, <---
3717
+ * )
3718
+ * when base app jump to another page from child page, it will replace child path with base app path
3719
+ * e.g: base-home --> child-home --> child-about(will replace with child-home before jump to base-home) --> base-home, when go back, it will back to child-home not child-about
3720
+ * So we take the fullPath as standard
3721
+ */
3722
+ // 问题:1、同一个页面多个子应用,一个修改后... --- native模式不支持多个子应用同时渲染,多个子应用推荐使用其它模式
3723
+ // if (isRouterModeCustom(appName)) {
3724
+ // return rawLocation.pathname + rawLocation.search + rawLocation.hash
3725
+ // }
3726
+ // return rawState?.__MICRO_APP_STATE__?.[appName]?.fullPath || null
3727
+ return ((_d = (_c = rawState === null || rawState === void 0 ? void 0 : rawState.__MICRO_APP_STATE__) === null || _c === void 0 ? void 0 : _c[appName]) === null || _d === void 0 ? void 0 : _d.fullPath) || (isRouterModeCustom(appName) ? rawLocation.pathname + rawLocation.search + rawLocation.hash : null);
3402
3728
  }
3403
3729
  /**
3404
3730
  * Attach child app fullPath to browser url
@@ -3406,10 +3732,11 @@ function getMicroPathFromURL(appName) {
3406
3732
  * @param targetLocation location of child app or rawLocation of window
3407
3733
  */
3408
3734
  function setMicroPathToURL(appName, targetLocation) {
3409
- const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3735
+ const rawLocation = globalEnv.rawWindow.location;
3736
+ let targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3410
3737
  let isAttach2Hash = false;
3411
- if (!isRouterModeCustom(appName)) {
3412
- let { pathname, search, hash } = globalEnv.rawWindow.location;
3738
+ if (isRouterModeSearch(appName)) {
3739
+ let { pathname, search, hash } = rawLocation;
3413
3740
  const queryObject = getQueryObjectFromURL(search, hash);
3414
3741
  const encodedMicroPath = encodeMicroPath(targetFullPath);
3415
3742
  /**
@@ -3419,6 +3746,7 @@ function setMicroPathToURL(appName, targetLocation) {
3419
3746
  // If hash exists and search does not exist, it is considered as a hash route
3420
3747
  if (hash && !search) {
3421
3748
  isAttach2Hash = true;
3749
+ // TODO: 这里和下面的if判断可以简化一下
3422
3750
  if (queryObject.hashQuery) {
3423
3751
  queryObject.hashQuery[formatQueryAppName(appName)] = encodedMicroPath;
3424
3752
  }
@@ -3446,6 +3774,9 @@ function setMicroPathToURL(appName, targetLocation) {
3446
3774
  isAttach2Hash,
3447
3775
  };
3448
3776
  }
3777
+ if (isRouterModeState(appName) || isRouterModePure(appName)) {
3778
+ targetFullPath = rawLocation.pathname + rawLocation.search + rawLocation.hash;
3779
+ }
3449
3780
  return {
3450
3781
  fullPath: targetFullPath,
3451
3782
  isAttach2Hash,
@@ -3454,13 +3785,12 @@ function setMicroPathToURL(appName, targetLocation) {
3454
3785
  /**
3455
3786
  * Delete child app fullPath from browser url
3456
3787
  * @param appName app.name
3457
- * @param targetLocation target Location, default is rawLocation
3458
3788
  */
3459
- function removeMicroPathFromURL(appName, targetLocation) {
3789
+ function removeMicroPathFromURL(appName) {
3460
3790
  var _a, _b, _c, _d;
3461
- let { pathname, search, hash } = targetLocation || globalEnv.rawWindow.location;
3791
+ let { pathname, search, hash } = globalEnv.rawWindow.location;
3462
3792
  let isAttach2Hash = false;
3463
- if (!isRouterModeCustom(appName)) {
3793
+ if (isRouterModeSearch(appName)) {
3464
3794
  const queryObject = getQueryObjectFromURL(search, hash);
3465
3795
  if ((_a = queryObject.hashQuery) === null || _a === void 0 ? void 0 : _a[formatQueryAppName(appName)]) {
3466
3796
  isAttach2Hash = true;
@@ -3521,14 +3851,38 @@ function isEffectiveApp(appName) {
3521
3851
  return !!(app && !app.isPrefetch);
3522
3852
  }
3523
3853
  /**
3524
- * router mode is custom
3525
- * NOTE:
3526
- * 1. if sandbox disabled, router mode defaults to custom
3527
- * 2. if app not exist, router mode defaults to custom
3854
+ * get router mode of app
3855
+ * NOTE: app maybe undefined
3528
3856
  */
3529
- function isRouterModeCustom(appName) {
3530
- const app = appInstanceMap.get(appName);
3531
- return !app || !app.sandBox || app.routerMode === ROUTER_MODE_CUSTOM;
3857
+ function getRouterMode(appName) {
3858
+ var _a;
3859
+ return (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.routerMode;
3860
+ }
3861
+ // router mode is search
3862
+ function isRouterModeSearch(appName) {
3863
+ return getRouterMode(appName) === DEFAULT_ROUTER_MODE;
3864
+ }
3865
+ // router mode is state
3866
+ function isRouterModeState(appName) {
3867
+ return getRouterMode(appName) === ROUTER_MODE_STATE;
3868
+ }
3869
+ // router mode is history
3870
+ function isRouterModeNative(appName) {
3871
+ return getRouterMode(appName) === ROUTER_MODE_NATIVE;
3872
+ }
3873
+ // router mode is disable
3874
+ function isRouterModeNativeScope(appName) {
3875
+ return getRouterMode(appName) === ROUTER_MODE_NATIVE_SCOPE;
3876
+ }
3877
+ // router mode is pure
3878
+ function isRouterModePure(appName) {
3879
+ return getRouterMode(appName) === ROUTER_MODE_PURE;
3880
+ }
3881
+ /**
3882
+ * router mode is history or disable
3883
+ */
3884
+ function isRouterModeCustom(appName) {
3885
+ return isRouterModeNative(appName) || isRouterModeNativeScope(appName);
3532
3886
  }
3533
3887
  /**
3534
3888
  * get memory router mode of child app
@@ -3536,21 +3890,21 @@ function isRouterModeCustom(appName) {
3536
3890
  * 1. if microAppElement exists, it means the app render by the micro-app element
3537
3891
  * 2. if microAppElement not exists, it means it is prerender app
3538
3892
  * @param mode native config
3539
- * @param microAppElement micro-app element
3540
- * @returns mode
3893
+ * @param inlineDisableMemoryRouter disable-memory-router set by micro-app element or prerender
3894
+ * @returns router mode
3541
3895
  */
3542
- function getRouterMode(mode, microAppElement) {
3543
- let routerMode;
3896
+ function initRouterMode(mode, inlineDisableMemoryRouter) {
3544
3897
  /**
3545
3898
  * compatible with disable-memory-router in older versions
3546
- * if disable-memory-router is true, router-mode will be custom
3899
+ * if disable-memory-router is true, router-mode will be disable
3900
+ * Priority:
3901
+ * inline disable-memory-router > inline router-mode > global disable-memory-router > global router-mode
3547
3902
  */
3548
- if (microAppElement) {
3549
- routerMode = microAppElement.getDisposeResult('disable-memory-router') ? ROUTER_MODE_CUSTOM : mode || microApp.options['router-mode'] || '';
3550
- }
3551
- else {
3552
- routerMode = microApp.options['disable-memory-router'] ? ROUTER_MODE_CUSTOM : mode || microApp.options['router-mode'] || '';
3553
- }
3903
+ const routerMode = ((inlineDisableMemoryRouter && ROUTER_MODE_NATIVE) ||
3904
+ mode ||
3905
+ (microApp.options['disable-memory-router'] && ROUTER_MODE_NATIVE) ||
3906
+ microApp.options['router-mode'] ||
3907
+ DEFAULT_ROUTER_MODE);
3554
3908
  return ROUTER_MODE_LIST.includes(routerMode) ? routerMode : DEFAULT_ROUTER_MODE;
3555
3909
  }
3556
3910
 
@@ -3565,6 +3919,7 @@ function addHistoryListener(appName) {
3565
3919
  const rawWindow = globalEnv.rawWindow;
3566
3920
  // handle popstate event and distribute to child app
3567
3921
  const popStateHandler = (e) => {
3922
+ var _a, _b, _c;
3568
3923
  /**
3569
3924
  * 1. unmount app & hidden keep-alive app will not receive popstate event
3570
3925
  * 2. filter out onlyForBrowser
@@ -3574,7 +3929,28 @@ function addHistoryListener(appName) {
3574
3929
  excludePreRender: true,
3575
3930
  }).includes(appName) &&
3576
3931
  !e.onlyForBrowser) {
3577
- updateMicroLocationWithEvent(appName, getMicroPathFromURL(appName));
3932
+ /**
3933
+ * base app may respond to popstateEvent async(lazy load page & browser back/forward), but child app will respond to popstateEvent immediately(vue2, react), this will cause some problems
3934
+ * 2 solutions:
3935
+ * 1. child app respond to popstateEvent async -- router-event-delay
3936
+ * 2. child app will not respond to popstateEvent in some scenarios (history.state===null || history.state?__MICRO_APP_STATE__[appName])
3937
+ * NOTE 1:
3938
+ * 1. browser back/forward
3939
+ * 2. location.hash/search/pathname = xxx
3940
+ * 3. <a href="/#/xxx">, <a href="/xxx">
3941
+ * 4. history.back/go/forward
3942
+ * 5. history.pushState/replaceState
3943
+ *
3944
+ * NOTE2:
3945
+ * 1、react16 hash mode navigate by location.hash = xxx, history.state is always null, but react16 respond to popstateEvent sync
3946
+ * 2、multiple child apps may has problems
3947
+ */
3948
+ if (!isRouterModeCustom(appName) ||
3949
+ !globalEnv.rawWindow.history.state ||
3950
+ getMicroRouterInfoState(appName)) {
3951
+ const container = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container;
3952
+ macro(() => updateMicroLocationWithEvent(appName, getMicroPathFromURL(appName)), (_c = (_b = (container && getRootContainer(container))) === null || _b === void 0 ? void 0 : _b.getRouterEventDelay()) !== null && _c !== void 0 ? _c : 0);
3953
+ }
3578
3954
  }
3579
3955
  };
3580
3956
  rawWindow.addEventListener('popstate', popStateHandler);
@@ -3592,24 +3968,26 @@ function addHistoryListener(appName) {
3592
3968
  */
3593
3969
  function updateMicroLocationWithEvent(appName, targetFullPath) {
3594
3970
  const app = appInstanceMap.get(appName);
3595
- const proxyWindow = app.sandBox.proxyWindow;
3596
- const microAppWindow = app.sandBox.microAppWindow;
3597
- let isHashChange = false;
3598
- // for hashChangeEvent
3599
- const oldHref = proxyWindow.location.href;
3600
- // Do not attach micro state to url when targetFullPath is empty
3601
- if (targetFullPath) {
3602
- const oldHash = proxyWindow.location.hash;
3603
- updateMicroLocation(appName, targetFullPath, microAppWindow.location);
3604
- isHashChange = proxyWindow.location.hash !== oldHash;
3605
- }
3606
- // dispatch formatted popStateEvent to child
3607
- dispatchPopStateEventToMicroApp(appName, proxyWindow, microAppWindow);
3608
- // dispatch formatted hashChangeEvent to child when hash change
3609
- if (isHashChange)
3610
- dispatchHashChangeEventToMicroApp(appName, proxyWindow, microAppWindow, oldHref);
3611
- // clear element scope before trigger event of next app
3612
- removeDomScope();
3971
+ if (app === null || app === void 0 ? void 0 : app.sandBox) {
3972
+ const proxyWindow = app.sandBox.proxyWindow;
3973
+ const microAppWindow = app.sandBox.microAppWindow;
3974
+ let isHashChange = false;
3975
+ // for hashChangeEvent
3976
+ const oldHref = proxyWindow.location.href;
3977
+ // Do not attach micro state to url when targetFullPath is empty
3978
+ if (targetFullPath) {
3979
+ const oldHash = proxyWindow.location.hash;
3980
+ updateMicroLocation(appName, targetFullPath, microAppWindow.location);
3981
+ isHashChange = proxyWindow.location.hash !== oldHash;
3982
+ }
3983
+ // dispatch formatted popStateEvent to child
3984
+ dispatchPopStateEventToMicroApp(appName, proxyWindow, microAppWindow);
3985
+ // dispatch formatted hashChangeEvent to child when hash change
3986
+ if (isHashChange)
3987
+ dispatchHashChangeEventToMicroApp(appName, proxyWindow, microAppWindow, oldHref);
3988
+ // clear element scope before trigger event of next app
3989
+ removeDomScope();
3990
+ }
3613
3991
  }
3614
3992
  /**
3615
3993
  * dispatch formatted popstate event to microApp
@@ -3622,14 +4000,18 @@ function dispatchPopStateEventToMicroApp(appName, proxyWindow, microAppWindow) {
3622
4000
  * TODO: test
3623
4001
  * angular14 takes e.type as type judgment
3624
4002
  * when e.type is popstate-appName popstate event will be invalid
4003
+ * Object.defineProperty(newPopStateEvent, 'type', {
4004
+ * value: 'popstate',
4005
+ * writable: true,
4006
+ * configurable: true,
4007
+ * enumerable: true,
4008
+ * })
4009
+ */
4010
+ /**
4011
+ * create PopStateEvent named popstate-appName with sub app state
4012
+ * TODO: feeling like there's something wrong, check carefully
4013
+ * In native mode, getMicroState(appName) return rawWindow.history.state when use microApp.router.push/replace or other scenes when state.__MICRO_APP_STATE__[appName] is null
3625
4014
  */
3626
- // Object.defineProperty(newPopStateEvent, 'type', {
3627
- // value: 'popstate',
3628
- // writable: true,
3629
- // configurable: true,
3630
- // enumerable: true,
3631
- // })
3632
- // create PopStateEvent named popstate-appName with sub app state
3633
4015
  const newPopStateEvent = new PopStateEvent('popstate', { state: getMicroState(appName) });
3634
4016
  microAppWindow.dispatchEvent(newPopStateEvent);
3635
4017
  if (!isIframeSandbox(appName)) {
@@ -3696,7 +4078,7 @@ function dispatchNativeEvent(appName, onlyForBrowser, oldHref) {
3696
4078
  * create proxyHistory for microApp
3697
4079
  * MDN https://developer.mozilla.org/en-US/docs/Web/API/History
3698
4080
  * @param appName app name
3699
- * @param microLocation microApp location
4081
+ * @param microLocation microApp location(with: proxyLocation iframe: iframeWindow.location)
3700
4082
  */
3701
4083
  function createMicroHistory(appName, microLocation) {
3702
4084
  const rawHistory = globalEnv.rawWindow.history;
@@ -3704,39 +4086,46 @@ function createMicroHistory(appName, microLocation) {
3704
4086
  return function (...rests) {
3705
4087
  var _a, _b, _c;
3706
4088
  // TODO: 测试iframe的URL兼容isURL的情况
3707
- if (isString(rests[2]) || isURL(rests[2])) {
3708
- const targetLocation = createURL(rests[2], microLocation.href);
3709
- const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3710
- navigateWithNativeEvent(appName, methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rests[0]), rests[1]);
3711
- if (targetFullPath !== microLocation.fullPath) {
3712
- updateMicroLocation(appName, targetFullPath, microLocation);
3713
- }
3714
- (_c = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : (_b = _a.sandBox).updateIframeBase) === null || _c === void 0 ? void 0 : _c.call(_b);
4089
+ rests[2] = isUndefined(rests[2]) || isNull(rests[2]) || ('' + rests[2] === '') ? microLocation.href : '' + rests[2];
4090
+ const targetLocation = createURL(rests[2], microLocation.href);
4091
+ const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
4092
+ if (!isRouterModePure(appName)) {
4093
+ navigateWithNativeEvent(appName, methodName, setMicroPathToURL(appName, targetLocation), true, setMicroState(appName, rests[0], targetLocation), rests[1]);
3715
4094
  }
3716
- else {
3717
- nativeHistoryNavigate(appName, methodName, rests[2], rests[0], rests[1]);
4095
+ if (targetFullPath !== microLocation.fullPath) {
4096
+ updateMicroLocation(appName, targetFullPath, microLocation);
3718
4097
  }
4098
+ (_c = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : (_b = _a.sandBox).updateIframeBase) === null || _c === void 0 ? void 0 : _c.call(_b);
3719
4099
  };
3720
4100
  }
3721
- const pushState = getMicroHistoryMethod('pushState');
3722
- const replaceState = getMicroHistoryMethod('replaceState');
3723
- if (isIframeSandbox(appName))
3724
- return { pushState, replaceState };
4101
+ const originalHistory = {
4102
+ pushState: getMicroHistoryMethod('pushState'),
4103
+ replaceState: getMicroHistoryMethod('replaceState'),
4104
+ };
4105
+ if (isIframeSandbox(appName)) {
4106
+ return assign({
4107
+ go(delta) {
4108
+ return rawHistory.go(delta);
4109
+ }
4110
+ }, originalHistory);
4111
+ }
3725
4112
  return new Proxy(rawHistory, {
3726
4113
  get(target, key) {
3727
- if (key === 'state') {
3728
- return getMicroState(appName);
3729
- }
3730
- else if (key === 'pushState') {
3731
- return pushState;
4114
+ if (key === 'pushState' || key === 'replaceState') {
4115
+ return originalHistory[key];
3732
4116
  }
3733
- else if (key === 'replaceState') {
3734
- return replaceState;
4117
+ else if (key === 'state') {
4118
+ return getMicroState(appName);
3735
4119
  }
3736
4120
  return bindFunctionToRawTarget(Reflect.get(target, key), target, 'HISTORY');
3737
4121
  },
3738
4122
  set(target, key, value) {
3739
- Reflect.set(target, key, value);
4123
+ if (key === 'pushState' || key === 'replaceState') {
4124
+ originalHistory[key] = value;
4125
+ }
4126
+ else {
4127
+ Reflect.set(target, key, value);
4128
+ }
3740
4129
  /**
3741
4130
  * If the set() method returns false, and the assignment happened in strict-mode code, a TypeError will be thrown.
3742
4131
  * e.g. history.state = {}
@@ -3784,8 +4173,8 @@ function navigateWithNativeEvent(appName, methodName, result, onlyForBrowser, st
3784
4173
  const oldHref = result.isAttach2Hash && oldFullPath !== result.fullPath ? rawLocation.href : null;
3785
4174
  // navigate with native history method
3786
4175
  nativeHistoryNavigate(appName, methodName, result.fullPath, state, title);
3787
- // TODO: 如果所有模式统一发送popstate事件,则!isRouterModeCustom(appName)要去掉
3788
- if (oldFullPath !== result.fullPath && !isRouterModeCustom(appName)) {
4176
+ // just search mode will dispatch native event
4177
+ if (oldFullPath !== result.fullPath && isRouterModeSearch(appName)) {
3789
4178
  dispatchNativeEvent(appName, onlyForBrowser, oldHref);
3790
4179
  }
3791
4180
  }
@@ -3801,22 +4190,22 @@ function attachRouteToBrowserURL(appName, result, state) {
3801
4190
  navigateWithNativeEvent(appName, 'replaceState', result, true, state);
3802
4191
  }
3803
4192
  /**
3804
- * When path is same, keep the microAppState in history.state
3805
- * Fix bug of missing microAppState when base app is next.js or angular
4193
+ * When path is same, keep the __MICRO_APP_STATE__ in history.state
4194
+ * Fix bug of missing __MICRO_APP_STATE__ when base app is next.js or angular
3806
4195
  * @param method history.pushState/replaceState
3807
4196
  */
3808
4197
  function reWriteHistoryMethod(method) {
3809
4198
  const rawWindow = globalEnv.rawWindow;
3810
4199
  return function (...rests) {
3811
4200
  var _a;
3812
- if (((_a = rawWindow.history.state) === null || _a === void 0 ? void 0 : _a.microAppState) &&
3813
- (!isPlainObject(rests[0]) || !rests[0].microAppState) &&
4201
+ if (((_a = rawWindow.history.state) === null || _a === void 0 ? void 0 : _a.__MICRO_APP_STATE__) &&
4202
+ (!isPlainObject(rests[0]) || !rests[0].__MICRO_APP_STATE__) &&
3814
4203
  (isString(rests[2]) || isURL(rests[2]))) {
3815
4204
  const currentHref = rawWindow.location.href;
3816
4205
  const targetLocation = createURL(rests[2], currentHref);
3817
4206
  if (targetLocation.href === currentHref) {
3818
4207
  rests[0] = assign({}, rests[0], {
3819
- microAppState: rawWindow.history.state.microAppState,
4208
+ __MICRO_APP_STATE__: rawWindow.history.state.__MICRO_APP_STATE__,
3820
4209
  });
3821
4210
  }
3822
4211
  }
@@ -3832,10 +4221,23 @@ function reWriteHistoryMethod(method) {
3832
4221
  excludeHiddenApp: true,
3833
4222
  excludePreRender: true,
3834
4223
  }).forEach(appName => {
3835
- if (!isRouterModeCustom(appName) && !getMicroPathFromURL(appName)) {
4224
+ if ((isRouterModeSearch(appName) || isRouterModeState(appName)) && !getMicroPathFromURL(appName)) {
3836
4225
  const app = appInstanceMap.get(appName);
3837
- attachRouteToBrowserURL(appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName)));
4226
+ attachRouteToBrowserURL(appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName), app.sandBox.proxyWindow.location));
4227
+ }
4228
+ if (isRouterModeCustom(appName) && !getMicroRouterInfoState(appName)) {
4229
+ nativeHistoryNavigate(appName, 'replaceState', rawWindow.location.href, setMicroState(appName));
3838
4230
  }
4231
+ // if (isRouterModeCustom(appName) || isRouterModeSearch(appName)) {
4232
+ /**
4233
+ * history.pushState/replaceState后主动触发子应用响应
4234
+ * 问题:子应用的卸载可能是异步的,而跳转的地址不一定在基础路径中,太快响应pushState可能会导致url地址被子应用改变或者子应用404,Promise太快卸载时出问题、setTimeout太慢keep-alive二次渲染后出问题
4235
+ * 1、history.pushState/replaceState执行后,子应用以异步的形式被主应用卸载,Promise响应时子应用还在,导致子应用跳转404后者浏览器url被子应用修改,产生异常
4236
+ * 2、keep-alive应用二次渲染时,由于setTimeout响应过慢,子应用在渲染后才接受到popstate事件,响应新的url,从而导致状态丢失
4237
+ * 3、同一个页面多个子应用,修改地址响应
4238
+ * 4、vue3跳转前会执行一次replace,有没有影响?
4239
+ */
4240
+ // }
3839
4241
  });
3840
4242
  // fix bug for nest app
3841
4243
  removeDomScope();
@@ -3843,7 +4245,7 @@ function reWriteHistoryMethod(method) {
3843
4245
  }
3844
4246
  /**
3845
4247
  * rewrite history.pushState/replaceState
3846
- * used to fix the problem that the microAppState maybe missing when mainApp navigate to same path
4248
+ * used to fix the problem that the __MICRO_APP_STATE__ maybe missing when mainApp navigate to same path
3847
4249
  * e.g: when nextjs, angular receive popstate event, they will use history.replaceState to update browser url with a new state object
3848
4250
  */
3849
4251
  function patchHistory() {
@@ -3866,7 +4268,7 @@ function createRouterApi() {
3866
4268
  * @param state to.state
3867
4269
  */
3868
4270
  function navigateWithRawHistory(appName, methodName, targetLocation, state) {
3869
- navigateWithNativeEvent(appName, methodName, setMicroPathToURL(appName, targetLocation), false, setMicroState(appName, state !== null && state !== void 0 ? state : null));
4271
+ navigateWithNativeEvent(appName, methodName, setMicroPathToURL(appName, targetLocation), false, setMicroState(appName, state !== null && state !== void 0 ? state : null, targetLocation));
3870
4272
  // clear element scope after navigate
3871
4273
  removeDomScope();
3872
4274
  }
@@ -3884,21 +4286,13 @@ function createRouterApi() {
3884
4286
  const currentFullPath = microLocation.pathname + microLocation.search + microLocation.hash;
3885
4287
  const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash;
3886
4288
  if (currentFullPath !== targetFullPath || getMicroPathFromURL(appName) !== targetFullPath) {
3887
- const methodName = (replace && to.replace !== false) || to.replace === true ? 'replaceState' : 'pushState';
3888
- navigateWithRawHistory(appName, methodName, targetLocation, to.state);
3889
- /**
3890
- * TODO:
3891
- * 1. 关闭虚拟路由的跳转地址不同:baseRoute + 子应用地址,文档中要说明
3892
- * 2. 关闭虚拟路由时跳转方式不同:1、基座跳转但不发送popstate事件 2、控制子应用更新location,内部发送popstate事件。
3893
- * 核心思路:减小对基座的影响(子应用跳转不向基座发送popstate事件,其他操作一致),但这是必要的吗,只是多了一个触发popstate的操作
3894
- * 路由优化方案有两种:
3895
- * 1、减少对基座的影响,主要是解决vue循环刷新的问题
3896
- * 2、全局发送popstate事件,解决主、子都是vue3的冲突问题
3897
- * 两者选一个吧,如果选2,则下面这两行代码可以去掉
3898
- * NOTE1: history和search模式采用2,这样可以解决vue3的问题,custom采用1,避免vue循环刷新的问题,这样在用户出现问题时各有解决方案。但反过来说,每种方案又分别导致另外的问题,不统一,导致复杂度增高
3899
- * NOTE2: 关闭虚拟路由,同时发送popstate事件还是无法解决vue3的问题(毕竟history.state理论上还是会冲突),那么就没必要发送popstate事件了。
3900
- */
3901
- if (isRouterModeCustom(appName)) {
4289
+ // pure mode will not call history.pushState/replaceState
4290
+ if (!isRouterModePure(appName)) {
4291
+ const methodName = (replace && to.replace !== false) || to.replace === true ? 'replaceState' : 'pushState';
4292
+ navigateWithRawHistory(appName, methodName, targetLocation, to.state);
4293
+ }
4294
+ // only search mode will dispatch PopStateEvent to browser
4295
+ if (!isRouterModeSearch(appName)) {
3902
4296
  updateMicroLocationWithEvent(appName, targetFullPath);
3903
4297
  }
3904
4298
  }
@@ -3913,52 +4307,44 @@ function createRouterApi() {
3913
4307
  */
3914
4308
  function createNavigationMethod(replace) {
3915
4309
  return function (to) {
3916
- const appName = formatAppName(to.name);
3917
- if (appName && isString(to.path)) {
3918
- /**
3919
- * active apps, exclude prerender app or hidden keep-alive app
3920
- * NOTE:
3921
- * 1. prerender app or hidden keep-alive app clear and record popstate event, so we cannot control app jump through the API
3922
- * 2. disable memory-router
3923
- */
3924
- /**
3925
- * TODO: 子应用开始渲染但是还没渲染完成
3926
- * 1、调用跳转改如何处理
3927
- * 2、iframe的沙箱还没初始化时执行跳转报错,如何处理。。。
3928
- * 3hidden app 是否支持跳转
3929
- */
3930
- if (getActiveApps({ excludeHiddenApp: true, excludePreRender: true }).includes(appName)) {
3931
- const app = appInstanceMap.get(appName);
3932
- const navigateAction = () => handleNavigate(appName, app, to, replace);
3933
- app.iframe ? app.sandBox.sandboxReady.then(navigateAction) : navigateAction();
4310
+ return new Promise((resolve, reject) => {
4311
+ const appName = formatAppName(to.name);
4312
+ if (appName && isString(to.path)) {
4313
+ /**
4314
+ * active apps, exclude prerender app or hidden keep-alive app
4315
+ * NOTE:
4316
+ * 1. prerender app or hidden keep-alive app clear and record popstate event, so we cannot control app jump through the API
4317
+ * 2. disable memory-router
4318
+ */
4319
+ /**
4320
+ * TODO:
4321
+ * 1、子应用开始渲染但是还没渲染完成,调用跳转改如何处理
4322
+ * 2iframe的沙箱还没初始化时执行跳转报错,如何处理。。。
4323
+ * 3、hidden app、预渲染 app 是否支持跳转 --- 支持(这里还涉及子应用内部跳转的支持)
4324
+ */
4325
+ if (getActiveApps({ excludeHiddenApp: true, excludePreRender: true }).includes(appName)) {
4326
+ const app = appInstanceMap.get(appName);
4327
+ resolve(app.sandBox.sandboxReady.then(() => handleNavigate(appName, app, to, replace)));
4328
+ }
4329
+ else {
4330
+ reject(logError('导航失败,请确保子应用渲染后再调用此方法'));
4331
+ }
4332
+ // const rawLocation = globalEnv.rawWindow.location
4333
+ // const targetLocation = createURL(to.path, rawLocation.origin)
4334
+ // const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash
4335
+ // if (getMicroPathFromURL(appName) !== targetFullPath) {
4336
+ // navigateWithRawHistory(
4337
+ // appName,
4338
+ // to.replace === false ? 'pushState' : 'replaceState',
4339
+ // targetLocation,
4340
+ // to.state,
4341
+ // )
4342
+ // }
3934
4343
  }
3935
4344
  else {
3936
- logWarn('navigation failed, app does not exist or is inactive');
4345
+ reject(logError(`navigation failed, name & path are required when use router.${replace ? 'replace' : 'push'}`));
3937
4346
  }
3938
- // /**
3939
- // * app not exit or unmounted, update browser URL with replaceState
3940
- // * use base app location.origin as baseURL
3941
- // * 应用不存在或已卸载,依然使用replaceState来更新浏览器地址 -- 不合理
3942
- // */
3943
- // /**
3944
- // * TODO: 应用还没渲染或已经卸载最好不要支持跳转了,我知道这是因为解决一些特殊场景,但这么做是非常反直觉的
3945
- // * 并且在新版本中有多种路由模式,如果应用不存在,我们根本无法知道是哪种模式,那么这里的操作就无意义了。
3946
- // */
3947
- // const rawLocation = globalEnv.rawWindow.location
3948
- // const targetLocation = createURL(to.path, rawLocation.origin)
3949
- // const targetFullPath = targetLocation.pathname + targetLocation.search + targetLocation.hash
3950
- // if (getMicroPathFromURL(appName) !== targetFullPath) {
3951
- // navigateWithRawHistory(
3952
- // appName,
3953
- // to.replace === false ? 'pushState' : 'replaceState',
3954
- // targetLocation,
3955
- // to.state,
3956
- // )
3957
- // }
3958
- }
3959
- else {
3960
- logError(`navigation failed, name & path are required when use router.${replace ? 'replace' : 'push'}`);
3961
- }
4347
+ });
3962
4348
  };
3963
4349
  }
3964
4350
  // create method of router.go/back/forward
@@ -4015,9 +4401,9 @@ function createRouterApi() {
4015
4401
  * 3. router mode is custom
4016
4402
  */
4017
4403
  function commonHandlerForAttachToURL(appName) {
4018
- if (!isRouterModeCustom(appName)) {
4404
+ if (isRouterModeSearch(appName) || isRouterModeState(appName)) {
4019
4405
  const app = appInstanceMap.get(appName);
4020
- attachRouteToBrowserURL(appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName)));
4406
+ attachRouteToBrowserURL(appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName), app.sandBox.proxyWindow.location));
4021
4407
  }
4022
4408
  }
4023
4409
  /**
@@ -4116,94 +4502,6 @@ function createRouterApi() {
4116
4502
  }
4117
4503
  const { router, executeNavigationGuard, clearRouterWhenUnmount, } = createRouterApi();
4118
4504
 
4119
- const escape2RawWindowKeys = [
4120
- 'getComputedStyle',
4121
- 'visualViewport',
4122
- 'matchMedia',
4123
- // 'DOMParser',
4124
- 'ResizeObserver',
4125
- 'IntersectionObserver',
4126
- ];
4127
- const escape2RawWindowRegExpKeys = [
4128
- /animationFrame$/i,
4129
- /mutationObserver$/i,
4130
- /height$|width$/i,
4131
- /offset$/i,
4132
- // /event$/i,
4133
- /selection$/i,
4134
- /^range/i,
4135
- /^screen/i,
4136
- /^scroll/i,
4137
- /X$|Y$/,
4138
- ];
4139
- const uniqueDocumentElement = [
4140
- 'body',
4141
- 'head',
4142
- 'html',
4143
- 'title',
4144
- ];
4145
- const hijackMicroLocationKeys = [
4146
- 'host',
4147
- 'hostname',
4148
- 'port',
4149
- 'protocol',
4150
- 'origin',
4151
- ];
4152
- // 有shadowRoot则代理到shadowRoot否则代理到原生document上 (属性)
4153
- const proxy2RawDocOrShadowKeys = [
4154
- 'childElementCount',
4155
- 'children',
4156
- 'firstElementChild',
4157
- 'firstChild',
4158
- 'lastElementChild',
4159
- 'activeElement',
4160
- 'fullscreenElement',
4161
- 'pictureInPictureElement',
4162
- 'pointerLockElement',
4163
- 'styleSheets',
4164
- ];
4165
- // 有shadowRoot则代理到shadowRoot否则代理到原生document上 (方法)
4166
- const proxy2RawDocOrShadowMethods = [
4167
- 'append',
4168
- 'contains',
4169
- 'replaceChildren',
4170
- 'createRange',
4171
- 'getSelection',
4172
- 'elementFromPoint',
4173
- 'elementsFromPoint',
4174
- 'getAnimations',
4175
- ];
4176
- // 直接代理到原生document上 (属性)
4177
- const proxy2RawDocumentKeys = [
4178
- 'characterSet',
4179
- 'compatMode',
4180
- 'contentType',
4181
- 'designMode',
4182
- 'dir',
4183
- 'doctype',
4184
- 'embeds',
4185
- 'fullscreenEnabled',
4186
- 'hidden',
4187
- 'implementation',
4188
- 'lastModified',
4189
- 'pictureInPictureEnabled',
4190
- 'plugins',
4191
- 'readyState',
4192
- 'referrer',
4193
- 'visibilityState',
4194
- 'fonts',
4195
- ];
4196
- // 直接代理到原生document上 (方法)
4197
- const proxy2RawDocumentMethods = [
4198
- 'execCommand',
4199
- 'createRange',
4200
- 'exitFullscreen',
4201
- 'exitPictureInPicture',
4202
- 'getElementsByTagNameNS',
4203
- 'hasFocus',
4204
- 'prepend',
4205
- ];
4206
-
4207
4505
  // origin is readonly, so we ignore when updateMicroLocation
4208
4506
  const locationKeys = ['href', 'pathname', 'search', 'hash', 'host', 'hostname', 'port', 'protocol', 'search'];
4209
4507
  // origin, fullPath is necessary for guardLocation
@@ -4248,38 +4546,46 @@ function createMicroLocation(appName, url, microAppWindow, childStaticLocation,
4248
4546
  const setMicroPathResult = setMicroPathToURL(appName, targetLocation);
4249
4547
  // if disable memory-router, navigate directly through rawLocation
4250
4548
  if (!isRouterModeCustom(appName)) {
4549
+ methodName = isRouterModePure(appName) ? 'replaceState' : methodName;
4251
4550
  /**
4252
4551
  * change hash with location.href will not trigger the browser reload
4253
4552
  * so we use pushState & reload to imitate href behavior
4254
4553
  * NOTE:
4255
- * 1. if child app only change hash, it should not trigger browser reload
4256
- * 2. if address is same and has hash, it should not add route stack
4554
+ * 1. if child app only change hash, it will not reload browser
4555
+ * 2. if address is same and has hash, it will not add route stack
4257
4556
  */
4258
4557
  if (targetLocation.pathname === proxyLocation.pathname &&
4259
4558
  targetLocation.search === proxyLocation.search) {
4260
4559
  let oldHref = null;
4261
- if (targetLocation.hash !== proxyLocation.hash) {
4262
- if (setMicroPathResult.isAttach2Hash)
4560
+ // NOTE: if pathname & search is same, it should record router info to history.state in pure mode
4561
+ if (targetLocation.hash !== proxyLocation.hash || isRouterModePure(appName)) {
4562
+ // search mode only
4563
+ if (setMicroPathResult.isAttach2Hash) {
4263
4564
  oldHref = rawLocation.href;
4264
- nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath);
4565
+ }
4566
+ // if router mode is pure and targetLocation.hash exist, it will not call nativeHistoryNavigate
4567
+ if (!isRouterModePure(appName) || !targetLocation.hash) {
4568
+ nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath, !isRouterModeSearch(appName) ? setMicroState(appName, null, targetLocation) : null);
4569
+ }
4265
4570
  }
4266
4571
  if (targetLocation.hash) {
4267
- dispatchNativeEvent(appName, false, oldHref);
4572
+ if (isRouterModeSearch(appName)) {
4573
+ dispatchNativeEvent(appName, false, oldHref);
4574
+ }
4575
+ else {
4576
+ updateMicroLocationWithEvent(appName, targetLocation.pathname + targetLocation.search + targetLocation.hash);
4577
+ }
4268
4578
  }
4269
4579
  else {
4270
4580
  reload();
4271
4581
  }
4272
4582
  return void 0;
4273
- /**
4274
- * when baseApp is hash router, address change of child can not reload browser
4275
- * so we imitate behavior of browser (reload) manually
4276
- */
4277
- }
4278
- else if (setMicroPathResult.isAttach2Hash) {
4279
- nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath);
4280
- reload();
4281
- return void 0;
4282
4583
  }
4584
+ // when pathname or search change, simulate behavior of browser (reload) manually
4585
+ // TODO: state模式下pushState会带上上一个页面的state,会不会有问题,尤其是vue3,应不应该将主应用的state设置为null
4586
+ nativeHistoryNavigate(appName, methodName, setMicroPathResult.fullPath, !isRouterModeSearch(appName) ? setMicroState(appName, null, targetLocation) : null);
4587
+ reload();
4588
+ return void 0;
4283
4589
  }
4284
4590
  return setMicroPathResult.fullPath;
4285
4591
  }
@@ -4304,7 +4610,9 @@ function createMicroLocation(appName, url, microAppWindow, childStaticLocation,
4304
4610
  * pathname: /path ==> /path#hash, /path ==> /path?query
4305
4611
  * search: ?query ==> ?query#hash
4306
4612
  */
4307
- nativeHistoryNavigate(appName, targetLocation[key] === proxyLocation[key] ? 'replaceState' : 'pushState', setMicroPathToURL(appName, targetLocation).fullPath);
4613
+ nativeHistoryNavigate(appName, (targetLocation[key] === proxyLocation[key] || isRouterModePure(appName))
4614
+ ? 'replaceState'
4615
+ : 'pushState', setMicroPathToURL(appName, targetLocation).fullPath, !isRouterModeSearch(appName) ? setMicroState(appName, null, targetLocation) : null);
4308
4616
  reload();
4309
4617
  }
4310
4618
  }
@@ -4333,16 +4641,6 @@ function createMicroLocation(appName, url, microAppWindow, childStaticLocation,
4333
4641
  const proxyLocation = new Proxy({}, {
4334
4642
  get: (_, key) => {
4335
4643
  const target = getTarget();
4336
- if (isIframe) {
4337
- // host hostname port protocol
4338
- if (hijackMicroLocationKeys.includes(key)) {
4339
- return childStaticLocation[key];
4340
- }
4341
- if (key === 'href') {
4342
- // do not use target, because target may be deleted
4343
- return target[key].replace(browserHost, childHost);
4344
- }
4345
- }
4346
4644
  if (key === 'assign')
4347
4645
  return assign;
4348
4646
  if (key === 'replace')
@@ -4351,21 +4649,47 @@ function createMicroLocation(appName, url, microAppWindow, childStaticLocation,
4351
4649
  return reload;
4352
4650
  if (key === 'self')
4353
4651
  return target;
4652
+ if (key === 'fullPath')
4653
+ return target.fullPath;
4654
+ /**
4655
+ * Special keys: host, hostname, port, protocol, origin, href
4656
+ * NOTE:
4657
+ * 1. In native mode this keys point to browser, in other mode this keys point to child app origin
4658
+ * 2. In iframe sandbox, iframe.src is base app address, so origin points to the browser by default, we need to replace it with child app origin
4659
+ * 3. In other modes, origin points to child app
4660
+ */
4661
+ if (HIJACK_LOCATION_KEYS.includes(key)) {
4662
+ if (isRouterModeNative(appName)) {
4663
+ return rawLocation[key];
4664
+ }
4665
+ if (isIframe) {
4666
+ return childStaticLocation[key];
4667
+ }
4668
+ }
4669
+ if (key === 'href') {
4670
+ if (isRouterModeNative(appName)) {
4671
+ return target[key].replace(target.origin, rawLocation.origin);
4672
+ }
4673
+ if (isIframe) {
4674
+ // target may be deleted
4675
+ return target[key].replace(browserHost, childHost);
4676
+ }
4677
+ }
4354
4678
  return bindFunctionToRawTarget(Reflect.get(target, key), target, 'LOCATION');
4355
4679
  },
4356
4680
  set: (_, key, value) => {
4357
4681
  if (isEffectiveApp(appName)) {
4358
4682
  const target = getTarget();
4359
4683
  if (key === 'href') {
4360
- const targetPath = commonHandler(value, 'pushState');
4361
4684
  /**
4362
4685
  * In vite, targetPath without origin will be completed with child origin
4363
4686
  * So we use browser origin to complete targetPath to avoid this problem
4364
- * But, why child app can affect browser jump?
4365
- * Guess(need check):
4366
- * 1. vite records the origin when init
4367
- * 2. listen for browser jump and automatically complete the address
4687
+ * NOTE:
4688
+ * 1. history mode & value is childOrigin + path ==> jump to browserOrigin + path
4689
+ * 2. disable mode & value is childOrigin + path ==> jump to childOrigin + path
4690
+ * 3. search mode & value is browserOrigin + path ==> jump to browserOrigin + path
4368
4691
  */
4692
+ const targetPath = commonHandler(value, 'pushState');
4369
4693
  if (targetPath) {
4370
4694
  rawLocation.href = createURL(targetPath, rawLocation.origin).href;
4371
4695
  }
@@ -4397,7 +4721,12 @@ function createMicroLocation(appName, url, microAppWindow, childStaticLocation,
4397
4721
  const targetLocation = createURL(targetPath, url);
4398
4722
  // The same hash will not trigger popStateEvent
4399
4723
  if (targetLocation.hash !== proxyLocation.hash) {
4400
- navigateWithNativeEvent(appName, 'pushState', setMicroPathToURL(appName, targetLocation), false);
4724
+ if (!isRouterModePure(appName)) {
4725
+ navigateWithNativeEvent(appName, 'pushState', setMicroPathToURL(appName, targetLocation), false, setMicroState(appName, null, targetLocation));
4726
+ }
4727
+ if (!isRouterModeSearch(appName)) {
4728
+ updateMicroLocationWithEvent(appName, targetLocation.pathname + targetLocation.search + targetLocation.hash);
4729
+ }
4401
4730
  }
4402
4731
  }
4403
4732
  }
@@ -4439,20 +4768,36 @@ function autoTriggerNavigationGuard(appName, microLocation) {
4439
4768
  * @param microLocation micro app location
4440
4769
  * @param type auto prevent
4441
4770
  */
4442
- function updateMicroLocation(appName, path, microLocation, type) {
4771
+ function updateMicroLocation(appName, targetFullPath, microLocation, type) {
4443
4772
  var _a;
4444
4773
  // record old values of microLocation to `from`
4445
4774
  const from = createGuardLocation(appName, microLocation);
4446
4775
  // if is iframeSandbox, microLocation muse be rawLocation of iframe, not proxyLocation
4447
- const newLocation = createURL(path, microLocation.href);
4776
+ const newLocation = createURL(targetFullPath, microLocation.href);
4448
4777
  if (isIframeSandbox(appName)) {
4449
4778
  const microAppWindow = appInstanceMap.get(appName).sandBox.microAppWindow;
4450
4779
  (_a = microAppWindow.rawReplaceState) === null || _a === void 0 ? void 0 : _a.call(microAppWindow.history, getMicroState(appName), '', newLocation.href);
4451
4780
  }
4452
4781
  else {
4453
- for (const key of locationKeys) {
4454
- microLocation.self[key] = newLocation[key];
4782
+ let targetHref = newLocation.href;
4783
+ if (microLocation.self.origin !== newLocation.origin) {
4784
+ targetHref = targetHref.replace(newLocation.origin, microLocation.self.origin);
4455
4785
  }
4786
+ microLocation.self.href = targetHref;
4787
+ }
4788
+ /**
4789
+ * The native mode also base of history.state, and the modification of the browser url cannot be controlled. It is very likely that the browser url and __MICRO_APP_STATE__ are different.
4790
+ * Especially during init of child or forward and backward of browser, because vue-router@4 will actively modify the browser URL, the above situation often occurs
4791
+ * To solve this problem, after child app is initialized and responds to the popstateEvent, it is determined whether __MICRO_APP_STATE__ and the browser url are different. If they are different, the browser url will updated to the address of __MICRO_APP_STATE__
4792
+ * NOTE:
4793
+ * 1. If __MICRO_APP_STATE__ is different from the URL, then the operation of updating the URL is correct, otherwise there will be a problem of inconsistency between the URL and the rendered page
4794
+ * 2. When there are multiple child app in native mode, if one of them changes the URL address, the other one will not change __MICRO_APP_STATE__, and refresh browser will cause problems
4795
+ */
4796
+ const rawLocation = globalEnv.rawWindow.location;
4797
+ if (isRouterModeCustom(appName) &&
4798
+ (targetFullPath !== rawLocation.pathname + rawLocation.search + rawLocation.hash) &&
4799
+ type !== 'prevent') {
4800
+ nativeHistoryNavigate(appName, 'replaceState', targetFullPath, globalEnv.rawWindow.history.state);
4456
4801
  }
4457
4802
  // update latest values of microLocation to `to`
4458
4803
  const to = createGuardLocation(appName, microLocation);
@@ -4462,15 +4807,6 @@ function updateMicroLocation(appName, path, microLocation, type) {
4462
4807
  }
4463
4808
  }
4464
4809
 
4465
- /**
4466
- * TODO: 关于关闭虚拟路由系统的临时笔记 - 即custom模式,虚拟路由不支持关闭
4467
- * 1. with沙箱关闭虚拟路由最好和iframe保持一致
4468
- * 2. default-page无法使用,但是用基座的地址可以实现一样的效果
4469
- * 3. keep-router-state功能失效,因为始终为true
4470
- * 4. 基座控制子应用跳转地址改变,正确的值为:baseRoute + 子应用地址,这要在文档中说明,否则很容易出错,确实也很难理解
4471
- * 5. 是否需要发送popstate事件,为了减小对基座的影响,现在不发送
4472
- * 6. 关闭后导致的vue3路由冲突问题需要在文档中明确指出(2处:在关闭虚拟路由系统的配置那里着重说明,在vue常见问题中说明)
4473
- */
4474
4810
  /**
4475
4811
  * The router system has two operations: read and write
4476
4812
  * Read through location and write through history & location
@@ -4495,6 +4831,9 @@ function initRouteStateWithURL(appName, microLocation, defaultPage) {
4495
4831
  const microPath = getMicroPathFromURL(appName);
4496
4832
  if (microPath) {
4497
4833
  updateMicroLocation(appName, microPath, microLocation, 'auto');
4834
+ if (isRouterModePure(appName)) {
4835
+ removePathFromBrowser(appName);
4836
+ }
4498
4837
  }
4499
4838
  else {
4500
4839
  updateBrowserURLWithLocation(appName, microLocation, defaultPage);
@@ -4510,8 +4849,10 @@ function updateBrowserURLWithLocation(appName, microLocation, defaultPage) {
4510
4849
  // update microLocation with defaultPage
4511
4850
  if (defaultPage)
4512
4851
  updateMicroLocation(appName, defaultPage, microLocation, 'prevent');
4513
- // attach microApp route info to browser URL
4514
- attachRouteToBrowserURL(appName, setMicroPathToURL(appName, microLocation), setMicroState(appName, null));
4852
+ if (!isRouterModePure(appName)) {
4853
+ // attach microApp route info to browser URL
4854
+ attachRouteToBrowserURL(appName, setMicroPathToURL(appName, microLocation), setMicroState(appName, null, microLocation));
4855
+ }
4515
4856
  // trigger guards after change browser URL
4516
4857
  autoTriggerNavigationGuard(appName, microLocation);
4517
4858
  }
@@ -4523,11 +4864,12 @@ function updateBrowserURLWithLocation(appName, microLocation, defaultPage) {
4523
4864
  * @param keepRouteState keep-router-state is only used to control whether to clear the location of microApp, default is false
4524
4865
  */
4525
4866
  function clearRouteStateFromURL(appName, url, microLocation, keepRouteState) {
4526
- if (!isRouterModeCustom(appName)) {
4527
- if (!keepRouteState) {
4528
- const { pathname, search, hash } = createURL(url);
4529
- updateMicroLocation(appName, pathname + search + hash, microLocation, 'prevent');
4530
- }
4867
+ // TODO: keep-router-state 功能太弱,是否可以增加优先级,或者去掉
4868
+ if (!keepRouteState && !isRouterModeCustom(appName)) {
4869
+ const { pathname, search, hash } = createURL(url);
4870
+ updateMicroLocation(appName, pathname + search + hash, microLocation, 'prevent');
4871
+ }
4872
+ if (!isRouterModePure(appName)) {
4531
4873
  removePathFromBrowser(appName);
4532
4874
  }
4533
4875
  clearRouterWhenUnmount(appName);
@@ -4642,32 +4984,26 @@ function useMicroEventSource() {
4642
4984
  }
4643
4985
 
4644
4986
  const { createMicroEventSource, clearMicroEventSource } = useMicroEventSource();
4645
- class WithSandBox {
4987
+ class WithSandBox extends BaseSandbox {
4646
4988
  constructor(appName, url) {
4989
+ super(appName, url);
4647
4990
  this.active = false;
4648
- /**
4649
- * Scoped global Properties(Properties that can only get and set in microAppWindow, will not escape to rawWindow)
4650
- * Fix https://github.com/micro-zoe/micro-app/issues/234
4651
- */
4652
- this.scopeProperties = [];
4653
- // Properties that can be escape to rawWindow
4654
- this.escapeProperties = [];
4655
- // Properties escape to rawWindow, cleared when unmount
4656
- this.escapeKeys = new Set();
4657
- // Properties newly added to microAppWindow
4658
- this.injectedKeys = new Set();
4659
- this.microAppWindow = new EventTarget(); // Proxy target
4660
- this.adapter = new Adapter();
4661
- // get scopeProperties and escapeProperties from plugins
4662
- this.getSpecialProperties(appName);
4663
- // create location, history for child app
4664
- this.patchRouter(appName, url, this.microAppWindow);
4665
- // patch window of child app
4666
- this.windowEffect = patchWindow(appName, this.microAppWindow, this);
4667
- // patch document of child app
4668
- this.documentEffect = patchDocument(appName, this.microAppWindow, this);
4669
- // inject global properties
4670
- this.initStaticGlobalKeys(appName, url, this.microAppWindow);
4991
+ this.microAppWindow = new CustomWindow(); // Proxy target
4992
+ this.patchWith((resolve) => {
4993
+ // get scopeProperties and escapeProperties from plugins
4994
+ this.getSpecialProperties(appName);
4995
+ // create location, history for child app
4996
+ this.patchRouter(appName, url, this.microAppWindow);
4997
+ // patch window of child app
4998
+ this.windowEffect = patchWindow(appName, this.microAppWindow, this);
4999
+ // patch document of child app
5000
+ this.documentEffect = patchDocument(appName, this.microAppWindow, this);
5001
+ // properties associated with the native window
5002
+ this.setMappingPropertiesWithRawDescriptor(this.microAppWindow);
5003
+ // inject global properties
5004
+ this.initStaticGlobalKeys(appName, url, this.microAppWindow);
5005
+ resolve();
5006
+ });
4671
5007
  }
4672
5008
  /**
4673
5009
  * open sandbox and perform some initial actions
@@ -4685,7 +5021,9 @@ class WithSandBox {
4685
5021
  this.initRouteState(defaultPage);
4686
5022
  // unique listener of popstate event for sub app
4687
5023
  this.removeHistoryListener = addHistoryListener(this.microAppWindow.__MICRO_APP_NAME__);
4688
- this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute;
5024
+ if (isRouterModeCustom(this.microAppWindow.__MICRO_APP_NAME__)) {
5025
+ this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute;
5026
+ }
4689
5027
  /* --- memory router part --- end */
4690
5028
  /**
4691
5029
  * Target: Ensure default mode action exactly same to first time when render again
@@ -4729,6 +5067,7 @@ class WithSandBox {
4729
5067
  * 1. injectedKeys and escapeKeys must be placed at the back
4730
5068
  * 2. if key in initial microAppWindow, and then rewrite, this key will be delete from microAppWindow when stop, and lost when restart
4731
5069
  * 3. umd mode will not delete global keys
5070
+ * 4. mount & unmount hook should delete in default mode when stop
4732
5071
  */
4733
5072
  if (!umdMode || destroy) {
4734
5073
  clearMicroEventSource(this.microAppWindow.__MICRO_APP_NAME__);
@@ -4740,6 +5079,7 @@ class WithSandBox {
4740
5079
  Reflect.deleteProperty(globalEnv.rawWindow, key);
4741
5080
  });
4742
5081
  this.escapeKeys.clear();
5082
+ this.clearHijackUmdHooks();
4743
5083
  }
4744
5084
  if (--globalEnv.activeSandbox === 0) {
4745
5085
  releasePatchElementAndDocument();
@@ -4750,6 +5090,7 @@ class WithSandBox {
4750
5090
  }
4751
5091
  /**
4752
5092
  * inject global properties to microAppWindow
5093
+ * TODO: 设置为只读变量
4753
5094
  * @param appName app name
4754
5095
  * @param url app url
4755
5096
  * @param microAppWindow micro window
@@ -4763,6 +5104,7 @@ class WithSandBox {
4763
5104
  microAppWindow.__MICRO_APP_WINDOW__ = microAppWindow;
4764
5105
  microAppWindow.__MICRO_APP_PRE_RENDER__ = false;
4765
5106
  microAppWindow.__MICRO_APP_UMD_MODE__ = false;
5107
+ microAppWindow.__MICRO_APP_PROXY_WINDOW__ = this.proxyWindow;
4766
5108
  microAppWindow.__MICRO_APP_SANDBOX__ = this;
4767
5109
  microAppWindow.__MICRO_APP_SANDBOX_TYPE__ = 'with';
4768
5110
  microAppWindow.rawWindow = globalEnv.rawWindow;
@@ -4770,9 +5112,9 @@ class WithSandBox {
4770
5112
  microAppWindow.microApp = assign(new EventCenterForMicroApp(appName), {
4771
5113
  removeDomScope,
4772
5114
  pureCreateElement,
5115
+ location: microAppWindow.location,
4773
5116
  router,
4774
5117
  });
4775
- this.setMappingPropertiesWithRawDescriptor(microAppWindow);
4776
5118
  }
4777
5119
  /**
4778
5120
  * Record global effect and then release (effect: global event, timeout, data listener)
@@ -4855,7 +5197,6 @@ class WithSandBox {
4855
5197
  */
4856
5198
  getSpecialProperties(appName) {
4857
5199
  var _a;
4858
- this.scopeProperties = this.scopeProperties.concat(this.adapter.staticScopeProperties);
4859
5200
  if (isPlainObject(microApp.options.plugins)) {
4860
5201
  this.commonActionForSpecialProperties(microApp.options.plugins.global);
4861
5202
  this.commonActionForSpecialProperties((_a = microApp.options.plugins.modules) === null || _a === void 0 ? void 0 : _a[appName]);
@@ -4883,6 +5224,9 @@ class WithSandBox {
4883
5224
  markUmdMode(state) {
4884
5225
  this.microAppWindow.__MICRO_APP_UMD_MODE__ = state;
4885
5226
  }
5227
+ patchWith(cb) {
5228
+ this.sandboxReady = new Promise((resolve) => cb(resolve));
5229
+ }
4886
5230
  // properties associated with the native window
4887
5231
  setMappingPropertiesWithRawDescriptor(microAppWindow) {
4888
5232
  let topValue, parentValue;
@@ -4894,9 +5238,10 @@ class WithSandBox {
4894
5238
  topValue = rawWindow.top;
4895
5239
  parentValue = rawWindow.parent;
4896
5240
  }
4897
- // TODO: 用rawDefineProperties
4898
- rawDefineProperty(microAppWindow, 'top', this.createDescriptorForMicroAppWindow('top', topValue));
4899
- rawDefineProperty(microAppWindow, 'parent', this.createDescriptorForMicroAppWindow('parent', parentValue));
5241
+ rawDefineProperties(microAppWindow, {
5242
+ top: this.createDescriptorForMicroAppWindow('top', topValue),
5243
+ parent: this.createDescriptorForMicroAppWindow('parent', parentValue),
5244
+ });
4900
5245
  GLOBAL_KEY_TO_WINDOW.forEach((key) => {
4901
5246
  rawDefineProperty(microAppWindow, key, this.createDescriptorForMicroAppWindow(key, this.proxyWindow));
4902
5247
  });
@@ -4934,7 +5279,7 @@ class WithSandBox {
4934
5279
  enumerable: false,
4935
5280
  get() {
4936
5281
  throttleDeferForSetAppName(appName);
4937
- return modifiedEval || eval;
5282
+ return modifiedEval || globalEnv.rawWindow.eval;
4938
5283
  },
4939
5284
  set: (value) => {
4940
5285
  modifiedEval = value;
@@ -5049,22 +5394,80 @@ class WithSandBox {
5049
5394
  * action before exec scripts when mount
5050
5395
  * Actions:
5051
5396
  * 1. patch static elements from html
5397
+ * 2. hijack umd hooks -- mount, unmount, micro-app-appName
5052
5398
  * @param container micro app container
5053
5399
  */
5054
- actionBeforeExecScripts(container) {
5400
+ actionsBeforeExecScripts(container, handleUmdHooks) {
5055
5401
  this.patchStaticElement(container);
5402
+ this.clearHijackUmdHooks = this.hijackUmdHooks(this.appName, this.microAppWindow, handleUmdHooks);
5403
+ }
5404
+ // hijack mount, unmount, micro-app-appName hook to microAppWindow
5405
+ hijackUmdHooks(appName, microAppWindow, handleUmdHooks) {
5406
+ let mount, unmount, microAppLibrary;
5407
+ rawDefineProperties(microAppWindow, {
5408
+ mount: {
5409
+ configurable: true,
5410
+ get: () => mount,
5411
+ set: (value) => {
5412
+ if (this.active && isFunction(value) && !mount) {
5413
+ handleUmdHooks(mount = value, unmount);
5414
+ }
5415
+ }
5416
+ },
5417
+ unmount: {
5418
+ configurable: true,
5419
+ get: () => unmount,
5420
+ set: (value) => {
5421
+ if (this.active && isFunction(value) && !unmount) {
5422
+ handleUmdHooks(mount, unmount = value);
5423
+ }
5424
+ }
5425
+ },
5426
+ [`micro-app-${appName}`]: {
5427
+ configurable: true,
5428
+ get: () => microAppLibrary,
5429
+ set: (value) => {
5430
+ if (this.active && isPlainObject(value) && !microAppLibrary) {
5431
+ microAppLibrary = value;
5432
+ handleUmdHooks(microAppLibrary.mount, microAppLibrary.unmount);
5433
+ }
5434
+ }
5435
+ }
5436
+ });
5437
+ return () => {
5438
+ mount = unmount = microAppLibrary = null;
5439
+ };
5440
+ }
5441
+ setStaticAppState(state) {
5442
+ this.microAppWindow.__MICRO_APP_STATE__ = state;
5056
5443
  }
5057
5444
  }
5058
5445
  WithSandBox.activeCount = 0; // number of active sandbox
5059
5446
 
5060
5447
  function patchRouter(appName, url, microAppWindow, browserHost) {
5061
- const childStaticLocation = new URL(url);
5448
+ const rawHistory = globalEnv.rawWindow.history;
5449
+ const childStaticLocation = createURL(url);
5062
5450
  const childHost = childStaticLocation.protocol + '//' + childStaticLocation.host;
5063
5451
  const childFullPath = childStaticLocation.pathname + childStaticLocation.search + childStaticLocation.hash;
5064
5452
  // rewrite microAppWindow.history
5065
5453
  const microHistory = microAppWindow.history;
5454
+ // save history.replaceState, it will be used in updateMicroLocation
5066
5455
  microAppWindow.rawReplaceState = microHistory.replaceState;
5456
+ // rewrite microAppWindow.history
5067
5457
  assign(microHistory, createMicroHistory(appName, microAppWindow.location));
5458
+ // scrollRestoration proxy to rawHistory
5459
+ rawDefineProperties(microHistory, {
5460
+ scrollRestoration: {
5461
+ configurable: true,
5462
+ enumerable: true,
5463
+ get() {
5464
+ return rawHistory.scrollRestoration;
5465
+ },
5466
+ set(value) {
5467
+ rawHistory.scrollRestoration = value;
5468
+ }
5469
+ }
5470
+ });
5068
5471
  /**
5069
5472
  * Init microLocation before exec sandbox.start
5070
5473
  * NOTE:
@@ -5076,6 +5479,149 @@ function patchRouter(appName, url, microAppWindow, browserHost) {
5076
5479
  return createMicroLocation(appName, url, microAppWindow, childStaticLocation, browserHost, childHost);
5077
5480
  }
5078
5481
 
5482
+ const escape2RawWindowKeys = [
5483
+ 'getComputedStyle',
5484
+ // FIX ISSUE: https://github.com/micro-zoe/micro-app/issues/1292
5485
+ 'DOMParser',
5486
+ 'visualViewport',
5487
+ 'matchMedia',
5488
+ 'ResizeObserver',
5489
+ 'IntersectionObserver',
5490
+ ];
5491
+ const escape2RawWindowRegExpKeys = [
5492
+ /animationFrame$/i,
5493
+ /mutationObserver$/i,
5494
+ /height$|width$/i,
5495
+ /offset$/i,
5496
+ /selection$/i,
5497
+ /^range/i,
5498
+ /^screen/i,
5499
+ /^scroll/i,
5500
+ /X$|Y$/,
5501
+ ];
5502
+ const uniqueDocumentElement = [
5503
+ 'body',
5504
+ 'head',
5505
+ 'html',
5506
+ 'title',
5507
+ ];
5508
+ // proxy to shadowRoot or rawDocument (property)
5509
+ const proxy2RawDocOrShadowKeys = [
5510
+ 'childElementCount',
5511
+ 'children',
5512
+ 'firstElementChild',
5513
+ 'firstChild',
5514
+ 'lastElementChild',
5515
+ 'activeElement',
5516
+ 'fullscreenElement',
5517
+ 'pictureInPictureElement',
5518
+ 'pointerLockElement',
5519
+ 'styleSheets',
5520
+ ];
5521
+ // proxy to shadowRoot or rawDocument (method)
5522
+ const proxy2RawDocOrShadowMethods = [
5523
+ 'append',
5524
+ 'contains',
5525
+ 'replaceChildren',
5526
+ 'createRange',
5527
+ 'getSelection',
5528
+ 'elementFromPoint',
5529
+ 'elementsFromPoint',
5530
+ 'getAnimations',
5531
+ ];
5532
+ // proxy to rawDocument (property)
5533
+ const proxy2RawDocumentKeys = [
5534
+ 'characterSet',
5535
+ 'compatMode',
5536
+ 'contentType',
5537
+ 'designMode',
5538
+ 'dir',
5539
+ 'doctype',
5540
+ 'embeds',
5541
+ 'fullscreenEnabled',
5542
+ 'hidden',
5543
+ 'implementation',
5544
+ 'lastModified',
5545
+ 'pictureInPictureEnabled',
5546
+ 'plugins',
5547
+ 'readyState',
5548
+ 'referrer',
5549
+ 'visibilityState',
5550
+ 'fonts',
5551
+ ];
5552
+ // proxy to rawDocument (method)
5553
+ const proxy2RawDocumentMethods = [
5554
+ 'execCommand',
5555
+ 'createRange',
5556
+ 'exitFullscreen',
5557
+ 'exitPictureInPicture',
5558
+ 'getElementsByTagNameNS',
5559
+ 'hasFocus',
5560
+ 'prepend',
5561
+ ];
5562
+
5563
+ // 重写 Worker 构造函数的类型
5564
+ const originalWorker = window.Worker;
5565
+ function isSameOrigin(url) {
5566
+ if (url instanceof URL && url.protocol === 'blob:') {
5567
+ // 如果 url 是 Blob URL,直接返回 true
5568
+ return true;
5569
+ }
5570
+ // 检查 URL 是否与当前页面在同一个源
5571
+ try {
5572
+ const parsedUrl = new URL(url);
5573
+ return (parsedUrl.protocol === window.location.protocol &&
5574
+ parsedUrl.hostname === window.location.hostname &&
5575
+ parsedUrl.port === window.location.port);
5576
+ }
5577
+ catch (error) {
5578
+ return false;
5579
+ }
5580
+ }
5581
+ function urlFromScript(script) {
5582
+ let blob;
5583
+ try {
5584
+ blob = new Blob([script], {
5585
+ type: 'application/javascript'
5586
+ });
5587
+ }
5588
+ catch (e) {
5589
+ const BlobBuilder =
5590
+ // @ts-ignore
5591
+ window.BlobBuilder ||
5592
+ // @ts-ignore
5593
+ window.WebKitBlobBuilder ||
5594
+ // @ts-ignore
5595
+ window.MozBlobBuilder ||
5596
+ // @ts-ignore
5597
+ window.MSBlobBuilder;
5598
+ const blobBuilder = new BlobBuilder();
5599
+ blobBuilder.append(script);
5600
+ blob = blobBuilder.getBlob('application/javascript');
5601
+ }
5602
+ const URL = window.URL || window.webkitURL;
5603
+ return URL.createObjectURL(blob);
5604
+ }
5605
+ // @ts-ignore
5606
+ const WorkerProxy = new Proxy(originalWorker, {
5607
+ construct(Target, args) {
5608
+ const [scriptURL, options] = args;
5609
+ if (!isSameOrigin(scriptURL)) {
5610
+ // 如果 scriptURL 是跨域的,使用 Blob URL 加载并执行 worker
5611
+ const script = `import "${scriptURL}";`;
5612
+ const workerPath = urlFromScript(script);
5613
+ options.type = 'module';
5614
+ return new Target(workerPath, options);
5615
+ }
5616
+ else {
5617
+ // 如果 scriptURL 是同源的,直接使用原生的 Worker 构造函数
5618
+ return new Target(scriptURL, options);
5619
+ }
5620
+ },
5621
+ });
5622
+ // @ts-ignore
5623
+ window.Worker = WorkerProxy;
5624
+
5079
5625
  /**
5080
5626
  * patch window of child app
5081
5627
  * @param appName app name
@@ -5084,7 +5630,7 @@ function patchRouter(appName, url, microAppWindow, browserHost) {
5084
5630
  * @returns EffectHook
5085
5631
  */
5086
5632
  function patchWindow$1(appName, microAppWindow, sandbox) {
5087
- patchWindowProperty$1(appName, microAppWindow);
5633
+ patchWindowProperty$1(appName, microAppWindow, sandbox);
5088
5634
  createProxyWindow$1(microAppWindow, sandbox);
5089
5635
  return patchWindowEffect$1(microAppWindow);
5090
5636
  }
@@ -5093,7 +5639,7 @@ function patchWindow$1(appName, microAppWindow, sandbox) {
5093
5639
  * @param appName app name
5094
5640
  * @param microAppWindow child app microWindow
5095
5641
  */
5096
- function patchWindowProperty$1(appName, microAppWindow) {
5642
+ function patchWindowProperty$1(appName, microAppWindow, sandbox) {
5097
5643
  const rawWindow = globalEnv.rawWindow;
5098
5644
  escape2RawWindowKeys.forEach((key) => {
5099
5645
  microAppWindow[key] = bindFunctionToRawTarget(rawWindow[key], rawWindow);
@@ -5123,7 +5669,26 @@ function patchWindowProperty$1(appName, microAppWindow) {
5123
5669
  }
5124
5670
  return false;
5125
5671
  });
5126
- return /^on/.test(key) && !SCOPE_WINDOW_ON_EVENT.includes(key);
5672
+ /**
5673
+ * In FireFox, iframe Element.prototype will point to native Element.prototype after insert to document
5674
+ * Rewrite all constructor's Symbol.hasInstance of iframeWindow
5675
+ * NOTE:
5676
+ * 1. native event instanceof iframe window.Event
5677
+ * 2. native node instanceof iframe window.Node
5678
+ * 3. native element instanceof iframe window.Element
5679
+ * 4. native url instanceof iframe window.URL
5680
+ * ...
5681
+ */
5682
+ if (isConstructor(microAppWindow[key]) && key in rawWindow) {
5683
+ rawDefineProperty(microAppWindow[key], Symbol.hasInstance, {
5684
+ configurable: true,
5685
+ enumerable: false,
5686
+ value(target) {
5687
+ return instanceOf(target, rawWindow[key]) || instanceOf(target, microAppWindow[key]);
5688
+ },
5689
+ });
5690
+ }
5691
+ return /^on/.test(key) && !SCOPE_WINDOW_ON_EVENT_OF_IFRAME.includes(key);
5127
5692
  })
5128
5693
  .forEach((eventName) => {
5129
5694
  const { enumerable, writable, set } = Object.getOwnPropertyDescriptor(microAppWindow, eventName) || {
@@ -5143,6 +5708,23 @@ function patchWindowProperty$1(appName, microAppWindow) {
5143
5708
  logWarn(e, appName);
5144
5709
  }
5145
5710
  });
5711
+ /**
5712
+ * In esmodule(vite) proxyWindow will not take effect,
5713
+ * escapeProperties should define to microAppWindow
5714
+ */
5715
+ sandbox.escapeProperties.forEach((key) => {
5716
+ let rawValue = microAppWindow[key];
5717
+ rawDefineProperty(microAppWindow, key, {
5718
+ enumerable: true,
5719
+ configurable: true,
5720
+ get() {
5721
+ return rawValue !== null && rawValue !== void 0 ? rawValue : bindFunctionToRawTarget(rawWindow[key], rawWindow);
5722
+ },
5723
+ set(value) {
5724
+ rawValue = value;
5725
+ }
5726
+ });
5727
+ });
5146
5728
  }
5147
5729
  /**
5148
5730
  * create proxyWindow with Proxy(microAppWindow)
@@ -5151,18 +5733,43 @@ function patchWindowProperty$1(appName, microAppWindow) {
5151
5733
  */
5152
5734
  function createProxyWindow$1(microAppWindow, sandbox) {
5153
5735
  const rawWindow = globalEnv.rawWindow;
5154
- const customProperties = [];
5736
+ const customProperties = new Set();
5737
+ Object.defineProperty(microAppWindow, 'Worker', {
5738
+ value: WorkerProxy,
5739
+ configurable: true,
5740
+ writable: true,
5741
+ });
5742
+ /**
5743
+ * proxyWindow will only take effect in certain scenes, such as window.key
5744
+ * e.g:
5745
+ * 1. window.key in normal app --> fall into proxyWindow
5746
+ * 2. window.key in module app(vite), fall into microAppWindow(iframeWindow)
5747
+ * 3. if (key)... --> fall into microAppWindow(iframeWindow)
5748
+ */
5155
5749
  const proxyWindow = new Proxy(microAppWindow, {
5156
5750
  get: (target, key) => {
5751
+ if (key === 'Worker') {
5752
+ return WorkerProxy;
5753
+ }
5157
5754
  if (key === 'location') {
5158
5755
  return sandbox.proxyLocation;
5159
5756
  }
5160
- if (GLOBAL_KEY_TO_WINDOW.includes(key.toString())) {
5757
+ if (includes(GLOBAL_KEY_TO_WINDOW, key)) {
5161
5758
  return proxyWindow;
5162
5759
  }
5163
- if (customProperties.includes(key)) {
5760
+ if (customProperties.has(key)) {
5164
5761
  return Reflect.get(target, key);
5165
5762
  }
5763
+ /**
5764
+ * Same as proxyWindow, escapeProperties will only take effect in certain scenes
5765
+ * e.g:
5766
+ * 1. window.key in normal app --> fall into proxyWindow, escapeProperties will effect
5767
+ * 2. window.key in module app(vite), fall into microAppWindow(iframeWindow), escapeProperties will not take effect
5768
+ * 3. if (key)... --> fall into microAppWindow(iframeWindow), escapeProperties will not take effect
5769
+ */
5770
+ if (includes(sandbox.escapeProperties, key) && !Reflect.get(target, key)) {
5771
+ return bindFunctionToRawTarget(Reflect.get(rawWindow, key), rawWindow);
5772
+ }
5166
5773
  return bindFunctionToRawTarget(Reflect.get(target, key), target);
5167
5774
  },
5168
5775
  set: (target, key, value) => {
@@ -5170,19 +5777,15 @@ function createProxyWindow$1(microAppWindow, sandbox) {
5170
5777
  return Reflect.set(rawWindow, key, value);
5171
5778
  }
5172
5779
  if (!Reflect.has(target, key)) {
5173
- customProperties.push(key);
5780
+ customProperties.add(key);
5174
5781
  }
5782
+ // sandbox.escapeProperties will not set to rawWindow from rc.9
5175
5783
  Reflect.set(target, key, value);
5176
- if (sandbox.escapeProperties.includes(key)) {
5177
- !Reflect.has(rawWindow, key) && sandbox.escapeKeys.add(key);
5178
- Reflect.set(rawWindow, key, value);
5179
- }
5180
5784
  return true;
5181
5785
  },
5182
5786
  has: (target, key) => key in target,
5183
5787
  deleteProperty: (target, key) => {
5184
5788
  if (Reflect.has(target, key)) {
5185
- sandbox.escapeKeys.has(key) && Reflect.deleteProperty(rawWindow, key);
5186
5789
  return Reflect.deleteProperty(target, key);
5187
5790
  }
5188
5791
  return true;
@@ -5191,11 +5794,15 @@ function createProxyWindow$1(microAppWindow, sandbox) {
5191
5794
  sandbox.proxyWindow = proxyWindow;
5192
5795
  }
5193
5796
  function patchWindowEffect$1(microAppWindow) {
5194
- const { rawWindow, rawAddEventListener, rawRemoveEventListener } = globalEnv;
5797
+ const { rawWindow, rawAddEventListener, rawRemoveEventListener, rawDispatchEvent } = globalEnv;
5195
5798
  const eventListenerMap = new Map();
5196
5799
  const sstEventListenerMap = new Map();
5197
5800
  function getEventTarget(type) {
5198
- return SCOPE_WINDOW_EVENT.includes(type) ? microAppWindow : rawWindow;
5801
+ /**
5802
+ * TODO: SCOPE_WINDOW_EVENT_OF_IFRAME的事件非常少,有可能导致问题
5803
+ * 1、一些未知的需要绑定到iframe的事件被错误的绑定到原生window上
5804
+ */
5805
+ return SCOPE_WINDOW_EVENT_OF_IFRAME.includes(type) ? microAppWindow : rawWindow;
5199
5806
  }
5200
5807
  // TODO: listener 是否需要绑定microAppWindow,否则函数中的this指向原生window
5201
5808
  microAppWindow.addEventListener = function (type, listener, options) {
@@ -5216,6 +5823,9 @@ function patchWindowEffect$1(microAppWindow) {
5216
5823
  }
5217
5824
  rawRemoveEventListener.call(getEventTarget(type), type, listener, options);
5218
5825
  };
5826
+ microAppWindow.dispatchEvent = function (event) {
5827
+ return rawDispatchEvent.call(getEventTarget(event === null || event === void 0 ? void 0 : event.type), event);
5828
+ };
5219
5829
  const reset = () => {
5220
5830
  sstEventListenerMap.clear();
5221
5831
  };
@@ -5288,7 +5898,9 @@ function patchDocumentPrototype(appName, microAppWindow) {
5288
5898
  const microRootDocument = microAppWindow.Document;
5289
5899
  const microDocument = microAppWindow.document;
5290
5900
  const rawMicroCreateElement = microRootDocument.prototype.createElement;
5901
+ const rawMicroCreateElementNS = microRootDocument.prototype.createElementNS;
5291
5902
  const rawMicroCreateTextNode = microRootDocument.prototype.createTextNode;
5903
+ const rawMicroCreateDocumentFragment = microRootDocument.prototype.createDocumentFragment;
5292
5904
  const rawMicroCreateComment = microRootDocument.prototype.createComment;
5293
5905
  const rawMicroQuerySelector = microRootDocument.prototype.querySelector;
5294
5906
  const rawMicroQuerySelectorAll = microRootDocument.prototype.querySelectorAll;
@@ -5309,42 +5921,71 @@ function patchDocumentPrototype(appName, microAppWindow) {
5309
5921
  const element = rawMicroCreateElement.call(this, tagName, options);
5310
5922
  return updateElementInfo(element, appName);
5311
5923
  };
5924
+ microRootDocument.prototype.createElementNS = function createElementNS(namespaceURI, name, options) {
5925
+ const element = rawMicroCreateElementNS.call(this, namespaceURI, name, options);
5926
+ return updateElementInfo(element, appName);
5927
+ };
5312
5928
  microRootDocument.prototype.createTextNode = function createTextNode(data) {
5313
5929
  const element = rawMicroCreateTextNode.call(this, data);
5314
5930
  return updateElementInfo(element, appName);
5315
5931
  };
5932
+ microRootDocument.prototype.createDocumentFragment = function createDocumentFragment() {
5933
+ const element = rawMicroCreateDocumentFragment.call(this);
5934
+ return updateElementInfo(element, appName);
5935
+ };
5316
5936
  microRootDocument.prototype.createComment = function createComment(data) {
5317
5937
  const element = rawMicroCreateComment.call(this, data);
5318
5938
  return updateElementInfo(element, appName);
5319
5939
  };
5320
- function getDefaultRawTarget(target) {
5321
- return microDocument !== target ? target : rawDocument;
5940
+ function getBindTarget(target) {
5941
+ /**
5942
+ * handler for:
5943
+ * 1. document.getElementsByTagName('head')[0].querySelector('script')
5944
+ * 2. document.querySelector('body').querySelectorAll('script')
5945
+ * ...
5946
+ */
5947
+ throttleDeferForIframeAppName(appName);
5948
+ // DOMParser.document !== microDocument
5949
+ return microDocument === target ? rawDocument : target;
5322
5950
  }
5323
5951
  // query element👇
5324
5952
  function querySelector(selectors) {
5325
- var _a, _b;
5953
+ var _a;
5954
+ const _this = getBindTarget(this);
5326
5955
  if (!selectors ||
5327
5956
  isUniqueElement(selectors) ||
5328
- microDocument !== this) {
5329
- const _this = getDefaultRawTarget(this);
5957
+ rawDocument !== _this) {
5330
5958
  return rawMicroQuerySelector.call(_this, selectors);
5331
5959
  }
5332
- return (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.querySelector(selectors)) !== null && _b !== void 0 ? _b : null;
5960
+ /**
5961
+ * The child app cannot query the base element inside iframe
5962
+ * Same for querySelectorAll
5963
+ *
5964
+ * Scenes:
5965
+ * 1. vue-router@4.x --> createWebHistory(base?: string)
5966
+ * const baseEl = document.querySelector('base')
5967
+ * base = (baseEl && baseEl.getAttribute('href')) || '/'
5968
+ *
5969
+ * Issue: https://github.com/micro-zoe/micro-app/issues/1335
5970
+ */
5971
+ const result = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.querySelector(selectors);
5972
+ return result || selectors === 'base' ? result : rawMicroQuerySelector.call(microDocument, selectors);
5333
5973
  }
5334
5974
  function querySelectorAll(selectors) {
5335
5975
  var _a, _b;
5976
+ const _this = getBindTarget(this);
5336
5977
  if (!selectors ||
5337
5978
  isUniqueElement(selectors) ||
5338
- microDocument !== this) {
5339
- const _this = getDefaultRawTarget(this);
5979
+ rawDocument !== _this) {
5340
5980
  return rawMicroQuerySelectorAll.call(_this, selectors);
5341
5981
  }
5342
- return (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.querySelectorAll(selectors)) !== null && _b !== void 0 ? _b : [];
5982
+ const result = (_b = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.querySelectorAll(selectors)) !== null && _b !== void 0 ? _b : [];
5983
+ return result.length || selectors === 'base' ? result : rawMicroQuerySelectorAll.call(microDocument, selectors);
5343
5984
  }
5344
5985
  microRootDocument.prototype.querySelector = querySelector;
5345
5986
  microRootDocument.prototype.querySelectorAll = querySelectorAll;
5346
5987
  microRootDocument.prototype.getElementById = function getElementById(key) {
5347
- const _this = getDefaultRawTarget(this);
5988
+ const _this = getBindTarget(this);
5348
5989
  if (isInvalidQuerySelectorKey(key)) {
5349
5990
  return rawMicroGetElementById.call(_this, key);
5350
5991
  }
@@ -5356,7 +5997,7 @@ function patchDocumentPrototype(appName, microAppWindow) {
5356
5997
  }
5357
5998
  };
5358
5999
  microRootDocument.prototype.getElementsByClassName = function getElementsByClassName(key) {
5359
- const _this = getDefaultRawTarget(this);
6000
+ const _this = getBindTarget(this);
5360
6001
  if (isInvalidQuerySelectorKey(key)) {
5361
6002
  return rawMicroGetElementsByClassName.call(_this, key);
5362
6003
  }
@@ -5368,22 +6009,24 @@ function patchDocumentPrototype(appName, microAppWindow) {
5368
6009
  }
5369
6010
  };
5370
6011
  microRootDocument.prototype.getElementsByTagName = function getElementsByTagName(key) {
5371
- var _a;
5372
- const _this = getDefaultRawTarget(this);
6012
+ const _this = getBindTarget(this);
5373
6013
  if (isUniqueElement(key) ||
5374
- isInvalidQuerySelectorKey(key) ||
5375
- (!((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.inline) && /^script$/i.test(key))) {
6014
+ isInvalidQuerySelectorKey(key)) {
5376
6015
  return rawMicroGetElementsByTagName.call(_this, key);
6016
+ // just script, not base
6017
+ }
6018
+ else if (/^script$/i.test(key)) {
6019
+ return rawMicroGetElementsByTagName.call(microDocument, key);
5377
6020
  }
5378
6021
  try {
5379
6022
  return querySelectorAll.call(this, key);
5380
6023
  }
5381
- catch (_b) {
6024
+ catch (_a) {
5382
6025
  return rawMicroGetElementsByTagName.call(_this, key);
5383
6026
  }
5384
6027
  };
5385
6028
  microRootDocument.prototype.getElementsByName = function getElementsByName(key) {
5386
- const _this = getDefaultRawTarget(this);
6029
+ const _this = getBindTarget(this);
5387
6030
  if (isInvalidQuerySelectorKey(key)) {
5388
6031
  return rawMicroGetElementsByName.call(_this, key);
5389
6032
  }
@@ -5412,6 +6055,7 @@ function patchDocumentProperty(appName, microAppWindow, sandbox) {
5412
6055
  const createDescriptors = () => {
5413
6056
  const result = {};
5414
6057
  const descList = [
6058
+ // if disable-memory-router or router-mode='disable', href point to base app
5415
6059
  ['documentURI', () => sandbox.proxyLocation.href],
5416
6060
  ['URL', () => sandbox.proxyLocation.href],
5417
6061
  ['documentElement', () => rawDocument.documentElement],
@@ -5421,6 +6065,7 @@ function patchDocumentProperty(appName, microAppWindow, sandbox) {
5421
6065
  ['links', () => microRootDocument.prototype.querySelectorAll.call(microDocument, 'a')],
5422
6066
  // unique keys of micro-app
5423
6067
  ['microAppElement', () => { var _a; return (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container; }],
6068
+ ['__MICRO_APP_NAME__', () => appName],
5424
6069
  ];
5425
6070
  descList.forEach((desc) => {
5426
6071
  result[desc[0]] = getCommonDescriptor(desc[0], desc[1]);
@@ -5447,13 +6092,16 @@ function patchDocumentProperty(appName, microAppWindow, sandbox) {
5447
6092
  rawDefineProperty(microDocument, tagName, {
5448
6093
  enumerable: true,
5449
6094
  configurable: true,
5450
- get: () => rawDocument[tagName],
6095
+ get: () => {
6096
+ throttleDeferForIframeAppName(appName);
6097
+ return rawDocument[tagName];
6098
+ },
5451
6099
  set: (value) => { rawDocument[tagName] = value; },
5452
6100
  });
5453
6101
  });
5454
6102
  }
5455
6103
  function patchDocumentEffect(appName, microAppWindow) {
5456
- const { rawDocument, rawAddEventListener, rawRemoveEventListener } = globalEnv;
6104
+ const { rawDocument, rawAddEventListener, rawRemoveEventListener, rawDispatchEvent } = globalEnv;
5457
6105
  const eventListenerMap = new Map();
5458
6106
  const sstEventListenerMap = new Map();
5459
6107
  let onClickHandler = null;
@@ -5483,6 +6131,9 @@ function patchDocumentEffect(appName, microAppWindow) {
5483
6131
  const handler = (listener === null || listener === void 0 ? void 0 : listener.__MICRO_APP_BOUND_FUNCTION__) || listener;
5484
6132
  rawRemoveEventListener.call(getEventTarget(type, this), type, handler, options);
5485
6133
  };
6134
+ microRootDocument.prototype.dispatchEvent = function (event) {
6135
+ return rawDispatchEvent.call(getEventTarget(event === null || event === void 0 ? void 0 : event.type, this), event);
6136
+ };
5486
6137
  // 重新定义microRootDocument.prototype 上的on开头方法
5487
6138
  function createSetterHandler(eventName) {
5488
6139
  if (eventName === 'onclick') {
@@ -5603,12 +6254,18 @@ function patchElement(appName, url, microAppWindow, sandbox) {
5603
6254
  patchIframeNode(appName, microAppWindow, sandbox);
5604
6255
  patchIframeAttribute(url, microAppWindow);
5605
6256
  }
6257
+ /**
6258
+ * patch iframe Node/Element
6259
+ *
6260
+ */
5606
6261
  function patchIframeNode(appName, microAppWindow, sandbox) {
5607
6262
  const rawRootElement = globalEnv.rawRootElement; // native root Element
6263
+ const rawRootNode = globalEnv.rawRootNode;
5608
6264
  const rawDocument = globalEnv.rawDocument;
5609
6265
  const microDocument = microAppWindow.document;
5610
6266
  const microRootNode = microAppWindow.Node;
5611
6267
  const microRootElement = microAppWindow.Element;
6268
+ const microDocumentFragment = microAppWindow.DocumentFragment;
5612
6269
  // const rawMicroGetRootNode = microRootNode.prototype.getRootNode
5613
6270
  const rawMicroAppendChild = microRootNode.prototype.appendChild;
5614
6271
  const rawMicroInsertBefore = microRootNode.prototype.insertBefore;
@@ -5616,10 +6273,13 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5616
6273
  const rawMicroRemoveChild = microRootNode.prototype.removeChild;
5617
6274
  const rawMicroAppend = microRootElement.prototype.append;
5618
6275
  const rawMicroPrepend = microRootElement.prototype.prepend;
6276
+ const rawMicroFragmentAppend = microDocumentFragment.prototype.append;
6277
+ const rawMicroFragmentPrepend = microDocumentFragment.prototype.prepend;
5619
6278
  const rawMicroInsertAdjacentElement = microRootElement.prototype.insertAdjacentElement;
5620
6279
  const rawMicroCloneNode = microRootNode.prototype.cloneNode;
5621
6280
  const rawInnerHTMLDesc = Object.getOwnPropertyDescriptor(microRootElement.prototype, 'innerHTML');
5622
- const rawParentNodeLDesc = Object.getOwnPropertyDescriptor(microRootNode.prototype, 'parentNode');
6281
+ const rawParentNodeDesc = Object.getOwnPropertyDescriptor(microRootNode.prototype, 'parentNode');
6282
+ const rawOwnerDocumentDesc = Object.getOwnPropertyDescriptor(microRootNode.prototype, 'ownerDocument');
5623
6283
  const isPureNode = (target) => {
5624
6284
  return (isScriptElement(target) || isBaseElement(target)) && target.__PURE_ELEMENT__;
5625
6285
  };
@@ -5632,41 +6292,34 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5632
6292
  }
5633
6293
  return parent;
5634
6294
  };
5635
- microRootNode.prototype.getRootNode = function getRootNode() {
5636
- return microDocument;
5637
- // TODO: 什么情况下返回原生document?
5638
- // const rootNode = rawMicroGetRootNode.call(this, options)
5639
- // if (rootNode === appInstanceMap.get(appName)?.container) return microDocument
5640
- // return rootNode
5641
- };
5642
6295
  microRootNode.prototype.appendChild = function appendChild(node) {
5643
6296
  updateElementInfo(node, appName);
5644
6297
  if (isPureNode(node)) {
5645
6298
  return rawMicroAppendChild.call(this, node);
5646
6299
  }
5647
- return rawRootElement.prototype.appendChild.call(getRawTarget(this), node);
6300
+ return rawRootNode.prototype.appendChild.call(getRawTarget(this), node);
5648
6301
  };
5649
6302
  microRootNode.prototype.insertBefore = function insertBefore(node, child) {
5650
6303
  updateElementInfo(node, appName);
5651
6304
  if (isPureNode(node)) {
5652
6305
  return rawMicroInsertBefore.call(this, node, child);
5653
6306
  }
5654
- return rawRootElement.prototype.insertBefore.call(getRawTarget(this), node, child);
6307
+ return rawRootNode.prototype.insertBefore.call(getRawTarget(this), node, child);
5655
6308
  };
5656
6309
  microRootNode.prototype.replaceChild = function replaceChild(node, child) {
5657
6310
  updateElementInfo(node, appName);
5658
6311
  if (isPureNode(node)) {
5659
6312
  return rawMicroReplaceChild.call(this, node, child);
5660
6313
  }
5661
- return rawRootElement.prototype.replaceChild.call(getRawTarget(this), node, child);
6314
+ return rawRootNode.prototype.replaceChild.call(getRawTarget(this), node, child);
5662
6315
  };
5663
6316
  microRootNode.prototype.removeChild = function removeChild(oldChild) {
5664
6317
  if (isPureNode(oldChild) || this.contains(oldChild)) {
5665
6318
  return rawMicroRemoveChild.call(this, oldChild);
5666
6319
  }
5667
- return rawRootElement.prototype.removeChild.call(getRawTarget(this), oldChild);
6320
+ return rawRootNode.prototype.removeChild.call(getRawTarget(this), oldChild);
5668
6321
  };
5669
- microRootElement.prototype.append = function append(...nodes) {
6322
+ microDocumentFragment.prototype.append = microRootElement.prototype.append = function append(...nodes) {
5670
6323
  let i = 0;
5671
6324
  let hasPureNode = false;
5672
6325
  while (i < nodes.length) {
@@ -5676,11 +6329,11 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5676
6329
  i++;
5677
6330
  }
5678
6331
  if (hasPureNode) {
5679
- return rawMicroAppend.call(this, ...nodes);
6332
+ return (isDocumentFragment(this) ? rawMicroFragmentAppend : rawMicroAppend).call(this, ...nodes);
5680
6333
  }
5681
6334
  return rawRootElement.prototype.append.call(getRawTarget(this), ...nodes);
5682
6335
  };
5683
- microRootElement.prototype.prepend = function prepend(...nodes) {
6336
+ microDocumentFragment.prototype.prepend = microRootElement.prototype.prepend = function prepend(...nodes) {
5684
6337
  let i = 0;
5685
6338
  let hasPureNode = false;
5686
6339
  while (i < nodes.length) {
@@ -5690,7 +6343,7 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5690
6343
  i++;
5691
6344
  }
5692
6345
  if (hasPureNode) {
5693
- return rawMicroPrepend.call(this, ...nodes);
6346
+ return (isDocumentFragment(this) ? rawMicroFragmentPrepend : rawMicroPrepend).call(this, ...nodes);
5694
6347
  }
5695
6348
  return rawRootElement.prototype.prepend.call(getRawTarget(this), ...nodes);
5696
6349
  };
@@ -5706,49 +6359,59 @@ function patchIframeNode(appName, microAppWindow, sandbox) {
5706
6359
  }
5707
6360
  return rawRootElement.prototype.insertAdjacentElement.call(getRawTarget(this), where, element);
5708
6361
  };
5709
- // patch cloneNode
5710
- microRootNode.prototype.cloneNode = function cloneNode(deep) {
5711
- const clonedNode = rawMicroCloneNode.call(this, deep);
5712
- return updateElementInfo(clonedNode, appName);
5713
- };
5714
- rawDefineProperty(microRootElement.prototype, 'innerHTML', {
6362
+ /**
6363
+ * Specific prototype properties:
6364
+ * 1. baseURI
6365
+ * 2. ownerDocument
6366
+ * 3. parentNode
6367
+ * 4. innerHTML
6368
+ */
6369
+ rawDefineProperty(microRootNode.prototype, 'baseURI', {
5715
6370
  configurable: true,
5716
6371
  enumerable: true,
5717
6372
  get() {
5718
- return rawInnerHTMLDesc.get.call(this);
6373
+ return sandbox.proxyWindow.location.href;
5719
6374
  },
5720
- set(code) {
5721
- rawInnerHTMLDesc.set.call(this, code);
5722
- Array.from(this.children).forEach((child) => {
5723
- if (isElement(child)) {
5724
- updateElementInfo(child, appName);
5725
- }
5726
- });
5727
- }
5728
6375
  });
5729
- // patch parentNode
5730
- rawDefineProperty(microRootNode.prototype, 'parentNode', {
6376
+ rawDefineProperty(microRootNode.prototype, 'ownerDocument', {
5731
6377
  configurable: true,
5732
6378
  enumerable: true,
5733
6379
  get() {
5734
- var _a, _b, _c;
5735
- // set html.parentNode to microDocument
5736
- throttleDeferForParentNode(microDocument);
5737
- const result = rawParentNodeLDesc.get.call(this);
5738
- /**
5739
- * If parentNode is <micro-app-body>, return rawDocument.body
5740
- * Scenes:
5741
- * 1. element-ui@2/lib/utils/vue-popper.js
5742
- * if (this.popperElm.parentNode === document.body) ...
5743
- * WARNING:
5744
- * Will it cause other problems ?
5745
- * e.g. target.parentNode.remove(target)
5746
- */
5747
- if (isMicroAppBody(result) && ((_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : _a.container)) {
5748
- return ((_c = (_b = microApp.options).getRootElementParentNode) === null || _c === void 0 ? void 0 : _c.call(_b, this, appName)) || rawDocument.body;
5749
- }
5750
- return result;
6380
+ var _a;
6381
+ return this.__PURE_ELEMENT__ || this === microDocument
6382
+ ? (_a = rawOwnerDocumentDesc.get) === null || _a === void 0 ? void 0 : _a.call(this) : microDocument;
6383
+ },
6384
+ });
6385
+ // patch parentNode
6386
+ rawDefineProperty(microRootNode.prototype, 'parentNode', getIframeParentNodeDesc(appName, rawParentNodeDesc));
6387
+ microRootNode.prototype.getRootNode = function getRootNode() {
6388
+ return microDocument;
6389
+ // TODO: any case return document?
6390
+ // const rootNode = rawMicroGetRootNode.call(this, options)
6391
+ // if (rootNode === appInstanceMap.get(appName)?.container) return microDocument
6392
+ // return rootNode
6393
+ };
6394
+ // patch cloneNode
6395
+ microRootNode.prototype.cloneNode = function cloneNode(deep) {
6396
+ const clonedNode = rawMicroCloneNode.call(this, deep);
6397
+ return updateElementInfo(clonedNode, appName);
6398
+ };
6399
+ rawDefineProperty(microRootElement.prototype, 'innerHTML', {
6400
+ configurable: true,
6401
+ enumerable: true,
6402
+ get() {
6403
+ var _a;
6404
+ return (_a = rawInnerHTMLDesc.get) === null || _a === void 0 ? void 0 : _a.call(this);
5751
6405
  },
6406
+ set(code) {
6407
+ var _a;
6408
+ (_a = rawInnerHTMLDesc.set) === null || _a === void 0 ? void 0 : _a.call(this, code);
6409
+ Array.from(this.children).forEach((child) => {
6410
+ if (isElement(child)) {
6411
+ updateElementInfo(child, appName);
6412
+ }
6413
+ });
6414
+ }
5752
6415
  });
5753
6416
  // Adapt to new image(...) scene
5754
6417
  const ImageProxy = new Proxy(microAppWindow.Image, {
@@ -5768,16 +6431,24 @@ function patchIframeAttribute(url, microAppWindow) {
5768
6431
  const microRootElement = microAppWindow.Element;
5769
6432
  const rawMicroSetAttribute = microRootElement.prototype.setAttribute;
5770
6433
  microRootElement.prototype.setAttribute = function setAttribute(key, value) {
5771
- if (((key === 'src' || key === 'srcset') && /^(img|script)$/i.test(this.tagName)) ||
5772
- (key === 'href' && /^link$/i.test(this.tagName))) {
5773
- value = CompletionPath(value, url);
6434
+ if (/^micro-app(-\S+)?/i.test(this.tagName) &&
6435
+ key === 'data' &&
6436
+ this.setAttribute !== microRootElement.prototype.setAttribute) {
6437
+ this.setAttribute(key, value);
6438
+ }
6439
+ else {
6440
+ if (((key === 'src' || key === 'srcset') && /^(img|script|video|audio|source|embed)$/i.test(this.tagName)) ||
6441
+ (key === 'href' && /^(a|link|image)$/i.test(this.tagName))) {
6442
+ value = CompletionPath(value, url);
6443
+ }
6444
+ rawMicroSetAttribute.call(this, key, value);
5774
6445
  }
5775
- rawMicroSetAttribute.call(this, key, value);
5776
6446
  };
5777
6447
  const protoAttrList = [
5778
6448
  [microAppWindow.HTMLImageElement.prototype, 'src'],
5779
6449
  [microAppWindow.HTMLScriptElement.prototype, 'src'],
5780
6450
  [microAppWindow.HTMLLinkElement.prototype, 'href'],
6451
+ [microAppWindow.SVGImageElement.prototype, 'href'],
5781
6452
  ];
5782
6453
  /**
5783
6454
  * element.setAttribute does not trigger this actions:
@@ -5808,16 +6479,17 @@ class IframeSandbox {
5808
6479
  this.active = false;
5809
6480
  // Properties that can be escape to rawWindow
5810
6481
  this.escapeProperties = [];
5811
- // Properties escape to rawWindow, cleared when unmount
5812
- this.escapeKeys = new Set();
5813
- // TODO: 初始化和每次跳转时都要更新base的href
6482
+ // Update the base.href when initial and each redirect
5814
6483
  this.updateIframeBase = () => {
5815
6484
  var _a;
5816
- (_a = this.baseElement) === null || _a === void 0 ? void 0 : _a.setAttribute('href', this.proxyLocation.protocol + '//' + this.proxyLocation.host + this.proxyLocation.pathname);
6485
+ // origin must be child app origin
6486
+ (_a = this.baseElement) === null || _a === void 0 ? void 0 : _a.setAttribute('href', createURL(this.url).origin + this.proxyLocation.pathname);
5817
6487
  };
6488
+ this.appName = appName;
6489
+ this.url = url;
5818
6490
  const rawLocation = globalEnv.rawWindow.location;
5819
6491
  const browserHost = rawLocation.protocol + '//' + rawLocation.host;
5820
- this.deleteIframeElement = this.createIframeElement(appName, browserHost);
6492
+ this.deleteIframeElement = this.createIframeElement(appName, browserHost + rawLocation.pathname);
5821
6493
  this.microAppWindow = this.iframe.contentWindow;
5822
6494
  this.patchIframe(this.microAppWindow, (resolve) => {
5823
6495
  // create new html to iframe
@@ -5845,15 +6517,16 @@ class IframeSandbox {
5845
6517
  /**
5846
6518
  * create iframe for sandbox
5847
6519
  * @param appName app name
5848
- * @param browserHost browser origin
6520
+ * @param browserPath browser origin
5849
6521
  * @returns release callback
5850
6522
  */
5851
- createIframeElement(appName, browserHost) {
6523
+ createIframeElement(appName, browserPath) {
5852
6524
  this.iframe = pureCreateElement('iframe');
5853
6525
  const iframeAttrs = {
5854
- src: browserHost,
5855
- style: 'display: none',
5856
6526
  id: appName,
6527
+ src: microApp.options.iframeSrc || browserPath,
6528
+ style: 'display: none',
6529
+ 'powered-by': 'micro-app',
5857
6530
  };
5858
6531
  Object.keys(iframeAttrs).forEach((key) => this.iframe.setAttribute(key, iframeAttrs[key]));
5859
6532
  // effect action during construct
@@ -5887,19 +6560,12 @@ class IframeSandbox {
5887
6560
  * 1. iframe router and browser router are separated, we should update iframe router manually
5888
6561
  * 2. withSandbox location is browser location when disable memory-router, so no need to do anything
5889
6562
  */
5890
- /**
5891
- * TODO:
5892
- * 1. iframe关闭虚拟路由系统后,default-page无法使用,推荐用户直接使用浏览器地址控制首页渲染
5893
- * 补充:keep-router-state 也无法配置,因为keep-router-state一定为true。
5894
- * 2. 导航拦截、current.route 可以正常使用
5895
- * 3. 可以正常控制子应用跳转,方式还是自上而下(也可以是子应用内部跳转,这种方式更好一点,减小对基座的影响,不会导致vue的循环刷新)
5896
- * 4. 关闭虚拟路由以后会对应 route-mode='custom' 模式,包括with沙箱也会这么做
5897
- * 5. 关闭虚拟路由是指尽可能模拟没有虚拟路由的情况,子应用直接获取浏览器location和history,控制浏览器跳转
5898
- */
5899
6563
  this.initRouteState(defaultPage);
5900
6564
  // unique listener of popstate event for child app
5901
6565
  this.removeHistoryListener = addHistoryListener(this.microAppWindow.__MICRO_APP_NAME__);
5902
- this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute;
6566
+ if (isRouterModeCustom(this.microAppWindow.__MICRO_APP_NAME__)) {
6567
+ this.microAppWindow.__MICRO_APP_BASE_ROUTE__ = this.microAppWindow.__MICRO_APP_BASE_URL__ = baseroute;
6568
+ }
5903
6569
  /* --- memory router part --- end */
5904
6570
  /**
5905
6571
  * create base element to iframe
@@ -5916,6 +6582,7 @@ class IframeSandbox {
5916
6582
  }
5917
6583
  stop({ umdMode, keepRouteState, destroy, clearData, }) {
5918
6584
  var _a;
6585
+ // sandbox.stop may exec before sandbox.start, e.g: iframe sandbox + default mode + remount
5919
6586
  if (!this.active)
5920
6587
  return;
5921
6588
  this.recordAndReleaseEffect({ clearData }, !umdMode || destroy);
@@ -5927,10 +6594,7 @@ class IframeSandbox {
5927
6594
  /* --- memory router part --- end */
5928
6595
  if (!umdMode || destroy) {
5929
6596
  this.deleteIframeElement();
5930
- this.escapeKeys.forEach((key) => {
5931
- Reflect.deleteProperty(globalEnv.rawWindow, key);
5932
- });
5933
- this.escapeKeys.clear();
6597
+ this.clearHijackUmdHooks();
5934
6598
  }
5935
6599
  if (--globalEnv.activeSandbox === 0) {
5936
6600
  releasePatchElementAndDocument();
@@ -5944,6 +6608,7 @@ class IframeSandbox {
5944
6608
  * NOTE:
5945
6609
  * 1. execute as early as possible
5946
6610
  * 2. run after patchRouter & createProxyWindow
6611
+ * TODO: 设置为只读变量
5947
6612
  */
5948
6613
  initStaticGlobalKeys(appName, url, microAppWindow) {
5949
6614
  microAppWindow.__MICRO_APP_ENVIRONMENT__ = true;
@@ -6145,12 +6810,55 @@ class IframeSandbox {
6145
6810
  patchElementTree(container, this.microAppWindow.__MICRO_APP_NAME__);
6146
6811
  }
6147
6812
  /**
6813
+ * action before exec scripts when mount
6148
6814
  * Actions:
6149
6815
  * 1. patch static elements from html
6816
+ * 2. hijack umd hooks -- mount, unmount, micro-app-appName
6150
6817
  * @param container micro app container
6151
6818
  */
6152
- actionBeforeExecScripts(container) {
6819
+ actionsBeforeExecScripts(container, handleUmdHooks) {
6153
6820
  this.patchStaticElement(container);
6821
+ this.clearHijackUmdHooks = this.hijackUmdHooks(this.appName, this.microAppWindow, handleUmdHooks);
6822
+ }
6823
+ // hijack mount, unmount, micro-app-appName hook to microAppWindow
6824
+ hijackUmdHooks(appName, microAppWindow, handleUmdHooks) {
6825
+ let mount, unmount, microAppLibrary;
6826
+ rawDefineProperties(microAppWindow, {
6827
+ mount: {
6828
+ configurable: true,
6829
+ get: () => mount,
6830
+ set: (value) => {
6831
+ if (this.active && isFunction(value) && !mount) {
6832
+ handleUmdHooks(mount = value, unmount);
6833
+ }
6834
+ }
6835
+ },
6836
+ unmount: {
6837
+ configurable: true,
6838
+ get: () => unmount,
6839
+ set: (value) => {
6840
+ if (this.active && isFunction(value) && !unmount) {
6841
+ handleUmdHooks(mount, unmount = value);
6842
+ }
6843
+ }
6844
+ },
6845
+ [`micro-app-${appName}`]: {
6846
+ configurable: true,
6847
+ get: () => microAppLibrary,
6848
+ set: (value) => {
6849
+ if (this.active && isPlainObject(value) && !microAppLibrary) {
6850
+ microAppLibrary = value;
6851
+ handleUmdHooks(microAppLibrary.mount, microAppLibrary.unmount);
6852
+ }
6853
+ }
6854
+ }
6855
+ });
6856
+ return () => {
6857
+ mount = unmount = microAppLibrary = null;
6858
+ };
6859
+ }
6860
+ setStaticAppState(state) {
6861
+ this.microAppWindow.__MICRO_APP_STATE__ = state;
6154
6862
  }
6155
6863
  }
6156
6864
  IframeSandbox.activeCount = 0; // number of active sandbox
@@ -6174,8 +6882,9 @@ class CreateApp {
6174
6882
  this.url = url;
6175
6883
  this.useSandbox = useSandbox;
6176
6884
  this.scopecss = this.useSandbox && scopecss;
6177
- this.inline = inline !== null && inline !== void 0 ? inline : false;
6885
+ // exec before getInlineModeState
6178
6886
  this.iframe = iframe !== null && iframe !== void 0 ? iframe : false;
6887
+ this.inline = this.getInlineModeState(inline);
6179
6888
  /**
6180
6889
  * NOTE:
6181
6890
  * 1. Navigate after micro-app created, before mount
@@ -6199,12 +6908,17 @@ class CreateApp {
6199
6908
  }
6200
6909
  /**
6201
6910
  * When resource is loaded, mount app if it is not prefetch or unmount
6911
+ * defaultPage disablePatchRequest routerMode baseroute is only for prerender app
6202
6912
  */
6203
- onLoad(html, defaultPage, disablePatchRequest, routerMode, baseroute) {
6913
+ onLoad({ html,
6914
+ // below params is only for prerender app
6915
+ defaultPage, routerMode, baseroute, disablePatchRequest, }) {
6204
6916
  var _a;
6205
6917
  if (++this.loadSourceLevel === 2) {
6206
6918
  this.source.html = html;
6207
- if (!this.isPrefetch && !this.isUnmounted()) {
6919
+ if (this.isUnmounted())
6920
+ return;
6921
+ if (!this.isPrefetch) {
6208
6922
  getRootContainer(this.container).mount(this);
6209
6923
  }
6210
6924
  else if (this.isPrerender) {
@@ -6231,11 +6945,11 @@ class CreateApp {
6231
6945
  this.mount({
6232
6946
  container,
6233
6947
  inline: this.inline,
6234
- routerMode: routerMode,
6235
- baseroute: baseroute || '',
6236
6948
  fiber: true,
6237
6949
  defaultPage: defaultPage || '',
6238
6950
  disablePatchRequest: disablePatchRequest !== null && disablePatchRequest !== void 0 ? disablePatchRequest : false,
6951
+ routerMode: routerMode,
6952
+ baseroute: baseroute || '',
6239
6953
  });
6240
6954
  }
6241
6955
  }
@@ -6272,6 +6986,11 @@ class CreateApp {
6272
6986
  this.container = container;
6273
6987
  // mount before prerender exec mount (loading source), set isPrerender to false
6274
6988
  this.isPrerender = false;
6989
+ // dispatch state event to micro app
6990
+ // TODO: statechange 还是 state-change,保持一致
6991
+ dispatchCustomEventToMicroApp(this, 'statechange', {
6992
+ appState: appStates.LOADING
6993
+ });
6275
6994
  // reset app state to LOADING
6276
6995
  return this.setAppState(appStates.LOADING);
6277
6996
  }
@@ -6292,6 +7011,14 @@ class CreateApp {
6292
7011
  if (this.isPrerender &&
6293
7012
  isDivElement(this.container) &&
6294
7013
  this.container.hasAttribute('prerender')) {
7014
+ /**
7015
+ * current this.container is <div prerender='true'></div>
7016
+ * set this.container to <micro-app></micro-app>
7017
+ * NOTE:
7018
+ * 1. must exec before this.sandBox.rebuildEffectSnapshot
7019
+ * 2. must exec before this.preRenderEvents?.forEach((cb) => cb())
7020
+ */
7021
+ this.container = this.cloneContainer(container, this.container, false);
6295
7022
  /**
6296
7023
  * rebuild effect event of window, document, data center
6297
7024
  * explain:
@@ -6300,14 +7027,6 @@ class CreateApp {
6300
7027
  * 3. rebuild after js exec end, normal recovery effect event
6301
7028
  */
6302
7029
  (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.rebuildEffectSnapshot();
6303
- // current this.container is <div prerender='true'></div>
6304
- cloneContainer(container, this.container, false);
6305
- /**
6306
- * set this.container to <micro-app></micro-app>
6307
- * NOTE:
6308
- * must exec before this.preRenderEvents?.forEach((cb) => cb())
6309
- */
6310
- this.container = container;
6311
7030
  (_b = this.preRenderEvents) === null || _b === void 0 ? void 0 : _b.forEach((cb) => cb());
6312
7031
  // reset isPrerender config
6313
7032
  this.isPrerender = false;
@@ -6318,10 +7037,12 @@ class CreateApp {
6318
7037
  }
6319
7038
  else {
6320
7039
  this.container = container;
6321
- this.inline = inline;
7040
+ this.inline = this.getInlineModeState(inline);
6322
7041
  this.fiber = fiber;
6323
7042
  this.routerMode = routerMode;
6324
- const dispatchBeforeMount = () => dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
7043
+ const dispatchBeforeMount = () => {
7044
+ dispatchLifecyclesEvent(this.container, this.name, lifeCycles.BEFOREMOUNT);
7045
+ };
6325
7046
  if (this.isPrerender) {
6326
7047
  ((_d = this.preRenderEvents) !== null && _d !== void 0 ? _d : (this.preRenderEvents = [])).push(dispatchBeforeMount);
6327
7048
  }
@@ -6329,8 +7050,12 @@ class CreateApp {
6329
7050
  dispatchBeforeMount();
6330
7051
  }
6331
7052
  this.setAppState(appStates.MOUNTING);
6332
- // TODO: 将所有cloneContainer中的'as Element'去掉,兼容shadowRoot的场景
6333
- cloneContainer(this.container, this.source.html, !this.umdMode);
7053
+ // dispatch state event to micro app
7054
+ dispatchCustomEventToMicroApp(this, 'statechange', {
7055
+ appState: appStates.MOUNTING
7056
+ });
7057
+ // TODO: 兼容shadowRoot的场景
7058
+ this.cloneContainer(this.container, this.source.html, !this.umdMode);
6334
7059
  (_e = this.sandBox) === null || _e === void 0 ? void 0 : _e.start({
6335
7060
  umdMode: this.umdMode,
6336
7061
  baseroute,
@@ -6338,37 +7063,35 @@ class CreateApp {
6338
7063
  disablePatchRequest,
6339
7064
  });
6340
7065
  if (!this.umdMode) {
6341
- // update element info of html
6342
- (_f = this.sandBox) === null || _f === void 0 ? void 0 : _f.actionBeforeExecScripts(this.container);
6343
- // if all js are executed, param isFinished will be true
6344
- execScripts(this, (isFinished) => {
6345
- if (!this.umdMode) {
6346
- const { mount, unmount } = this.getUmdLibraryHooks();
6347
- /**
6348
- * umdHookUnmount can works in default mode
6349
- * register through window.unmount
6350
- */
6351
- this.umdHookUnmount = unmount;
7066
+ // patch element info of html
7067
+ (_f = this.sandBox) === null || _f === void 0 ? void 0 : _f.actionsBeforeExecScripts(this.container, (mount, unmount) => {
7068
+ var _a;
7069
+ if (!this.umdMode && !this.isUnmounted()) {
7070
+ this.umdHookMount = isFunction(mount) ? mount : null;
7071
+ // umdHookUnmount can works in default mode, register by window.unmount
7072
+ this.umdHookUnmount = isFunction(unmount) ? unmount : null;
6352
7073
  // if mount & unmount is function, the sub app is umd mode
6353
- if (isFunction(mount) && isFunction(unmount)) {
6354
- this.umdHookMount = mount;
6355
- // sandbox must exist
6356
- this.sandBox.markUmdMode(this.umdMode = true);
7074
+ if (isFunction(this.umdHookMount) && isFunction(this.umdHookUnmount)) {
7075
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.markUmdMode(this.umdMode = true);
6357
7076
  try {
6358
- this.handleMounted(this.umdHookMount(microApp.getData(this.name, true)));
7077
+ // if appState is mounted, it means that isFinished is true and this.handleMounted has already been executed, just exec this.umdHookMount
7078
+ if (this.getAppState() === appStates.MOUNTED) {
7079
+ this.umdHookMount(microApp.getData(this.name, true));
7080
+ }
7081
+ else {
7082
+ this.handleMounted(this.umdHookMount(microApp.getData(this.name, true)));
7083
+ }
6359
7084
  }
6360
7085
  catch (e) {
6361
- /**
6362
- * TODO:
6363
- * 1. 是否应该直接抛出错误
6364
- * 2. 是否应该触发error生命周期
6365
- */
6366
- logError('An error occurred in window.mount \n', this.name, e);
7086
+ logError('An error occurred when mount \n', this.name, e);
6367
7087
  }
6368
7088
  }
6369
- else if (isFinished === true) {
6370
- this.handleMounted();
6371
- }
7089
+ }
7090
+ });
7091
+ // if all js are executed, param isFinished will be true
7092
+ execScripts(this, (isFinished) => {
7093
+ if (!this.umdMode && isFinished === true) {
7094
+ this.handleMounted();
6372
7095
  }
6373
7096
  });
6374
7097
  }
@@ -6378,13 +7101,22 @@ class CreateApp {
6378
7101
  this.handleMounted(this.umdHookMount(microApp.getData(this.name, true)));
6379
7102
  }
6380
7103
  catch (e) {
6381
- logError('An error occurred in window.mount \n', this.name, e);
7104
+ logError('An error occurred when mount \n', this.name, e);
6382
7105
  }
6383
7106
  }
6384
7107
  }
6385
7108
  };
6386
- // TODO: any替换为iframe沙箱类型
6387
- this.iframe ? this.sandBox.sandboxReady.then(nextAction) : nextAction();
7109
+ /**
7110
+ * Initialization of sandbox is async, especially iframe sandbox are macro tasks
7111
+ * when child apps switch quickly, we need to pay attention to the following points:
7112
+ * NOTE:
7113
+ * 1. unmount app before exec nextAction (especially: iframe sandbox + default mode + remount)
7114
+ * this.container is null, this.sandBox will not start
7115
+ * 2. remount app of note 1
7116
+ * 3. unmount app during exec js
7117
+ */
7118
+ // TODO: 可优化?
7119
+ this.sandBox ? this.sandBox.sandboxReady.then(() => !this.isUnmounted() && nextAction()) : nextAction();
6388
7120
  }
6389
7121
  /**
6390
7122
  * handle for promise umdHookMount
@@ -6393,16 +7125,17 @@ class CreateApp {
6393
7125
  handleMounted(umdHookMountResult) {
6394
7126
  var _a, _b;
6395
7127
  const dispatchAction = () => {
7128
+ const nextAction = () => this.actionsAfterMounted();
6396
7129
  if (isPromise(umdHookMountResult)) {
6397
7130
  umdHookMountResult
6398
- .then(() => this.dispatchMountedEvent())
7131
+ .then(nextAction)
6399
7132
  .catch((e) => {
6400
7133
  logError('An error occurred in window.mount \n', this.name, e);
6401
- this.dispatchMountedEvent();
7134
+ nextAction();
6402
7135
  });
6403
7136
  }
6404
7137
  else {
6405
- this.dispatchMountedEvent();
7138
+ nextAction();
6406
7139
  }
6407
7140
  };
6408
7141
  if (this.isPrerender) {
@@ -6416,12 +7149,18 @@ class CreateApp {
6416
7149
  /**
6417
7150
  * dispatch mounted event when app run finished
6418
7151
  */
6419
- dispatchMountedEvent() {
7152
+ actionsAfterMounted() {
6420
7153
  var _a;
6421
7154
  if (!this.isUnmounted()) {
6422
7155
  this.setAppState(appStates.MOUNTED);
6423
7156
  // call window.onmount of child app
6424
7157
  execMicroAppGlobalHook(this.getMicroAppGlobalHook(microGlobalEvent.ONMOUNT), this.name, microGlobalEvent.ONMOUNT, microApp.getData(this.name, true));
7158
+ // dispatch state event to micro app
7159
+ dispatchCustomEventToMicroApp(this, 'statechange', {
7160
+ appState: appStates.MOUNTED
7161
+ });
7162
+ // dispatch mounted event to micro app
7163
+ dispatchCustomEventToMicroApp(this, 'mounted');
6425
7164
  // dispatch event mounted to parent
6426
7165
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.MOUNTED);
6427
7166
  /**
@@ -6444,6 +7183,8 @@ class CreateApp {
6444
7183
  * unmount app
6445
7184
  * NOTE:
6446
7185
  * 1. do not add any params on account of unmountApp
7186
+ * 2. this.container maybe null: Initialization of sandbox is async, child app may unmount before exec nextAction of mount
7187
+ * 3. unmount app when loading files (this.container is not null)
6447
7188
  * @param destroy completely destroy, delete cache resources
6448
7189
  * @param clearData clear data of dateCenter
6449
7190
  * @param keepRouteState keep route state when unmount, default is false
@@ -6453,25 +7194,12 @@ class CreateApp {
6453
7194
  var _a;
6454
7195
  destroy = destroy || this.state === appStates.LOAD_FAILED;
6455
7196
  this.setAppState(appStates.UNMOUNT);
6456
- let umdHookUnmountResult = null;
6457
7197
  try {
6458
- // call umd unmount hook before the sandbox is cleared
6459
- umdHookUnmountResult = (_a = this.umdHookUnmount) === null || _a === void 0 ? void 0 : _a.call(this, microApp.getData(this.name, true));
7198
+ this.handleUnmounted(destroy, clearData, keepRouteState, unmountcb, (_a = this.umdHookUnmount) === null || _a === void 0 ? void 0 : _a.call(this, microApp.getData(this.name, true)));
6460
7199
  }
6461
7200
  catch (e) {
6462
- logError('An error occurred in window.unmount \n', this.name, e);
7201
+ logError('An error occurred when unmount \n', this.name, e);
6463
7202
  }
6464
- // dispatch unmount event to micro app
6465
- dispatchCustomEventToMicroApp(this, 'unmount');
6466
- // call window.onunmount of child app
6467
- execMicroAppGlobalHook(this.getMicroAppGlobalHook(microGlobalEvent.ONUNMOUNT), this.name, microGlobalEvent.ONUNMOUNT);
6468
- this.handleUnmounted({
6469
- destroy,
6470
- clearData,
6471
- keepRouteState,
6472
- unmountcb,
6473
- umdHookUnmountResult,
6474
- });
6475
7203
  }
6476
7204
  /**
6477
7205
  * handle for promise umdHookUnmount
@@ -6481,8 +7209,16 @@ class CreateApp {
6481
7209
  * @param unmountcb callback of unmount
6482
7210
  * @param umdHookUnmountResult result of umdHookUnmount
6483
7211
  */
6484
- handleUnmounted({ destroy, clearData, keepRouteState, unmountcb, umdHookUnmountResult, }) {
6485
- const nextAction = () => this.actionsForUnmount({
7212
+ handleUnmounted(destroy, clearData, keepRouteState, unmountcb, umdHookUnmountResult) {
7213
+ // dispatch state event to micro app
7214
+ dispatchCustomEventToMicroApp(this, 'statechange', {
7215
+ appState: appStates.UNMOUNT
7216
+ });
7217
+ // dispatch unmount event to micro app
7218
+ dispatchCustomEventToMicroApp(this, 'unmount');
7219
+ // call window.onunmount of child app
7220
+ execMicroAppGlobalHook(this.getMicroAppGlobalHook(microGlobalEvent.ONUNMOUNT), this.name, microGlobalEvent.ONUNMOUNT);
7221
+ const nextAction = () => this.actionsAfterUnmounted({
6486
7222
  destroy,
6487
7223
  clearData,
6488
7224
  keepRouteState,
@@ -6491,7 +7227,12 @@ class CreateApp {
6491
7227
  if (isPromise(umdHookUnmountResult)) {
6492
7228
  // async window.unmount will cause appName bind error in nest app
6493
7229
  removeDomScope();
6494
- umdHookUnmountResult.then(nextAction).catch(nextAction);
7230
+ umdHookUnmountResult
7231
+ .then(nextAction)
7232
+ .catch((e) => {
7233
+ logError('An error occurred in window.unmount \n', this.name, e);
7234
+ nextAction();
7235
+ });
6495
7236
  }
6496
7237
  else {
6497
7238
  nextAction();
@@ -6504,10 +7245,10 @@ class CreateApp {
6504
7245
  * @param keepRouteState keep route state when unmount, default is false
6505
7246
  * @param unmountcb callback of unmount
6506
7247
  */
6507
- actionsForUnmount({ destroy, clearData, keepRouteState, unmountcb, }) {
7248
+ actionsAfterUnmounted({ destroy, clearData, keepRouteState, unmountcb, }) {
6508
7249
  var _a;
6509
7250
  if (this.umdMode && this.container && !destroy) {
6510
- cloneContainer(this.source.html, this.container, false);
7251
+ this.cloneContainer(this.source.html, this.container, false);
6511
7252
  }
6512
7253
  /**
6513
7254
  * this.container maybe contains micro-app element, stop sandbox should exec after cloneContainer
@@ -6527,12 +7268,27 @@ class CreateApp {
6527
7268
  unmountcb === null || unmountcb === void 0 ? void 0 : unmountcb();
6528
7269
  }
6529
7270
  clearOptions(destroy) {
6530
- this.container.innerHTML = '';
6531
- this.container = null;
7271
+ var _a, _b;
6532
7272
  this.isPrerender = false;
6533
7273
  this.preRenderEvents = null;
6534
7274
  this.setKeepAliveState(null);
7275
+ if (this.container) {
7276
+ this.container.innerHTML = '';
7277
+ this.container = null;
7278
+ }
7279
+ else if (!this.umdMode) {
7280
+ /**
7281
+ * this.container is null means sandBox.start has not exec, so sandBox.stop won't exec either
7282
+ * we should remove iframeElement in default mode manually
7283
+ */
7284
+ (_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.deleteIframeElement) === null || _b === void 0 ? void 0 : _b.call(_a);
7285
+ }
6535
7286
  // in iframe sandbox & default mode, delete the sandbox & iframeElement
7287
+ /**
7288
+ * TODO:
7289
+ * 1. with沙箱与iframe沙箱保持一致:with沙箱默认模式下删除 或者 iframe沙箱umd模式下保留
7290
+ * 2. 接1.0,this.sandBox置空,还需要注意后续app.sandBox相关操作,比如 scripts.ts --> app.iframe ? app.sandBox!.microBody : app.querySelector('micro-app-body'),如果是fiber或者预加载,会存在卸载后js还在处理的情况
7291
+ */
6536
7292
  if (this.iframe && !this.umdMode)
6537
7293
  this.sandBox = null;
6538
7294
  if (destroy)
@@ -6563,7 +7319,7 @@ class CreateApp {
6563
7319
  });
6564
7320
  // dispatch afterHidden event to base app
6565
7321
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.AFTERHIDDEN);
6566
- if (this.routerMode !== ROUTER_MODE_CUSTOM) {
7322
+ if (isRouterModeSearch(this.name)) {
6567
7323
  // called after lifeCyclesEvent
6568
7324
  (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.removeRouteInfoForKeepAliveApp();
6569
7325
  }
@@ -6574,7 +7330,7 @@ class CreateApp {
6574
7330
  getRootContainer(this.container).unmount();
6575
7331
  }
6576
7332
  else {
6577
- this.container = cloneContainer(pureCreateElement('div'), this.container, false);
7333
+ this.container = this.cloneContainer(pureCreateElement('div'), this.container, false);
6578
7334
  (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.recordAndReleaseEffect({ keepAlive: true });
6579
7335
  }
6580
7336
  callback === null || callback === void 0 ? void 0 : callback();
@@ -6582,6 +7338,14 @@ class CreateApp {
6582
7338
  // show app when connectedCallback called with keep-alive
6583
7339
  showKeepAliveApp(container) {
6584
7340
  var _a, _b;
7341
+ /**
7342
+ * NOTE:
7343
+ * 1. this.container must set to container(micro-app element) before exec rebuildEffectSnapshot
7344
+ * ISSUE: https://github.com/micro-zoe/micro-app/issues/1115
7345
+ * 2. rebuildEffectSnapshot must exec before dispatch beforeshow event
7346
+ */
7347
+ const oldContainer = this.container;
7348
+ this.container = container;
6585
7349
  (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.rebuildEffectSnapshot();
6586
7350
  // dispatch beforeShow event to micro-app
6587
7351
  dispatchCustomEventToMicroApp(this, 'appstate-change', {
@@ -6590,13 +7354,13 @@ class CreateApp {
6590
7354
  // dispatch beforeShow event to base app
6591
7355
  dispatchLifecyclesEvent(container, this.name, lifeCycles.BEFORESHOW);
6592
7356
  this.setKeepAliveState(keepAliveStates.KEEP_ALIVE_SHOW);
6593
- this.container = cloneContainer(container, this.container, false);
7357
+ this.cloneContainer(this.container, oldContainer, false);
6594
7358
  /**
6595
7359
  * TODO:
6596
7360
  * 问题:当路由模式为custom时,keep-alive应用在重新展示,是否需要根据子应用location信息更新浏览器地址?
6597
- * 暂时不这么做吧,因为无法确定二次展示时新旧地址是否相同,是否带有特殊信息
7361
+ * 暂时不这么做,因为无法确定二次展示时新旧地址是否相同,是否带有特殊信息
6598
7362
  */
6599
- if (this.routerMode !== ROUTER_MODE_CUSTOM) {
7363
+ if (isRouterModeSearch(this.name)) {
6600
7364
  // called before lifeCyclesEvent
6601
7365
  (_b = this.sandBox) === null || _b === void 0 ? void 0 : _b.setRouteInfoForKeepAliveApp();
6602
7366
  }
@@ -6612,8 +7376,40 @@ class CreateApp {
6612
7376
  * @param e Error
6613
7377
  */
6614
7378
  onerror(e) {
7379
+ // dispatch state event to micro app
7380
+ dispatchCustomEventToMicroApp(this, 'statechange', {
7381
+ appState: appStates.LOAD_FAILED
7382
+ });
6615
7383
  dispatchLifecyclesEvent(this.container, this.name, lifeCycles.ERROR, e);
6616
7384
  }
7385
+ /**
7386
+ * Parse htmlString to DOM
7387
+ * NOTE: iframe sandbox will use DOMParser of iframeWindow, with sandbox will use DOMParser of base app
7388
+ * @param htmlString DOMString
7389
+ * @returns parsed DOM
7390
+ */
7391
+ parseHtmlString(htmlString) {
7392
+ var _a;
7393
+ const DOMParser = ((_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow) ? this.sandBox.proxyWindow.DOMParser
7394
+ : globalEnv.rawWindow.DOMParser;
7395
+ return (new DOMParser()).parseFromString(htmlString, 'text/html').body;
7396
+ }
7397
+ /**
7398
+ * clone origin elements to target
7399
+ * @param target Accept cloned elements
7400
+ * @param origin Cloned element
7401
+ * @param deep deep clone or transfer dom
7402
+ */
7403
+ cloneContainer(target, origin, deep) {
7404
+ // 在基座接受到afterhidden方法后立即执行unmount,彻底destroy应用时,因为unmount时同步执行,所以this.container为null后才执行cloneContainer
7405
+ if (origin && target) {
7406
+ target.innerHTML = '';
7407
+ Array.from(deep ? this.parseHtmlString(origin.innerHTML).childNodes : origin.childNodes).forEach((node) => {
7408
+ target.appendChild(node);
7409
+ });
7410
+ }
7411
+ return target;
7412
+ }
6617
7413
  /**
6618
7414
  * Scene:
6619
7415
  * 1. create app
@@ -6622,17 +7418,15 @@ class CreateApp {
6622
7418
  */
6623
7419
  createSandbox() {
6624
7420
  if (this.useSandbox && !this.sandBox) {
6625
- if (this.iframe) {
6626
- this.sandBox = new IframeSandbox(this.name, this.url);
6627
- }
6628
- else {
6629
- this.sandBox = new WithSandBox(this.name, this.url);
6630
- }
7421
+ this.sandBox = this.iframe ? new IframeSandbox(this.name, this.url) : new WithSandBox(this.name, this.url);
6631
7422
  }
6632
7423
  }
6633
7424
  // set app state
6634
7425
  setAppState(state) {
7426
+ var _a;
6635
7427
  this.state = state;
7428
+ // set window.__MICRO_APP_STATE__
7429
+ (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.setStaticAppState(state);
6636
7430
  }
6637
7431
  // get app state
6638
7432
  getAppState() {
@@ -6654,23 +7448,6 @@ class CreateApp {
6654
7448
  isHidden() {
6655
7449
  return keepAliveStates.KEEP_ALIVE_HIDDEN === this.keepAliveState;
6656
7450
  }
6657
- // get umd library, if it not exist, return empty object
6658
- getUmdLibraryHooks() {
6659
- // after execScripts, the app maybe unmounted
6660
- if (!this.isUnmounted() && this.sandBox) {
6661
- const libraryName = getRootContainer(this.container).getAttribute('library') || `micro-app-${this.name}`;
6662
- const proxyWindow = this.sandBox.proxyWindow;
6663
- // compatible with pre versions
6664
- if (isObject(proxyWindow[libraryName])) {
6665
- return proxyWindow[libraryName];
6666
- }
6667
- return {
6668
- mount: proxyWindow.mount,
6669
- unmount: proxyWindow.unmount,
6670
- };
6671
- }
6672
- return {};
6673
- }
6674
7451
  getMicroAppGlobalHook(eventName) {
6675
7452
  var _a, _b;
6676
7453
  const listener = (_b = (_a = this.sandBox) === null || _a === void 0 ? void 0 : _a.proxyWindow) === null || _b === void 0 ? void 0 : _b[eventName];
@@ -6682,6 +7459,15 @@ class CreateApp {
6682
7459
  querySelectorAll(selectors) {
6683
7460
  return this.container ? globalEnv.rawElementQuerySelectorAll.call(this.container, selectors) : [];
6684
7461
  }
7462
+ /**
7463
+ * NOTE:
7464
+ * 1. If the iframe sandbox no longer enforces the use of inline mode in the future, the way getElementsByTagName retrieves the script from the iframe by default needs to be changed, because in non inline mode, the script in the iframe may be empty
7465
+ * @param inline inline mode config
7466
+ */
7467
+ getInlineModeState(inline) {
7468
+ var _a;
7469
+ return (_a = (this.iframe || inline)) !== null && _a !== void 0 ? _a : false;
7470
+ }
6685
7471
  }
6686
7472
  // iframe route mode
6687
7473
  function isIframeSandbox(appName) {
@@ -6767,15 +7553,11 @@ function handleNewNode(child, app) {
6767
7553
  * @param app app
6768
7554
  * @param method raw method
6769
7555
  * @param parent parent node
6770
- * @param targetChild target node
6771
- * @param passiveChild second param of insertBefore and replaceChild
7556
+ * @param targetNode target node
7557
+ * @param passiveNode second param of insertBefore and replaceChild
6772
7558
  */
6773
- function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild) {
6774
- const hijackParent = getHijackParent(parent, targetChild, app);
6775
- /**
6776
- * If passiveChild is not the child node, insertBefore replaceChild will have a problem, at this time, it will be degraded to appendChild
6777
- * E.g: document.head.insertBefore(targetChild, document.head.childNodes[0])
6778
- */
7559
+ function invokePrototypeMethod(app, rawMethod, parent, targetNode, passiveNode) {
7560
+ const hijackParent = getHijackParent(parent, targetNode, app);
6779
7561
  if (hijackParent) {
6780
7562
  /**
6781
7563
  * If parentNode is <micro-app-body>, return rawDocument.body
@@ -6792,9 +7574,9 @@ function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild
6792
7574
  if (!isIframeSandbox(app.name) &&
6793
7575
  isMicroAppBody(hijackParent) &&
6794
7576
  rawMethod !== globalEnv.rawRemoveChild) {
6795
- const descriptor = Object.getOwnPropertyDescriptor(targetChild, 'parentNode');
6796
- if ((!descriptor || descriptor.configurable) && !targetChild.__MICRO_APP_HAS_DPN__) {
6797
- rawDefineProperties(targetChild, {
7577
+ const descriptor = Object.getOwnPropertyDescriptor(targetNode, 'parentNode');
7578
+ if ((!descriptor || descriptor.configurable) && !targetNode.__MICRO_APP_HAS_DPN__) {
7579
+ rawDefineProperties(targetNode, {
6798
7580
  parentNode: {
6799
7581
  configurable: true,
6800
7582
  get() {
@@ -6814,57 +7596,85 @@ function invokePrototypeMethod(app, rawMethod, parent, targetChild, passiveChild
6814
7596
  });
6815
7597
  }
6816
7598
  }
7599
+ if ((process.env.NODE_ENV !== 'production') &&
7600
+ isIFrameElement(targetNode) &&
7601
+ rawMethod === globalEnv.rawAppendChild) {
7602
+ fixReactHMRConflict(app);
7603
+ }
6817
7604
  /**
6818
- * 1. If passiveChild exists, it must be insertBefore or replaceChild
6819
- * 2. When removeChild, targetChild may not be in microAppHead or head
7605
+ * 1. If passiveNode exists, it must be insertBefore or replaceChild
7606
+ * 2. When removeChild, targetNode may not be in microAppHead or head
7607
+ * NOTE:
7608
+ * 1. If passiveNode not in hijackParent, insertBefore replaceChild will be degraded to appendChild
7609
+ * E.g: document.head.replaceChild(targetNode, document.scripts[0])
7610
+ * 2. If passiveNode not in hijackParent but in parent and method is insertBefore, try insert it into the position corresponding to hijackParent
7611
+ * E.g: document.head.insertBefore(targetNode, document.head.childNodes[0])
7612
+ * ISSUE: https://github.com/micro-zoe/micro-app/issues/1071
6820
7613
  */
6821
- if (passiveChild && !hijackParent.contains(passiveChild)) {
6822
- return globalEnv.rawAppendChild.call(hijackParent, targetChild);
6823
- }
6824
- else if (rawMethod === globalEnv.rawRemoveChild && !hijackParent.contains(targetChild)) {
6825
- if (parent.contains(targetChild)) {
6826
- return rawMethod.call(parent, targetChild);
7614
+ if (passiveNode && !hijackParent.contains(passiveNode)) {
7615
+ if (rawMethod === globalEnv.rawInsertBefore && parent.contains(passiveNode)) {
7616
+ const indexOfParent = Array.from(parent.childNodes).indexOf(passiveNode);
7617
+ if (hijackParent.childNodes[indexOfParent]) {
7618
+ return invokeRawMethod(rawMethod, hijackParent, targetNode, hijackParent.childNodes[indexOfParent], app);
7619
+ }
6827
7620
  }
6828
- return targetChild;
7621
+ return globalEnv.rawAppendChild.call(hijackParent, targetNode);
6829
7622
  }
6830
- if ((process.env.NODE_ENV !== 'production') &&
6831
- isIFrameElement(targetChild) &&
6832
- rawMethod === globalEnv.rawAppendChild) {
6833
- fixReactHMRConflict(app);
7623
+ else if (rawMethod === globalEnv.rawRemoveChild && !hijackParent.contains(targetNode)) {
7624
+ if (parent.contains(targetNode)) {
7625
+ return rawMethod.call(parent, targetNode);
7626
+ }
7627
+ return targetNode;
6834
7628
  }
6835
- return invokeRawMethod(rawMethod, hijackParent, targetChild, passiveChild);
7629
+ return invokeRawMethod(rawMethod, hijackParent, targetNode, passiveNode, app);
6836
7630
  }
6837
- return invokeRawMethod(rawMethod, parent, targetChild, passiveChild);
7631
+ return invokeRawMethod(rawMethod, parent, targetNode, passiveNode, app);
6838
7632
  }
6839
7633
  // head/body map to micro-app-head/micro-app-body
6840
- function getHijackParent(parent, targetChild, app) {
7634
+ function getHijackParent(parent, targetNode, app) {
6841
7635
  if (app) {
6842
7636
  if (parent === document.head) {
6843
- if (app.iframe && isScriptElement(targetChild)) {
7637
+ if (app.iframe && isScriptElement(targetNode)) {
6844
7638
  return app.sandBox.microHead;
6845
7639
  }
6846
7640
  return app.querySelector('micro-app-head');
6847
7641
  }
6848
7642
  if (parent === document.body || parent === document.body.parentNode) {
6849
- if (app.iframe && isScriptElement(targetChild)) {
7643
+ if (app.iframe && isScriptElement(targetNode)) {
6850
7644
  return app.sandBox.microBody;
6851
7645
  }
6852
7646
  return app.querySelector('micro-app-body');
6853
7647
  }
6854
- if (app.iframe && isScriptElement(targetChild)) {
7648
+ if (app.iframe && isScriptElement(targetNode)) {
6855
7649
  return app.sandBox.microBody;
6856
7650
  }
6857
7651
  }
6858
7652
  return null;
6859
7653
  }
6860
- function invokeRawMethod(rawMethod, parent, targetChild, passiveChild) {
7654
+ function invokeRawMethod(rawMethod, parent, targetNode, passiveNode, app) {
6861
7655
  if (isPendMethod(rawMethod)) {
6862
- return rawMethod.call(parent, targetChild);
7656
+ /**
7657
+ * In iframe sandbox, script will pend to iframe.body, so we should reset rawMethod, because:
7658
+ * Element.prototype.append === DocumentFragment.prototype.append ==> false
7659
+ * Element.prototype.prepend === DocumentFragment.prototype.prepend ==> false
7660
+ */
7661
+ if ((app === null || app === void 0 ? void 0 : app.iframe) && isScriptElement(targetNode)) {
7662
+ if (rawMethod === globalEnv.rawFragmentAppend) {
7663
+ rawMethod = globalEnv.rawAppend;
7664
+ }
7665
+ else if (rawMethod === globalEnv.rawFragmentPrepend) {
7666
+ rawMethod = globalEnv.rawPrepend;
7667
+ }
7668
+ }
7669
+ return rawMethod.call(parent, targetNode);
6863
7670
  }
6864
- return rawMethod.call(parent, targetChild, passiveChild);
7671
+ return rawMethod.call(parent, targetNode, passiveNode);
6865
7672
  }
6866
7673
  function isPendMethod(method) {
6867
- return method === globalEnv.rawAppend || method === globalEnv.rawPrepend;
7674
+ return (method === globalEnv.rawAppend ||
7675
+ method === globalEnv.rawPrepend ||
7676
+ method === globalEnv.rawFragmentAppend ||
7677
+ method === globalEnv.rawFragmentPrepend);
6868
7678
  }
6869
7679
  /**
6870
7680
  * Attempt to complete the static resource address again before insert the node
@@ -6881,7 +7691,7 @@ function completePathDynamic(app, newChild) {
6881
7691
  globalEnv.rawSetAttribute.call(newChild, 'srcset', CompletionPath(newChild.getAttribute('srcset'), app.url));
6882
7692
  }
6883
7693
  }
6884
- else if (/^link$/i.test(newChild.tagName) && newChild.hasAttribute('href')) {
7694
+ else if (/^(a|link|image)$/i.test(newChild.tagName) && newChild.hasAttribute('href')) {
6885
7695
  globalEnv.rawSetAttribute.call(newChild, 'href', CompletionPath(newChild.getAttribute('href'), app.url));
6886
7696
  }
6887
7697
  }
@@ -6890,26 +7700,26 @@ function completePathDynamic(app, newChild) {
6890
7700
  * method of handle new node
6891
7701
  * @param parent parent node
6892
7702
  * @param newChild new node
6893
- * @param passiveChild passive node
7703
+ * @param passiveNode passive node
6894
7704
  * @param rawMethod method
6895
7705
  */
6896
- function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
7706
+ function commonElementHandler(parent, newChild, passiveNode, rawMethod) {
6897
7707
  const currentAppName = getCurrentAppName();
6898
7708
  if (isNode(newChild) &&
6899
7709
  !newChild.__PURE_ELEMENT__ &&
6900
7710
  (newChild.__MICRO_APP_NAME__ ||
6901
7711
  currentAppName)) {
6902
- newChild.__MICRO_APP_NAME__ = newChild.__MICRO_APP_NAME__ || currentAppName;
7712
+ updateElementInfo(newChild, newChild.__MICRO_APP_NAME__ || currentAppName);
6903
7713
  const app = appInstanceMap.get(newChild.__MICRO_APP_NAME__);
6904
7714
  if (app === null || app === void 0 ? void 0 : app.container) {
7715
+ if (isStyleElement(newChild)) {
7716
+ parent.getRootNode() instanceof ShadowRoot && newChild.setAttribute('ignore', 'true');
7717
+ }
6905
7718
  completePathDynamic(app, newChild);
6906
- return invokePrototypeMethod(app, rawMethod, parent, handleNewNode(newChild, app), passiveChild && getMappingNode(passiveChild));
7719
+ return invokePrototypeMethod(app, rawMethod, parent, handleNewNode(newChild, app), passiveNode && getMappingNode(passiveNode));
6907
7720
  }
6908
7721
  }
6909
- if (rawMethod === globalEnv.rawAppend || rawMethod === globalEnv.rawPrepend) {
6910
- return rawMethod.call(parent, newChild);
6911
- }
6912
- return rawMethod.call(parent, newChild, passiveChild);
7722
+ return invokeRawMethod(rawMethod, parent, newChild, passiveNode);
6913
7723
  }
6914
7724
  /**
6915
7725
  * Rewrite element prototype method
@@ -6917,36 +7727,20 @@ function commonElementHandler(parent, newChild, passiveChild, rawMethod) {
6917
7727
  function patchElementAndDocument() {
6918
7728
  patchDocument$2();
6919
7729
  const rawRootElement = globalEnv.rawRootElement;
7730
+ const rawRootNode = globalEnv.rawRootNode;
7731
+ const rawDocumentFragment = globalEnv.rawDocumentFragment;
6920
7732
  // prototype methods of add element👇
6921
- rawRootElement.prototype.appendChild = function appendChild(newChild) {
7733
+ rawRootNode.prototype.appendChild = function appendChild(newChild) {
6922
7734
  return commonElementHandler(this, newChild, null, globalEnv.rawAppendChild);
6923
7735
  };
6924
- rawRootElement.prototype.insertBefore = function insertBefore(newChild, refChild) {
7736
+ rawRootNode.prototype.insertBefore = function insertBefore(newChild, refChild) {
6925
7737
  return commonElementHandler(this, newChild, refChild, globalEnv.rawInsertBefore);
6926
7738
  };
6927
- rawRootElement.prototype.replaceChild = function replaceChild(newChild, oldChild) {
7739
+ rawRootNode.prototype.replaceChild = function replaceChild(newChild, oldChild) {
6928
7740
  return commonElementHandler(this, newChild, oldChild, globalEnv.rawReplaceChild);
6929
7741
  };
6930
- rawRootElement.prototype.append = function append(...nodes) {
6931
- let i = 0;
6932
- while (i < nodes.length) {
6933
- let node = nodes[i];
6934
- node = isNode(node) ? node : globalEnv.rawCreateTextNode.call(globalEnv.rawDocument, node);
6935
- commonElementHandler(this, markElement(node), null, globalEnv.rawAppend);
6936
- i++;
6937
- }
6938
- };
6939
- rawRootElement.prototype.prepend = function prepend(...nodes) {
6940
- let i = nodes.length;
6941
- while (i > 0) {
6942
- let node = nodes[i - 1];
6943
- node = isNode(node) ? node : globalEnv.rawCreateTextNode.call(globalEnv.rawDocument, node);
6944
- commonElementHandler(this, markElement(node), null, globalEnv.rawPrepend);
6945
- i--;
6946
- }
6947
- };
6948
7742
  // prototype methods of delete element👇
6949
- rawRootElement.prototype.removeChild = function removeChild(oldChild) {
7743
+ rawRootNode.prototype.removeChild = function removeChild(oldChild) {
6950
7744
  if (oldChild === null || oldChild === void 0 ? void 0 : oldChild.__MICRO_APP_NAME__) {
6951
7745
  const app = appInstanceMap.get(oldChild.__MICRO_APP_NAME__);
6952
7746
  if (app === null || app === void 0 ? void 0 : app.container) {
@@ -6961,6 +7755,24 @@ function patchElementAndDocument() {
6961
7755
  }
6962
7756
  return globalEnv.rawRemoveChild.call(this, oldChild);
6963
7757
  };
7758
+ rawDocumentFragment.prototype.append = rawRootElement.prototype.append = function append(...nodes) {
7759
+ let i = 0;
7760
+ while (i < nodes.length) {
7761
+ let node = nodes[i];
7762
+ node = isNode(node) ? node : globalEnv.rawCreateTextNode.call(globalEnv.rawDocument, node);
7763
+ commonElementHandler(this, markElement(node), null, isDocumentFragment(this) ? globalEnv.rawFragmentAppend : globalEnv.rawAppend);
7764
+ i++;
7765
+ }
7766
+ };
7767
+ rawDocumentFragment.prototype.prepend = rawRootElement.prototype.prepend = function prepend(...nodes) {
7768
+ let i = nodes.length;
7769
+ while (i > 0) {
7770
+ let node = nodes[i - 1];
7771
+ node = isNode(node) ? node : globalEnv.rawCreateTextNode.call(globalEnv.rawDocument, node);
7772
+ commonElementHandler(this, markElement(node), null, isDocumentFragment(this) ? globalEnv.rawFragmentPrepend : globalEnv.rawPrepend);
7773
+ i--;
7774
+ }
7775
+ };
6964
7776
  /**
6965
7777
  * The insertAdjacentElement method of the Element interface inserts a given element node at a given position relative to the element it is invoked upon.
6966
7778
  * NOTE:
@@ -6980,48 +7792,81 @@ function patchElementAndDocument() {
6980
7792
  }
6981
7793
  return globalEnv.rawInsertAdjacentElement.call(this, where, element);
6982
7794
  };
6983
- // patch cloneNode
6984
- rawRootElement.prototype.cloneNode = function cloneNode(deep) {
6985
- const clonedNode = globalEnv.rawCloneNode.call(this, deep);
6986
- this.__MICRO_APP_NAME__ && (clonedNode.__MICRO_APP_NAME__ = this.__MICRO_APP_NAME__);
6987
- return clonedNode;
6988
- };
6989
- function getQueryTarget(node) {
6990
- const currentAppName = getCurrentAppName();
6991
- if ((node === document.body || node === document.head) && currentAppName) {
7795
+ /**
7796
+ * document.body(head).querySelector(querySelectorAll) hijack to microAppBody(microAppHead).querySelector(querySelectorAll)
7797
+ * NOTE:
7798
+ * 1. May cause some problems!
7799
+ * 2. Add config options?
7800
+ */
7801
+ function getElementQueryTarget(targetNode) {
7802
+ const currentAppName = getIframeCurrentAppName() || getCurrentAppName();
7803
+ if ((targetNode === document.body || targetNode === document.head) && currentAppName) {
6992
7804
  const app = appInstanceMap.get(currentAppName);
6993
7805
  if (app === null || app === void 0 ? void 0 : app.container) {
6994
- if (node === document.body) {
7806
+ if (targetNode === document.body) {
6995
7807
  return app.querySelector('micro-app-body');
6996
7808
  }
6997
- else if (node === document.head) {
7809
+ else if (targetNode === document.head) {
6998
7810
  return app.querySelector('micro-app-head');
6999
7811
  }
7000
7812
  }
7001
7813
  }
7002
- return null;
7814
+ return targetNode;
7815
+ }
7816
+ /**
7817
+ * In iframe sandbox, script will render to iframe instead of micro-app-body
7818
+ * So when query elements, we need to search both micro-app and iframe
7819
+ * @param isEmpty get empty result
7820
+ * @param targetNode targetNode element
7821
+ * @param result origin result
7822
+ * @param selectors selectors
7823
+ * @param methodName querySelector or querySelectorAll
7824
+ */
7825
+ function getElementQueryResult(isEmpty, targetNode, result, selectors, methodName) {
7826
+ if (isEmpty) {
7827
+ const currentAppName = getIframeCurrentAppName() || getCurrentAppName();
7828
+ if (currentAppName && isIframeSandbox(currentAppName)) {
7829
+ const app = appInstanceMap.get(currentAppName);
7830
+ if (isMicroAppHead(targetNode)) {
7831
+ return app.sandBox.microHead[methodName](selectors);
7832
+ }
7833
+ if (isMicroAppBody(targetNode)) {
7834
+ return app.sandBox.microBody[methodName](selectors);
7835
+ }
7836
+ }
7837
+ }
7838
+ return result;
7003
7839
  }
7004
7840
  rawRootElement.prototype.querySelector = function querySelector(selectors) {
7005
7841
  var _a;
7006
- const target = (_a = getQueryTarget(this)) !== null && _a !== void 0 ? _a : this;
7007
- return globalEnv.rawElementQuerySelector.call(target, selectors);
7842
+ const _this = (_a = getElementQueryTarget(this)) !== null && _a !== void 0 ? _a : this;
7843
+ const result = globalEnv.rawElementQuerySelector.call(_this, selectors);
7844
+ return getElementQueryResult(isNull(result) && _this !== this, _this, result, selectors, 'querySelector');
7008
7845
  };
7009
7846
  rawRootElement.prototype.querySelectorAll = function querySelectorAll(selectors) {
7010
7847
  var _a;
7011
- const target = (_a = getQueryTarget(this)) !== null && _a !== void 0 ? _a : this;
7012
- return globalEnv.rawElementQuerySelectorAll.call(target, selectors);
7848
+ const _this = (_a = getElementQueryTarget(this)) !== null && _a !== void 0 ? _a : this;
7849
+ const result = globalEnv.rawElementQuerySelectorAll.call(_this, selectors);
7850
+ return getElementQueryResult(!result.length && _this !== this, _this, result, selectors, 'querySelectorAll');
7013
7851
  };
7014
7852
  // rewrite setAttribute, complete resource address
7015
7853
  rawRootElement.prototype.setAttribute = function setAttribute(key, value) {
7016
- const appName = this.__MICRO_APP_NAME__ || getCurrentAppName();
7017
- if (appName &&
7018
- appInstanceMap.has(appName) &&
7019
- (((key === 'src' || key === 'srcset') && /^(img|script|video|audio|source|embed)$/i.test(this.tagName)) ||
7020
- (key === 'href' && /^link$/i.test(this.tagName)))) {
7021
- const app = appInstanceMap.get(appName);
7022
- value = CompletionPath(value, app.url);
7854
+ if (/^micro-app(-\S+)?/i.test(this.tagName) &&
7855
+ key === 'data' &&
7856
+ this.setAttribute !== rawRootElement.prototype.setAttribute) {
7857
+ this.setAttribute(key, value);
7858
+ }
7859
+ else {
7860
+ const appName = this.__MICRO_APP_NAME__ || getCurrentAppName();
7861
+ if (appName &&
7862
+ appInstanceMap.has(appName) &&
7863
+ (((key === 'src' || key === 'srcset') && /^(img|script|video|audio|source|embed)$/i.test(this.tagName)) ||
7864
+ (key === 'href' && /^(a|link|image)$/i.test(this.tagName)))) {
7865
+ const app = appInstanceMap.get(appName);
7866
+ value = CompletionPath(value, app.url);
7867
+ }
7868
+ globalEnv.rawSetAttribute.call(this, key, value);
7023
7869
  }
7024
- globalEnv.rawSetAttribute.call(this, key, value);
7025
7870
  };
7026
7871
  /**
7027
7872
  * TODO: 兼容直接通过img.src等操作设置的资源
@@ -7048,7 +7893,7 @@ function patchElementAndDocument() {
7048
7893
  // return get?.call(this)
7049
7894
  // },
7050
7895
  // set: function (value) {
7051
- // const currentAppName = getCurrentAppName()
7896
+ // const currentAppName = this.__MICRO_APP_NAME__ || getCurrentAppName()
7052
7897
  // if (currentAppName && appInstanceMap.has(currentAppName)) {
7053
7898
  // const app = appInstanceMap.get(currentAppName)
7054
7899
  // value = CompletionPath(value, app!.url)
@@ -7057,6 +7902,43 @@ function patchElementAndDocument() {
7057
7902
  // },
7058
7903
  // })
7059
7904
  // })
7905
+ rawDefineProperty(rawRootNode.prototype, 'parentNode', {
7906
+ configurable: true,
7907
+ enumerable: true,
7908
+ get() {
7909
+ var _a, _b, _c;
7910
+ /**
7911
+ * hijack parentNode of html for with sandbox
7912
+ * Scenes:
7913
+ * 1. element-ui@2/lib/utils/popper.js
7914
+ * // root is child app window, so root.document is proxyDocument or microDocument
7915
+ * if (element.parentNode === root.document) ...
7916
+ */
7917
+ const currentAppName = getIframeCurrentAppName() || getCurrentAppName();
7918
+ if (currentAppName && this === globalEnv.rawDocument.firstElementChild) {
7919
+ const microDocument = (_c = (_b = (_a = appInstanceMap.get(currentAppName)) === null || _a === void 0 ? void 0 : _a.sandBox) === null || _b === void 0 ? void 0 : _b.proxyWindow) === null || _c === void 0 ? void 0 : _c.document;
7920
+ if (microDocument)
7921
+ return microDocument;
7922
+ }
7923
+ // NOTE: run after hijack html.parentNode
7924
+ const result = globalEnv.rawParentNodeDesc.get.call(this);
7925
+ /**
7926
+ * If parentNode is <micro-app-body>, return rawDocument.body
7927
+ * Scenes:
7928
+ * 1. element-ui@2/lib/utils/vue-popper.js
7929
+ * if (this.popperElm.parentNode === document.body) ...
7930
+ * WARNING:
7931
+ * Will it cause other problems ?
7932
+ * e.g. target.parentNode.remove(target)
7933
+ * BUG:
7934
+ * 1. vue2 umdMode, throw error when render again (<div id='app'></div> will be deleted when render again ) -- Abandon this way at 2023.2.28 before v1.0.0-beta.0, it will cause vue2 throw error when render again
7935
+ */
7936
+ // if (isMicroAppBody(result) && appInstanceMap.get(this.__MICRO_APP_NAME__)?.container) {
7937
+ // return document.body
7938
+ // }
7939
+ return result;
7940
+ },
7941
+ });
7060
7942
  rawDefineProperty(rawRootElement.prototype, 'innerHTML', {
7061
7943
  configurable: true,
7062
7944
  enumerable: true,
@@ -7065,50 +7947,26 @@ function patchElementAndDocument() {
7065
7947
  },
7066
7948
  set(code) {
7067
7949
  globalEnv.rawInnerHTMLDesc.set.call(this, code);
7068
- const currentAppName = getCurrentAppName();
7950
+ const currentAppName = this.__MICRO_APP_NAME__ || getIframeCurrentAppName() || getCurrentAppName();
7069
7951
  Array.from(this.children).forEach((child) => {
7070
7952
  if (isElement(child) && currentAppName) {
7071
- child.__MICRO_APP_NAME__ = currentAppName;
7953
+ updateElementInfo(child, currentAppName);
7072
7954
  }
7073
7955
  });
7074
7956
  }
7075
7957
  });
7076
- /**
7077
- * NOTE:Abandon this way at 2023.2.28 before v1.0.0-beta.0, it will cause vue2 throw error when render again
7078
- */
7079
- // rawDefineProperty(Node.prototype, 'parentNode', {
7080
- // configurable: true,
7081
- // enumerable: true,
7082
- // get () {
7083
- // const result = globalEnv.rawParentNodeDesc.get.call(this)
7084
- // /**
7085
- // * If parentNode is <micro-app-body>, return rawDocument.body
7086
- // * Scenes:
7087
- // * 1. element-ui@2/lib/utils/vue-popper.js
7088
- // * if (this.popperElm.parentNode === document.body) ...
7089
- // * WARNING:
7090
- // * Will it cause other problems ?
7091
- // * e.g. target.parentNode.remove(target)
7092
- // * BUG:
7093
- // * 1. vue2 umdMode, throw error when render again (<div id='app'></div> will be deleted when render again )
7094
- // */
7095
- // if (isMicroAppBody(result) && appInstanceMap.get(this.__MICRO_APP_NAME__)?.container) {
7096
- // return document.body
7097
- // }
7098
- // return result
7099
- // },
7100
- // set: undefined,
7101
- // })
7958
+ // patch cloneNode
7959
+ rawRootNode.prototype.cloneNode = function cloneNode(deep) {
7960
+ const clonedNode = globalEnv.rawCloneNode.call(this, deep);
7961
+ return updateElementInfo(clonedNode, this.__MICRO_APP_NAME__);
7962
+ };
7102
7963
  }
7103
7964
  /**
7104
7965
  * Mark the newly created element in the micro application
7105
7966
  * @param element new element
7106
7967
  */
7107
7968
  function markElement(element) {
7108
- const currentAppName = getCurrentAppName();
7109
- if (currentAppName)
7110
- element.__MICRO_APP_NAME__ = currentAppName;
7111
- return element;
7969
+ return updateElementInfo(element, getCurrentAppName());
7112
7970
  }
7113
7971
  // methods of document
7114
7972
  function patchDocument$2() {
@@ -7126,14 +7984,15 @@ function patchDocument$2() {
7126
7984
  const element = globalEnv.rawCreateElementNS.call(getBindTarget(this), namespaceURI, name, options);
7127
7985
  return markElement(element);
7128
7986
  };
7129
- rawRootDocument.prototype.createDocumentFragment = function createDocumentFragment() {
7130
- const element = globalEnv.rawCreateDocumentFragment.call(getBindTarget(this));
7131
- return markElement(element);
7132
- };
7987
+ // TODO: 放开
7133
7988
  // rawRootDocument.prototype.createTextNode = function createTextNode (data: string): Text {
7134
7989
  // const element = globalEnv.rawCreateTextNode.call(getBindTarget(this), data)
7135
7990
  // return markElement(element)
7136
7991
  // }
7992
+ rawRootDocument.prototype.createDocumentFragment = function createDocumentFragment() {
7993
+ const element = globalEnv.rawCreateDocumentFragment.call(getBindTarget(this));
7994
+ return markElement(element);
7995
+ };
7137
7996
  rawRootDocument.prototype.createComment = function createComment(data) {
7138
7997
  const element = globalEnv.rawCreateComment.call(getBindTarget(this), data);
7139
7998
  return markElement(element);
@@ -7146,7 +8005,7 @@ function patchDocument$2() {
7146
8005
  if (!currentAppName ||
7147
8006
  !selectors ||
7148
8007
  isUniqueElement(selectors) ||
7149
- // see https://github.com/micro-zoe/micro-app/issues/56
8008
+ // ISSUE: https://github.com/micro-zoe/micro-app/issues/56
7150
8009
  rawDocument !== _this) {
7151
8010
  return globalEnv.rawQuerySelector.call(_this, selectors);
7152
8011
  }
@@ -7237,16 +8096,18 @@ function releasePatchElementAndDocument() {
7237
8096
  removeDomScope();
7238
8097
  releasePatchDocument();
7239
8098
  const rawRootElement = globalEnv.rawRootElement;
7240
- rawRootElement.prototype.appendChild = globalEnv.rawAppendChild;
7241
- rawRootElement.prototype.insertBefore = globalEnv.rawInsertBefore;
7242
- rawRootElement.prototype.replaceChild = globalEnv.rawReplaceChild;
7243
- rawRootElement.prototype.removeChild = globalEnv.rawRemoveChild;
8099
+ const rawRootNode = globalEnv.rawRootNode;
8100
+ rawRootNode.prototype.appendChild = globalEnv.rawAppendChild;
8101
+ rawRootNode.prototype.insertBefore = globalEnv.rawInsertBefore;
8102
+ rawRootNode.prototype.replaceChild = globalEnv.rawReplaceChild;
8103
+ rawRootNode.prototype.removeChild = globalEnv.rawRemoveChild;
8104
+ rawRootNode.prototype.cloneNode = globalEnv.rawCloneNode;
7244
8105
  rawRootElement.prototype.append = globalEnv.rawAppend;
7245
8106
  rawRootElement.prototype.prepend = globalEnv.rawPrepend;
7246
- rawRootElement.prototype.cloneNode = globalEnv.rawCloneNode;
7247
8107
  rawRootElement.prototype.querySelector = globalEnv.rawElementQuerySelector;
7248
8108
  rawRootElement.prototype.querySelectorAll = globalEnv.rawElementQuerySelectorAll;
7249
8109
  rawRootElement.prototype.setAttribute = globalEnv.rawSetAttribute;
8110
+ rawDefineProperty(rawRootNode.prototype, 'parentNode', globalEnv.rawParentNodeDesc);
7250
8111
  rawDefineProperty(rawRootElement.prototype, 'innerHTML', globalEnv.rawInnerHTMLDesc);
7251
8112
  }
7252
8113
  // Set the style of micro-app-head and micro-app-body
@@ -7277,15 +8138,18 @@ function initGlobalEnv() {
7277
8138
  const rawRootElement = rawWindow.Element;
7278
8139
  const rawRootNode = rawWindow.Node;
7279
8140
  const rawRootEventTarget = rawWindow.EventTarget;
8141
+ const rawDocumentFragment = rawWindow.DocumentFragment;
7280
8142
  // save patch raw methods, pay attention to this binding
8143
+ const rawAppendChild = rawRootNode.prototype.appendChild;
8144
+ const rawInsertBefore = rawRootNode.prototype.insertBefore;
8145
+ const rawReplaceChild = rawRootNode.prototype.replaceChild;
8146
+ const rawRemoveChild = rawRootNode.prototype.removeChild;
7281
8147
  const rawSetAttribute = rawRootElement.prototype.setAttribute;
7282
- const rawAppendChild = rawRootElement.prototype.appendChild;
7283
- const rawInsertBefore = rawRootElement.prototype.insertBefore;
7284
- const rawReplaceChild = rawRootElement.prototype.replaceChild;
7285
- const rawRemoveChild = rawRootElement.prototype.removeChild;
7286
8148
  const rawAppend = rawRootElement.prototype.append;
7287
8149
  const rawPrepend = rawRootElement.prototype.prepend;
7288
- const rawCloneNode = rawRootElement.prototype.cloneNode;
8150
+ const rawFragmentAppend = rawDocumentFragment.prototype.append;
8151
+ const rawFragmentPrepend = rawDocumentFragment.prototype.prepend;
8152
+ const rawCloneNode = rawRootNode.prototype.cloneNode;
7289
8153
  const rawElementQuerySelector = rawRootElement.prototype.querySelector;
7290
8154
  const rawElementQuerySelectorAll = rawRootElement.prototype.querySelectorAll;
7291
8155
  const rawInsertAdjacentElement = rawRootElement.prototype.insertAdjacentElement;
@@ -7294,8 +8158,8 @@ function initGlobalEnv() {
7294
8158
  // Document proto methods
7295
8159
  const rawCreateElement = rawRootDocument.prototype.createElement;
7296
8160
  const rawCreateElementNS = rawRootDocument.prototype.createElementNS;
7297
- const rawCreateDocumentFragment = rawRootDocument.prototype.createDocumentFragment;
7298
8161
  const rawCreateTextNode = rawRootDocument.prototype.createTextNode;
8162
+ const rawCreateDocumentFragment = rawRootDocument.prototype.createDocumentFragment;
7299
8163
  const rawCreateComment = rawRootDocument.prototype.createComment;
7300
8164
  const rawQuerySelector = rawRootDocument.prototype.querySelector;
7301
8165
  const rawQuerySelectorAll = rawRootDocument.prototype.querySelectorAll;
@@ -7303,13 +8167,10 @@ function initGlobalEnv() {
7303
8167
  const rawGetElementsByClassName = rawRootDocument.prototype.getElementsByClassName;
7304
8168
  const rawGetElementsByTagName = rawRootDocument.prototype.getElementsByTagName;
7305
8169
  const rawGetElementsByName = rawRootDocument.prototype.getElementsByName;
7306
- const ImageProxy = new Proxy(Image, {
8170
+ // TODO: 将ImageProxy移出去
8171
+ const ImageProxy = new Proxy(rawWindow.Image, {
7307
8172
  construct(Target, args) {
7308
- const elementImage = new Target(...args);
7309
- const currentAppName = getCurrentAppName();
7310
- if (currentAppName)
7311
- elementImage.__MICRO_APP_NAME__ = currentAppName;
7312
- return elementImage;
8173
+ return updateElementInfo(new Target(...args), getCurrentAppName());
7313
8174
  },
7314
8175
  });
7315
8176
  /**
@@ -7335,6 +8196,7 @@ function initGlobalEnv() {
7335
8196
  rawRootDocument,
7336
8197
  rawRootElement,
7337
8198
  rawRootNode,
8199
+ rawDocumentFragment,
7338
8200
  // source/patch
7339
8201
  rawSetAttribute,
7340
8202
  rawAppendChild,
@@ -7343,6 +8205,8 @@ function initGlobalEnv() {
7343
8205
  rawRemoveChild,
7344
8206
  rawAppend,
7345
8207
  rawPrepend,
8208
+ rawFragmentAppend,
8209
+ rawFragmentPrepend,
7346
8210
  rawCloneNode,
7347
8211
  rawElementQuerySelector,
7348
8212
  rawElementQuerySelectorAll,
@@ -7380,7 +8244,7 @@ function initGlobalEnv() {
7380
8244
  /**
7381
8245
  * define element
7382
8246
  * @param tagName element name
7383
- */
8247
+ */
7384
8248
  function defineElement(tagName) {
7385
8249
  class MicroAppElement extends HTMLElement {
7386
8250
  constructor() {
@@ -7447,6 +8311,13 @@ function defineElement(tagName) {
7447
8311
  // baseRoute: route prefix, default is ''
7448
8312
  // keep-alive: open keep-alive mode
7449
8313
  connectedCallback() {
8314
+ /**
8315
+ * In FireFox, iframe Node.prototype will point to native Node.prototype after insert to document
8316
+ * If <micro-app>.prototype is not MicroAppElement.prototype, we should reset it
8317
+ */
8318
+ if (Object.getPrototypeOf(this) !== MicroAppElement.prototype) {
8319
+ Object.setPrototypeOf(this, MicroAppElement.prototype);
8320
+ }
7450
8321
  const cacheCount = ++this.connectedCount;
7451
8322
  this.connectStateMap.set(cacheCount, true);
7452
8323
  /**
@@ -7508,8 +8379,7 @@ function defineElement(tagName) {
7508
8379
  if (this.legalAttribute(attr, newVal) &&
7509
8380
  this[attr === ObservedAttrName.NAME ? 'appName' : 'appUrl'] !== newVal) {
7510
8381
  if (attr === ObservedAttrName.URL && (!this.appUrl ||
7511
- !this.connectStateMap.get(this.connectedCount) // TODO: 这里的逻辑可否再优化一下
7512
- )) {
8382
+ !this.connectStateMap.get(this.connectedCount))) {
7513
8383
  newVal = formatAppURL(newVal, this.appName);
7514
8384
  if (!newVal) {
7515
8385
  return logError(`Invalid attribute url ${newVal}`, this.appName);
@@ -7518,13 +8388,11 @@ function defineElement(tagName) {
7518
8388
  this.handleInitialNameAndUrl();
7519
8389
  }
7520
8390
  else if (attr === ObservedAttrName.NAME && (!this.appName ||
7521
- !this.connectStateMap.get(this.connectedCount) // TODO: 这里的逻辑可否再优化一下
7522
- )) {
8391
+ !this.connectStateMap.get(this.connectedCount))) {
7523
8392
  const formatNewName = formatAppName(newVal);
7524
8393
  if (!formatNewName) {
7525
8394
  return logError(`Invalid attribute name ${newVal}`, this.appName);
7526
8395
  }
7527
- // TODO: 当micro-app还未插入文档中就修改name,逻辑可否再优化一下
7528
8396
  if (this.cacheData) {
7529
8397
  microApp.setData(formatNewName, this.cacheData);
7530
8398
  this.cacheData = null;
@@ -7579,12 +8447,12 @@ function defineElement(tagName) {
7579
8447
  /**
7580
8448
  * url is different & old app is unmounted or prefetch, create new app to replace old one
7581
8449
  */
7582
- logWarn(`the ${oldApp.isPrefetch ? 'prefetch' : 'unmounted'} app with url: ${oldAppUrl} replaced by a new app with url: ${targetUrl}`, this.appName);
8450
+ logWarn(`the ${oldApp.isPrefetch ? 'prefetch' : 'unmounted'} app with url ${oldAppUrl} replaced by a new app with url ${targetUrl}`, this.appName);
7583
8451
  }
7584
8452
  this.handleCreateApp();
7585
8453
  }
7586
8454
  else {
7587
- logError(`app name conflict, an app named: ${this.appName} with url: ${oldAppUrl} is running`);
8455
+ logError(`app name conflict, an app named ${this.appName} with url ${oldAppUrl} is running`);
7588
8456
  }
7589
8457
  }
7590
8458
  else {
@@ -7828,6 +8696,7 @@ function defineElement(tagName) {
7828
8696
  }
7829
8697
  else {
7830
8698
  // get path from browser URL
8699
+ // TODO: 新版本路由系统要重新兼容ssr
7831
8700
  let targetPath = getNoHashMicroPathFromURL(this.appName, baseUrl);
7832
8701
  const defaultPagePath = this.getDefaultPage();
7833
8702
  if (!targetPath && defaultPagePath) {
@@ -7855,7 +8724,10 @@ function defineElement(tagName) {
7855
8724
  * @returns router-mode
7856
8725
  */
7857
8726
  getMemoryRouterMode() {
7858
- return getRouterMode(this.getAttribute('router-mode'), this);
8727
+ return initRouterMode(this.getAttribute('router-mode'),
8728
+ // is micro-app element set disable-memory-router, like <micro-app disable-memory-router></micro-app>
8729
+ // or <micro-app disable-memory-router='false'></micro-app>
8730
+ this.compatibleProperties('disable-memory-router') && this.compatibleDisableProperties('disable-memory-router'));
7859
8731
  }
7860
8732
  /**
7861
8733
  * rewrite micro-app.setAttribute, process attr data
@@ -7881,6 +8753,17 @@ function defineElement(tagName) {
7881
8753
  globalEnv.rawSetAttribute.call(this, key, value);
7882
8754
  }
7883
8755
  }
8756
+ /**
8757
+ * get delay time of router event
8758
+ * @returns delay time
8759
+ */
8760
+ getRouterEventDelay() {
8761
+ let delay = parseInt(this.getAttribute('router-event-delay'));
8762
+ if (isNaN(delay)) {
8763
+ delay = parseInt((isFunction(microApp.options['router-event-delay']) ? microApp.options['router-event-delay'](this.appName) : microApp.options['router-event-delay']));
8764
+ }
8765
+ return !isNaN(delay) ? delay : 0;
8766
+ }
7884
8767
  /**
7885
8768
  * Data from the base application
7886
8769
  */
@@ -7904,8 +8787,20 @@ function defineElement(tagName) {
7904
8787
  }
7905
8788
  return null;
7906
8789
  }
8790
+ /**
8791
+ * get publicPath from a valid address,it can used in micro-app-devtools
8792
+ */
8793
+ get publicPath() {
8794
+ return getEffectivePath(this.appUrl);
8795
+ }
8796
+ /**
8797
+ * get baseRoute from attribute,it can used in micro-app-devtools
8798
+ */
8799
+ get baseRoute() {
8800
+ return this.getBaseRouteCompatible();
8801
+ }
7907
8802
  }
7908
- globalEnv.rawWindow.customElements.define(tagName, MicroAppElement);
8803
+ window.customElements.define(tagName, MicroAppElement);
7909
8804
  }
7910
8805
 
7911
8806
  /**
@@ -7937,7 +8832,7 @@ function preFetch(apps, delay) {
7937
8832
  const delayTime = isNumber(delay) ? delay : microApp.options.prefetchDelay;
7938
8833
  /**
7939
8834
  * TODO: remove setTimeout
7940
- * Is there a better way?
8835
+ * 如果要保留setTimeout,则需要考虑清空定时器的情况
7941
8836
  */
7942
8837
  setTimeout(() => {
7943
8838
  // releasePrefetchEffect()
@@ -7982,9 +8877,22 @@ function preFetchAction(options) {
7982
8877
  });
7983
8878
  const oldOnload = app.onLoad;
7984
8879
  const oldOnLoadError = app.onLoadError;
7985
- app.onLoad = (html) => {
8880
+ app.onLoad = (onLoadParam) => {
8881
+ if (app.isPrerender) {
8882
+ assign(onLoadParam, {
8883
+ defaultPage: options['default-page'],
8884
+ /**
8885
+ * TODO: 预渲染支持disable-memory-router,默认渲染首页即可,文档中也要保留
8886
+ * 问题:
8887
+ * 1、如何确保子应用进行跳转时不影响到浏览器地址??pure??
8888
+ */
8889
+ routerMode: initRouterMode(options['router-mode']),
8890
+ baseroute: options.baseroute,
8891
+ disablePatchRequest: options['disable-patch-request'],
8892
+ });
8893
+ }
7986
8894
  resolve();
7987
- oldOnload.call(app, html, options['default-page'], options['disable-patch-request'], getRouterMode(options['router-mode']), options.baseroute);
8895
+ oldOnload.call(app, onLoadParam);
7988
8896
  };
7989
8897
  app.onLoadError = (...rests) => {
7990
8898
  resolve();
@@ -8015,7 +8923,7 @@ function getGlobalAssets(assets) {
8015
8923
  // TODO: requestIdleCallback for every file
8016
8924
  function fetchGlobalResources(resources, suffix, sourceHandler) {
8017
8925
  if (isArray(resources)) {
8018
- const effectiveResource = resources.filter((path) => isString(path) && path.includes(`.${suffix}`) && !sourceHandler.hasInfo(path));
8926
+ const effectiveResource = resources.filter((path) => isString(path) && isTargetExtension(path, suffix) && !sourceHandler.hasInfo(path));
8019
8927
  const fetchResourcePromise = effectiveResource.map((path) => fetchSource(path));
8020
8928
  // fetch resource with stream
8021
8929
  promiseStream(fetchResourcePromise, (res) => {
@@ -8159,7 +9067,7 @@ function unmountApp(appName, options) {
8159
9067
  }
8160
9068
  }
8161
9069
  else {
8162
- logWarn(`app ${appName} does not exist`);
9070
+ logWarn(`app ${appName} does not exist when unmountApp`);
8163
9071
  resolve(false);
8164
9072
  }
8165
9073
  });
@@ -8189,7 +9097,7 @@ function reload(appName, destroy) {
8189
9097
  }
8190
9098
  }
8191
9099
  else {
8192
- logWarn(`app ${appName} does not exist`);
9100
+ logWarn(`app ${appName} does not exist when reload app`);
8193
9101
  resolve(false);
8194
9102
  }
8195
9103
  });
@@ -8282,7 +9190,7 @@ class MicroApp extends EventCenterForBaseApp {
8282
9190
  }
8283
9191
  }
8284
9192
  initGlobalEnv();
8285
- if (globalEnv.rawWindow.customElements.get(this.tagName)) {
9193
+ if (window.customElements.get(this.tagName)) {
8286
9194
  return logWarn(`element ${this.tagName} is already defined`);
8287
9195
  }
8288
9196
  if (isPlainObject(options)) {