@micro-zoe/micro-app 1.0.0-rc.7 → 1.0.0-rc.9

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.d.ts CHANGED
@@ -143,6 +143,10 @@ declare module '@micro-zoe/micro-app/libs/utils' {
143
143
  export function isURL(target: unknown): target is URL;
144
144
  export function isElement(target: unknown): target is Element;
145
145
  export function isNode(target: unknown): target is Node;
146
+ export function isCanvasElement(target: unknown): target is HTMLAnchorElement;
147
+ export function isAnchorElement(target: unknown): target is HTMLAnchorElement;
148
+ export function isAudioElement(target: unknown): target is HTMLAnchorElement;
149
+ export function isVideoElement(target: unknown): target is HTMLAnchorElement;
146
150
  export function isLinkElement(target: unknown): target is HTMLLinkElement;
147
151
  export function isStyleElement(target: unknown): target is HTMLStyleElement;
148
152
  export function isScriptElement(target: unknown): target is HTMLScriptElement;
@@ -340,6 +344,12 @@ declare module '@micro-zoe/micro-app/libs/utils' {
340
344
  * target maybe number, string, array ...
341
345
  */
342
346
  export function isEmptyObject(target: unknown): boolean;
347
+ /**
348
+ *
349
+ * @param {string} url input url
350
+ * @returns {boolean} is relative path
351
+ */
352
+ export const isRelativePath: (url: string) => boolean;
343
353
  }
344
354
 
345
355
  declare module '@micro-zoe/micro-app/interact' {
package/lib/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- const version = '1.0.0-rc.7';
1
+ const version = '1.0.0-rc.9';
2
2
  // do not use isUndefined
3
3
  const isBrowser = typeof window !== 'undefined';
4
4
  // do not use isUndefined
@@ -89,6 +89,15 @@ function isNode(target) {
89
89
  var _a;
90
90
  return target instanceof Node || isNumber((_a = target) === null || _a === void 0 ? void 0 : _a.nodeType);
91
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
+ }
92
101
  function isLinkElement(target) {
93
102
  return toTypeString(target) === '[object HTMLLinkElement]';
94
103
  }
@@ -643,6 +652,12 @@ function formatEventType(type, appName) {
643
652
  function isEmptyObject(target) {
644
653
  return isPlainObject(target) ? !Object.keys(target).length : true;
645
654
  }
655
+ /**
656
+ *
657
+ * @param {string} url input url
658
+ * @returns {boolean} is relative path
659
+ */
660
+ const isRelativePath = (url) => !/^https?:\/\//i.test(url) && !/^\/\//i.test(url);
646
661
 
647
662
  function formatEventInfo(event, element) {
648
663
  Object.defineProperties(event, {
@@ -865,7 +880,7 @@ class CSSParser {
865
880
  // reset scopecssDisableNextLine
866
881
  this.scopecssDisableNextLine = false;
867
882
  if (!selectors)
868
- return parseError('selector missing', this.linkPath);
883
+ return this.printError('selector missing', this.linkPath);
869
884
  this.recordResult(selectors);
870
885
  this.matchComments();
871
886
  this.styleDeclarations();
@@ -910,10 +925,10 @@ class CSSParser {
910
925
  // https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration
911
926
  styleDeclarations() {
912
927
  if (!this.matchOpenBrace())
913
- return parseError("Declaration missing '{'", this.linkPath);
928
+ return this.printError("Declaration missing '{'", this.linkPath);
914
929
  this.matchAllDeclarations();
915
930
  if (!this.matchCloseBrace())
916
- return parseError("Declaration missing '}'", this.linkPath);
931
+ return this.printError("Declaration missing '}'", this.linkPath);
917
932
  return true;
918
933
  }
919
934
  matchAllDeclarations(nesting = 0) {
@@ -936,7 +951,7 @@ class CSSParser {
936
951
  }
937
952
  // reset scopecssDisableNextLine
938
953
  this.scopecssDisableNextLine = false;
939
- if (!this.cssText)
954
+ if (!this.cssText.length)
940
955
  return;
941
956
  // extract comments in declarations
942
957
  if (this.cssText.charAt(0) === '/') {
@@ -990,16 +1005,16 @@ class CSSParser {
990
1005
  if (!this.commonMatch(/^@([-\w]+)?keyframes\s*/))
991
1006
  return false;
992
1007
  if (!this.commonMatch(/^[^{]+/))
993
- return parseError('@keyframes missing name', this.linkPath);
1008
+ return this.printError('@keyframes missing name', this.linkPath);
994
1009
  this.matchComments();
995
1010
  if (!this.matchOpenBrace())
996
- return parseError("@keyframes missing '{'", this.linkPath);
1011
+ return this.printError("@keyframes missing '{'", this.linkPath);
997
1012
  this.matchComments();
998
1013
  while (this.keyframeRule()) {
999
1014
  this.matchComments();
1000
1015
  }
1001
1016
  if (!this.matchCloseBrace())
1002
- return parseError("@keyframes missing '}'", this.linkPath);
1017
+ return this.printError("@keyframes missing '}'", this.linkPath);
1003
1018
  this.matchLeadingSpaces();
1004
1019
  return true;
1005
1020
  }
@@ -1047,7 +1062,7 @@ class CSSParser {
1047
1062
  this.matchComments();
1048
1063
  this.matchRules();
1049
1064
  if (!this.matchCloseBrace())
1050
- return parseError('@layer missing \'}\'', this.linkPath);
1065
+ return this.printError('@layer missing \'}\'', this.linkPath);
1051
1066
  this.matchLeadingSpaces();
1052
1067
  return true;
1053
1068
  }
@@ -1057,11 +1072,11 @@ class CSSParser {
1057
1072
  if (!this.commonMatch(reg))
1058
1073
  return false;
1059
1074
  if (!this.matchOpenBrace())
1060
- return parseError(`${name} missing '{'`, this.linkPath);
1075
+ return this.printError(`${name} missing '{'`, this.linkPath);
1061
1076
  this.matchComments();
1062
1077
  this.matchRules();
1063
1078
  if (!this.matchCloseBrace())
1064
- return parseError(`${name} missing '}'`, this.linkPath);
1079
+ return this.printError(`${name} missing '}'`, this.linkPath);
1065
1080
  this.matchLeadingSpaces();
1066
1081
  return true;
1067
1082
  };
@@ -1079,10 +1094,10 @@ class CSSParser {
1079
1094
  // common handler for @font-face, @page
1080
1095
  commonHandlerForAtRuleWithSelfRule(name) {
1081
1096
  if (!this.matchOpenBrace())
1082
- return parseError(`@${name} missing '{'`, this.linkPath);
1097
+ return this.printError(`@${name} missing '{'`, this.linkPath);
1083
1098
  this.matchAllDeclarations();
1084
1099
  if (!this.matchCloseBrace())
1085
- return parseError(`@${name} missing '}'`, this.linkPath);
1100
+ return this.printError(`@${name} missing '}'`, this.linkPath);
1086
1101
  this.matchLeadingSpaces();
1087
1102
  return true;
1088
1103
  }
@@ -1102,7 +1117,7 @@ class CSSParser {
1102
1117
  ++i;
1103
1118
  i += 2;
1104
1119
  if (this.cssText.charAt(i - 1) === '') {
1105
- return parseError('End of comment missing', this.linkPath);
1120
+ return this.printError('End of comment missing', this.linkPath);
1106
1121
  }
1107
1122
  // get comment content
1108
1123
  let commentText = this.cssText.slice(2, i - 2);
@@ -1162,6 +1177,11 @@ class CSSParser {
1162
1177
  this.result += strFragment;
1163
1178
  }
1164
1179
  }
1180
+ printError(msg, linkPath) {
1181
+ if (this.cssText.length) {
1182
+ parseError(msg, linkPath);
1183
+ }
1184
+ }
1165
1185
  }
1166
1186
  /**
1167
1187
  * common method of bind CSS
@@ -2968,14 +2988,37 @@ function updateElementInfo(node, appName) {
2968
2988
  * 1. 测试baseURI和ownerDocument在with沙箱中是否正确
2969
2989
  * 经过验证with沙箱不能重写ownerDocument,否则react点击事件会触发两次
2970
2990
  */
2971
- rawDefineProperties(node, {
2991
+ const props = {
2972
2992
  __MICRO_APP_NAME__: {
2973
2993
  configurable: true,
2974
2994
  enumerable: true,
2975
2995
  writable: true,
2976
2996
  value: appName,
2977
2997
  },
2978
- });
2998
+ };
2999
+ if (isAnchorElement(node)) {
3000
+ // a 标签
3001
+ const microApp = AppManager.getInstance().get(appName);
3002
+ if (microApp) {
3003
+ let originalHref = node.href;
3004
+ props.href = {
3005
+ get() {
3006
+ if (isRelativePath(originalHref)) {
3007
+ return `${microApp.url}${originalHref}`;
3008
+ }
3009
+ return originalHref;
3010
+ },
3011
+ set(value) {
3012
+ originalHref = value;
3013
+ }
3014
+ };
3015
+ }
3016
+ }
3017
+ if (isImageElement(node) || isVideoElement(node) || isAudioElement(node)) {
3018
+ // @ts-ignore
3019
+ node.crossOrigin = 'anonymous';
3020
+ }
3021
+ rawDefineProperties(node, props);
2979
3022
  /**
2980
3023
  * In FireFox, iframe Node.prototype will point to native Node.prototype after insert to document
2981
3024
  *
@@ -4065,32 +4108,34 @@ function createMicroHistory(appName, microLocation) {
4065
4108
  (_c = (_a = appInstanceMap.get(appName)) === null || _a === void 0 ? void 0 : (_b = _a.sandBox).updateIframeBase) === null || _c === void 0 ? void 0 : _c.call(_b);
4066
4109
  };
4067
4110
  }
4068
- const pushState = getMicroHistoryMethod('pushState');
4069
- const replaceState = getMicroHistoryMethod('replaceState');
4111
+ const originalHistory = {
4112
+ pushState: getMicroHistoryMethod('pushState'),
4113
+ replaceState: getMicroHistoryMethod('replaceState'),
4114
+ };
4070
4115
  if (isIframeSandbox(appName)) {
4071
- return {
4072
- pushState,
4073
- replaceState,
4116
+ return assign({
4074
4117
  go(delta) {
4075
4118
  return rawHistory.go(delta);
4076
4119
  }
4077
- };
4120
+ }, originalHistory);
4078
4121
  }
4079
4122
  return new Proxy(rawHistory, {
4080
4123
  get(target, key) {
4081
- if (key === 'state') {
4082
- return getMicroState(appName);
4083
- }
4084
- else if (key === 'pushState') {
4085
- return pushState;
4124
+ if (key === 'pushState' || key === 'replaceState') {
4125
+ return originalHistory[key];
4086
4126
  }
4087
- else if (key === 'replaceState') {
4088
- return replaceState;
4127
+ else if (key === 'state') {
4128
+ return getMicroState(appName);
4089
4129
  }
4090
4130
  return bindFunctionToRawTarget(Reflect.get(target, key), target, 'HISTORY');
4091
4131
  },
4092
4132
  set(target, key, value) {
4093
- Reflect.set(target, key, value);
4133
+ if (key === 'pushState' || key === 'replaceState') {
4134
+ originalHistory[key] = value;
4135
+ }
4136
+ else {
4137
+ Reflect.set(target, key, value);
4138
+ }
4094
4139
  /**
4095
4140
  * If the set() method returns false, and the assignment happened in strict-mode code, a TypeError will be thrown.
4096
4141
  * e.g. history.state = {}
@@ -4186,7 +4231,6 @@ function reWriteHistoryMethod(method) {
4186
4231
  excludeHiddenApp: true,
4187
4232
  excludePreRender: true,
4188
4233
  }).forEach(appName => {
4189
- // TODO: 大部分情况下,history.pushState 都是先执行,micro-app后卸载,所以会产生一种情况:跳转到新地址后,search模式会在url上添加参数,卸载后再将参数删除,所以会导致浏览器地址闪烁,是否需要去掉这个功能
4190
4234
  if ((isRouterModeSearch(appName) || isRouterModeState(appName)) && !getMicroPathFromURL(appName)) {
4191
4235
  const app = appInstanceMap.get(appName);
4192
4236
  attachRouteToBrowserURL(appName, setMicroPathToURL(appName, app.sandBox.proxyWindow.location), setMicroState(appName, getMicroState(appName), app.sandBox.proxyWindow.location));
@@ -5534,7 +5578,7 @@ const proxy2RawDocumentMethods = [
5534
5578
  * @returns EffectHook
5535
5579
  */
5536
5580
  function patchWindow$1(appName, microAppWindow, sandbox) {
5537
- patchWindowProperty$1(appName, microAppWindow);
5581
+ patchWindowProperty$1(appName, microAppWindow, sandbox);
5538
5582
  createProxyWindow$1(microAppWindow, sandbox);
5539
5583
  return patchWindowEffect$1(microAppWindow);
5540
5584
  }
@@ -5543,7 +5587,7 @@ function patchWindow$1(appName, microAppWindow, sandbox) {
5543
5587
  * @param appName app name
5544
5588
  * @param microAppWindow child app microWindow
5545
5589
  */
5546
- function patchWindowProperty$1(appName, microAppWindow) {
5590
+ function patchWindowProperty$1(appName, microAppWindow, sandbox) {
5547
5591
  const rawWindow = globalEnv.rawWindow;
5548
5592
  escape2RawWindowKeys.forEach((key) => {
5549
5593
  microAppWindow[key] = bindFunctionToRawTarget(rawWindow[key], rawWindow);
@@ -5588,25 +5632,10 @@ function patchWindowProperty$1(appName, microAppWindow) {
5588
5632
  configurable: true,
5589
5633
  enumerable: false,
5590
5634
  value(target) {
5591
- return target instanceof rawWindow[key] || instanceOf(target, microAppWindow[key]);
5635
+ return instanceOf(target, rawWindow[key]) || instanceOf(target, microAppWindow[key]);
5592
5636
  },
5593
5637
  });
5594
5638
  }
5595
- // hijackInstanceOfWindowRegExpKeys.some((reg: RegExp) => {
5596
- // if (reg.test(key) && key in rawWindow) {
5597
- // rawDefineProperty(microAppWindow[key], Symbol.hasInstance, {
5598
- // configurable: true,
5599
- // enumerable: false,
5600
- // value: (target: unknown) => {
5601
- // return target instanceof rawWindow[key]
5602
- // ? true
5603
- // : instanceOf(target, microAppWindow[key])
5604
- // },
5605
- // })
5606
- // return true
5607
- // }
5608
- // return false
5609
- // })
5610
5639
  return /^on/.test(key) && !SCOPE_WINDOW_ON_EVENT_OF_IFRAME.includes(key);
5611
5640
  })
5612
5641
  .forEach((eventName) => {
@@ -5627,6 +5656,23 @@ function patchWindowProperty$1(appName, microAppWindow) {
5627
5656
  logWarn(e, appName);
5628
5657
  }
5629
5658
  });
5659
+ /**
5660
+ * In esmodule(vite) proxyWindow will not take effect,
5661
+ * escapeProperties should define to microAppWindow
5662
+ */
5663
+ sandbox.escapeProperties.forEach((key) => {
5664
+ let rawValue = microAppWindow[key];
5665
+ rawDefineProperty(microAppWindow, key, {
5666
+ enumerable: true,
5667
+ configurable: true,
5668
+ get() {
5669
+ return rawValue !== null && rawValue !== void 0 ? rawValue : bindFunctionToRawTarget(rawWindow[key], rawWindow);
5670
+ },
5671
+ set(value) {
5672
+ rawValue = value;
5673
+ }
5674
+ });
5675
+ });
5630
5676
  }
5631
5677
  /**
5632
5678
  * create proxyWindow with Proxy(microAppWindow)
@@ -5661,7 +5707,7 @@ function createProxyWindow$1(microAppWindow, sandbox) {
5661
5707
  * 2. window.key in module app(vite), fall into microAppWindow(iframeWindow), escapeProperties will not take effect
5662
5708
  * 3. if (key)... --> fall into microAppWindow(iframeWindow), escapeProperties will not take effect
5663
5709
  */
5664
- if (includes(sandbox.escapeProperties, key) && !Reflect.has(target, key)) {
5710
+ if (includes(sandbox.escapeProperties, key) && !Reflect.get(target, key)) {
5665
5711
  return bindFunctionToRawTarget(Reflect.get(rawWindow, key), rawWindow);
5666
5712
  }
5667
5713
  return bindFunctionToRawTarget(Reflect.get(target, key), target);
@@ -5673,17 +5719,13 @@ function createProxyWindow$1(microAppWindow, sandbox) {
5673
5719
  if (!Reflect.has(target, key)) {
5674
5720
  customProperties.add(key);
5675
5721
  }
5722
+ // sandbox.escapeProperties will not set to rawWindow from rc.9
5676
5723
  Reflect.set(target, key, value);
5677
- if (includes(sandbox.escapeProperties, key)) {
5678
- !Reflect.has(rawWindow, key) && sandbox.escapeKeys.add(key);
5679
- Reflect.set(rawWindow, key, value);
5680
- }
5681
5724
  return true;
5682
5725
  },
5683
5726
  has: (target, key) => key in target,
5684
5727
  deleteProperty: (target, key) => {
5685
5728
  if (Reflect.has(target, key)) {
5686
- sandbox.escapeKeys.has(key) && Reflect.deleteProperty(rawWindow, key);
5687
5729
  return Reflect.deleteProperty(target, key);
5688
5730
  }
5689
5731
  return true;
@@ -6377,8 +6419,6 @@ class IframeSandbox {
6377
6419
  this.active = false;
6378
6420
  // Properties that can be escape to rawWindow
6379
6421
  this.escapeProperties = [];
6380
- // Properties escape to rawWindow, cleared when unmount
6381
- this.escapeKeys = new Set();
6382
6422
  // Update the base.href when initial and each redirect
6383
6423
  this.updateIframeBase = () => {
6384
6424
  var _a;
@@ -6494,10 +6534,6 @@ class IframeSandbox {
6494
6534
  /* --- memory router part --- end */
6495
6535
  if (!umdMode || destroy) {
6496
6536
  this.deleteIframeElement();
6497
- this.escapeKeys.forEach((key) => {
6498
- Reflect.deleteProperty(globalEnv.rawWindow, key);
6499
- });
6500
- this.escapeKeys.clear();
6501
6537
  this.clearHijackUmdHooks();
6502
6538
  }
6503
6539
  if (--globalEnv.activeSandbox === 0) {
@@ -7851,7 +7887,7 @@ function patchElementAndDocument() {
7851
7887
  },
7852
7888
  set(code) {
7853
7889
  globalEnv.rawInnerHTMLDesc.set.call(this, code);
7854
- const currentAppName = this.__MICRO_APP_NAME__ || getCurrentAppName();
7890
+ const currentAppName = this.__MICRO_APP_NAME__ || getIframeCurrentAppName() || getCurrentAppName();
7855
7891
  Array.from(this.children).forEach((child) => {
7856
7892
  if (isElement(child) && currentAppName) {
7857
7893
  updateElementInfo(child, currentAppName);
@@ -8026,6 +8062,68 @@ function rejectMicroAppStyle() {
8026
8062
  }
8027
8063
  }
8028
8064
 
8065
+ // 重写 Worker 构造函数的类型
8066
+ const originalWorker = window.Worker;
8067
+ function isSameOrigin(url) {
8068
+ if (url instanceof URL && url.protocol === 'blob:') {
8069
+ // 如果 url 是 Blob URL,直接返回 true
8070
+ return true;
8071
+ }
8072
+ // 检查 URL 是否与当前页面在同一个源
8073
+ try {
8074
+ const parsedUrl = new URL(url);
8075
+ return (parsedUrl.protocol === window.location.protocol &&
8076
+ parsedUrl.hostname === window.location.hostname &&
8077
+ parsedUrl.port === window.location.port);
8078
+ }
8079
+ catch (error) {
8080
+ return false;
8081
+ }
8082
+ }
8083
+ function urlFromScript(script) {
8084
+ let blob;
8085
+ try {
8086
+ blob = new Blob([script], {
8087
+ type: 'application/javascript'
8088
+ });
8089
+ }
8090
+ catch (e) {
8091
+ const BlobBuilder =
8092
+ // @ts-ignore
8093
+ window.BlobBuilder ||
8094
+ // @ts-ignore
8095
+ window.WebKitBlobBuilder ||
8096
+ // @ts-ignore
8097
+ window.MozBlobBuilder ||
8098
+ // @ts-ignore
8099
+ window.MSBlobBuilder;
8100
+ const blobBuilder = new BlobBuilder();
8101
+ blobBuilder.append(script);
8102
+ blob = blobBuilder.getBlob('application/javascript');
8103
+ }
8104
+ const URL = window.URL || window.webkitURL;
8105
+ return URL.createObjectURL(blob);
8106
+ }
8107
+ // @ts-ignore
8108
+ const WorkerProxy = new Proxy(originalWorker, {
8109
+ construct(Target, args) {
8110
+ const [scriptURL, options] = args;
8111
+ if (!isSameOrigin(scriptURL)) {
8112
+ // 如果 scriptURL 是跨域的,使用 Blob URL 加载并执行 worker
8113
+ const script = `import "${scriptURL}";`;
8114
+ const workerPath = urlFromScript(script);
8115
+ options.type = 'module';
8116
+ return new Target(workerPath, options);
8117
+ }
8118
+ else {
8119
+ // 如果 scriptURL 是同源的,直接使用原生的 Worker 构造函数
8120
+ return new Target(scriptURL, options);
8121
+ }
8122
+ },
8123
+ });
8124
+ // @ts-ignore
8125
+ window.Worker = WorkerProxy;
8126
+
8029
8127
  const globalEnv = {
8030
8128
  // active sandbox count
8031
8129
  activeSandbox: 0,
@@ -8283,8 +8381,7 @@ function defineElement(tagName) {
8283
8381
  if (this.legalAttribute(attr, newVal) &&
8284
8382
  this[attr === ObservedAttrName.NAME ? 'appName' : 'appUrl'] !== newVal) {
8285
8383
  if (attr === ObservedAttrName.URL && (!this.appUrl ||
8286
- !this.connectStateMap.get(this.connectedCount) // TODO: 这里的逻辑可否再优化一下
8287
- )) {
8384
+ !this.connectStateMap.get(this.connectedCount))) {
8288
8385
  newVal = formatAppURL(newVal, this.appName);
8289
8386
  if (!newVal) {
8290
8387
  return logError(`Invalid attribute url ${newVal}`, this.appName);
@@ -8293,13 +8390,11 @@ function defineElement(tagName) {
8293
8390
  this.handleInitialNameAndUrl();
8294
8391
  }
8295
8392
  else if (attr === ObservedAttrName.NAME && (!this.appName ||
8296
- !this.connectStateMap.get(this.connectedCount) // TODO: 这里的逻辑可否再优化一下
8297
- )) {
8393
+ !this.connectStateMap.get(this.connectedCount))) {
8298
8394
  const formatNewName = formatAppName(newVal);
8299
8395
  if (!formatNewName) {
8300
8396
  return logError(`Invalid attribute name ${newVal}`, this.appName);
8301
8397
  }
8302
- // TODO: 当micro-app还未插入文档中就修改name,逻辑可否再优化一下
8303
8398
  if (this.cacheData) {
8304
8399
  microApp.setData(formatNewName, this.cacheData);
8305
8400
  this.cacheData = null;