@blueking/bk-weweb 0.0.23 → 0.0.26

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.
@@ -37,6 +37,7 @@ var AppState;
37
37
  AppState["UNSET"] = "UNSET";
38
38
  })(AppState || (AppState = {}));
39
39
 
40
+ // 当前正在运行的app
40
41
  let currentRunningApp = null;
41
42
  function getCurrentRunningApp() {
42
43
  return currentRunningApp;
@@ -44,12 +45,6 @@ function getCurrentRunningApp() {
44
45
  function setCurrentRunningApp(appInstance) {
45
46
  currentRunningApp = appInstance;
46
47
  }
47
- const SCOPED_CSS_STYLE_ID = 'SCOPED_CSS_STYLE_ID';
48
- const templateStyle = document.createElement('style');
49
- templateStyle.setAttribute('id', SCOPED_CSS_STYLE_ID);
50
- document.body.appendChild(templateStyle);
51
- templateStyle.sheet.disabled = true;
52
- const disabledStyleDom = templateStyle;
53
48
  const windowNativeFuncMap = new Map();
54
49
  /**
55
50
  * 收集原生window方法
@@ -164,122 +159,6 @@ const isJsonpUrl = (url) => {
164
159
  return !pathname.match(/\.js$/);
165
160
  };
166
161
 
167
- const { document: document$1 } = window;
168
- const { createElement, getElementById, getElementsByClassName, getElementsByName, getElementsByTagName, querySelector, querySelectorAll, } = Document.prototype;
169
- const { querySelector: bodyQuerySelector } = HTMLBodyElement.prototype;
170
- const SPECIAL_ELEMENT_TAG = ['body', 'html', 'head'];
171
- let hasRewrite$1 = false;
172
- function rewriteDocumentPrototypeMethods() {
173
- if (hasRewrite$1)
174
- return;
175
- hasRewrite$1 = true;
176
- Document.prototype.createElement = function (tagName, options) {
177
- const element = createElement.call(this, tagName, options);
178
- const app = getCurrentRunningApp();
179
- // img.src = 'xxx' iframe.src = 'xxx' 均不能在setAttributes上监听 但是这里所有的src都是 全地址 无法判断是否需要添加子应用域名
180
- // if (tagName.toLocaleLowerCase() === 'img') {
181
- // const observer = new MutationObserver((list, observer) => {
182
- // observer.disconnect();
183
- // const url = new URL((element as HTMLImageElement).src)
184
- // (element as HTMLImageElement).src = `${}`
185
- // });
186
- // observer.observe(element, { attributeFilter: ['src'], subtree: false, childList: false });
187
- // }
188
- if (app)
189
- element.__BK_WEWEB_APP_KEY__ = app.appCacheKey;
190
- return element;
191
- };
192
- /**
193
- * 查询选择器的新方法
194
- * @param selectors 选择器字符串
195
- * @returns 匹配的元素或 null
196
- */
197
- function querySelectorNew(selectors) {
198
- const app = getCurrentRunningApp();
199
- // 如果选择器是特殊元素标签
200
- if (SPECIAL_ELEMENT_TAG.includes(selectors)) {
201
- // 如果当前应用程序容器是 ShadowRoot 类型
202
- if (app?.container instanceof ShadowRoot) {
203
- return app?.container;
204
- }
205
- if (this instanceof HTMLBodyElement)
206
- return bodyQuerySelector.call(this, selectors);
207
- // 否则调用原始的 querySelector 方法
208
- return querySelector.call(this, selectors);
209
- }
210
- // 如果没有当前应用程序或选择器为空或文档不是当前文档
211
- if (!app || !selectors || ![document$1, document$1.body].includes(this)) {
212
- if (this instanceof HTMLBodyElement)
213
- return bodyQuerySelector.call(this, selectors);
214
- // 调用原始的 querySelector 方法
215
- return querySelector.call(this, selectors);
216
- }
217
- // 返回当前应用程序容器中匹配选择器的元素,如果没有匹配的元素则返回 null
218
- return app?.container?.querySelector(selectors) ?? null;
219
- }
220
- /**
221
- * 重写了 Document 类的 querySelectorAll 方法
222
- * @param selectors - 要查询的选择器
223
- * @returns 匹配到的元素列表或空数组
224
- */
225
- function querySelectorAllNew(selectors) {
226
- const app = getCurrentRunningApp();
227
- // 如果选择器是特殊元素标签,则返回容器元素或调用原生 querySelector 方法
228
- if (SPECIAL_ELEMENT_TAG.includes(selectors)) {
229
- if (app?.container instanceof ShadowRoot) {
230
- return app?.container;
231
- }
232
- return querySelector.call(this, selectors);
233
- }
234
- // 如果没有运行中的应用程序、选择器为空或文档不是当前文档,则调用原生 querySelectorAll 方法
235
- if (!app || !selectors || document$1 !== this) {
236
- return querySelectorAll.call(this, selectors);
237
- }
238
- // 返回运行中应用程序的容器元素中匹配到的元素列表或空数组
239
- return app?.container?.querySelectorAll(selectors) ?? [];
240
- }
241
- Document.prototype.querySelector = querySelectorNew;
242
- Document.prototype.querySelectorAll = querySelectorAllNew;
243
- HTMLBodyElement.prototype.querySelector = querySelectorNew;
244
- Document.prototype.getElementById = function getElementByIdNew(key) {
245
- return getCurrentRunningApp() ? querySelectorNew.call(this, `#${key}`) : getElementById.call(this, key);
246
- };
247
- Document.prototype.getElementsByClassName = function (key) {
248
- return getCurrentRunningApp() ? querySelectorAllNew.call(this, `.${key}`) : getElementsByClassName.call(this, key);
249
- };
250
- // eslint-disable-next-line max-len
251
- Document.prototype.getElementsByTagName = function (key) {
252
- const app = getCurrentRunningApp();
253
- if (!app || SPECIAL_ELEMENT_TAG.includes(key) || (!app?.showSourceCode && key.toLocaleLowerCase() === 'script')) {
254
- return getElementsByTagName.call(this, key);
255
- }
256
- return querySelectorAllNew.call(this, key);
257
- };
258
- Document.prototype.getElementsByName = function (key) {
259
- return getCurrentRunningApp() ? querySelectorAllNew.call(this, `[name=${key}]`) : getElementsByName.call(this, key);
260
- };
261
- }
262
- function resetDocumentPrototypeMethods() {
263
- Document.prototype.createElement = createElement;
264
- Document.prototype.querySelector = querySelector;
265
- HTMLBodyElement.prototype.querySelector = bodyQuerySelector;
266
- Document.prototype.querySelectorAll = querySelectorAll;
267
- Document.prototype.getElementById = getElementById;
268
- Document.prototype.getElementsByClassName = getElementsByClassName;
269
- Document.prototype.getElementsByTagName = getElementsByTagName;
270
- Document.prototype.getElementsByName = getElementsByName;
271
- hasRewrite$1 = false;
272
- }
273
- // vue3.3之后会换成document 这些需要初始化调用的app
274
- function createRewriteDocument(rawDocument, app) {
275
- return Object.defineProperty(rawDocument, 'createElement', {
276
- get() {
277
- app?.registerRunningApp();
278
- return Document.prototype.createElement;
279
- },
280
- });
281
- }
282
-
283
162
  /*
284
163
  * Tencent is pleased to support the open source community by making
285
164
  * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.
@@ -350,6 +229,42 @@ function dispatchLinkOrScriptError(element) {
350
229
  element.dispatchEvent(event);
351
230
  }
352
231
 
232
+ /*
233
+ * Tencent is pleased to support the open source community by making
234
+ * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.
235
+ *
236
+ * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
237
+ *
238
+ * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.
239
+ *
240
+ * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):
241
+ *
242
+ * ---------------------------------------------------
243
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
244
+ * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
245
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
246
+ * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
247
+ *
248
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of
249
+ * the Software.
250
+ *
251
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
252
+ * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
253
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
254
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
255
+ * IN THE SOFTWARE.
256
+ */
257
+ // deactivated
258
+ function deactivated(appKey) {
259
+ const app = appCache.getApp(appKey);
260
+ if (app && [AppState.ACTIVATED, AppState.MOUNTED].includes(app.status)) {
261
+ app.keepAlive ? app.deactivated() : app.unmount();
262
+ }
263
+ if (!appCache.hasActiveApp) {
264
+ resetBodyAndHeaderMethods();
265
+ }
266
+ }
267
+
353
268
  /*
354
269
  * Tencent is pleased to support the open source community by making
355
270
  * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.
@@ -441,6 +356,7 @@ function loadInstance(props) {
441
356
  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
442
357
  * IN THE SOFTWARE.
443
358
  */
359
+ // unmount
444
360
  function unmount(appKey) {
445
361
  const app = appCache.getApp(appKey);
446
362
  if (app && app.status !== AppState.UNMOUNT) {
@@ -448,43 +364,6 @@ function unmount(appKey) {
448
364
  }
449
365
  if (!appCache.hasActiveApp) {
450
366
  resetBodyAndHeaderMethods();
451
- resetDocumentPrototypeMethods();
452
- }
453
- }
454
-
455
- /*
456
- * Tencent is pleased to support the open source community by making
457
- * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.
458
- *
459
- * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
460
- *
461
- * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.
462
- *
463
- * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):
464
- *
465
- * ---------------------------------------------------
466
- * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
467
- * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
468
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
469
- * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
470
- *
471
- * The above copyright notice and this permission notice shall be included in all copies or substantial portions of
472
- * the Software.
473
- *
474
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
475
- * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
476
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
477
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
478
- * IN THE SOFTWARE.
479
- */
480
- function deactivated(appKey) {
481
- const app = appCache.getApp(appKey);
482
- if (app && [AppState.ACTIVATED, AppState.MOUNTED].includes(app.status)) {
483
- app.keepAlive ? app.deactivated() : app.unmount();
484
- }
485
- if (!appCache.hasActiveApp) {
486
- resetBodyAndHeaderMethods();
487
- resetDocumentPrototypeMethods();
488
367
  }
489
368
  }
490
369
 
@@ -675,7 +554,6 @@ function fetchSource(url, options = {}) {
675
554
  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
676
555
  * IN THE SOFTWARE.
677
556
  */
678
- /* eslint-disable no-param-reassign */
679
557
  class Style {
680
558
  code = '';
681
559
  fromHtml;
@@ -694,9 +572,15 @@ class Style {
694
572
  this.fromHtml = fromHtml;
695
573
  this.initial = initial ?? false;
696
574
  }
697
- commonScoped(templateStyle, styleElement, app) {
575
+ /**
576
+ * @param styleElement 样式node
577
+ * @param app 应用实例
578
+ */
579
+ commonScoped(styleElement, app) {
698
580
  if (app.scopeCss && !(app.container instanceof ShadowRoot)) {
699
- const rules = Array.from(templateStyle.sheet?.cssRules ?? []);
581
+ const cssStyleSheet = new CSSStyleSheet({ disabled: true });
582
+ cssStyleSheet.replaceSync(styleElement.textContent || this.code);
583
+ const rules = Array.from(cssStyleSheet?.cssRules ?? []);
700
584
  const cssPrefix = `#${app.name}`;
701
585
  const scopedCss = this.scopeRule(rules, cssPrefix);
702
586
  const cssText = this.resetUrlHost(scopedCss, app.url, this.url);
@@ -704,7 +588,7 @@ class Style {
704
588
  this.scopedCode = cssText;
705
589
  }
706
590
  else {
707
- const cssText = this.resetUrlHost(styleElement.textContent || templateStyle.textContent || '', app.url, this.url);
591
+ const cssText = this.resetUrlHost(styleElement.textContent || this.code || '', app.url, this.url);
708
592
  // fix https://bugs.chromium.org/p/chromium/issues/detail?id=336876
709
593
  if (cssText && app.container instanceof ShadowRoot) {
710
594
  let fontContent = '';
@@ -730,6 +614,10 @@ class Style {
730
614
  delete styleElement.__BK_WEWEB_APP_KEY__;
731
615
  return styleElement;
732
616
  }
617
+ /**
618
+ * @param app 应用实例
619
+ * @returns 返回执行后的style标签
620
+ */
733
621
  async excuteCode(app) {
734
622
  app.registerRunningApp();
735
623
  let styleElement = this.createStyleElement();
@@ -837,11 +725,9 @@ class Style {
837
725
  const container = needKeepAlive ? document.head : app.container;
838
726
  try {
839
727
  if (this.code) {
840
- disabledStyleDom.textContent = styleElement.textContent || this.code;
841
- this.commonScoped(disabledStyleDom, styleElement, app);
728
+ this.commonScoped(styleElement, app);
842
729
  container?.prepend(styleElement);
843
730
  linkElement && dispatchLinkOrScriptLoad(linkElement);
844
- disabledStyleDom.textContent = '';
845
731
  }
846
732
  else if (linkElement.getAttribute('href')) {
847
733
  this.url = fillUpPath(linkElement.getAttribute('href'), app.url);
@@ -885,9 +771,7 @@ class Style {
885
771
  }
886
772
  if (this.linkedBaseStyle(styleElement, app))
887
773
  return styleElement;
888
- disabledStyleDom.textContent = this.code;
889
- this.commonScoped(disabledStyleDom, styleElement, app);
890
- disabledStyleDom.textContent = '';
774
+ this.commonScoped(styleElement, app);
891
775
  }
892
776
  else {
893
777
  const observer = new MutationObserver(() => {
@@ -895,7 +779,7 @@ class Style {
895
779
  return;
896
780
  observer.disconnect();
897
781
  if (!this.linkedBaseStyle(styleElement, app)) {
898
- this.commonScoped(styleElement, styleElement, app);
782
+ this.commonScoped(styleElement, app);
899
783
  }
900
784
  });
901
785
  observer.observe(styleElement, { attributes: false, characterData: true, childList: true, subtree: true });
@@ -946,21 +830,22 @@ async function excuteAppStyles(app, container) {
946
830
  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
947
831
  * IN THE SOFTWARE.
948
832
  */
833
+ // bk-kweweb 微模块模式
949
834
  class MicroInstanceModel {
950
- state = AppState.UNSET;
951
- appCacheKey;
952
- container;
953
- data;
954
- initSource;
955
- isPreLoad = false;
956
- keepAlive;
957
- name;
958
- sandBox;
959
- scopeCss = true;
960
- scopeJs = false;
961
- showSourceCode = true;
962
- source;
963
- url;
835
+ state = AppState.UNSET; // 当前实例状态
836
+ appCacheKey; // 缓存key
837
+ container; // 容器
838
+ data; // 数据
839
+ initSource; // 初始资源
840
+ isPreLoad = false; // 是否预加载
841
+ keepAlive; // 是否缓存
842
+ name; // 名称
843
+ sandBox; // 沙箱
844
+ scopeCss = true; // 是否隔离样式
845
+ scopeJs = false; // 是否隔离js
846
+ showSourceCode = true; // 是否显示源码
847
+ source; // 入口资源
848
+ url; // url
964
849
  constructor(props) {
965
850
  this.name = props.id !== props.url ? props.id : random(5);
966
851
  this.appCacheKey = props.id || this.name;
@@ -1042,12 +927,6 @@ class MicroInstanceModel {
1042
927
  await this.source.importEntery(this);
1043
928
  }
1044
929
  }
1045
- set status(v) {
1046
- this.state = v;
1047
- }
1048
- get status() {
1049
- return this.state;
1050
- }
1051
930
  unmount(needDestroy) {
1052
931
  this.state = AppState.UNMOUNT;
1053
932
  this.sandBox?.deactivated();
@@ -1055,6 +934,12 @@ class MicroInstanceModel {
1055
934
  this.container.innerHTML = '';
1056
935
  this.container = undefined;
1057
936
  }
937
+ set status(v) {
938
+ this.state = v;
939
+ }
940
+ get status() {
941
+ return this.state;
942
+ }
1058
943
  }
1059
944
 
1060
945
  /*
@@ -1082,9 +967,6 @@ class MicroInstanceModel {
1082
967
  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
1083
968
  * IN THE SOFTWARE.
1084
969
  */
1085
- /* eslint-disable no-restricted-syntax */
1086
- /* eslint-disable no-prototype-builtins */
1087
- /* eslint-disable no-param-reassign */
1088
970
  let firstGlobalProp;
1089
971
  let secondGlobalProp;
1090
972
  let lastGlobalProp;
@@ -1109,6 +991,11 @@ class Script {
1109
991
  this.fromHtml = fromHtml ?? false;
1110
992
  this.initial = initial ?? false;
1111
993
  }
994
+ /**
995
+ * @param app 应用
996
+ * @param needRelaceScriptElement 是否需要替换script标签
997
+ * @returns 返回执行后的script标签或注释
998
+ */
1112
999
  async excuteCode(app, needRelaceScriptElement = false) {
1113
1000
  try {
1114
1001
  if (!this.code)
@@ -1154,26 +1041,27 @@ class Script {
1154
1041
  }
1155
1042
  return;
1156
1043
  }
1044
+ // 内存脚本执行
1157
1045
  executeMemoryScript(app, scopedCode) {
1158
1046
  try {
1159
1047
  const isScopedLocation = app instanceof MicroAppModel && app.scopeLocation;
1160
1048
  app.registerRunningApp();
1161
- // eslint-disable-next-line no-new-func
1162
1049
  new Function('window', 'location', 'history', scopedCode)(app.sandBox.proxyWindow, isScopedLocation ? app.iframe.contentWindow.location : window.location, isScopedLocation ? app.iframe.contentWindow.history : window.history);
1163
1050
  }
1164
1051
  catch (e) {
1165
1052
  console.error(e);
1166
1053
  }
1167
1054
  }
1055
+ // 脚本标签执行
1168
1056
  executeSourceScript(scriptElement, scopedCode) {
1169
1057
  if (this.isModule) {
1170
- if (this.url?.match(/\.ts$/)) {
1171
- scriptElement.src = this.url;
1172
- }
1173
- else {
1174
- const blob = new Blob([scopedCode], { type: 'text/javascript' });
1175
- scriptElement.src = URL.createObjectURL(blob);
1176
- }
1058
+ scriptElement.src = this.url + '?key=' + Date.now();
1059
+ // if (this.url?.match(/\.ts$/)) {
1060
+ // // scriptElement.src = this.url + '?key=' + Date.now()!;
1061
+ // } else {
1062
+ // // const blob = new Blob([this.code], { type: 'text/javascript' });
1063
+ // // scriptElement.src = URL.createObjectURL(blob);
1064
+ // }
1177
1065
  scriptElement.setAttribute('type', 'module');
1178
1066
  }
1179
1067
  else {
@@ -1181,6 +1069,7 @@ class Script {
1181
1069
  }
1182
1070
  this.url && scriptElement.setAttribute('origin-src', this.url);
1183
1071
  }
1072
+ // 获取脚本内容
1184
1073
  async getCode(app) {
1185
1074
  if (this.code.length || !this.url) {
1186
1075
  return this.code;
@@ -1204,9 +1093,15 @@ class Script {
1204
1093
  setCode(code) {
1205
1094
  this.code = code;
1206
1095
  }
1096
+ // 转换脚本内容
1207
1097
  transformCode(app) {
1208
1098
  if (app.sandBox) {
1209
- if (app.showSourceCode || this.isModule) {
1099
+ if (this.isModule) {
1100
+ return ` with(window.${app.sandBox.windowSymbolKey}){
1101
+ ;${this.code}\n
1102
+ }`;
1103
+ }
1104
+ if (app.showSourceCode) {
1210
1105
  return `;(function(window, self){
1211
1106
  with(window){
1212
1107
  ;${this.code}\n
@@ -1277,14 +1172,15 @@ function noteGlobalProps(global) {
1277
1172
  }
1278
1173
  // app初始化dom 脚本执行
1279
1174
  async function execAppScripts(app) {
1280
- const appInitialScriptList = Array.from(app.source.scripts.values()).filter(script => script.initial);
1281
- // 初始化脚本最先执行
1282
- if (appInitialScriptList.length) {
1283
- await Promise.all(appInitialScriptList.map(script => script.excuteCode(app)));
1284
- }
1285
- const appScriptList = Array.from(app.source.scripts.values()).filter(script => script.fromHtml && !script.initial);
1175
+ // const appInitialScriptList = Array.from(app.source!.scripts.values()).filter(script => script.initial);
1176
+ // // 初始化脚本最先执行
1177
+ // if (appInitialScriptList.length) {
1178
+ // await Promise.all(appInitialScriptList.map(script => script.excuteCode(app)));
1179
+ // }
1180
+ const appScriptList = Array.from(app.source.scripts.values()).filter(script => script.fromHtml || script.initial);
1286
1181
  const commomList = appScriptList.filter(script => (!script.async && !script.defer) || script.isModule);
1287
1182
  // 保证同步脚本 和 module类型 最先执行
1183
+ await Promise.all(commomList.map(script => script.getCode(app)));
1288
1184
  await Promise.all(commomList.map(script => script.excuteCode(app)));
1289
1185
  // 最后执行 defer 和 async 脚本
1290
1186
  const deferScriptList = [];
@@ -1600,9 +1496,9 @@ function resetBodyAndHeaderMethods() {
1600
1496
  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
1601
1497
  * IN THE SOFTWARE.
1602
1498
  */
1499
+ // before load
1603
1500
  function beforeLoad() {
1604
1501
  rewriteBodyAndHeaderMethods();
1605
- rewriteDocumentPrototypeMethods();
1606
1502
  }
1607
1503
 
1608
1504
  /*
@@ -1630,6 +1526,7 @@ function beforeLoad() {
1630
1526
  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
1631
1527
  * IN THE SOFTWARE.
1632
1528
  */
1529
+ // 激活应用
1633
1530
  function activated(appKey, container, callback) {
1634
1531
  const app = appCache.getApp(appKey);
1635
1532
  if (app?.status === AppState.DEACTIVATED && app.keepAlive) {
@@ -1647,6 +1544,8 @@ function activated(appKey, container, callback) {
1647
1544
  }
1648
1545
  }
1649
1546
 
1547
+ /* eslint-disable @typescript-eslint/member-ordering */
1548
+ /* eslint-disable @typescript-eslint/no-explicit-any */
1650
1549
  /*
1651
1550
  * Tencent is pleased to support the open source community by making
1652
1551
  * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.
@@ -1693,7 +1592,9 @@ class BkWewebElement extends HTMLElement {
1693
1592
  return [WewebCustomAttrs.url];
1694
1593
  }
1695
1594
  getBooleanAttr(name) {
1696
- return this.hasAttribute(name) ? this.getAttribute(name) !== 'false' : undefined;
1595
+ return this.hasAttribute(name) || this.hasAttribute(name.replace(/([A-Z])/g, `-$1`).toLocaleLowerCase())
1596
+ ? this.getAttribute(name) !== 'false'
1597
+ : undefined;
1697
1598
  }
1698
1599
  async handleAttributeChanged() {
1699
1600
  if (!this.appKey)
@@ -1708,6 +1609,29 @@ class BkWewebElement extends HTMLElement {
1708
1609
  }
1709
1610
  await load(this.appProps);
1710
1611
  }
1612
+ attributeChangedCallback(attr, _oldVal, newVal) {
1613
+ if (attr !== WewebCustomAttrs.url || this[attr] === newVal || !this.connected)
1614
+ return;
1615
+ this.appUrl = newVal;
1616
+ const cacheApp = appCache.getApp(this.appKey);
1617
+ (this.connected || cacheApp) && this.handleAttributeChanged();
1618
+ }
1619
+ async connectedCallback() {
1620
+ if (this.getBooleanAttr(WewebCustomAttrs.setShodowDom) && !this.shadowRoot) {
1621
+ this.attachShadow({ delegatesFocus: false, mode: 'open' });
1622
+ }
1623
+ await load(this.appProps);
1624
+ activated(this.appKey, this.shadowRoot ?? this);
1625
+ this.connected = true;
1626
+ }
1627
+ disconnectedCallback() {
1628
+ this.connected = false;
1629
+ if (this.appProps.keepAlive) {
1630
+ deactivated(this.appKey);
1631
+ }
1632
+ else
1633
+ unmount(this.appKey);
1634
+ }
1711
1635
  get appData() {
1712
1636
  if (this.hasAttribute(WewebCustomAttrs.data)) {
1713
1637
  try {
@@ -1748,29 +1672,6 @@ class BkWewebElement extends HTMLElement {
1748
1672
  url: this.getAttribute(WewebCustomAttrs.url),
1749
1673
  };
1750
1674
  }
1751
- attributeChangedCallback(attr, _oldVal, newVal) {
1752
- if (attr !== WewebCustomAttrs.url || this[attr] === newVal || !this.connected)
1753
- return;
1754
- this.appUrl = newVal;
1755
- const cacheApp = appCache.getApp(this.appKey);
1756
- (this.connected || cacheApp) && this.handleAttributeChanged();
1757
- }
1758
- async connectedCallback() {
1759
- if (this.getBooleanAttr(WewebCustomAttrs.setShodowDom) && !this.shadowRoot) {
1760
- this.attachShadow({ delegatesFocus: false, mode: 'open' });
1761
- }
1762
- await load(this.appProps);
1763
- activated(this.appKey, this.shadowRoot ?? this);
1764
- this.connected = true;
1765
- }
1766
- disconnectedCallback() {
1767
- this.connected = false;
1768
- if (this.appProps.keepAlive) {
1769
- deactivated(this.appKey);
1770
- }
1771
- else
1772
- unmount(this.appKey);
1773
- }
1774
1675
  }
1775
1676
 
1776
1677
  var WewebMode;
@@ -1806,8 +1707,8 @@ var WewebMode;
1806
1707
  * IN THE SOFTWARE.
1807
1708
  */
1808
1709
  const WINDOW_WHITE_LIST = [
1809
- 'System',
1810
- '__cjsWrapper',
1710
+ 'System', // SystemJS
1711
+ '__cjsWrapper', // SystemJS CommonJS wrapper
1811
1712
  process.env.NODE_ENV !== 'production' ? '__REACT_DEVTOOLS_GLOBAL_HOOK__' : '',
1812
1713
  ];
1813
1714
  // 一定需要在子应用自身上下文获取的属性名
@@ -1819,6 +1720,11 @@ const BK_WEWEB_INJECT_KEY_LIST = [
1819
1720
  const WINDOW_ALIAS_LIST = ['window', 'self', 'globalThis'];
1820
1721
  // 设置了scopedLocation 后需要监听属性名
1821
1722
  const BK_WEWEB_LOCATION_KEY_LIST = ['location', 'history'];
1723
+ const COMMON_MICRO_APP_WINDOE_KEY_MAP = {
1724
+ __bk_pop_manager: true,
1725
+ __bk_zIndex_manager: true,
1726
+ i18n: true,
1727
+ };
1822
1728
  const DEV_MICRO_APP_WINDOE_KEY_MAP = process.env.NODE_ENV !== 'production'
1823
1729
  ? {
1824
1730
  __DEV__: true,
@@ -1828,16 +1734,185 @@ const DEV_MICRO_APP_WINDOE_KEY_MAP = process.env.NODE_ENV !== 'production'
1828
1734
  __VUE_I18N_FULL_INSTALL__: true,
1829
1735
  __VUE_I18N_LEGACY_API__: true,
1830
1736
  __VUE_OPTIONS_API__: true,
1831
- __bk_pop_manager: true,
1832
- __bk_zIndex_manager: true,
1833
1737
  '__core-js_shared__': true,
1834
- i18n: true,
1835
1738
  webpackChunkapm: true,
1836
1739
  webpackChunkpc: true,
1837
1740
  webpackChunktrace: true,
1838
1741
  webpackJsonp: true,
1742
+ ...COMMON_MICRO_APP_WINDOE_KEY_MAP,
1839
1743
  }
1840
- : {};
1744
+ : COMMON_MICRO_APP_WINDOE_KEY_MAP;
1745
+
1746
+ const SPECIAL_ELEMENT_TAG = ['body', 'html', 'head'];
1747
+ /**
1748
+ * 创建代理的document
1749
+ * @param rawDocument 原始document
1750
+ * @param app 应用实例
1751
+ * @returns 代理的document
1752
+ */
1753
+ const createProxyDocument = (rawDocument, app) => {
1754
+ const fakeDocument = {};
1755
+ function shadowRootInsertAdjacentHTML(where, domString) {
1756
+ const temporaryContainer = document.createElement('div');
1757
+ temporaryContainer.innerHTML = domString;
1758
+ const elements = Array.from(temporaryContainer.childNodes);
1759
+ const shadow = app.container;
1760
+ switch (where) {
1761
+ case 'beforebegin':
1762
+ elements.forEach(el => shadow.host.parentNode?.insertBefore(el, shadow.host));
1763
+ break;
1764
+ case 'afterbegin':
1765
+ elements.reverse().forEach(el => shadow.insertBefore(el, shadow.firstChild));
1766
+ break;
1767
+ case 'beforeend':
1768
+ elements.forEach(el => shadow.appendChild(el));
1769
+ break;
1770
+ case 'afterend':
1771
+ elements.forEach(el => shadow.host.parentNode?.insertBefore(el, shadow.host.nextSibling));
1772
+ break;
1773
+ }
1774
+ }
1775
+ const proxyBody = new Proxy({}, {
1776
+ get(_, key) {
1777
+ // ShadowRoot 处理逻辑简化
1778
+ if (app.container instanceof ShadowRoot) {
1779
+ if (key === 'insertAdjacentHTML') {
1780
+ // shadowRoot 中没有 insertAdjacentHTML
1781
+ return shadowRootInsertAdjacentHTML.bind(app.container);
1782
+ }
1783
+ const value = app.container[key];
1784
+ if (typeof value === 'function') {
1785
+ return value.bind(app.container);
1786
+ }
1787
+ if (value !== undefined) {
1788
+ return value;
1789
+ }
1790
+ }
1791
+ // 直接返回 rawDocument.body 的属性或绑定函数
1792
+ const value = Reflect.get(rawDocument.body, key);
1793
+ return typeof value === 'function' ? value.bind(rawDocument.body) : value;
1794
+ },
1795
+ set(_, key, value) {
1796
+ // ShadowRoot 处理逻辑简化
1797
+ if (app.container instanceof ShadowRoot) {
1798
+ app.container[key] = value;
1799
+ return true;
1800
+ }
1801
+ // 直接设置 rawDocument.body 的属性
1802
+ Reflect.set(rawDocument.body, key, value);
1803
+ return true;
1804
+ },
1805
+ });
1806
+ /**
1807
+ * @param tagName 标签名
1808
+ * @param options 选项
1809
+ * @returns
1810
+ */
1811
+ function createElement(tagName, options) {
1812
+ const element = rawDocument.createElement(tagName, options);
1813
+ // img.src = 'xxx' iframe.src = 'xxx' 均不能在setAttributes上监听 但是这里所有的src都是 全地址 无法判断是否需要添加子应用域名
1814
+ // if (tagName.toLocaleLowerCase() === 'img') {
1815
+ // const observer = new MutationObserver((list, observer) => {
1816
+ // observer.disconnect();
1817
+ // const url = new URL((element as HTMLImageElement).src)
1818
+ // (element as HTMLImageElement).src = `${}`
1819
+ // });
1820
+ // observer.observe(element, { attributeFilter: ['src'], subtree: false, childList: false });
1821
+ // }
1822
+ element.__BK_WEWEB_APP_KEY__ = app.appCacheKey;
1823
+ return element;
1824
+ }
1825
+ /**
1826
+ * 查询选择器的新方法
1827
+ * @param selectors 选择器字符串
1828
+ * @returns 匹配的元素或 null
1829
+ */
1830
+ function querySelectorNew(selectors) {
1831
+ if (selectors === proxyBody) {
1832
+ return app.container instanceof ShadowRoot ? app.container : rawDocument.body;
1833
+ }
1834
+ // 如果选择器是特殊元素标签
1835
+ if (SPECIAL_ELEMENT_TAG.includes(selectors)) {
1836
+ // 如果当前应用程序容器是 ShadowRoot 类型
1837
+ if (app?.container instanceof ShadowRoot) {
1838
+ return app?.container;
1839
+ }
1840
+ // 否则调用原始的 querySelector 方法
1841
+ return rawDocument.querySelector.call(this, selectors);
1842
+ }
1843
+ // 返回当前应用程序容器中匹配选择器的元素,如果没有匹配的元素则返回 null
1844
+ try {
1845
+ return app?.container?.querySelector(selectors) ?? null;
1846
+ }
1847
+ catch {
1848
+ return null;
1849
+ }
1850
+ }
1851
+ /**
1852
+ * 重写了 Document 类的 querySelectorAll 方法
1853
+ * @param selectors - 要查询的选择器
1854
+ * @returns 匹配到的元素列表或空数组
1855
+ */
1856
+ function querySelectorAllNew(selectors) {
1857
+ // 如果选择器是特殊元素标签,则返回容器元素或调用原生 querySelector 方法
1858
+ if (SPECIAL_ELEMENT_TAG.includes(selectors)) {
1859
+ if (app?.container instanceof ShadowRoot) {
1860
+ return app?.container;
1861
+ }
1862
+ return rawDocument.querySelector(selectors);
1863
+ }
1864
+ // 返回运行中应用程序的容器元素中匹配到的元素列表或空数组
1865
+ return app?.container?.querySelectorAll(selectors) ?? [];
1866
+ }
1867
+ function getElementByIdNew(key) {
1868
+ return querySelectorNew.call(rawDocument, `#${key}`);
1869
+ }
1870
+ function getElementsByClassName(key) {
1871
+ return querySelectorAllNew(`.${key}`);
1872
+ }
1873
+ function getElementsByTagName(key) {
1874
+ if (SPECIAL_ELEMENT_TAG.includes(key) || (!app?.showSourceCode && key.toLocaleLowerCase() === 'script')) {
1875
+ return rawDocument.getElementsByTagName(key);
1876
+ }
1877
+ return querySelectorAllNew(key);
1878
+ }
1879
+ function getElementsByNameNew(key) {
1880
+ return querySelectorAllNew(`[name=${key}]`);
1881
+ }
1882
+ return new Proxy(fakeDocument, {
1883
+ get(_, key) {
1884
+ if (key === 'createElement') {
1885
+ return createElement.bind(rawDocument);
1886
+ }
1887
+ if (key === 'querySelector') {
1888
+ return querySelectorNew.bind(rawDocument);
1889
+ }
1890
+ if (key === 'querySelectorAll') {
1891
+ return querySelectorAllNew.bind(rawDocument);
1892
+ }
1893
+ if (key === 'getElementById') {
1894
+ return getElementByIdNew.bind(rawDocument);
1895
+ }
1896
+ if (key === 'getElementsByClassName') {
1897
+ return getElementsByClassName.bind(rawDocument);
1898
+ }
1899
+ if (key === 'getElementsByTagName') {
1900
+ return getElementsByTagName.bind(rawDocument);
1901
+ }
1902
+ if (key === 'getElementsByName') {
1903
+ return getElementsByNameNew.bind(rawDocument);
1904
+ }
1905
+ if (key === 'body') {
1906
+ return proxyBody;
1907
+ }
1908
+ const result = Reflect.get(rawDocument, key);
1909
+ if (typeof result === 'function') {
1910
+ return result.bind(rawDocument);
1911
+ }
1912
+ return result;
1913
+ },
1914
+ });
1915
+ };
1841
1916
 
1842
1917
  /*
1843
1918
  * Tencent is pleased to support the open source community by making
@@ -1934,8 +2009,6 @@ function rewriteDocumentAndBodyEvent() {
1934
2009
  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
1935
2010
  * IN THE SOFTWARE.
1936
2011
  */
1937
- /* eslint-disable max-len */
1938
- /* eslint-disable no-prototype-builtins */
1939
2012
  // 正则表达式,用于匹配类定义
1940
2013
  const CLASS_REGEX = /^class\b/;
1941
2014
  // 正则表达式,用于匹配以大写字母开头的函数定义
@@ -2009,6 +2082,31 @@ function bindFunctionToRawWindow(rawWindow, value) {
2009
2082
  return value;
2010
2083
  }
2011
2084
 
2085
+ /*
2086
+ * Tencent is pleased to support the open source community by making
2087
+ * 蓝鲸智云PaaS平台 (BlueKing PaaS) available.
2088
+ *
2089
+ * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
2090
+ *
2091
+ * 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.
2092
+ *
2093
+ * License for 蓝鲸智云PaaS平台 (BlueKing PaaS):
2094
+ *
2095
+ * ---------------------------------------------------
2096
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
2097
+ * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
2098
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
2099
+ * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
2100
+ *
2101
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of
2102
+ * the Software.
2103
+ *
2104
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
2105
+ * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2106
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
2107
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2108
+ * IN THE SOFTWARE.
2109
+ */
2012
2110
  // rewrite window funtion like settimeout setinterval ...
2013
2111
  function rewriteWindowFunction(fakeWindow) {
2014
2112
  const windowEventLisenerMap = new Map();
@@ -2083,9 +2181,6 @@ function rewriteWindowFunction(fakeWindow) {
2083
2181
  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2084
2182
  * IN THE SOFTWARE.
2085
2183
  */
2086
- /* eslint-disable no-new-func */
2087
- /* eslint-disable no-param-reassign */
2088
- /* eslint-disable no-prototype-builtins */
2089
2184
  class SandBox {
2090
2185
  app;
2091
2186
  active = false;
@@ -2093,7 +2188,8 @@ class SandBox {
2093
2188
  resetDocumentAndBodyEvent;
2094
2189
  resetWindowFunction;
2095
2190
  sameRawWindowKeySet = new Set();
2096
- fakeWindow = {};
2191
+ fakeWindow;
2192
+ proxyDocument;
2097
2193
  proxyWindow;
2098
2194
  rawDocument;
2099
2195
  rawWindow;
@@ -2102,13 +2198,15 @@ class SandBox {
2102
2198
  this.app = app;
2103
2199
  const windowDescriptorSet = new Set();
2104
2200
  const rawWindow = window;
2105
- const rawDocument = createRewriteDocument(document, this.app);
2106
- this.fakeWindow.__POWERED_BY_BK_WEWEB__ = true;
2107
- this.fakeWindow.__BK_WEWEB_APP_KEY__ = app.appCacheKey;
2108
2201
  this.rawWindow = rawWindow;
2109
- this.rawDocument = rawDocument;
2110
- this.fakeWindow.rawWindow = rawWindow;
2111
- this.fakeWindow.rawDocument = rawDocument;
2202
+ this.rawDocument = createProxyDocument(document, app);
2203
+ const fakeWindow = Object.create({});
2204
+ fakeWindow.__BK_WEWEB_APP_KEY__ = app.appCacheKey;
2205
+ fakeWindow.__POWERED_BY_BK_WEWEB__ = true;
2206
+ fakeWindow.rawDocument = document;
2207
+ fakeWindow.rawWindow = rawWindow;
2208
+ fakeWindow.__proto__ = Window;
2209
+ this.fakeWindow = fakeWindow;
2112
2210
  const { resetWindowFunction } = rewriteWindowFunction(this.fakeWindow);
2113
2211
  this.resetWindowFunction = resetWindowFunction;
2114
2212
  this.windowSymbolKey = `__${(app.name || app.appCacheKey).replace(/(-|,|:|~|'|")/gim, '_')}_${random(10)}__`;
@@ -2137,11 +2235,10 @@ class SandBox {
2137
2235
  return this.proxyWindow;
2138
2236
  if (key === 'document') {
2139
2237
  app.registerRunningApp();
2140
- return rawDocument;
2238
+ return this.rawDocument;
2141
2239
  }
2142
2240
  if (key === 'eval') {
2143
2241
  app.registerRunningApp();
2144
- // eslint-disable-next-line no-eval
2145
2242
  return eval;
2146
2243
  }
2147
2244
  if (BK_WEWEB_LOCATION_KEY_LIST.includes(key) &&
@@ -2158,6 +2255,14 @@ class SandBox {
2158
2255
  }
2159
2256
  return Reflect.get(rawWindow, key); // iframe
2160
2257
  }
2258
+ if (key === 'getComputedStyle') {
2259
+ return (element, pseudoElt) => {
2260
+ if (element instanceof Element) {
2261
+ return rawWindow.getComputedStyle(element, pseudoElt);
2262
+ }
2263
+ return rawWindow.getComputedStyle(document.body, pseudoElt);
2264
+ };
2265
+ }
2161
2266
  if (Reflect.has(target, key) || BK_WEWEB_INJECT_KEY_LIST.includes(key))
2162
2267
  return Reflect.get(target, key);
2163
2268
  const rawValue = Reflect.get(rawWindow, key);
@@ -2234,6 +2339,7 @@ class SandBox {
2234
2339
  activeated(data) {
2235
2340
  if (!this.active) {
2236
2341
  this.active = true;
2342
+ this.rawDocument = createProxyDocument(document, this.app);
2237
2343
  this.fakeWindow.__BK_WEWEB_DATA__ = data ?? {};
2238
2344
  const { resetDocumentAndBodyEvent } = rewriteDocumentAndBodyEvent();
2239
2345
  this.resetDocumentAndBodyEvent = resetDocumentAndBodyEvent;
@@ -2280,23 +2386,25 @@ class SandBox {
2280
2386
  * IN THE SOFTWARE.
2281
2387
  */
2282
2388
  const BLANK_ORIGN = 'about:blank';
2389
+ // bk-weweb 微应用模式
2283
2390
  class MicroAppModel {
2284
- state = AppState.UNSET;
2285
- container;
2286
- data;
2287
- iframe = null;
2288
- initSource;
2289
- isPreLoad = false;
2391
+ state = AppState.UNSET; // 状态
2392
+ container; // 容器
2393
+ data; // 数据
2394
+ iframe = null; // scoped iframe
2395
+ initSource; // 初始资源
2396
+ isModuleApp = false; // 是否预加载
2397
+ isPreLoad = false; // 是否缓存
2290
2398
  keepAlive;
2291
- mode = WewebMode.APP;
2292
- name;
2293
- sandBox;
2294
- scopeCss;
2295
- scopeJs;
2296
- scopeLocation;
2297
- showSourceCode;
2298
- source;
2299
- url;
2399
+ mode = WewebMode.APP; // 名称
2400
+ name; // 沙箱
2401
+ sandBox; // 是否隔离样式
2402
+ scopeCss; // 是否隔离js
2403
+ scopeJs; // 是否隔离location
2404
+ scopeLocation; // 是否显示源码
2405
+ showSourceCode; // 入口资源
2406
+ source; // url
2407
+ url; // 是否是esm应用
2300
2408
  constructor(props) {
2301
2409
  this.name = props.id !== props.url ? props.id : random(5);
2302
2410
  this.mode = props.mode ?? WewebMode.APP;
@@ -2317,26 +2425,35 @@ class MicroAppModel {
2317
2425
  this.container.setAttribute(CSS_ATTRIBUTE_KEY, this.name);
2318
2426
  }
2319
2427
  }
2428
+ // 激活
2320
2429
  activated(container, callback) {
2321
2430
  this.isPreLoad = false;
2322
2431
  this.state = AppState.ACTIVATED;
2432
+ const app = this;
2323
2433
  if (container && this.container) {
2324
2434
  if (container instanceof Element)
2325
2435
  container.setAttribute(CSS_ATTRIBUTE_KEY, this.name);
2326
2436
  const fragment = document.createDocumentFragment();
2327
2437
  Array.from(this.container.childNodes).forEach((node) => {
2438
+ node.__BK_WEWEB_APP_KEY__ = this.appCacheKey;
2439
+ Object.defineProperties(node, {
2440
+ ownerDocument: {
2441
+ get() {
2442
+ return app.sandBox?.rawDocument;
2443
+ },
2444
+ },
2445
+ });
2328
2446
  fragment.appendChild(node);
2329
2447
  });
2330
2448
  container.innerHTML = '';
2331
2449
  container.appendChild(fragment);
2332
2450
  this.container = container;
2451
+ this.initShadowRootContainer();
2333
2452
  this.sandBox?.activeated(this.data);
2334
2453
  callback?.(this);
2335
2454
  }
2336
2455
  }
2337
- get appCacheKey() {
2338
- return this.url;
2339
- }
2456
+ // 创建隔离iframe
2340
2457
  createIframe() {
2341
2458
  return new Promise(resolve => {
2342
2459
  const iframe = document.createElement('iframe');
@@ -2365,16 +2482,38 @@ class MicroAppModel {
2365
2482
  this.state = AppState.DEACTIVATED;
2366
2483
  this.sandBox?.deactivated();
2367
2484
  }
2485
+ initShadowRootContainer() {
2486
+ if (this.container instanceof ShadowRoot) {
2487
+ // inject echarts in shadowRoot
2488
+ Object.defineProperties(this.container, {
2489
+ getBoundingClientRect: {
2490
+ get() {
2491
+ return this.host.getBoundingClientRect;
2492
+ },
2493
+ },
2494
+ });
2495
+ }
2496
+ }
2368
2497
  mount(container, callback) {
2369
2498
  this.isPreLoad = false;
2370
2499
  this.container = container ?? this.container;
2500
+ this.initShadowRootContainer();
2371
2501
  this.state = AppState.MOUNTING;
2502
+ const app = this;
2372
2503
  if (this.container) {
2373
2504
  if (this.container instanceof Element)
2374
2505
  this.container.setAttribute(CSS_ATTRIBUTE_KEY, this.name);
2375
2506
  const clonedNode = this.source.html.cloneNode(true);
2376
2507
  const fragment = document.createDocumentFragment();
2377
2508
  Array.from(clonedNode.childNodes).forEach((node) => {
2509
+ node.__BK_WEWEB_APP_KEY__ = this.appCacheKey;
2510
+ Object.defineProperties(node, {
2511
+ ownerDocument: {
2512
+ get() {
2513
+ return app.sandBox?.rawDocument;
2514
+ },
2515
+ },
2516
+ });
2378
2517
  fragment.appendChild(node);
2379
2518
  });
2380
2519
  this.container.innerHTML = '';
@@ -2402,7 +2541,7 @@ class MicroAppModel {
2402
2541
  async start() {
2403
2542
  if (!this.source || [AppState.ERROR, AppState.UNSET].includes(this.status)) {
2404
2543
  this.state = AppState.LOADING;
2405
- if (this.scopeLocation) {
2544
+ if (this.scopeLocation || this.isModuleApp) {
2406
2545
  const iframe = await this.createIframe();
2407
2546
  this.iframe = iframe;
2408
2547
  }
@@ -2410,12 +2549,6 @@ class MicroAppModel {
2410
2549
  await this.source.importEntery(this);
2411
2550
  }
2412
2551
  }
2413
- get status() {
2414
- return this.state;
2415
- }
2416
- set status(v) {
2417
- this.state = v;
2418
- }
2419
2552
  unmount(needDestroy = false) {
2420
2553
  this.state = AppState.UNMOUNT;
2421
2554
  this.sandBox?.deactivated();
@@ -2423,6 +2556,15 @@ class MicroAppModel {
2423
2556
  this.container.innerHTML = '';
2424
2557
  this.container = undefined;
2425
2558
  }
2559
+ get appCacheKey() {
2560
+ return this.url;
2561
+ }
2562
+ get status() {
2563
+ return this.state;
2564
+ }
2565
+ set status(v) {
2566
+ this.state = v;
2567
+ }
2426
2568
  }
2427
2569
 
2428
2570
  /*
@@ -2509,7 +2651,7 @@ class EntrySource {
2509
2651
  }
2510
2652
  collectScript(script, parent, needReplaceELement = false) {
2511
2653
  if (script.hasAttribute('ignore') ||
2512
- isJsonpUrl(script.getAttribute('src')) ||
2654
+ (script.type !== 'module' && isJsonpUrl(script.getAttribute('src'))) ||
2513
2655
  (script.hasAttribute('type') && !SCRIPT_TYPE_NAMES.includes(script.type))) {
2514
2656
  return;
2515
2657
  }
@@ -2542,9 +2684,11 @@ class EntrySource {
2542
2684
  const nonceStr = randomUrl();
2543
2685
  const scriptInstance = new Script({
2544
2686
  async: false,
2687
+ // code: script.textContent.replace(/var\s+(\w+)=/gm, `window.$1 = `),
2545
2688
  code: script.textContent,
2546
2689
  defer: script.type === 'module',
2547
2690
  fromHtml: !needReplaceELement,
2691
+ initial: false,
2548
2692
  isModule: script.type === 'module',
2549
2693
  url: nonceStr,
2550
2694
  });
@@ -2558,36 +2702,64 @@ class EntrySource {
2558
2702
  }
2559
2703
  return { replace: script };
2560
2704
  }
2561
- collectScriptAndStyle(parent, app) {
2562
- const children = Array.from(parent.children);
2563
- children.length &&
2564
- children.forEach(child => {
2565
- this.collectScriptAndStyle(child, app);
2566
- });
2567
- children?.forEach(dom => {
2568
- if (dom instanceof HTMLLinkElement) {
2569
- this.collectLink(dom, parent);
2570
- }
2571
- else if (dom instanceof HTMLStyleElement) {
2572
- if (!dom.hasAttribute('exclude') && !dom.hasAttribute('ignore')) {
2573
- this.styles.set(randomUrl(), new Style({
2574
- code: dom.textContent || '',
2575
- fromHtml: true,
2576
- url: '',
2577
- }));
2578
- dom.remove();
2579
- }
2580
- }
2581
- else if (dom instanceof HTMLScriptElement) {
2582
- this.collectScript(dom, parent);
2583
- }
2584
- else if (dom instanceof HTMLMetaElement || dom instanceof HTMLTitleElement) {
2585
- parent.removeChild(dom);
2705
+ collectScriptAndStyle(parent) {
2706
+ const links = Array.from(parent.querySelectorAll('link'));
2707
+ links?.forEach(link => {
2708
+ this.collectLink(link, link.parentElement);
2709
+ });
2710
+ const styles = Array.from(parent.querySelectorAll('style'));
2711
+ styles?.forEach(style => {
2712
+ if (!style.hasAttribute('exclude') && !style.hasAttribute('ignore')) {
2713
+ this.styles.set(randomUrl(), new Style({
2714
+ code: style.textContent || '',
2715
+ fromHtml: true,
2716
+ url: '',
2717
+ }));
2718
+ style.remove();
2586
2719
  }
2587
- else if (dom instanceof HTMLImageElement && dom.hasAttribute('src')) {
2588
- dom.setAttribute('src', fillUpPath(dom.getAttribute('src'), this.url));
2720
+ });
2721
+ const scripts = Array.from(parent.querySelectorAll('script'));
2722
+ scripts?.forEach(script => {
2723
+ this.collectScript(script, script.parentElement);
2724
+ });
2725
+ const metas = Array.from(parent.querySelectorAll('meta'));
2726
+ metas?.forEach(meta => {
2727
+ meta.parentElement.removeChild(meta);
2728
+ });
2729
+ const imgs = Array.from(parent.querySelectorAll('img'));
2730
+ imgs?.forEach(img => {
2731
+ if (img.hasAttribute('src')) {
2732
+ img.setAttribute('src', fillUpPath(img.getAttribute('src'), this.url));
2589
2733
  }
2590
2734
  });
2735
+ // const children = Array.from(parent.children);
2736
+ // children?.forEach(dom => {
2737
+ // if (dom instanceof HTMLLinkElement) {
2738
+ // this.collectLink(dom, parent);
2739
+ // } else if (dom instanceof HTMLStyleElement) {
2740
+ // if (!dom.hasAttribute('exclude') && !dom.hasAttribute('ignore')) {
2741
+ // this.styles.set(
2742
+ // randomUrl(),
2743
+ // new Style({
2744
+ // code: dom.textContent || '',
2745
+ // fromHtml: true,
2746
+ // url: '',
2747
+ // }),
2748
+ // );
2749
+ // dom.remove();
2750
+ // }
2751
+ // } else if (dom instanceof HTMLScriptElement) {
2752
+ // this.collectScript(dom, parent);
2753
+ // } else if (dom instanceof HTMLMetaElement || dom instanceof HTMLTitleElement) {
2754
+ // parent.removeChild(dom);
2755
+ // } else if (dom instanceof HTMLImageElement && dom.hasAttribute('src')) {
2756
+ // dom.setAttribute('src', fillUpPath(dom.getAttribute('src')!, this.url));
2757
+ // }
2758
+ // });
2759
+ // children.length &&
2760
+ // children.forEach(child => {
2761
+ // this.collectScriptAndStyle(child as HTMLElement, app);
2762
+ // });
2591
2763
  }
2592
2764
  getScript(url) {
2593
2765
  return this.scripts.get(url);
@@ -2625,7 +2797,7 @@ class EntrySource {
2625
2797
  if (wrapElement.__BK_WEWEB_APP_KEY__)
2626
2798
  delete wrapElement.__BK_WEWEB_APP_KEY__;
2627
2799
  wrapElement.innerHTML = htmlStr.replace(/<\/?head>/gim, '').replace(/<\/?body>/i, '');
2628
- this.collectScriptAndStyle(wrapElement, app);
2800
+ this.collectScriptAndStyle(wrapElement);
2629
2801
  await excuteAppStyles(app, wrapElement);
2630
2802
  this.html = wrapElement;
2631
2803
  }
@@ -2688,11 +2860,12 @@ class AppCache {
2688
2860
  constructor() {
2689
2861
  this.cache = new Map();
2690
2862
  this.baseSource = new EntrySource(location.href);
2691
- // this.baseApp = new
2692
2863
  }
2864
+ // 删除缓存
2693
2865
  deleteApp(url) {
2694
2866
  this.cache.delete(url);
2695
2867
  }
2868
+ // 获取缓存app
2696
2869
  getApp(name) {
2697
2870
  if (!name)
2698
2871
  return undefined;
@@ -2701,6 +2874,7 @@ class AppCache {
2701
2874
  return app;
2702
2875
  return Array.from(this.cache.values()).find((item) => item.name === name);
2703
2876
  }
2877
+ // 获取缓存app
2704
2878
  getBaseAppStyle(urlOrCode) {
2705
2879
  return this.baseSource.getStyle(urlOrCode);
2706
2880
  }
@@ -2733,9 +2907,6 @@ class AppCache {
2733
2907
  });
2734
2908
  return style;
2735
2909
  }
2736
- get hasActiveApp() {
2737
- return Array.from(this.cache.values()).some((app) => app.status !== AppState.UNMOUNT);
2738
- }
2739
2910
  setApp(app) {
2740
2911
  this.cache.set(app.appCacheKey, app);
2741
2912
  }
@@ -2745,6 +2916,9 @@ class AppCache {
2745
2916
  setBaseAppStyle(url, style) {
2746
2917
  this.baseSource.setStyle(url, style);
2747
2918
  }
2919
+ get hasActiveApp() {
2920
+ return Array.from(this.cache.values()).some((app) => app.status !== AppState.UNMOUNT);
2921
+ }
2748
2922
  }
2749
2923
  const appCache = new AppCache();
2750
2924
  // 注册全局获取缓存app 或者 instance
@@ -2779,6 +2953,12 @@ window.__getAppOrInstance__ = function (id) {
2779
2953
  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2780
2954
  * IN THE SOFTWARE.
2781
2955
  */
2956
+ /**
2957
+ * @param url 资源地址
2958
+ * @param style 样式实例
2959
+ * @param originLink 原始link标签
2960
+ * @returns 返回替换的style标签
2961
+ */
2782
2962
  function getStyleSource(url, style, originLink) {
2783
2963
  const replaceStyle = document.createElement('style');
2784
2964
  setMarkElement(replaceStyle);
@@ -2795,6 +2975,12 @@ function getStyleSource(url, style, originLink) {
2795
2975
  });
2796
2976
  return replaceStyle;
2797
2977
  }
2978
+ /**
2979
+ * @param url 资源地址
2980
+ * @param script 脚本实例
2981
+ * @param originScript 原始script标签
2982
+ * @returns 返回替换的script标签
2983
+ */
2798
2984
  function getScriptSource(url, script, originScript) {
2799
2985
  const replaceScript = document.createElement('script');
2800
2986
  setMarkElement(replaceScript);
@@ -2818,6 +3004,10 @@ function getScriptSource(url, script, originScript) {
2818
3004
  });
2819
3005
  return replaceScript;
2820
3006
  }
3007
+ /**
3008
+ * @param child link或者script标签
3009
+ * @returns 返回替换的link或者script标签
3010
+ */
2821
3011
  function createNewNode(child) {
2822
3012
  if (child instanceof HTMLLinkElement) {
2823
3013
  const rel = child.getAttribute('rel');
@@ -2852,9 +3042,20 @@ function createNewNode(child) {
2852
3042
  }
2853
3043
  return child;
2854
3044
  }
3045
+ /**
3046
+ * @param node 节点
3047
+ * @returns 返回是否是link或者script标签
3048
+ */
2855
3049
  function isLinkOrScript(node) {
2856
3050
  return node instanceof HTMLLinkElement || node instanceof HTMLScriptElement;
2857
3051
  }
3052
+ /**
3053
+ * @param parent 父节点
3054
+ * @param newChild 新节点
3055
+ * @param passiveChild 被动节点
3056
+ * @param rawMethod 原始方法
3057
+ * @returns 返回原始方法的执行结果
3058
+ */
2858
3059
  function baseElementInertHandle(parent, newChild, passiveChild, rawMethod) {
2859
3060
  if (isLinkOrScript(newChild)) {
2860
3061
  const targetChild = createNewNode(newChild);
@@ -2862,6 +3063,12 @@ function baseElementInertHandle(parent, newChild, passiveChild, rawMethod) {
2862
3063
  }
2863
3064
  return rawMethod.call(parent, newChild, passiveChild);
2864
3065
  }
3066
+ /**
3067
+ * @param parent 父节点
3068
+ * @param newChild 新节点
3069
+ * @param rawMethod 原始方法
3070
+ * @returns 返回原始方法的执行结果
3071
+ */
2865
3072
  function baseElementAppendHandle(parent, newChild, rawMethod) {
2866
3073
  if (isLinkOrScript(newChild)) {
2867
3074
  const targetChild = createNewNode(newChild);
@@ -2895,6 +3102,9 @@ function baseElementAppendHandle(parent, newChild, rawMethod) {
2895
3102
  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2896
3103
  * IN THE SOFTWARE.
2897
3104
  */
3105
+ /**
3106
+ * 收集主应用的资源
3107
+ */
2898
3108
  function collectBaseSource() {
2899
3109
  const rawBodyAppendChild = HTMLBodyElement.prototype.appendChild;
2900
3110
  const rawHeadAppendChild = HTMLHeadElement.prototype.appendChild;