@blueking/bk-weweb 0.0.35-beta.9 → 0.0.36-beta.16

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/dist/index.esm.js CHANGED
@@ -209,1538 +209,1592 @@ var isJsonpUrl = (url) => {
209
209
  }
210
210
  };
211
211
 
212
- // src/context/document.ts
213
- var SPECIAL_ELEMENT_TAGS = ["body", "html", "head"];
214
- var APP_KEY_PROPERTY = "__BK_WEWEB_APP_KEY__";
215
- function createShadowRootInsertAdjacentHTML(app) {
216
- return function shadowRootInsertAdjacentHTML(where, domString) {
217
- const temporaryContainer = document.createElement("div");
218
- temporaryContainer.innerHTML = domString;
219
- const elements = Array.from(temporaryContainer.childNodes);
220
- const shadow = app.container;
221
- switch (where) {
222
- case "beforebegin":
223
- for (const item of elements) {
224
- shadow.host.parentNode?.insertBefore(item, shadow.host);
225
- }
226
- break;
227
- case "afterbegin":
228
- for (const item of elements.reverse()) {
229
- shadow.insertBefore(item, shadow.firstChild);
230
- }
231
- break;
232
- case "beforeend":
233
- for (const item of elements) {
234
- shadow.appendChild(item);
235
- }
236
- break;
237
- case "afterend":
238
- for (const item of elements) {
239
- shadow.host.parentNode?.insertBefore(item, shadow.host.nextSibling);
240
- }
241
- break;
242
- }
243
- };
244
- }
245
- function createProxyBody(rawDocument, app) {
246
- return new Proxy(
247
- {},
248
- {
249
- get(_, key) {
250
- if (app.container instanceof ShadowRoot) {
251
- if (key === "insertAdjacentHTML") {
252
- return createShadowRootInsertAdjacentHTML(app);
253
- }
254
- const value2 = Reflect.get(app.container, key);
255
- if (typeof value2 === "function") {
256
- return value2.bind(app.container);
257
- }
258
- if (value2 !== void 0) {
259
- return value2;
260
- }
261
- }
262
- const value = Reflect.get(rawDocument.body, key);
263
- return typeof value === "function" ? value.bind(rawDocument.body) : value;
264
- },
265
- set(_, key, value) {
266
- if (app.container instanceof ShadowRoot) {
267
- Reflect.set(app.container, key, value);
268
- return true;
269
- }
270
- Reflect.set(rawDocument.body, key, value);
271
- return true;
272
- }
273
- }
274
- );
275
- }
276
- function createElementWithAppKey(rawDocument, app) {
277
- return function createElement(tagName, options) {
278
- const element = rawDocument.createElement(tagName, options);
279
- element[APP_KEY_PROPERTY] = app.appCacheKey;
280
- return element;
281
- };
282
- }
283
- function isSpecialElementTag(selector) {
284
- return SPECIAL_ELEMENT_TAGS.includes(selector);
285
- }
286
- function safeQuerySelector(container, selector) {
287
- try {
288
- return container?.querySelector(selector) ?? null;
289
- } catch {
290
- return null;
291
- }
292
- }
293
- function safeQuerySelectorAll(container, selector) {
294
- try {
295
- return container?.querySelectorAll(selector) ?? [];
296
- } catch {
297
- return [];
298
- }
299
- }
300
- function createProxyQuerySelector(rawDocument, app, proxyBody) {
301
- return function querySelectorNew(selectors) {
302
- if (selectors === proxyBody) {
303
- return app.container instanceof ShadowRoot ? app.container : rawDocument.body;
304
- }
305
- if (isSpecialElementTag(selectors)) {
306
- if (app?.container instanceof ShadowRoot) {
307
- return app?.container;
308
- }
309
- return rawDocument.querySelector.call(this, selectors);
310
- }
311
- return safeQuerySelector(app?.container, selectors);
312
- };
313
- }
314
- function createProxyQuerySelectorAll(rawDocument, app) {
315
- return function querySelectorAllNew(selectors) {
316
- if (isSpecialElementTag(selectors)) {
317
- if (app?.container instanceof ShadowRoot) {
318
- return [app?.container];
319
- }
320
- const result = rawDocument.querySelector(selectors);
321
- return result ? [result] : [];
322
- }
323
- return safeQuerySelectorAll(app?.container, selectors);
324
- };
325
- }
326
- function createProxyGetElementById(rawDocument, querySelector) {
327
- return function getElementByIdNew(id) {
328
- return querySelector.call(rawDocument, `#${id}`);
329
- };
330
- }
331
- function createProxyGetElementsByClassName(querySelectorAll) {
332
- return function getElementsByClassName(className) {
333
- return querySelectorAll(`.${className}`);
334
- };
335
- }
336
- function createProxyGetElementsByTagName(rawDocument, app, querySelectorAll) {
337
- return function getElementsByTagName(tagName) {
338
- if (isSpecialElementTag(tagName) || !app?.showSourceCode && tagName.toLowerCase() === "script") {
339
- return rawDocument.getElementsByTagName(tagName);
340
- }
341
- return querySelectorAll(tagName);
342
- };
343
- }
344
- function createProxyGetElementsByName(querySelectorAll) {
345
- return function getElementsByNameNew(name) {
346
- return querySelectorAll(`[name="${name}"]`);
347
- };
348
- }
349
- function createProxyMethodMap(rawDocument, app, proxyBody) {
350
- const createElement = createElementWithAppKey(rawDocument, app);
351
- const querySelector = createProxyQuerySelector(rawDocument, app, proxyBody);
352
- const querySelectorAll = createProxyQuerySelectorAll(rawDocument, app);
353
- const getElementById = createProxyGetElementById(rawDocument, querySelector);
354
- const getElementsByClassName = createProxyGetElementsByClassName(querySelectorAll);
355
- const getElementsByTagName = createProxyGetElementsByTagName(rawDocument, app, querySelectorAll);
356
- const getElementsByName = createProxyGetElementsByName(querySelectorAll);
357
- return {
358
- createElement: createElement.bind(rawDocument),
359
- querySelector: querySelector.bind(rawDocument),
360
- querySelectorAll: querySelectorAll.bind(rawDocument),
361
- getElementById: getElementById.bind(rawDocument),
362
- getElementsByClassName: getElementsByClassName.bind(rawDocument),
363
- getElementsByTagName: getElementsByTagName.bind(rawDocument),
364
- getElementsByName: getElementsByName.bind(rawDocument)
365
- };
366
- }
367
- var createProxyDocument = (rawDocument, app) => {
368
- const fakeDocument = {};
369
- const proxyBody = createProxyBody(rawDocument, app);
370
- const methodMap = createProxyMethodMap(rawDocument, app, proxyBody);
371
- return new Proxy(fakeDocument, {
372
- get(_, key) {
373
- if (key === "body") {
374
- return proxyBody;
375
- }
376
- if (typeof key === "string" && key in methodMap) {
377
- return methodMap[key];
378
- }
379
- const result = Reflect.get(rawDocument, key);
380
- return typeof result === "function" ? result.bind(rawDocument) : result;
381
- }
382
- });
212
+ // src/utils/custom.ts
213
+ var ELEMENT_TARGET_NAMES = ["currentTarget", "srcElement", "target"];
214
+ var defineEventSourceElement = (element, eventName = "custom") => {
215
+ const targetProperties = ELEMENT_TARGET_NAMES.reduce((properties, targetName) => {
216
+ properties[targetName] = {
217
+ get: () => element,
218
+ enumerable: true,
219
+ configurable: true
220
+ };
221
+ return properties;
222
+ }, {});
223
+ return Object.defineProperties(new CustomEvent(eventName), targetProperties);
383
224
  };
384
-
385
- // src/context/event.ts
386
- function rewriteDocumentAndBodyEvent() {
387
- const { addEventListener, removeEventListener } = window.document;
388
- const { addEventListener: bodyAddEventListener, removeEventListener: bodyRemoveEventListener } = window.document.body;
389
- const documentListenerMap = /* @__PURE__ */ new Map();
390
- document.addEventListener = function(type, listener, options) {
391
- const app = getCurrentRunningApp();
392
- if (app?.keepAlive) {
393
- const listeners = documentListenerMap.get(type) || [];
394
- documentListenerMap.set(type, [...listeners, listener]);
395
- }
396
- addEventListener.call(app?.container instanceof ShadowRoot ? app.container : this, type, listener, options);
397
- };
398
- document.body.addEventListener = document.addEventListener;
399
- document.removeEventListener = function(type, listener, options) {
400
- const app = getCurrentRunningApp();
401
- if (app?.keepAlive) {
402
- const listeners = documentListenerMap.get(type) || [];
403
- if (listeners.length && listeners.some((l) => l === listener)) {
404
- listeners.splice(listeners.indexOf(listener), 1);
405
- }
406
- }
407
- removeEventListener.call(app?.container instanceof ShadowRoot ? app.container : this, type, listener, options);
408
- };
409
- document.body.removeEventListener = document.removeEventListener;
410
- function resetDocumentAndBodyEvent() {
411
- const app = getCurrentRunningApp();
412
- if (app?.keepAlive && documentListenerMap.values()) {
413
- for (const [type, listeners] of documentListenerMap.entries()) {
414
- for (const listener of listeners || []) {
415
- document.removeEventListener.call(document, type, listener);
416
- }
417
- }
418
- }
419
- document.addEventListener = addEventListener;
420
- document.body.addEventListener = bodyAddEventListener;
421
- document.removeEventListener = removeEventListener;
422
- document.body.removeEventListener = bodyRemoveEventListener;
423
- documentListenerMap.clear();
424
- }
425
- return {
426
- resetDocumentAndBodyEvent
427
- };
428
- }
429
-
430
- // src/context/function.ts
431
- var CLASS_REGEX = /^class\b/;
432
- var COMMON_CONSTRUCT_FU_REGEX = /^function\b\s[A-Z].*/;
433
- var ConstructFunCacheMap = /* @__PURE__ */ new WeakMap();
434
- function isConstructFun(fn) {
435
- if (fn.prototype?.constructor === fn && Object.getOwnPropertyNames(fn.prototype).length > 1) {
436
- return true;
437
- }
438
- if (ConstructFunCacheMap.has(fn)) {
439
- return ConstructFunCacheMap.get(fn);
440
- }
441
- const constructable = COMMON_CONSTRUCT_FU_REGEX.test(fn.toString()) || CLASS_REGEX.test(fn.toString());
442
- ConstructFunCacheMap.set(fn, constructable);
443
- return constructable;
444
- }
445
- var functionBoundedValueMap = /* @__PURE__ */ new WeakMap();
446
- function bindFunctionToRawWindow(rawWindow, value) {
447
- if (functionBoundedValueMap.has(value)) {
448
- return functionBoundedValueMap.get(value);
225
+ var dispatchLinkOrScriptLoad = (element) => {
226
+ const loadEvent = defineEventSourceElement(element, "load");
227
+ if (typeof element.onload === "function") {
228
+ element.onload.call(element, loadEvent);
229
+ return;
449
230
  }
450
- if (typeof value === "function" && !isConstructFun(value)) {
451
- const boundValue = Function.prototype.bind.call(value, rawWindow);
452
- for (const key in value) {
453
- boundValue[key] = value[key];
454
- }
455
- if (Object.hasOwn(value, "prototype") && !Object.hasOwn(boundValue, "prototype")) {
456
- Object.defineProperty(boundValue, "prototype", {
457
- enumerable: false,
458
- value: value.prototype,
459
- writable: true
460
- });
461
- }
462
- if (typeof value.toString === "function") {
463
- const valueHasInstanceToString = Object.hasOwn(value, "toString") && !Object.hasOwn(boundValue, "toString");
464
- const boundValueHasPrototypeToString = boundValue.toString === Function.prototype.toString;
465
- if (valueHasInstanceToString || boundValueHasPrototypeToString) {
466
- const originToStringDescriptor = Object.getOwnPropertyDescriptor(
467
- valueHasInstanceToString ? value : Function.prototype,
468
- "toString"
469
- );
470
- Object.defineProperty(boundValue, "toString", {
471
- ...originToStringDescriptor,
472
- ...originToStringDescriptor?.get ? null : { value: () => value.toString() }
473
- });
474
- }
475
- }
476
- functionBoundedValueMap.set(value, boundValue);
477
- return boundValue;
231
+ element.dispatchEvent(loadEvent);
232
+ };
233
+ var dispatchLinkOrScriptError = (element) => {
234
+ const errorEvent = defineEventSourceElement(element, "error");
235
+ if (typeof element.onerror === "function") {
236
+ element.onerror.call(element, errorEvent);
237
+ return;
478
238
  }
479
- return value;
480
- }
239
+ element.dispatchEvent(errorEvent);
240
+ };
481
241
 
482
- // src/context/window.ts
483
- function rewriteWindowFunction(fakeWindow) {
484
- const windowEventListenerMap = /* @__PURE__ */ new Map();
485
- const intervalTimerList = [];
486
- const rawWindow = window;
487
- const { addEventListener, clearInterval: clearInterval2, removeEventListener, setInterval: setInterval2 } = window;
488
- fakeWindow.addEventListener = (type, listener, options) => {
489
- windowEventListenerMap.set(type, [...windowEventListenerMap.get(type) || [], listener]);
490
- addEventListener.call(rawWindow, type, listener, options);
491
- };
492
- fakeWindow.removeEventListener = (type, listener, options) => {
493
- const listenerList = windowEventListenerMap.get(type);
494
- if (listenerList?.length) {
495
- const index = listenerList.indexOf(listener);
496
- index > -1 && listenerList.splice(index, 1);
497
- }
498
- removeEventListener.call(rawWindow, type, listener, options);
499
- };
500
- fakeWindow.setInterval = (handler, timeout, ...args) => {
501
- const timer = setInterval2(handler, timeout, ...args);
502
- intervalTimerList.push(timer);
503
- return timer;
504
- };
505
- fakeWindow.clearInterval = (timer) => {
506
- const index = intervalTimerList.indexOf(timer);
507
- index > -1 && intervalTimerList.splice(index, 1);
508
- clearInterval2.call(rawWindow, timer);
509
- };
510
- function resetWindowFunction() {
511
- if (windowEventListenerMap.size) {
512
- windowEventListenerMap.forEach((listenerList, type) => {
513
- for (const listener of listenerList) {
514
- removeEventListener.call(rawWindow, type, listener);
515
- }
516
- });
517
- windowEventListenerMap.clear();
518
- }
519
- if (intervalTimerList.length) {
520
- for (const timer of intervalTimerList) {
521
- clearInterval2.call(rawWindow, timer);
522
- }
242
+ // src/utils/fetch.ts
243
+ var fetchSource = async (url, options = {}, app) => {
244
+ if (typeof app?.fetchSource === "function") {
245
+ try {
246
+ return await app.fetchSource(url, options);
247
+ } catch {
248
+ return "";
523
249
  }
524
250
  }
525
- return {
526
- resetWindowFunction
527
- };
528
- }
529
-
530
- // src/context/sandbox.ts
531
- var SandBox = class {
532
- /** 初始化沙箱环境 */
533
- constructor(app) {
534
- this.app = app;
535
- const windowDescriptorSet = /* @__PURE__ */ new Set();
536
- const rawWindow = window;
537
- this.rawWindow = rawWindow;
538
- this.rawDocument = createProxyDocument(document, app);
539
- const fakeWindow = /* @__PURE__ */ Object.create({});
540
- fakeWindow.__BK_WEWEB_APP_KEY__ = app.appCacheKey;
541
- fakeWindow.__POWERED_BY_BK_WEWEB__ = true;
542
- fakeWindow.rawDocument = document;
543
- fakeWindow.rawWindow = rawWindow;
544
- fakeWindow.__proto__ = Window;
545
- this.fakeWindow = fakeWindow;
546
- const { resetWindowFunction } = rewriteWindowFunction(this.fakeWindow);
547
- this.resetWindowFunction = resetWindowFunction;
548
- const appIdentifier = (app.name || app.appCacheKey).replace(/[-,:~'"]/g, "_");
549
- this.windowSymbolKey = `__${appIdentifier}_${random(10)}__`;
550
- this.proxyWindow = new Proxy(this.fakeWindow, {
551
- defineProperty: (target, key, value) => {
552
- if (windowDescriptorSet.has(key)) {
553
- return Reflect.defineProperty(rawWindow, key, value);
554
- }
555
- return Reflect.defineProperty(target, key, value);
556
- },
557
- deleteProperty: (target, key) => {
558
- if (Object.hasOwn(target, key)) {
559
- if (this.sameRawWindowKeySet.has(key)) {
560
- this.sameRawWindowKeySet.delete(key);
561
- }
562
- if (this.inRawWindowKeySet.has(key)) {
563
- Reflect.deleteProperty(rawWindow, key);
564
- }
565
- return Reflect.deleteProperty(target, key);
566
- }
567
- return true;
568
- },
569
- get: (target, key) => {
570
- return this.handleProxyGet(target, key, rawWindow);
571
- },
572
- getOwnPropertyDescriptor: (target, key) => {
573
- if (Object.hasOwn(target, key)) {
574
- return Object.getOwnPropertyDescriptor(target, key);
575
- }
576
- if (Object.hasOwn(rawWindow, key)) {
577
- windowDescriptorSet.add(key);
578
- const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
579
- if (descriptor && !descriptor.configurable) {
580
- descriptor.configurable = true;
581
- }
582
- return descriptor;
583
- }
584
- return void 0;
585
- },
586
- has: (target, key) => windowNativeFuncMap.has(key) || key in target || key in rawWindow,
587
- ownKeys: (target) => Array.from(new Set(Reflect.ownKeys(rawWindow).concat(Reflect.ownKeys(target)))),
588
- set: (target, key, value) => {
589
- return this.handleProxySet(target, key, value, rawWindow);
590
- }
591
- });
592
- rawWindow[this.windowSymbolKey] = this.proxyWindow;
251
+ if (src_default.fetchSource) {
252
+ return src_default.fetchSource(url, options);
593
253
  }
594
- /** 沙箱激活状态标识 */
595
- active = false;
596
- /** 记录在原始 window 上新增的属性键集合 */
597
- inRawWindowKeySet = /* @__PURE__ */ new Set();
598
- /** 重置文档和 body 事件的函数 */
599
- resetDocumentAndBodyEvent;
600
- /** 重置 window 函数的方法 */
601
- resetWindowFunction;
602
- /** 记录与原始 window 相同的属性键集合 */
603
- sameRawWindowKeySet = /* @__PURE__ */ new Set();
604
- /** 伪造的 window 对象 */
605
- fakeWindow;
606
- /** 代理的 document 对象 */
607
- proxyDocument;
608
- /** 代理的 window 对象 */
609
- proxyWindow;
610
- /** 原始 document 对象 */
611
- rawDocument;
612
- /** 原始 window 对象 */
613
- rawWindow;
614
- /** 在 window 上的唯一标识键 */
615
- windowSymbolKey;
616
- /** 处理代理对象的 get 操作 */
617
- handleProxyGet(target, key, rawWindow) {
618
- if (key === Symbol.unscopables || windowNativeFuncMap.has(key)) {
619
- return rawWindow[key];
620
- }
621
- if (DEV_MICRO_APP_WINDOW_KEY_MAP[key]) {
622
- return this.fakeWindow[key];
623
- }
624
- if (WINDOW_ALIAS_LIST.includes(key)) {
625
- return this.proxyWindow;
626
- }
627
- if (key === "document") {
628
- this.app.registerRunningApp();
629
- return this.rawDocument;
630
- }
631
- if (key === "eval") {
632
- this.app.registerRunningApp();
633
- return eval;
634
- }
635
- if (this.shouldUseIframeLocation(key)) {
636
- if (this.app instanceof MicroAppModel && this.app.iframe?.contentWindow) {
637
- return this.app.iframe.contentWindow[key];
638
- }
639
- return void 0;
254
+ try {
255
+ const response = await window.fetch(url, options);
256
+ return await response.text();
257
+ } catch {
258
+ return "";
259
+ }
260
+ };
261
+
262
+ // src/entry/style.ts
263
+ var CSS_SELECTORS = {
264
+ ROOT_SELECTOR: /^((html[\s>~,]+body)|(html|body|:root))$/,
265
+ BUILT_IN_ROOT_SELECTOR: /(^|\s+)((html[\s>~]+body)|(html|body|:root))(?=[\s>~]+|$)/,
266
+ FONT_FACE: /@font-face\s*\{[^}]+\}/g,
267
+ URL_PATTERN: /url\(["']?([^)"']+)["']?\)/gm,
268
+ DATA_BLOB_PROTOCOL: /^(data|blob):/,
269
+ HTTP_PROTOCOL: /^(https?:)?\/\//,
270
+ RELATIVE_PATH: /^((\.\.?\/)|[^/])/,
271
+ ROOT_HOST_PATTERN: /(:?:root|html)/gm
272
+ };
273
+ var STYLE_ATTRIBUTES = {
274
+ TYPE: "text/css",
275
+ POWERED_BY: "bk-weweb",
276
+ LINKED_FROM_BASE: "linked-from-base",
277
+ ORIGIN_SRC: "origin-src"
278
+ };
279
+ var PACK_RULE_NAMES = {
280
+ MEDIA: "media",
281
+ SUPPORTS: "supports"
282
+ };
283
+ var Style = class {
284
+ code = "";
285
+ fromHtml;
286
+ initial;
287
+ prefetch = false;
288
+ preload = false;
289
+ scoped;
290
+ scopedCode = "";
291
+ url;
292
+ constructor({ code, fromHtml, initial, prefetch, preload, url }) {
293
+ this.scoped = false;
294
+ this.code = code;
295
+ this.prefetch = prefetch ?? false;
296
+ this.preload = preload ?? false;
297
+ this.url = url;
298
+ this.fromHtml = fromHtml;
299
+ this.initial = initial ?? false;
300
+ }
301
+ /**
302
+ * 通用样式作用域处理
303
+ * @param styleElement 样式元素
304
+ * @param app 应用实例
305
+ */
306
+ commonScoped(styleElement, app) {
307
+ if (app.scopeCss && !(app.container instanceof ShadowRoot)) {
308
+ this.applyScopedCSS(styleElement, app);
309
+ } else {
310
+ this.applyUnscopedCSS(styleElement, app);
640
311
  }
641
- if (key === "hasOwnProperty") {
642
- return (checkKey) => Object.hasOwn(this.fakeWindow, checkKey) || Object.hasOwn(rawWindow, checkKey);
312
+ this.scoped = true;
313
+ }
314
+ /**
315
+ * 创建样式元素
316
+ */
317
+ createStyleElement() {
318
+ const styleElement = document.createElement("style");
319
+ if (styleElement.__BK_WEWEB_APP_KEY__) {
320
+ styleElement.__BK_WEWEB_APP_KEY__ = void 0;
643
321
  }
644
- if (key === "top" || key === "parent") {
645
- if (rawWindow === rawWindow.parent) {
646
- return this.proxyWindow;
322
+ return styleElement;
323
+ }
324
+ /**
325
+ * 执行样式代码
326
+ * @param app 应用实例
327
+ * @returns 返回执行后的style标签
328
+ */
329
+ async executeCode(app) {
330
+ app.registerRunningApp();
331
+ let styleElement = this.createStyleElement();
332
+ styleElement.setAttribute("type", STYLE_ATTRIBUTES.TYPE);
333
+ styleElement.textContent = this.code;
334
+ try {
335
+ if (!this.code) {
336
+ await this.getCode(app);
647
337
  }
648
- return Reflect.get(rawWindow, key);
649
- }
650
- if (key === "getComputedStyle") {
651
- return this.createGetComputedStyleProxy(rawWindow);
338
+ styleElement = this.scopedStyleCSS(app, styleElement);
339
+ this.scoped = true;
340
+ } catch (error) {
341
+ console.error("scoped style error", error);
652
342
  }
653
- if (Reflect.has(target, key) || BK_WEWEB_INJECT_KEY_LIST.includes(key)) {
654
- return Reflect.get(target, key);
343
+ return styleElement;
344
+ }
345
+ /**
346
+ * 获取样式代码
347
+ */
348
+ async getCode(app) {
349
+ if (this.code.length || !this.url) {
350
+ return this.code;
655
351
  }
656
- const rawValue = Reflect.get(rawWindow, key);
657
- return bindFunctionToRawWindow(rawWindow, rawValue);
352
+ const code = this.getCodeFromAppSource(app) || this.getCodeFromCache() || await this.fetchCodeFromRemote(app);
353
+ this.code = code;
354
+ return code;
658
355
  }
659
356
  /**
660
- * 处理代理对象的 set 操作
661
- * @description 统一处理代理对象属性设置的复杂逻辑
662
- * @param target - 目标对象
663
- * @param key - 属性键
664
- * @param value - 属性值
665
- * @param rawWindow - 原始 window 对象
666
- * @returns boolean - 设置是否成功
667
- * @private
357
+ * 检查并链接基础应用样式
358
+ * @param styleElement 样式元素
359
+ * @param app 应用实例
360
+ * @returns 是否已链接基础样式
668
361
  */
669
- handleProxySet(target, key, value, rawWindow) {
670
- if (!this.active) {
362
+ linkedBaseStyle(styleElement, app) {
363
+ if (!(app.container instanceof ShadowRoot) && styleElement.textContent && appCache.getBaseAppStyle(styleElement.textContent)) {
364
+ this.clearStyleElement(styleElement);
365
+ styleElement.setAttribute(STYLE_ATTRIBUTES.LINKED_FROM_BASE, "true");
671
366
  return true;
672
367
  }
673
- if (this.shouldUseIframeLocation(key)) {
674
- const iframe = this.app instanceof MicroAppModel ? this.app.iframe : null;
675
- return iframe?.contentWindow ? Reflect.set(iframe.contentWindow, key, value) : true;
676
- }
677
- if (key === "location") {
678
- Reflect.set(rawWindow, key, value);
679
- } else if (this.shouldSetOnTarget(target, key, rawWindow)) {
680
- this.setPropertyOnTarget(target, key, value, rawWindow);
681
- } else {
682
- Reflect.set(target, key, value);
683
- this.sameRawWindowKeySet.add(key);
368
+ return false;
369
+ }
370
+ /**
371
+ * 重置包装规则
372
+ */
373
+ resetPackRule(rule, prefix, packName) {
374
+ const result = this.scopeRule(Array.from(rule.cssRules), prefix);
375
+ return `@${packName} ${rule.conditionText} {${result}}`;
376
+ }
377
+ /**
378
+ * 重置URL地址
379
+ */
380
+ resetUrlHost(cssText, uri, linkPath) {
381
+ let baseURI = uri;
382
+ return cssText.replace(CSS_SELECTORS.URL_PATTERN, (text, matchedUrl) => {
383
+ if (CSS_SELECTORS.DATA_BLOB_PROTOCOL.test(matchedUrl) || CSS_SELECTORS.HTTP_PROTOCOL.test(matchedUrl)) {
384
+ return text;
385
+ }
386
+ if (CSS_SELECTORS.RELATIVE_PATH.test(matchedUrl) && linkPath) {
387
+ baseURI = this.buildBaseURI(linkPath);
388
+ }
389
+ return `url("${fillUpPath(matchedUrl, baseURI)}")`;
390
+ });
391
+ }
392
+ /**
393
+ * css rule 处理
394
+ */
395
+ scopeRule(rules, cssPrefix) {
396
+ let result = "";
397
+ for (const rule of rules) {
398
+ switch (rule.type) {
399
+ case 1 /* STYLE_RULE */:
400
+ result += this.scopeStyleRule(rule, cssPrefix);
401
+ break;
402
+ case 4 /* MEDIA_RULE */:
403
+ result += this.resetPackRule(rule, cssPrefix, PACK_RULE_NAMES.MEDIA);
404
+ break;
405
+ case 12 /* SUPPORTS_RULE */:
406
+ result += this.resetPackRule(rule, cssPrefix, PACK_RULE_NAMES.SUPPORTS);
407
+ break;
408
+ default:
409
+ result += rule.cssText;
410
+ break;
411
+ }
684
412
  }
685
- this.handleWhiteListProperty(key, value, rawWindow);
686
- return true;
413
+ return result.replace(/^\s+/, "");
687
414
  }
688
415
  /**
689
- * 判断是否应该使用 iframe 的 location
690
- * @description 检查是否在 iframe 模式下访问 location 相关属性
691
- * @param key - 属性键
692
- * @returns boolean - 是否使用 iframe location
693
- * @private
416
+ * style rule 处理
694
417
  */
695
- shouldUseIframeLocation(key) {
696
- return !!(BK_WEWEB_LOCATION_KEY_LIST.includes(key) && this.app instanceof MicroAppModel && this.app.iframe && this.app.scopeLocation);
418
+ scopeStyleRule(rule, prefix) {
419
+ const { cssText, selectorText } = rule;
420
+ if (CSS_SELECTORS.ROOT_SELECTOR.test(selectorText)) {
421
+ return cssText.replace(CSS_SELECTORS.ROOT_SELECTOR, prefix);
422
+ }
423
+ if (selectorText === "*") {
424
+ return cssText.replace("*", `${prefix} *`);
425
+ }
426
+ return cssText.replace(
427
+ /^[\s\S]+{/,
428
+ (selectors) => selectors.replace(/(^|,)([^,]+)/g, (all, delimiter, selector) => {
429
+ if (CSS_SELECTORS.BUILT_IN_ROOT_SELECTOR.test(selector)) {
430
+ return all.replace(CSS_SELECTORS.BUILT_IN_ROOT_SELECTOR, prefix);
431
+ }
432
+ return `${delimiter} ${prefix} ${selector.replace(/^\s*/, "")}`;
433
+ })
434
+ );
697
435
  }
698
436
  /**
699
- * 创建 getComputedStyle 方法的代理
700
- * @description 为 getComputedStyle 方法创建安全的代理实现
701
- * @param rawWindow - 原始 window 对象
702
- * @returns Function - 代理后的 getComputedStyle 方法
703
- * @private
437
+ * link style 处理
704
438
  */
705
- createGetComputedStyleProxy(rawWindow) {
706
- return (element, pseudoElt) => {
707
- if (element instanceof Element) {
708
- return rawWindow.getComputedStyle(element, pseudoElt);
439
+ scopedLinkCSS(app, linkElement) {
440
+ const styleElement = this.createStyleElement();
441
+ styleElement.setAttribute("type", STYLE_ATTRIBUTES.TYPE);
442
+ const needKeepAlive = !!app.keepAlive && !(app.container instanceof ShadowRoot);
443
+ setMarkElement(styleElement, app, needKeepAlive);
444
+ const container = needKeepAlive ? document.head : app.container;
445
+ try {
446
+ if (this.code) {
447
+ this.handleExistingCode(styleElement, app, container, linkElement);
448
+ } else if (linkElement.getAttribute("href")) {
449
+ this.handleHrefAttribute(styleElement, app, container, linkElement);
450
+ } else {
451
+ this.handleMissingHref(styleElement, app, container, linkElement);
709
452
  }
710
- return rawWindow.getComputedStyle(document.body, pseudoElt);
711
- };
453
+ } catch {
454
+ linkElement && dispatchLinkOrScriptError(linkElement);
455
+ }
456
+ return styleElement;
712
457
  }
713
458
  /**
714
- * 判断是否应该在目标对象上设置属性
715
- * @description 检查属性设置的逻辑条件
716
- * @param target - 目标对象
717
- * @param key - 属性键
718
- * @param rawWindow - 原始 window 对象
719
- * @returns boolean - 是否在目标对象上设置
720
- * @private
459
+ * 隔离 style
721
460
  */
722
- shouldSetOnTarget(target, key, rawWindow) {
723
- return !Object.hasOwn(target, key) && Object.hasOwn(rawWindow, key) && !BK_WEWEB_INJECT_KEY_LIST.includes(key);
461
+ scopedStyleCSS(app, styleElement) {
462
+ const needKeepAlive = !!app.keepAlive && !(app.container instanceof ShadowRoot);
463
+ setMarkElement(styleElement, app, needKeepAlive);
464
+ if (this.code || styleElement.textContent) {
465
+ this.processExistingContent(styleElement, app);
466
+ } else {
467
+ this.observeContentChanges(styleElement, app);
468
+ }
469
+ if (this.url) {
470
+ styleElement.setAttribute(STYLE_ATTRIBUTES.ORIGIN_SRC, this.url);
471
+ }
472
+ return styleElement;
724
473
  }
725
474
  /**
726
- * 在目标对象上设置属性
727
- * @description 安全地在目标对象上设置属性,保持描述符特性
728
- * @param target - 目标对象
729
- * @param key - 属性键
730
- * @param value - 属性值
731
- * @param rawWindow - 原始 window 对象
732
- * @private
475
+ * 应用隔离 style
733
476
  */
734
- setPropertyOnTarget(target, key, value, rawWindow) {
735
- const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
736
- if (!descriptor) {
737
- return;
738
- }
739
- const { configurable, enumerable, writable } = descriptor;
740
- if (writable) {
741
- Object.defineProperty(target, key, {
742
- configurable,
743
- enumerable,
744
- value,
745
- writable
746
- });
747
- this.sameRawWindowKeySet.add(key);
477
+ applyScopedCSS(styleElement, app) {
478
+ const cssStyleSheet = new CSSStyleSheet({ disabled: true });
479
+ cssStyleSheet.replaceSync(styleElement.textContent || this.code);
480
+ const rules = Array.from(cssStyleSheet?.cssRules ?? []);
481
+ const cssPrefix = `#${app.name}`;
482
+ const scopedCss = this.scopeRule(rules, cssPrefix);
483
+ const cssText = this.resetUrlHost(scopedCss, app.url, this.url);
484
+ styleElement.textContent = cssText;
485
+ this.scopedCode = cssText;
486
+ }
487
+ applyUnscopedCSS(styleElement, app) {
488
+ const cssText = this.resetUrlHost(styleElement.textContent || this.code || "", app.url, this.url);
489
+ if (cssText && app.container instanceof ShadowRoot) {
490
+ this.handleShadowRootFonts(cssText, app);
748
491
  }
492
+ styleElement.textContent = cssText.replace(CSS_SELECTORS.ROOT_HOST_PATTERN, ":host");
749
493
  }
750
494
  /**
751
- * 处理白名单属性
752
- * @description 处理需要在原始 window 上设置的白名单属性
753
- * @param key - 属性键
754
- * @param value - 属性值
755
- * @param rawWindow - 原始 window 对象
756
- * @private
495
+ * 处理ShadowRoot中的字体
757
496
  */
758
- handleWhiteListProperty(key, value, rawWindow) {
759
- if (WINDOW_WHITE_LIST.includes(key) && !Reflect.has(rawWindow, key) && !BK_WEWEB_INJECT_KEY_LIST.includes(key)) {
760
- Reflect.set(rawWindow, key, value);
761
- this.inRawWindowKeySet.add(key);
497
+ handleShadowRootFonts(cssText, app) {
498
+ let fontContent = "";
499
+ const fontFaces = cssText.match(CSS_SELECTORS.FONT_FACE) || [];
500
+ for (const fontFace of fontFaces) {
501
+ fontContent += `${fontFace}
502
+ `;
503
+ }
504
+ const rawDocument = app.sandBox?.rawDocument;
505
+ if (rawDocument && fontContent) {
506
+ const fontStyle = rawDocument.createElement("style");
507
+ fontStyle.setAttribute("type", STYLE_ATTRIBUTES.TYPE);
508
+ fontStyle.setAttribute("powered-by", STYLE_ATTRIBUTES.POWERED_BY);
509
+ fontStyle.textContent = fontContent;
510
+ rawDocument?.head?.append(fontStyle);
511
+ }
512
+ }
513
+ getCodeFromAppSource(app) {
514
+ if (this.url && app?.source?.styles?.has(this.url)) {
515
+ return app.source.styles.get(this.url)?.code || "";
516
+ }
517
+ return "";
518
+ }
519
+ getCodeFromCache() {
520
+ if (this.url && appCache.getCacheStyle(this.url)) {
521
+ const style = appCache.getCacheStyle(this.url);
522
+ return style?.code || "";
762
523
  }
524
+ return "";
525
+ }
526
+ async fetchCodeFromRemote(app) {
527
+ return await fetchSource(this.url, {}, app).catch(() => "");
528
+ }
529
+ clearStyleElement(styleElement) {
530
+ styleElement.textContent = "";
531
+ styleElement.innerHTML = "";
532
+ }
533
+ buildBaseURI(linkPath) {
534
+ const pathArr = linkPath.split("/");
535
+ pathArr.pop();
536
+ return addUrlProtocol(`${pathArr.join("/")}/`);
537
+ }
538
+ handleExistingCode(styleElement, app, container, linkElement) {
539
+ this.commonScoped(styleElement, app);
540
+ container?.prepend(styleElement);
541
+ linkElement && dispatchLinkOrScriptLoad(linkElement);
763
542
  }
764
543
  /**
765
- * 激活沙箱
766
- * @description 启动沙箱环境,初始化代理对象和事件处理
767
- * @param data - 传递给沙箱的数据(可选)
544
+ * 处理href属性
768
545
  */
769
- activated(data) {
770
- if (!this.active) {
771
- this.active = true;
772
- this.rawDocument = createProxyDocument(document, this.app);
773
- this.fakeWindow.__BK_WEWEB_DATA__ = data ?? {};
774
- const { resetDocumentAndBodyEvent } = rewriteDocumentAndBodyEvent();
775
- this.resetDocumentAndBodyEvent = resetDocumentAndBodyEvent;
776
- }
546
+ handleHrefAttribute(styleElement, app, container, linkElement) {
547
+ this.url = fillUpPath(linkElement.getAttribute("href"), app.url);
548
+ this.getCode(app).then(() => {
549
+ this.scopedStyleCSS(app, styleElement);
550
+ linkElement.remove();
551
+ container?.prepend(styleElement);
552
+ linkElement && dispatchLinkOrScriptLoad(linkElement);
553
+ this.scoped = true;
554
+ });
777
555
  }
778
556
  /**
779
- * 停用沙箱
780
- * @description 关闭沙箱环境,清理所有副作用和修改
557
+ * 处理缺失的href
781
558
  */
782
- deactivated() {
783
- if (!this.active) return;
784
- this.active = false;
785
- this.resetWindowFunction();
786
- for (const key of this.inRawWindowKeySet) {
787
- Reflect.deleteProperty(window, key);
559
+ handleMissingHref(styleElement, app, container, linkElement) {
560
+ const observer = new MutationObserver(() => {
561
+ const href = linkElement.getAttribute("href");
562
+ if (!href) return;
563
+ observer.disconnect();
564
+ this.url = fillUpPath(href, app.url);
565
+ this.getCode(app).then(() => {
566
+ this.scopedStyleCSS(app, styleElement);
567
+ linkElement.remove();
568
+ container?.prepend(styleElement);
569
+ linkElement && dispatchLinkOrScriptLoad(linkElement);
570
+ this.scoped = true;
571
+ });
572
+ });
573
+ const observerConfig = {
574
+ attributeFilter: ["href"],
575
+ childList: false,
576
+ subtree: false
577
+ };
578
+ observer.observe(linkElement, observerConfig);
579
+ }
580
+ processExistingContent(styleElement, app) {
581
+ if (styleElement.textContent) {
582
+ this.clearStyleElement(styleElement);
583
+ }
584
+ if (!this.linkedBaseStyle(styleElement, app)) {
585
+ this.commonScoped(styleElement, app);
788
586
  }
789
- this.inRawWindowKeySet.clear();
790
- this.resetDocumentAndBodyEvent?.();
791
587
  }
792
- };
793
-
794
- // src/utils/custom.ts
795
- var ELEMENT_TARGET_NAMES = ["currentTarget", "srcElement", "target"];
796
- var defineEventSourceElement = (element, eventName = "custom") => {
797
- const targetProperties = ELEMENT_TARGET_NAMES.reduce((properties, targetName) => {
798
- properties[targetName] = {
799
- get: () => element,
800
- enumerable: true,
801
- configurable: true
588
+ observeContentChanges(styleElement, app) {
589
+ const observer = new MutationObserver(() => {
590
+ if (!(styleElement.textContent || styleElement.sheet?.cssRules?.length)) return;
591
+ observer.disconnect();
592
+ if (!this.linkedBaseStyle(styleElement, app)) {
593
+ this.commonScoped(styleElement, app);
594
+ }
595
+ });
596
+ const observerConfig = {
597
+ attributes: false,
598
+ characterData: true,
599
+ childList: true,
600
+ subtree: true
802
601
  };
803
- return properties;
804
- }, {});
805
- return Object.defineProperties(new CustomEvent(eventName), targetProperties);
806
- };
807
- var dispatchLinkOrScriptLoad = (element) => {
808
- const loadEvent = defineEventSourceElement(element, "load");
809
- if (typeof element.onload === "function") {
810
- element.onload.call(element, loadEvent);
811
- return;
602
+ observer.observe(styleElement, observerConfig);
812
603
  }
813
- element.dispatchEvent(loadEvent);
814
604
  };
815
- var dispatchLinkOrScriptError = (element) => {
816
- const errorEvent = defineEventSourceElement(element, "error");
817
- if (typeof element.onerror === "function") {
818
- element.onerror.call(element, errorEvent);
819
- return;
605
+ async function executeAppStyles(app, container) {
606
+ const styleList = Array.from(app.source?.styles?.values() || []);
607
+ const promiseList = [];
608
+ for (const style of styleList) {
609
+ promiseList.push(style.executeCode(app));
820
610
  }
821
- element.dispatchEvent(errorEvent);
822
- };
611
+ await Promise.all(promiseList).then((styleElementList) => {
612
+ const parentElement = container || app.container;
613
+ if (app.keepAlive && !(parentElement instanceof ShadowRoot)) {
614
+ document.head.append(...styleElementList);
615
+ } else {
616
+ parentElement?.append(...styleElementList);
617
+ }
618
+ });
619
+ }
823
620
 
824
- // src/utils/element.ts
825
- var { appendChild: bodyAppendChild } = HTMLBodyElement.prototype;
826
- var handleStyleElement = (child, app) => {
827
- if (child.hasAttribute("exclude")) {
828
- return document.createComment("\u3010bk-weweb\u3011style with exclude attribute is ignored");
621
+ // src/mode/instance.ts
622
+ var DEFAULT_RANDOM_LENGTH = 5;
623
+ var WRAPPER_SUFFIX = "-wrapper";
624
+ var MicroInstanceModel = class {
625
+ state = AppState.UNSET;
626
+ appCacheKey;
627
+ container;
628
+ data;
629
+ fetchSource;
630
+ initSource;
631
+ isPreLoad = false;
632
+ keepAlive;
633
+ name;
634
+ sandBox;
635
+ scopeCss = true;
636
+ scopeJs = false;
637
+ showSourceCode = true;
638
+ source;
639
+ url;
640
+ constructor(props) {
641
+ this.name = props.id !== props.url ? props.id : random(DEFAULT_RANDOM_LENGTH);
642
+ this.appCacheKey = props.id || this.name;
643
+ this.url = props.url;
644
+ this.container = props.container ?? void 0;
645
+ this.scopeJs = props.scopeJs ?? true;
646
+ this.showSourceCode = props.showSourceCode ?? true;
647
+ this.scopeCss = props.scopeCss ?? true;
648
+ this.keepAlive = props.keepAlive ?? false;
649
+ this.data = props.data ?? {};
650
+ this.initSource = props.initSource ?? [];
651
+ this.fetchSource = props.fetchSource;
652
+ this.initializeSandBox();
829
653
  }
830
- if (child.textContent) {
831
- if (!(app.container instanceof ShadowRoot) && appCache.getBaseAppStyle(child.textContent)) {
832
- return document.createComment("\u3010bk-weweb\u3011style is effective in base app");
654
+ /** 激活微模块 */
655
+ activated(container, callback) {
656
+ this.isPreLoad = false;
657
+ this.state = AppState.ACTIVATED;
658
+ if (this.container && container) {
659
+ this.setContainerAttribute(container);
660
+ this.transferNodes(container);
661
+ this.container = container;
662
+ this.initShadowRootContainer();
663
+ this.sandBox?.activated();
664
+ const scriptInfo = this.getScriptInfo();
665
+ callback?.(this, scriptInfo?.exportInstance);
833
666
  }
834
667
  }
835
- if (!child.hasAttribute("ignore")) {
836
- const styleInstance = new Style({
837
- code: child.textContent || "",
838
- fromHtml: false,
839
- url: ""
840
- });
841
- app.source?.setStyle(randomUrl(), styleInstance);
842
- styleInstance.scopedStyleCSS(app, child);
668
+ /** 停用微模块 */
669
+ deactivated() {
670
+ this.state = AppState.DEACTIVATED;
671
+ this.sandBox?.deactivated();
843
672
  }
844
- return child;
845
- };
846
- var handleLinkElement = (child, parent, app) => {
847
- const result = app.source?.collectLink(child, parent, true);
848
- if (!result) return child;
849
- if (result.style) {
850
- result.style.scopedLinkCSS(app, child);
673
+ /** 挂载微模块 */
674
+ mount(container, callback) {
675
+ this.isPreLoad = false;
676
+ this.container = container ?? this.container;
677
+ this.initShadowRootContainer();
678
+ this.state = AppState.MOUNTING;
679
+ this.setContainerAttribute(this.container);
680
+ this.setupContainer();
681
+ this.executeStyles();
682
+ this.sandBox?.activated();
683
+ execAppScripts(this).finally(() => {
684
+ this.state = AppState.MOUNTED;
685
+ this.renderInstance();
686
+ const scriptInfo = this.getScriptInfo();
687
+ callback?.(this, scriptInfo?.exportInstance);
688
+ });
851
689
  }
852
- return result.replace !== child ? result.replace : child;
853
- };
854
- var createScriptObserver = (child, parent, app) => {
855
- const observer = new MutationObserver(() => {
856
- if (child.getAttribute("src")) {
857
- observer.disconnect();
858
- const scriptInfo = app.source.collectScript(child, parent, true);
859
- if (scriptInfo?.replace) {
860
- bodyAppendChild.call(app.container, scriptInfo.replace);
861
- }
862
- if (isJsonpUrl(child.getAttribute("src"))) {
863
- app.container?.append(child);
864
- return;
865
- }
866
- if (scriptInfo?.script) {
867
- scriptInfo.script.executeCode(app);
868
- }
869
- child.remove();
870
- } else if (child.textContent) {
871
- observer.disconnect();
872
- const scriptInstance = new Script({
873
- async: false,
874
- code: child.textContent,
875
- defer: child.type === "module",
876
- fromHtml: false,
877
- isModule: child.type === "module"
878
- });
879
- app.source.scripts.set(randomUrl(), scriptInstance);
880
- try {
881
- scriptInstance.executeCode(app);
882
- } catch (error) {
883
- console.error(error);
884
- } finally {
885
- if (!scriptInstance.isModule) {
886
- dispatchLinkOrScriptLoad(child);
887
- }
888
- child.remove();
889
- }
890
- }
891
- });
892
- return observer;
893
- };
894
- var handleScriptElement = (child, parent, app) => {
895
- const replaceInfo = app.source.collectScript(child, parent, true);
896
- if (!replaceInfo) {
897
- return child;
690
+ /** 错误处理 */
691
+ onError() {
692
+ this.state = AppState.ERROR;
898
693
  }
899
- if (replaceInfo.script) {
900
- replaceInfo.script.executeCode(app);
694
+ /** 挂载处理 */
695
+ onMount() {
696
+ if (this.isPreLoad) return;
697
+ this.state = AppState.LOADED;
698
+ this.mount();
901
699
  }
902
- if (replaceInfo.replace !== child) {
903
- return replaceInfo.replace;
700
+ /** 注册运行中的应用 */
701
+ registerRunningApp() {
702
+ setCurrentRunningApp(this);
703
+ Promise.resolve().then(() => setCurrentRunningApp(null));
904
704
  }
905
- if (app.scopeJs && !child.getAttribute("src") && !child.textContent) {
906
- const observer = createScriptObserver(child, parent, app);
907
- observer.observe(child, { attributeFilter: ["src"], childList: true, subtree: false });
908
- return document.createComment("\u3010bk-weweb\u3011dynamic script or module");
705
+ /** 启动微模块 */
706
+ async start() {
707
+ if (!this.source || this.needsReload()) {
708
+ this.source = new EntrySource(this.url);
709
+ await this.source.importEntry(this);
710
+ }
909
711
  }
910
- return child;
911
- };
912
- var resetNewElement = (parent, child, app) => {
913
- if (child instanceof HTMLStyleElement) {
914
- return handleStyleElement(child, app);
712
+ /** 卸载微模块 */
713
+ unmount(needDestroy) {
714
+ this.state = AppState.UNMOUNT;
715
+ this.sandBox?.deactivated();
716
+ if (needDestroy) {
717
+ appCache.deleteApp(this.url);
718
+ }
719
+ if (this.container) {
720
+ this.container.innerHTML = "";
721
+ this.container = void 0;
722
+ }
915
723
  }
916
- if (child instanceof HTMLLinkElement) {
917
- return handleLinkElement(child, parent, app);
724
+ /** 初始化ShadowRoot容器 */
725
+ initShadowRootContainer() {
726
+ if (this.container instanceof ShadowRoot) {
727
+ Object.defineProperties(this.container, {
728
+ getBoundingClientRect: {
729
+ get() {
730
+ return this.host.getBoundingClientRect;
731
+ }
732
+ }
733
+ });
734
+ }
918
735
  }
919
- if (child instanceof HTMLScriptElement) {
920
- return handleScriptElement(child, parent, app);
736
+ set status(value) {
737
+ this.state = value;
921
738
  }
922
- return child;
923
- };
924
- var isSpecialElement = (node) => {
925
- return node instanceof HTMLScriptElement || node instanceof HTMLStyleElement || node instanceof HTMLLinkElement;
926
- };
927
- var getTargetContainer = (app, isSpecial) => {
928
- const needKeepAlive = isSpecial && !!app.keepAlive && !(app.container instanceof ShadowRoot);
929
- return needKeepAlive ? document.head : app.container;
930
- };
931
- var elementAppendHandler = (parent, newChild, rawMethod) => {
932
- if (!newChild.__BK_WEWEB_APP_KEY__) {
933
- return rawMethod.call(parent, newChild);
739
+ get status() {
740
+ return this.state;
934
741
  }
935
- const app = appCache.getApp(newChild.__BK_WEWEB_APP_KEY__);
936
- if (!app?.container) {
937
- return rawMethod.call(parent, newChild);
742
+ /** 初始化沙盒 */
743
+ initializeSandBox() {
744
+ if (this.scopeJs) {
745
+ this.sandBox = new SandBox(this);
746
+ }
938
747
  }
939
- const targetChild = resetNewElement(parent, newChild, app);
940
- const isSpecial = isSpecialElement(newChild);
941
- const needKeepAlive = isSpecial && !!app.keepAlive && !(app.container instanceof ShadowRoot);
942
- const container = getTargetContainer(app, isSpecial);
943
- setMarkElement(targetChild, app, needKeepAlive);
944
- return rawMethod.call(container, targetChild);
945
- };
946
- var elementInsertHandler = (parent, newChild, passiveChild, rawMethod) => {
947
- if (!newChild.__BK_WEWEB_APP_KEY__) {
948
- return rawMethod.call(parent, newChild, passiveChild);
748
+ /** 设置容器属性 */
749
+ setContainerAttribute(container) {
750
+ if (container instanceof HTMLElement) {
751
+ container.setAttribute(CSS_ATTRIBUTE_KEY, this.name);
752
+ }
949
753
  }
950
- const app = appCache.getApp(newChild.__BK_WEWEB_APP_KEY__);
951
- if (!app?.container) {
952
- return rawMethod.call(parent, newChild, passiveChild);
754
+ /** 转移节点到新容器 */
755
+ transferNodes(container) {
756
+ if (!this.container) return;
757
+ const fragment = document.createDocumentFragment();
758
+ const nodeList = Array.from(this.container.childNodes);
759
+ for (const node of nodeList) {
760
+ fragment.appendChild(node);
761
+ }
762
+ container.appendChild(fragment);
953
763
  }
954
- const isSpecial = isSpecialElement(newChild);
955
- const needKeepAlive = isSpecial && app.keepAlive && !(app.container instanceof ShadowRoot);
956
- const container = getTargetContainer(app, isSpecial);
957
- const targetChild = resetNewElement(parent, newChild, app);
958
- if (needKeepAlive) {
959
- setMarkElement(targetChild, app, needKeepAlive);
764
+ /** 设置容器 */
765
+ setupContainer() {
766
+ if (this.container) {
767
+ this.container.innerHTML = "";
768
+ const instanceWrapper = this.createInstanceWrapper();
769
+ this.container.appendChild(instanceWrapper);
770
+ }
960
771
  }
961
- if (passiveChild && !container.contains(passiveChild)) {
962
- return bodyAppendChild.call(container, targetChild);
772
+ /** 执行样式 */
773
+ executeStyles() {
774
+ if (this.source?.styles.size && this.container) {
775
+ executeAppStyles(this, this.container);
776
+ }
963
777
  }
964
- return rawMethod.call(container, targetChild, passiveChild);
965
- };
966
- var setMarkElement = (element, app, keepAlive) => {
967
- if (keepAlive && app) {
968
- element.__KEEP_ALIVE__ = app.appCacheKey;
969
- element.setAttribute("data-from", app.name);
970
- element.setAttribute("data-keep-alive", "true");
778
+ /** 创建实例包装器 */
779
+ createInstanceWrapper() {
780
+ const wrapper = document.createElement("div");
781
+ wrapper.id = `${this.name}${WRAPPER_SUFFIX}`;
782
+ return wrapper;
971
783
  }
972
- element.setAttribute?.("powered-by", "bk-weweb");
973
- return element;
974
- };
975
-
976
- // src/utils/fetch.ts
977
- var fetchSource = async (url, options = {}, app) => {
978
- if (typeof app?.fetchSource === "function") {
979
- try {
980
- return await app.fetchSource(url, options);
981
- } catch {
982
- return "";
784
+ /** 渲染实例 */
785
+ renderInstance() {
786
+ const scriptInfo = this.getScriptInfo();
787
+ if (scriptInfo?.exportInstance?.render && this.container) {
788
+ const targetContainer = this.container.querySelector(`#${this.name}${WRAPPER_SUFFIX}`);
789
+ if (targetContainer) {
790
+ scriptInfo.exportInstance.render(targetContainer, this.data);
791
+ }
983
792
  }
984
793
  }
985
- if (src_default.fetchSource) {
986
- return src_default.fetchSource(url, options);
794
+ /** 获取脚本信息 */
795
+ getScriptInfo() {
796
+ const script = this.source?.getScript(this.url);
797
+ return script ? { exportInstance: script.exportInstance } : void 0;
987
798
  }
988
- try {
989
- const response = await window.fetch(url, options);
990
- return await response.text();
991
- } catch {
992
- return "";
799
+ /** 检查是否需要重新加载 */
800
+ needsReload() {
801
+ return this.status === AppState.ERROR || this.status === AppState.UNSET;
993
802
  }
994
803
  };
995
804
 
996
- // src/entry/style.ts
997
- var CSS_SELECTORS = {
998
- ROOT_SELECTOR: /^((html[\s>~,]+body)|(html|body|:root))$/,
999
- BUILT_IN_ROOT_SELECTOR: /(^|\s+)((html[\s>~]+body)|(html|body|:root))(?=[\s>~]+|$)/,
1000
- FONT_FACE: /@font-face\s*\{[^}]+\}/g,
1001
- URL_PATTERN: /url\(["']?([^)"']+)["']?\)/gm,
1002
- DATA_BLOB_PROTOCOL: /^(data|blob):/,
1003
- HTTP_PROTOCOL: /^(https?:)?\/\//,
1004
- RELATIVE_PATH: /^((\.\.?\/)|[^/])/,
1005
- ROOT_HOST_PATTERN: /(:?:root|html)/gm
1006
- };
1007
- var STYLE_ATTRIBUTES = {
1008
- TYPE: "text/css",
1009
- POWERED_BY: "bk-weweb",
1010
- LINKED_FROM_BASE: "linked-from-base",
1011
- ORIGIN_SRC: "origin-src"
1012
- };
1013
- var PACK_RULE_NAMES = {
1014
- MEDIA: "media",
1015
- SUPPORTS: "supports"
1016
- };
1017
- var Style = class {
805
+ // src/entry/script.ts
806
+ var firstGlobalProp;
807
+ var secondGlobalProp;
808
+ var lastGlobalProp;
809
+ var STRICT_MODE_REGEX = /^"use\sstrict";$/gim;
810
+ var Script = class {
811
+ async = false;
1018
812
  code = "";
813
+ defer = false;
814
+ exportInstance;
1019
815
  fromHtml;
816
+ blobUrl;
1020
817
  initial;
1021
- prefetch = false;
1022
- preload = false;
818
+ isModule = false;
1023
819
  scoped;
1024
- scopedCode = "";
1025
820
  url;
1026
- constructor({ code, fromHtml, initial, prefetch, preload, url }) {
1027
- this.scoped = false;
1028
- this.code = code;
1029
- this.prefetch = prefetch ?? false;
1030
- this.preload = preload ?? false;
821
+ constructor({ async, code, defer, fromHtml, initial, isModule, url }) {
822
+ this.code = code?.replace(STRICT_MODE_REGEX, "");
823
+ this.async = async;
824
+ this.defer = defer;
825
+ this.isModule = isModule;
1031
826
  this.url = url;
1032
- this.fromHtml = fromHtml;
827
+ this.scoped = false;
828
+ this.fromHtml = fromHtml ?? false;
1033
829
  this.initial = initial ?? false;
830
+ this.blobUrl = void 0;
1034
831
  }
1035
- /**
1036
- * 通用样式作用域处理
1037
- * @param styleElement 样式元素
1038
- * @param app 应用实例
1039
- */
1040
- commonScoped(styleElement, app) {
1041
- if (app.scopeCss && !(app.container instanceof ShadowRoot)) {
1042
- this.applyScopedCSS(styleElement, app);
832
+ /** 执行脚本代码 */
833
+ async executeCode(app, needReplaceScriptElement = false) {
834
+ try {
835
+ if (!this.code) await this.getCode(app);
836
+ if (app instanceof MicroInstanceModel) {
837
+ const globalWindow = app.scopeJs ? app.sandBox?.proxyWindow || window : window;
838
+ noteGlobalProps(globalWindow);
839
+ }
840
+ const scopedCode = this.transformCode(app);
841
+ if (this.isModule) {
842
+ app.registerRunningApp();
843
+ const scriptElement = document.createElement("script");
844
+ let blobUrl = "";
845
+ if (scriptElement.__BK_WEWEB_APP_KEY__) {
846
+ scriptElement.__BK_WEWEB_APP_KEY__ = void 0;
847
+ }
848
+ if (this.url) {
849
+ blobUrl = `${this.url.replace(/^\//, `${app.url}/`)}?key=${Date.now()}`;
850
+ } else {
851
+ const blob = new Blob([scopedCode], { type: "text/javascript" });
852
+ blobUrl = URL.createObjectURL(blob);
853
+ }
854
+ scriptElement.src = blobUrl;
855
+ scriptElement.type = "module";
856
+ if (needReplaceScriptElement) return scriptElement;
857
+ const needKeepAlive = !!app.keepAlive && !(app.container instanceof ShadowRoot);
858
+ const container = needKeepAlive ? document.head : app.container;
859
+ setMarkElement(scriptElement, app, needKeepAlive);
860
+ container.appendChild(scriptElement);
861
+ URL.revokeObjectURL(blobUrl);
862
+ } else if (app.showSourceCode) {
863
+ const scriptElement = document.createElement("script");
864
+ if (scriptElement.__BK_WEWEB_APP_KEY__) {
865
+ scriptElement.__BK_WEWEB_APP_KEY__ = void 0;
866
+ }
867
+ app.registerRunningApp();
868
+ this.executeSourceScript(scriptElement, scopedCode);
869
+ if (needReplaceScriptElement) return scriptElement;
870
+ const needKeepAlive = !!app.keepAlive && !(app.container instanceof ShadowRoot);
871
+ const container = needKeepAlive ? document.head : app.container;
872
+ setMarkElement(scriptElement, app, needKeepAlive);
873
+ container.appendChild(scriptElement);
874
+ } else {
875
+ this.executeMemoryScript(app, scopedCode);
876
+ if (needReplaceScriptElement) return document.createComment("\u3010bk-weweb\u3011dynamic script");
877
+ }
878
+ if (app instanceof MicroInstanceModel) {
879
+ const globalWindow = app.scopeJs ? app.sandBox?.proxyWindow || window : window;
880
+ const isIifeVar = !!this.code.replace(/\/\*[\s\S]*?\*\//g, "").match(/^\s*var\s/);
881
+ const exportProp = getGlobalProp(globalWindow, isIifeVar);
882
+ if (exportProp) {
883
+ this.exportInstance = globalWindow[exportProp];
884
+ if (!app.scopeJs) {
885
+ const descriptor = Object.getOwnPropertyDescriptor(globalWindow, exportProp);
886
+ if (descriptor?.configurable) {
887
+ delete globalWindow[exportProp];
888
+ }
889
+ }
890
+ }
891
+ }
892
+ } catch (error) {
893
+ console.error("execute script code error", error);
894
+ }
895
+ return;
896
+ }
897
+ /** 内存脚本执行 */
898
+ executeMemoryScript(app, scopedCode) {
899
+ try {
900
+ const isScopedLocation = app instanceof MicroAppModel && app.scopeLocation;
901
+ app.registerRunningApp();
902
+ new Function("window", "location", "history", scopedCode)(
903
+ app.sandBox.proxyWindow || window,
904
+ isScopedLocation ? app.iframe.contentWindow.location : window.location,
905
+ isScopedLocation ? app.iframe.contentWindow.history : window.history
906
+ );
907
+ } catch (error) {
908
+ console.error(error);
909
+ }
910
+ }
911
+ /** 脚本标签执行 */
912
+ executeSourceScript(scriptElement, scopedCode) {
913
+ if (this.isModule) {
914
+ scriptElement.src = `${this.url}?key=${Date.now()}`;
915
+ scriptElement.setAttribute("type", "module");
1043
916
  } else {
1044
- this.applyUnscopedCSS(styleElement, app);
917
+ scriptElement.textContent = scopedCode;
1045
918
  }
1046
- this.scoped = true;
919
+ this.url && scriptElement.setAttribute("origin-src", this.url);
1047
920
  }
1048
- /**
1049
- * 创建样式元素
1050
- */
1051
- createStyleElement() {
1052
- const styleElement = document.createElement("style");
1053
- if (styleElement.__BK_WEWEB_APP_KEY__) {
1054
- styleElement.__BK_WEWEB_APP_KEY__ = void 0;
921
+ /** 获取脚本内容 */
922
+ async getCode(app) {
923
+ if (this.code.length || !this.url || this.isModule) {
924
+ return this.code;
1055
925
  }
1056
- return styleElement;
926
+ let code = "";
927
+ if (app?.source?.getScript(this.url)) {
928
+ code = app.source.getScript(this.url)?.code || "";
929
+ }
930
+ if (!code && appCache.getCacheScript(this.url)) {
931
+ code = appCache.getCacheScript(this.url)?.code || "";
932
+ }
933
+ if (!code) {
934
+ code = await fetchSource(this.url, {}, app).catch((error) => {
935
+ console.error(`fetch script ${this.url} error`, error);
936
+ return "";
937
+ });
938
+ }
939
+ code = code.replace(STRICT_MODE_REGEX, "");
940
+ this.code = code;
941
+ return code;
1057
942
  }
1058
- /**
1059
- * 执行样式代码
1060
- * @param app 应用实例
1061
- * @returns 返回执行后的style标签
1062
- */
1063
- async executeCode(app) {
1064
- app.registerRunningApp();
1065
- let styleElement = this.createStyleElement();
1066
- styleElement.setAttribute("type", STYLE_ATTRIBUTES.TYPE);
1067
- styleElement.textContent = this.code;
1068
- try {
1069
- if (!this.code) {
1070
- await this.getCode(app);
943
+ setCode(code) {
944
+ this.code = code;
945
+ }
946
+ /** 转换脚本内容 */
947
+ transformCode(app) {
948
+ const sourceMapUrl = this.url ? `//# sourceURL=${this.url}
949
+ ` : "";
950
+ if (app.sandBox) {
951
+ if (this.isModule) {
952
+ if (this.url) {
953
+ return this.code;
954
+ }
955
+ const importList = this.code.match(/import\s*.*(?:from)?\s*"([^"]+)";/gm);
956
+ for (const item of importList || []) {
957
+ this.code = this.code.replace(item, item.replace(/"\//, `"${app.url}/`).replace(/"$/, `?key=${Date.now()}"`));
958
+ }
959
+ return `
960
+ ${this.code};
961
+ `;
962
+ }
963
+ if (app.showSourceCode) {
964
+ return `;(function(window, self, globalThis){
965
+ with(window){
966
+ ${getGlobalContextCode()}
967
+
968
+ ${this.code}
969
+
970
+ ${sourceMapUrl}
971
+ }
972
+ }).call(window['${app.sandBox.windowSymbolKey}'],
973
+ window['${app.sandBox.windowSymbolKey}'], window['${app.sandBox.windowSymbolKey}'], window['${app.sandBox.windowSymbolKey}']);`;
974
+ }
975
+ return `
976
+ with(window) {
977
+ try {
978
+ ${getGlobalContextCode()}
979
+
980
+ ${this.code}
981
+
982
+ ${sourceMapUrl}
983
+ }
984
+ catch(e) {
985
+ console.error(e)
986
+ }
987
+ }
988
+ `;
989
+ }
990
+ return this.code;
991
+ }
992
+ };
993
+ function shouldSkipProperty(global, property) {
994
+ const globalWindow = global;
995
+ return (
996
+ // biome-ignore lint/suspicious/noPrototypeBuiltins: <explanation>
997
+ !global.hasOwnProperty(property) || !Number.isNaN(property) && property < global.length || typeof navigator !== "undefined" && navigator.userAgent.indexOf("Trident") !== -1 && Boolean(globalWindow[property]) && typeof window !== "undefined" && globalWindow[property]?.parent === window
998
+ );
999
+ }
1000
+ function getGlobalProp(global, useFirstGlobalProp) {
1001
+ let cnt = 0;
1002
+ let foundLastProp;
1003
+ let result;
1004
+ for (const property in global) {
1005
+ if (shouldSkipProperty(global, property)) continue;
1006
+ if (cnt === 0 && property !== firstGlobalProp || cnt === 1 && property !== secondGlobalProp) {
1007
+ return property;
1008
+ }
1009
+ if (foundLastProp) {
1010
+ lastGlobalProp = property;
1011
+ result = useFirstGlobalProp && result || property;
1012
+ } else {
1013
+ foundLastProp = property === lastGlobalProp;
1014
+ }
1015
+ cnt += 1;
1016
+ }
1017
+ return result;
1018
+ }
1019
+ function noteGlobalProps(global) {
1020
+ secondGlobalProp = void 0;
1021
+ firstGlobalProp = secondGlobalProp;
1022
+ for (const property in global) {
1023
+ if (shouldSkipProperty(global, property)) continue;
1024
+ if (!firstGlobalProp) {
1025
+ firstGlobalProp = property;
1026
+ } else if (!secondGlobalProp) {
1027
+ secondGlobalProp = property;
1028
+ }
1029
+ lastGlobalProp = property;
1030
+ }
1031
+ return lastGlobalProp;
1032
+ }
1033
+ async function execAppScripts(app) {
1034
+ const appScriptList = Array.from(app.source.scripts.values()).filter((script) => script.fromHtml || script.initial);
1035
+ const commonList = appScriptList.filter(
1036
+ (script) => !script.async && !script.defer || script.isModule && !script.fromHtml
1037
+ );
1038
+ await Promise.all(commonList.map((script) => script.getCode(app)));
1039
+ await Promise.all(commonList.map((script) => script.executeCode(app)));
1040
+ const deferScriptList = [];
1041
+ const asyncScriptList = [];
1042
+ for (const script of appScriptList) {
1043
+ if (script.defer || script.async) {
1044
+ if (!script.code && script.defer) {
1045
+ deferScriptList.push(script.executeCode(app));
1046
+ } else {
1047
+ asyncScriptList.push(script.executeCode(app));
1071
1048
  }
1072
- styleElement = this.scopedStyleCSS(app, styleElement);
1073
- this.scoped = true;
1074
- } catch (error) {
1075
- console.error("scoped style error", error);
1076
1049
  }
1077
- return styleElement;
1078
1050
  }
1079
- /**
1080
- * 获取样式代码
1081
- */
1082
- async getCode(app) {
1083
- if (this.code.length || !this.url) {
1084
- return this.code;
1085
- }
1086
- const code = this.getCodeFromAppSource(app) || this.getCodeFromCache() || await this.fetchCodeFromRemote(app);
1087
- this.code = code;
1088
- return code;
1051
+ await Promise.all([...asyncScriptList, ...deferScriptList]).catch((error) => {
1052
+ console.error(error);
1053
+ });
1054
+ }
1055
+
1056
+ // src/utils/element.ts
1057
+ var { appendChild: bodyAppendChild } = HTMLBodyElement.prototype;
1058
+ var handleStyleElement = (child, app) => {
1059
+ if (child.hasAttribute("exclude")) {
1060
+ return document.createComment("\u3010bk-weweb\u3011style with exclude attribute is ignored");
1089
1061
  }
1090
- /**
1091
- * 检查并链接基础应用样式
1092
- * @param styleElement 样式元素
1093
- * @param app 应用实例
1094
- * @returns 是否已链接基础样式
1095
- */
1096
- linkedBaseStyle(styleElement, app) {
1097
- if (!(app.container instanceof ShadowRoot) && styleElement.textContent && appCache.getBaseAppStyle(styleElement.textContent)) {
1098
- this.clearStyleElement(styleElement);
1099
- styleElement.setAttribute(STYLE_ATTRIBUTES.LINKED_FROM_BASE, "true");
1100
- return true;
1062
+ if (child.textContent) {
1063
+ if (!(app.container instanceof ShadowRoot) && appCache.getBaseAppStyle(child.textContent)) {
1064
+ return document.createComment("\u3010bk-weweb\u3011style is effective in base app");
1101
1065
  }
1102
- return false;
1103
1066
  }
1104
- /**
1105
- * 重置包装规则
1106
- */
1107
- resetPackRule(rule, prefix, packName) {
1108
- const result = this.scopeRule(Array.from(rule.cssRules), prefix);
1109
- return `@${packName} ${rule.conditionText} {${result}}`;
1067
+ if (!child.hasAttribute("ignore")) {
1068
+ const styleInstance = new Style({
1069
+ code: child.textContent || "",
1070
+ fromHtml: false,
1071
+ url: ""
1072
+ });
1073
+ app.source?.setStyle(randomUrl(), styleInstance);
1074
+ styleInstance.scopedStyleCSS(app, child);
1110
1075
  }
1111
- /**
1112
- * 重置URL地址
1113
- */
1114
- resetUrlHost(cssText, uri, linkPath) {
1115
- let baseURI = uri;
1116
- return cssText.replace(CSS_SELECTORS.URL_PATTERN, (text, matchedUrl) => {
1117
- if (CSS_SELECTORS.DATA_BLOB_PROTOCOL.test(matchedUrl) || CSS_SELECTORS.HTTP_PROTOCOL.test(matchedUrl)) {
1118
- return text;
1076
+ return child;
1077
+ };
1078
+ var handleLinkElement = (child, parent, app) => {
1079
+ const result = app.source?.collectLink(child, parent, true);
1080
+ if (!result) return child;
1081
+ if (result.style) {
1082
+ result.style.scopedLinkCSS(app, child);
1083
+ }
1084
+ return result.replace !== child ? result.replace : child;
1085
+ };
1086
+ var createScriptObserver = (child, parent, app) => {
1087
+ const observer = new MutationObserver(() => {
1088
+ if (child.getAttribute("src")) {
1089
+ observer.disconnect();
1090
+ const scriptInfo = app.source.collectScript(child, parent, true);
1091
+ if (scriptInfo?.replace) {
1092
+ bodyAppendChild.call(app.container, scriptInfo.replace);
1119
1093
  }
1120
- if (CSS_SELECTORS.RELATIVE_PATH.test(matchedUrl) && linkPath) {
1121
- baseURI = this.buildBaseURI(linkPath);
1094
+ if (isJsonpUrl(child.getAttribute("src"))) {
1095
+ app.container?.append(child);
1096
+ return;
1122
1097
  }
1123
- return `url("${fillUpPath(matchedUrl, baseURI)}")`;
1124
- });
1125
- }
1126
- /**
1127
- * css rule 处理
1128
- */
1129
- scopeRule(rules, cssPrefix) {
1130
- let result = "";
1131
- for (const rule of rules) {
1132
- switch (rule.type) {
1133
- case 1 /* STYLE_RULE */:
1134
- result += this.scopeStyleRule(rule, cssPrefix);
1135
- break;
1136
- case 4 /* MEDIA_RULE */:
1137
- result += this.resetPackRule(rule, cssPrefix, PACK_RULE_NAMES.MEDIA);
1138
- break;
1139
- case 12 /* SUPPORTS_RULE */:
1140
- result += this.resetPackRule(rule, cssPrefix, PACK_RULE_NAMES.SUPPORTS);
1141
- break;
1142
- default:
1143
- result += rule.cssText;
1144
- break;
1098
+ if (scriptInfo?.script) {
1099
+ scriptInfo.script.executeCode(app);
1145
1100
  }
1146
- }
1147
- return result.replace(/^\s+/, "");
1148
- }
1149
- /**
1150
- * style rule 处理
1151
- */
1152
- scopeStyleRule(rule, prefix) {
1153
- const { cssText, selectorText } = rule;
1154
- if (CSS_SELECTORS.ROOT_SELECTOR.test(selectorText)) {
1155
- return cssText.replace(CSS_SELECTORS.ROOT_SELECTOR, prefix);
1156
- }
1157
- if (selectorText === "*") {
1158
- return cssText.replace("*", `${prefix} *`);
1159
- }
1160
- return cssText.replace(
1161
- /^[\s\S]+{/,
1162
- (selectors) => selectors.replace(/(^|,)([^,]+)/g, (all, delimiter, selector) => {
1163
- if (CSS_SELECTORS.BUILT_IN_ROOT_SELECTOR.test(selector)) {
1164
- return all.replace(CSS_SELECTORS.BUILT_IN_ROOT_SELECTOR, prefix);
1101
+ child.remove();
1102
+ } else if (child.textContent) {
1103
+ observer.disconnect();
1104
+ const scriptInstance = new Script({
1105
+ async: false,
1106
+ code: child.textContent,
1107
+ defer: child.type === "module",
1108
+ fromHtml: false,
1109
+ isModule: child.type === "module"
1110
+ });
1111
+ app.source.scripts.set(randomUrl(), scriptInstance);
1112
+ try {
1113
+ scriptInstance.executeCode(app);
1114
+ } catch (error) {
1115
+ console.error(error);
1116
+ } finally {
1117
+ if (!scriptInstance.isModule) {
1118
+ dispatchLinkOrScriptLoad(child);
1165
1119
  }
1166
- return `${delimiter} ${prefix} ${selector.replace(/^\s*/, "")}`;
1167
- })
1168
- );
1169
- }
1170
- /**
1171
- * link style 处理
1172
- */
1173
- scopedLinkCSS(app, linkElement) {
1174
- const styleElement = this.createStyleElement();
1175
- styleElement.setAttribute("type", STYLE_ATTRIBUTES.TYPE);
1176
- const needKeepAlive = !!app.keepAlive && !(app.container instanceof ShadowRoot);
1177
- setMarkElement(styleElement, app, needKeepAlive);
1178
- const container = needKeepAlive ? document.head : app.container;
1179
- try {
1180
- if (this.code) {
1181
- this.handleExistingCode(styleElement, app, container, linkElement);
1182
- } else if (linkElement.getAttribute("href")) {
1183
- this.handleHrefAttribute(styleElement, app, container, linkElement);
1184
- } else {
1185
- this.handleMissingHref(styleElement, app, container, linkElement);
1120
+ child.remove();
1186
1121
  }
1187
- } catch {
1188
- linkElement && dispatchLinkOrScriptError(linkElement);
1189
1122
  }
1190
- return styleElement;
1123
+ });
1124
+ return observer;
1125
+ };
1126
+ var handleScriptElement = (child, parent, app) => {
1127
+ const replaceInfo = app.source.collectScript(child, parent, true);
1128
+ if (!replaceInfo) {
1129
+ return child;
1191
1130
  }
1192
- /**
1193
- * 隔离 style
1194
- */
1195
- scopedStyleCSS(app, styleElement) {
1196
- const needKeepAlive = !!app.keepAlive && !(app.container instanceof ShadowRoot);
1197
- setMarkElement(styleElement, app, needKeepAlive);
1198
- if (this.code || styleElement.textContent) {
1199
- this.processExistingContent(styleElement, app);
1200
- } else {
1201
- this.observeContentChanges(styleElement, app);
1202
- }
1203
- if (this.url) {
1204
- styleElement.setAttribute(STYLE_ATTRIBUTES.ORIGIN_SRC, this.url);
1205
- }
1206
- return styleElement;
1131
+ if (replaceInfo.script) {
1132
+ replaceInfo.script.executeCode(app);
1207
1133
  }
1208
- /**
1209
- * 应用隔离 style
1210
- */
1211
- applyScopedCSS(styleElement, app) {
1212
- const cssStyleSheet = new CSSStyleSheet({ disabled: true });
1213
- cssStyleSheet.replaceSync(styleElement.textContent || this.code);
1214
- const rules = Array.from(cssStyleSheet?.cssRules ?? []);
1215
- const cssPrefix = `#${app.name}`;
1216
- const scopedCss = this.scopeRule(rules, cssPrefix);
1217
- const cssText = this.resetUrlHost(scopedCss, app.url, this.url);
1218
- styleElement.textContent = cssText;
1219
- this.scopedCode = cssText;
1134
+ if (replaceInfo.replace !== child) {
1135
+ return replaceInfo.replace;
1220
1136
  }
1221
- applyUnscopedCSS(styleElement, app) {
1222
- const cssText = this.resetUrlHost(styleElement.textContent || this.code || "", app.url, this.url);
1223
- if (cssText && app.container instanceof ShadowRoot) {
1224
- this.handleShadowRootFonts(cssText, app);
1225
- }
1226
- styleElement.textContent = cssText.replace(CSS_SELECTORS.ROOT_HOST_PATTERN, ":host");
1137
+ if (app.scopeJs && !child.getAttribute("src") && !child.textContent) {
1138
+ const observer = createScriptObserver(child, parent, app);
1139
+ observer.observe(child, { attributeFilter: ["src"], childList: true, subtree: false });
1140
+ return document.createComment("\u3010bk-weweb\u3011dynamic script or module");
1227
1141
  }
1228
- /**
1229
- * 处理ShadowRoot中的字体
1230
- */
1231
- handleShadowRootFonts(cssText, app) {
1232
- let fontContent = "";
1233
- const fontFaces = cssText.match(CSS_SELECTORS.FONT_FACE) || [];
1234
- for (const fontFace of fontFaces) {
1235
- fontContent += `${fontFace}
1236
- `;
1237
- }
1238
- const rawDocument = app.sandBox?.rawDocument;
1239
- if (rawDocument && fontContent) {
1240
- const fontStyle = rawDocument.createElement("style");
1241
- fontStyle.setAttribute("type", STYLE_ATTRIBUTES.TYPE);
1242
- fontStyle.setAttribute("powered-by", STYLE_ATTRIBUTES.POWERED_BY);
1243
- fontStyle.textContent = fontContent;
1244
- rawDocument?.head?.append(fontStyle);
1245
- }
1142
+ return child;
1143
+ };
1144
+ var resetNewElement = (parent, child, app) => {
1145
+ if (child instanceof HTMLStyleElement) {
1146
+ return handleStyleElement(child, app);
1246
1147
  }
1247
- getCodeFromAppSource(app) {
1248
- if (this.url && app?.source?.styles?.has(this.url)) {
1249
- return app.source.styles.get(this.url)?.code || "";
1250
- }
1251
- return "";
1148
+ if (child instanceof HTMLLinkElement) {
1149
+ return handleLinkElement(child, parent, app);
1252
1150
  }
1253
- getCodeFromCache() {
1254
- if (this.url && appCache.getCacheStyle(this.url)) {
1255
- const style = appCache.getCacheStyle(this.url);
1256
- return style?.code || "";
1257
- }
1258
- return "";
1151
+ if (child instanceof HTMLScriptElement) {
1152
+ return handleScriptElement(child, parent, app);
1259
1153
  }
1260
- async fetchCodeFromRemote(app) {
1261
- return await fetchSource(this.url, {}, app).catch(() => "");
1154
+ return child;
1155
+ };
1156
+ var isSpecialElement = (node) => {
1157
+ return node instanceof HTMLScriptElement || node instanceof HTMLStyleElement || node instanceof HTMLLinkElement;
1158
+ };
1159
+ var getTargetContainer = (app, isSpecial) => {
1160
+ const needKeepAlive = isSpecial && !!app.keepAlive && !(app.container instanceof ShadowRoot);
1161
+ return needKeepAlive ? document.head : app.container;
1162
+ };
1163
+ var elementAppendHandler = (parent, newChild, rawMethod) => {
1164
+ if (!newChild.__BK_WEWEB_APP_KEY__) {
1165
+ return rawMethod.call(parent, newChild);
1262
1166
  }
1263
- clearStyleElement(styleElement) {
1264
- styleElement.textContent = "";
1265
- styleElement.innerHTML = "";
1167
+ const app = appCache.getApp(newChild.__BK_WEWEB_APP_KEY__);
1168
+ if (!app?.container) {
1169
+ return rawMethod.call(parent, newChild);
1266
1170
  }
1267
- buildBaseURI(linkPath) {
1268
- const pathArr = linkPath.split("/");
1269
- pathArr.pop();
1270
- return addUrlProtocol(`${pathArr.join("/")}/`);
1171
+ const targetChild = resetNewElement(parent, newChild, app);
1172
+ const isSpecial = isSpecialElement(newChild);
1173
+ const needKeepAlive = isSpecial && !!app.keepAlive && !(app.container instanceof ShadowRoot);
1174
+ const container = getTargetContainer(app, isSpecial);
1175
+ setMarkElement(targetChild, app, needKeepAlive);
1176
+ return rawMethod.call(container, targetChild);
1177
+ };
1178
+ var elementInsertHandler = (parent, newChild, passiveChild, rawMethod) => {
1179
+ if (!newChild.__BK_WEWEB_APP_KEY__) {
1180
+ return rawMethod.call(parent, newChild, passiveChild);
1271
1181
  }
1272
- handleExistingCode(styleElement, app, container, linkElement) {
1273
- this.commonScoped(styleElement, app);
1274
- container?.prepend(styleElement);
1275
- linkElement && dispatchLinkOrScriptLoad(linkElement);
1182
+ const app = appCache.getApp(newChild.__BK_WEWEB_APP_KEY__);
1183
+ if (!app?.container) {
1184
+ return rawMethod.call(parent, newChild, passiveChild);
1276
1185
  }
1277
- /**
1278
- * 处理href属性
1279
- */
1280
- handleHrefAttribute(styleElement, app, container, linkElement) {
1281
- this.url = fillUpPath(linkElement.getAttribute("href"), app.url);
1282
- this.getCode(app).then(() => {
1283
- this.scopedStyleCSS(app, styleElement);
1284
- linkElement.remove();
1285
- container?.prepend(styleElement);
1286
- linkElement && dispatchLinkOrScriptLoad(linkElement);
1287
- this.scoped = true;
1288
- });
1186
+ const isSpecial = isSpecialElement(newChild);
1187
+ const needKeepAlive = isSpecial && app.keepAlive && !(app.container instanceof ShadowRoot);
1188
+ const container = getTargetContainer(app, isSpecial);
1189
+ const targetChild = resetNewElement(parent, newChild, app);
1190
+ if (needKeepAlive) {
1191
+ setMarkElement(targetChild, app, needKeepAlive);
1289
1192
  }
1290
- /**
1291
- * 处理缺失的href
1292
- */
1293
- handleMissingHref(styleElement, app, container, linkElement) {
1294
- const observer = new MutationObserver(() => {
1295
- const href = linkElement.getAttribute("href");
1296
- if (!href) return;
1297
- observer.disconnect();
1298
- this.url = fillUpPath(href, app.url);
1299
- this.getCode(app).then(() => {
1300
- this.scopedStyleCSS(app, styleElement);
1301
- linkElement.remove();
1302
- container?.prepend(styleElement);
1303
- linkElement && dispatchLinkOrScriptLoad(linkElement);
1304
- this.scoped = true;
1305
- });
1306
- });
1307
- const observerConfig = {
1308
- attributeFilter: ["href"],
1309
- childList: false,
1310
- subtree: false
1311
- };
1312
- observer.observe(linkElement, observerConfig);
1193
+ if (passiveChild && !container.contains(passiveChild)) {
1194
+ return bodyAppendChild.call(container, targetChild);
1313
1195
  }
1314
- processExistingContent(styleElement, app) {
1315
- if (styleElement.textContent) {
1316
- this.clearStyleElement(styleElement);
1196
+ return rawMethod.call(container, targetChild, passiveChild);
1197
+ };
1198
+ var setMarkElement = (element, app, keepAlive) => {
1199
+ if (keepAlive && app) {
1200
+ element.__KEEP_ALIVE__ = app.appCacheKey;
1201
+ element.setAttribute("data-from", app.name);
1202
+ element.setAttribute("data-keep-alive", "true");
1203
+ }
1204
+ element.setAttribute?.("powered-by", "bk-weweb");
1205
+ return element;
1206
+ };
1207
+
1208
+ // src/context/document.ts
1209
+ var SPECIAL_ELEMENT_TAGS = ["body", "html", "head"];
1210
+ var APP_KEY_PROPERTY = "__BK_WEWEB_APP_KEY__";
1211
+ function createShadowRootInsertAdjacentHTML(app) {
1212
+ return function shadowRootInsertAdjacentHTML(where, domString) {
1213
+ const temporaryContainer = document.createElement("div");
1214
+ temporaryContainer.innerHTML = domString;
1215
+ const elements = Array.from(temporaryContainer.childNodes);
1216
+ const shadow = app.container;
1217
+ switch (where) {
1218
+ case "beforebegin":
1219
+ for (const item of elements) {
1220
+ shadow.host.parentNode?.insertBefore(item, shadow.host);
1221
+ }
1222
+ break;
1223
+ case "afterbegin":
1224
+ for (const item of elements.reverse()) {
1225
+ shadow.insertBefore(item, shadow.firstChild);
1226
+ }
1227
+ break;
1228
+ case "beforeend":
1229
+ for (const item of elements) {
1230
+ shadow.appendChild(item);
1231
+ }
1232
+ break;
1233
+ case "afterend":
1234
+ for (const item of elements) {
1235
+ shadow.host.parentNode?.insertBefore(item, shadow.host.nextSibling);
1236
+ }
1237
+ break;
1317
1238
  }
1318
- if (!this.linkedBaseStyle(styleElement, app)) {
1319
- this.commonScoped(styleElement, app);
1239
+ };
1240
+ }
1241
+ function createProxyBody(rawDocument, app) {
1242
+ return new Proxy(
1243
+ {},
1244
+ {
1245
+ get(_, key) {
1246
+ if (app.container instanceof ShadowRoot) {
1247
+ if (key === "insertAdjacentHTML") {
1248
+ return createShadowRootInsertAdjacentHTML(app);
1249
+ }
1250
+ const value2 = Reflect.get(app.container, key);
1251
+ if (typeof value2 === "function") {
1252
+ return value2.bind(app.container);
1253
+ }
1254
+ if (value2 !== void 0) {
1255
+ return value2;
1256
+ }
1257
+ }
1258
+ const value = Reflect.get(rawDocument.body, key);
1259
+ return typeof value === "function" ? value.bind(rawDocument.body) : value;
1260
+ },
1261
+ set(_, key, value) {
1262
+ if (app.container instanceof ShadowRoot) {
1263
+ Reflect.set(app.container, key, value);
1264
+ return true;
1265
+ }
1266
+ Reflect.set(rawDocument.body, key, value);
1267
+ return true;
1268
+ }
1269
+ }
1270
+ );
1271
+ }
1272
+ function createElementWithAppKey(rawDocument, app) {
1273
+ return function createElement(tagName, options) {
1274
+ const element = rawDocument.createElement(tagName, options);
1275
+ element[APP_KEY_PROPERTY] = app.appCacheKey;
1276
+ if (element instanceof HTMLImageElement) {
1277
+ Object.defineProperty(element, "src", {
1278
+ get() {
1279
+ return element.getAttribute("src");
1280
+ },
1281
+ set(value) {
1282
+ element.setAttribute("src", fillUpPath(value, app.url));
1283
+ }
1284
+ });
1320
1285
  }
1286
+ return element;
1287
+ };
1288
+ }
1289
+ function isSpecialElementTag(selector) {
1290
+ return SPECIAL_ELEMENT_TAGS.includes(selector);
1291
+ }
1292
+ function safeQuerySelector(container, selector) {
1293
+ try {
1294
+ return container?.querySelector(selector) ?? null;
1295
+ } catch {
1296
+ return null;
1321
1297
  }
1322
- observeContentChanges(styleElement, app) {
1323
- const observer = new MutationObserver(() => {
1324
- if (!(styleElement.textContent || styleElement.sheet?.cssRules?.length)) return;
1325
- observer.disconnect();
1326
- if (!this.linkedBaseStyle(styleElement, app)) {
1327
- this.commonScoped(styleElement, app);
1298
+ }
1299
+ function safeQuerySelectorAll(container, selector) {
1300
+ try {
1301
+ return container?.querySelectorAll(selector) ?? [];
1302
+ } catch {
1303
+ return [];
1304
+ }
1305
+ }
1306
+ function createProxyQuerySelector(rawDocument, app, proxyBody) {
1307
+ return function querySelectorNew(selectors) {
1308
+ if (selectors === proxyBody) {
1309
+ return app.container instanceof ShadowRoot ? app.container : rawDocument.body;
1310
+ }
1311
+ if (isSpecialElementTag(selectors)) {
1312
+ if (app?.container instanceof ShadowRoot) {
1313
+ return app?.container;
1314
+ }
1315
+ return rawDocument.querySelector.call(this, selectors);
1316
+ }
1317
+ return safeQuerySelector(app?.container, selectors);
1318
+ };
1319
+ }
1320
+ function createProxyQuerySelectorAll(rawDocument, app) {
1321
+ return function querySelectorAllNew(selectors) {
1322
+ if (isSpecialElementTag(selectors)) {
1323
+ if (app?.container instanceof ShadowRoot) {
1324
+ return [app?.container];
1325
+ }
1326
+ const result = rawDocument.querySelector(selectors);
1327
+ return result ? [result] : [];
1328
+ }
1329
+ return safeQuerySelectorAll(app?.container, selectors);
1330
+ };
1331
+ }
1332
+ function createProxyGetElementById(rawDocument, querySelector) {
1333
+ return function getElementByIdNew(id) {
1334
+ return querySelector.call(rawDocument, `#${id}`);
1335
+ };
1336
+ }
1337
+ function createProxyGetElementsByClassName(querySelectorAll) {
1338
+ return function getElementsByClassName(className) {
1339
+ return querySelectorAll(`.${className}`);
1340
+ };
1341
+ }
1342
+ function createProxyGetElementsByTagName(rawDocument, app, querySelectorAll) {
1343
+ return function getElementsByTagName(tagName) {
1344
+ if (isSpecialElementTag(tagName) || !app?.showSourceCode && tagName.toLowerCase() === "script") {
1345
+ return rawDocument.getElementsByTagName(tagName);
1346
+ }
1347
+ return querySelectorAll(tagName);
1348
+ };
1349
+ }
1350
+ function createProxyGetElementsByName(querySelectorAll) {
1351
+ return function getElementsByNameNew(name) {
1352
+ return querySelectorAll(`[name="${name}"]`);
1353
+ };
1354
+ }
1355
+ function createProxyMethodMap(rawDocument, app, proxyBody) {
1356
+ const createElement = createElementWithAppKey(rawDocument, app);
1357
+ const querySelector = createProxyQuerySelector(rawDocument, app, proxyBody);
1358
+ const querySelectorAll = createProxyQuerySelectorAll(rawDocument, app);
1359
+ const getElementById = createProxyGetElementById(rawDocument, querySelector);
1360
+ const getElementsByClassName = createProxyGetElementsByClassName(querySelectorAll);
1361
+ const getElementsByTagName = createProxyGetElementsByTagName(rawDocument, app, querySelectorAll);
1362
+ const getElementsByName = createProxyGetElementsByName(querySelectorAll);
1363
+ return {
1364
+ createElement: createElement.bind(rawDocument),
1365
+ querySelector: querySelector.bind(rawDocument),
1366
+ querySelectorAll: querySelectorAll.bind(rawDocument),
1367
+ getElementById: getElementById.bind(rawDocument),
1368
+ getElementsByClassName: getElementsByClassName.bind(rawDocument),
1369
+ getElementsByTagName: getElementsByTagName.bind(rawDocument),
1370
+ getElementsByName: getElementsByName.bind(rawDocument)
1371
+ };
1372
+ }
1373
+ var createProxyDocument = (rawDocument, app) => {
1374
+ const fakeDocument = {};
1375
+ const proxyBody = createProxyBody(rawDocument, app);
1376
+ const methodMap = createProxyMethodMap(rawDocument, app, proxyBody);
1377
+ return new Proxy(fakeDocument, {
1378
+ get(_, key) {
1379
+ if (key === "body") {
1380
+ return proxyBody;
1328
1381
  }
1329
- });
1330
- const observerConfig = {
1331
- attributes: false,
1332
- characterData: true,
1333
- childList: true,
1334
- subtree: true
1335
- };
1336
- observer.observe(styleElement, observerConfig);
1337
- }
1338
- };
1339
- async function executeAppStyles(app, container) {
1340
- const styleList = Array.from(app.source?.styles?.values() || []);
1341
- const promiseList = [];
1342
- for (const style of styleList) {
1343
- promiseList.push(style.executeCode(app));
1344
- }
1345
- await Promise.all(promiseList).then((styleElementList) => {
1346
- const parentElement = container || app.container;
1347
- if (app.keepAlive && !(parentElement instanceof ShadowRoot)) {
1348
- document.head.append(...styleElementList);
1349
- } else {
1350
- parentElement?.append(...styleElementList);
1382
+ if (typeof key === "string" && key in methodMap) {
1383
+ return methodMap[key];
1384
+ }
1385
+ const result = Reflect.get(rawDocument, key);
1386
+ return typeof result === "function" ? result.bind(rawDocument) : result;
1351
1387
  }
1352
1388
  });
1353
- }
1389
+ };
1354
1390
 
1355
- // src/mode/instance.ts
1356
- var DEFAULT_RANDOM_LENGTH = 5;
1357
- var WRAPPER_SUFFIX = "-wrapper";
1358
- var MicroInstanceModel = class {
1359
- state = AppState.UNSET;
1360
- appCacheKey;
1361
- container;
1362
- data;
1363
- fetchSource;
1364
- initSource;
1365
- isPreLoad = false;
1366
- keepAlive;
1367
- name;
1368
- sandBox;
1369
- scopeCss = true;
1370
- scopeJs = false;
1371
- showSourceCode = true;
1372
- source;
1373
- url;
1374
- constructor(props) {
1375
- this.name = props.id !== props.url ? props.id : random(DEFAULT_RANDOM_LENGTH);
1376
- this.appCacheKey = props.id || this.name;
1377
- this.url = props.url;
1378
- this.container = props.container ?? void 0;
1379
- this.scopeJs = props.scopeJs ?? true;
1380
- this.showSourceCode = props.showSourceCode ?? true;
1381
- this.scopeCss = props.scopeCss ?? true;
1382
- this.keepAlive = props.keepAlive ?? false;
1383
- this.data = props.data ?? {};
1384
- this.initSource = props.initSource ?? [];
1385
- this.fetchSource = props.fetchSource;
1386
- this.initializeSandBox();
1387
- }
1388
- /** 激活微模块 */
1389
- activated(container, callback) {
1390
- this.isPreLoad = false;
1391
- this.state = AppState.ACTIVATED;
1392
- if (this.container && container) {
1393
- this.setContainerAttribute(container);
1394
- this.transferNodes(container);
1395
- this.container = container;
1396
- this.sandBox?.activated();
1397
- const scriptInfo = this.getScriptInfo();
1398
- callback?.(this, scriptInfo?.exportInstance);
1399
- }
1400
- }
1401
- /** 停用微模块 */
1402
- deactivated() {
1403
- this.state = AppState.DEACTIVATED;
1404
- this.sandBox?.deactivated();
1405
- }
1406
- /** 挂载微模块 */
1407
- mount(container, callback) {
1408
- this.isPreLoad = false;
1409
- this.container = container ?? this.container;
1410
- this.state = AppState.MOUNTING;
1411
- this.setContainerAttribute(this.container);
1412
- this.setupContainer();
1413
- this.executeStyles();
1414
- this.sandBox?.activated();
1415
- execAppScripts(this).finally(() => {
1416
- this.state = AppState.MOUNTED;
1417
- this.renderInstance();
1418
- const scriptInfo = this.getScriptInfo();
1419
- callback?.(this, scriptInfo?.exportInstance);
1420
- });
1421
- }
1422
- /** 错误处理 */
1423
- onError() {
1424
- this.state = AppState.ERROR;
1425
- }
1426
- /** 挂载处理 */
1427
- onMount() {
1428
- if (this.isPreLoad) return;
1429
- this.state = AppState.LOADED;
1430
- this.mount();
1431
- }
1432
- /** 注册运行中的应用 */
1433
- registerRunningApp() {
1434
- setCurrentRunningApp(this);
1435
- Promise.resolve().then(() => setCurrentRunningApp(null));
1436
- }
1437
- /** 启动微模块 */
1438
- async start() {
1439
- if (!this.source || this.needsReload()) {
1440
- this.source = new EntrySource(this.url);
1441
- await this.source.importEntry(this);
1391
+ // src/context/event.ts
1392
+ function rewriteDocumentAndBodyEvent() {
1393
+ const { addEventListener, removeEventListener } = window.document;
1394
+ const { addEventListener: bodyAddEventListener, removeEventListener: bodyRemoveEventListener } = window.document.body;
1395
+ const documentListenerMap = /* @__PURE__ */ new Map();
1396
+ document.addEventListener = function(type, listener, options) {
1397
+ const app = getCurrentRunningApp();
1398
+ if (app?.keepAlive) {
1399
+ const listeners = documentListenerMap.get(type) || [];
1400
+ documentListenerMap.set(type, [...listeners, listener]);
1442
1401
  }
1443
- }
1444
- /** 卸载微模块 */
1445
- unmount(needDestroy) {
1446
- this.state = AppState.UNMOUNT;
1447
- this.sandBox?.deactivated();
1448
- if (needDestroy) {
1449
- appCache.deleteApp(this.url);
1402
+ addEventListener.call(app?.container instanceof ShadowRoot ? app.container : this, type, listener, options);
1403
+ };
1404
+ document.body.addEventListener = document.addEventListener;
1405
+ document.removeEventListener = function(type, listener, options) {
1406
+ const app = getCurrentRunningApp();
1407
+ if (app?.keepAlive) {
1408
+ const listeners = documentListenerMap.get(type) || [];
1409
+ if (listeners.length && listeners.some((l) => l === listener)) {
1410
+ listeners.splice(listeners.indexOf(listener), 1);
1411
+ }
1450
1412
  }
1451
- if (this.container) {
1452
- this.container.innerHTML = "";
1453
- this.container = void 0;
1413
+ removeEventListener.call(app?.container instanceof ShadowRoot ? app.container : this, type, listener, options);
1414
+ };
1415
+ document.body.removeEventListener = document.removeEventListener;
1416
+ function resetDocumentAndBodyEvent() {
1417
+ const app = getCurrentRunningApp();
1418
+ if (app?.keepAlive && documentListenerMap.values()) {
1419
+ for (const [type, listeners] of documentListenerMap.entries()) {
1420
+ for (const listener of listeners || []) {
1421
+ document.removeEventListener.call(document, type, listener);
1422
+ }
1423
+ }
1454
1424
  }
1425
+ document.addEventListener = addEventListener;
1426
+ document.body.addEventListener = bodyAddEventListener;
1427
+ document.removeEventListener = removeEventListener;
1428
+ document.body.removeEventListener = bodyRemoveEventListener;
1429
+ documentListenerMap.clear();
1455
1430
  }
1456
- set status(value) {
1457
- this.state = value;
1431
+ return {
1432
+ resetDocumentAndBodyEvent
1433
+ };
1434
+ }
1435
+
1436
+ // src/context/function.ts
1437
+ var CLASS_REGEX = /^class\b/;
1438
+ var COMMON_CONSTRUCT_FU_REGEX = /^function\b\s[A-Z].*/;
1439
+ var ConstructFunCacheMap = /* @__PURE__ */ new WeakMap();
1440
+ function isConstructFun(fn) {
1441
+ if (fn.prototype?.constructor === fn && Object.getOwnPropertyNames(fn.prototype).length > 1) {
1442
+ return true;
1458
1443
  }
1459
- get status() {
1460
- return this.state;
1444
+ if (ConstructFunCacheMap.has(fn)) {
1445
+ return ConstructFunCacheMap.get(fn);
1461
1446
  }
1462
- /** 初始化沙盒 */
1463
- initializeSandBox() {
1464
- if (this.scopeJs) {
1465
- this.sandBox = new SandBox(this);
1466
- }
1447
+ const constructable = COMMON_CONSTRUCT_FU_REGEX.test(fn.toString()) || CLASS_REGEX.test(fn.toString());
1448
+ ConstructFunCacheMap.set(fn, constructable);
1449
+ return constructable;
1450
+ }
1451
+ var functionBoundedValueMap = /* @__PURE__ */ new WeakMap();
1452
+ function bindFunctionToRawWindow(rawWindow, value) {
1453
+ if (functionBoundedValueMap.has(value)) {
1454
+ return functionBoundedValueMap.get(value);
1467
1455
  }
1468
- /** 设置容器属性 */
1469
- setContainerAttribute(container) {
1470
- if (container instanceof HTMLElement) {
1471
- container.setAttribute(CSS_ATTRIBUTE_KEY, this.name);
1456
+ if (typeof value === "function" && !isConstructFun(value)) {
1457
+ const boundValue = Function.prototype.bind.call(value, rawWindow);
1458
+ for (const key in value) {
1459
+ boundValue[key] = value[key];
1472
1460
  }
1473
- }
1474
- /** 转移节点到新容器 */
1475
- transferNodes(container) {
1476
- if (!this.container) return;
1477
- const fragment = document.createDocumentFragment();
1478
- const nodeList = Array.from(this.container.childNodes);
1479
- for (const node of nodeList) {
1480
- fragment.appendChild(node);
1461
+ if (Object.hasOwn(value, "prototype") && !Object.hasOwn(boundValue, "prototype")) {
1462
+ Object.defineProperty(boundValue, "prototype", {
1463
+ enumerable: false,
1464
+ value: value.prototype,
1465
+ writable: true
1466
+ });
1481
1467
  }
1482
- container.appendChild(fragment);
1483
- }
1484
- /** 设置容器 */
1485
- setupContainer() {
1486
- if (this.container) {
1487
- this.container.innerHTML = "";
1488
- const instanceWrapper = this.createInstanceWrapper();
1489
- this.container.appendChild(instanceWrapper);
1468
+ if (typeof value.toString === "function") {
1469
+ const valueHasInstanceToString = Object.hasOwn(value, "toString") && !Object.hasOwn(boundValue, "toString");
1470
+ const boundValueHasPrototypeToString = boundValue.toString === Function.prototype.toString;
1471
+ if (valueHasInstanceToString || boundValueHasPrototypeToString) {
1472
+ const originToStringDescriptor = Object.getOwnPropertyDescriptor(
1473
+ valueHasInstanceToString ? value : Function.prototype,
1474
+ "toString"
1475
+ );
1476
+ Object.defineProperty(boundValue, "toString", {
1477
+ ...originToStringDescriptor,
1478
+ ...originToStringDescriptor?.get ? null : { value: () => value.toString() }
1479
+ });
1480
+ }
1490
1481
  }
1482
+ functionBoundedValueMap.set(value, boundValue);
1483
+ return boundValue;
1491
1484
  }
1492
- /** 执行样式 */
1493
- executeStyles() {
1494
- if (this.source?.styles.size && this.container) {
1495
- executeAppStyles(this, this.container);
1485
+ return value;
1486
+ }
1487
+
1488
+ // src/context/window.ts
1489
+ function rewriteWindowFunction(fakeWindow) {
1490
+ const windowEventListenerMap = /* @__PURE__ */ new Map();
1491
+ const intervalTimerList = [];
1492
+ const rawWindow = window;
1493
+ const { addEventListener, clearInterval: clearInterval2, removeEventListener, setInterval: setInterval2 } = window;
1494
+ fakeWindow.addEventListener = (type, listener, options) => {
1495
+ windowEventListenerMap.set(type, [...windowEventListenerMap.get(type) || [], listener]);
1496
+ addEventListener.call(rawWindow, type, listener, options);
1497
+ };
1498
+ fakeWindow.removeEventListener = (type, listener, options) => {
1499
+ const listenerList = windowEventListenerMap.get(type);
1500
+ if (listenerList?.length) {
1501
+ const index = listenerList.indexOf(listener);
1502
+ index > -1 && listenerList.splice(index, 1);
1496
1503
  }
1497
- }
1498
- /** 创建实例包装器 */
1499
- createInstanceWrapper() {
1500
- const wrapper = document.createElement("div");
1501
- wrapper.id = `${this.name}${WRAPPER_SUFFIX}`;
1502
- return wrapper;
1503
- }
1504
- /** 渲染实例 */
1505
- renderInstance() {
1506
- const scriptInfo = this.getScriptInfo();
1507
- if (scriptInfo?.exportInstance?.render && this.container) {
1508
- const targetContainer = this.container.querySelector(`#${this.name}${WRAPPER_SUFFIX}`);
1509
- if (targetContainer) {
1510
- scriptInfo.exportInstance.render(targetContainer, this.data);
1504
+ removeEventListener.call(rawWindow, type, listener, options);
1505
+ };
1506
+ fakeWindow.setInterval = (handler, timeout, ...args) => {
1507
+ const timer = setInterval2(handler, timeout, ...args);
1508
+ intervalTimerList.push(timer);
1509
+ return timer;
1510
+ };
1511
+ fakeWindow.clearInterval = (timer) => {
1512
+ const index = intervalTimerList.indexOf(timer);
1513
+ index > -1 && intervalTimerList.splice(index, 1);
1514
+ clearInterval2.call(rawWindow, timer);
1515
+ };
1516
+ function resetWindowFunction() {
1517
+ if (windowEventListenerMap.size) {
1518
+ windowEventListenerMap.forEach((listenerList, type) => {
1519
+ for (const listener of listenerList) {
1520
+ removeEventListener.call(rawWindow, type, listener);
1521
+ }
1522
+ });
1523
+ windowEventListenerMap.clear();
1524
+ }
1525
+ if (intervalTimerList.length) {
1526
+ for (const timer of intervalTimerList) {
1527
+ clearInterval2.call(rawWindow, timer);
1511
1528
  }
1512
1529
  }
1513
1530
  }
1514
- /** 获取脚本信息 */
1515
- getScriptInfo() {
1516
- const script = this.source?.getScript(this.url);
1517
- return script ? { exportInstance: script.exportInstance } : void 0;
1518
- }
1519
- /** 检查是否需要重新加载 */
1520
- needsReload() {
1521
- return this.status === AppState.ERROR || this.status === AppState.UNSET;
1522
- }
1523
- };
1531
+ return {
1532
+ resetWindowFunction
1533
+ };
1534
+ }
1524
1535
 
1525
- // src/entry/script.ts
1526
- var firstGlobalProp;
1527
- var secondGlobalProp;
1528
- var lastGlobalProp;
1529
- var STRICT_MODE_REGEX = /^"use\sstrict";$/gim;
1530
- var Script = class {
1531
- async = false;
1532
- code = "";
1533
- defer = false;
1534
- exportInstance;
1535
- fromHtml;
1536
- initial;
1537
- isModule = false;
1538
- scoped;
1539
- url;
1540
- constructor({ async, code, defer, fromHtml, initial, isModule, url }) {
1541
- this.code = code?.replace(STRICT_MODE_REGEX, "");
1542
- this.async = async;
1543
- this.defer = defer;
1544
- this.isModule = isModule;
1545
- this.url = url;
1546
- this.scoped = false;
1547
- this.fromHtml = fromHtml ?? false;
1548
- this.initial = initial ?? false;
1549
- }
1550
- /** 执行脚本代码 */
1551
- async executeCode(app, needReplaceScriptElement = false) {
1552
- try {
1553
- if (!this.code) await this.getCode(app);
1554
- if (app instanceof MicroInstanceModel) {
1555
- const globalWindow = app.scopeJs ? app.sandBox?.proxyWindow || window : window;
1556
- noteGlobalProps(globalWindow);
1557
- }
1558
- const scopedCode = this.transformCode(app);
1559
- if (app.showSourceCode || this.isModule) {
1560
- const scriptElement = document.createElement("script");
1561
- if (scriptElement.__BK_WEWEB_APP_KEY__) {
1562
- scriptElement.__BK_WEWEB_APP_KEY__ = void 0;
1536
+ // src/context/sandbox.ts
1537
+ var SandBox = class {
1538
+ /** 初始化沙箱环境 */
1539
+ constructor(app) {
1540
+ this.app = app;
1541
+ const windowDescriptorSet = /* @__PURE__ */ new Set();
1542
+ const rawWindow = window;
1543
+ this.rawWindow = rawWindow;
1544
+ this.rawDocument = createProxyDocument(document, app);
1545
+ const fakeWindow = /* @__PURE__ */ Object.create({});
1546
+ fakeWindow.__BK_WEWEB_APP_KEY__ = app.appCacheKey;
1547
+ fakeWindow.__POWERED_BY_BK_WEWEB__ = true;
1548
+ fakeWindow.rawDocument = document;
1549
+ fakeWindow.rawWindow = rawWindow;
1550
+ fakeWindow.__proto__ = Window;
1551
+ this.fakeWindow = fakeWindow;
1552
+ const { resetWindowFunction } = rewriteWindowFunction(this.fakeWindow);
1553
+ this.resetWindowFunction = resetWindowFunction;
1554
+ this.windowSymbolKey = `${app.name || app.appCacheKey}`;
1555
+ this.proxyWindow = new Proxy(this.fakeWindow, {
1556
+ defineProperty: (target, key, value) => {
1557
+ if (windowDescriptorSet.has(key)) {
1558
+ return Reflect.defineProperty(rawWindow, key, value);
1563
1559
  }
1564
- app.registerRunningApp();
1565
- this.executeSourceScript(scriptElement, scopedCode);
1566
- if (needReplaceScriptElement) return scriptElement;
1567
- const needKeepAlive = !!app.keepAlive && !(app.container instanceof ShadowRoot);
1568
- const container = needKeepAlive ? document.head : app.container;
1569
- setMarkElement(scriptElement, app, needKeepAlive);
1570
- container.appendChild(scriptElement);
1571
- } else {
1572
- this.executeMemoryScript(app, scopedCode);
1573
- if (needReplaceScriptElement) return document.createComment("\u3010bk-weweb\u3011dynamic script");
1574
- }
1575
- if (app instanceof MicroInstanceModel) {
1576
- const globalWindow = app.scopeJs ? app.sandBox?.proxyWindow || window : window;
1577
- const isIifeVar = !!this.code.replace(/\/\*[\s\S]*?\*\//g, "").match(/^\s*var\s/);
1578
- const exportProp = getGlobalProp(globalWindow, isIifeVar);
1579
- if (exportProp) {
1580
- this.exportInstance = globalWindow[exportProp];
1581
- if (!app.scopeJs) {
1582
- const descriptor = Object.getOwnPropertyDescriptor(globalWindow, exportProp);
1583
- if (descriptor?.configurable) {
1584
- delete globalWindow[exportProp];
1585
- }
1560
+ return Reflect.defineProperty(target, key, value);
1561
+ },
1562
+ deleteProperty: (target, key) => {
1563
+ if (Object.hasOwn(target, key)) {
1564
+ if (this.sameRawWindowKeySet.has(key)) {
1565
+ this.sameRawWindowKeySet.delete(key);
1566
+ }
1567
+ if (this.inRawWindowKeySet.has(key)) {
1568
+ Reflect.deleteProperty(rawWindow, key);
1569
+ }
1570
+ return Reflect.deleteProperty(target, key);
1571
+ }
1572
+ return true;
1573
+ },
1574
+ get: (target, key) => {
1575
+ return this.handleProxyGet(target, key, rawWindow);
1576
+ },
1577
+ getOwnPropertyDescriptor: (target, key) => {
1578
+ if (Object.hasOwn(target, key)) {
1579
+ return Object.getOwnPropertyDescriptor(target, key);
1580
+ }
1581
+ if (Object.hasOwn(rawWindow, key)) {
1582
+ windowDescriptorSet.add(key);
1583
+ const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
1584
+ if (descriptor && !descriptor.configurable) {
1585
+ descriptor.configurable = true;
1586
1586
  }
1587
+ return descriptor;
1587
1588
  }
1589
+ return void 0;
1590
+ },
1591
+ has: (target, key) => windowNativeFuncMap.has(key) || key in target || key in rawWindow,
1592
+ ownKeys: (target) => Array.from(new Set(Reflect.ownKeys(rawWindow).concat(Reflect.ownKeys(target)))),
1593
+ set: (target, key, value) => {
1594
+ return this.handleProxySet(target, key, value, rawWindow);
1588
1595
  }
1589
- } catch (error) {
1590
- console.error("execute script code error", error);
1591
- }
1592
- return;
1596
+ });
1597
+ rawWindow[this.windowSymbolKey] = this.proxyWindow;
1593
1598
  }
1594
- /** 内存脚本执行 */
1595
- executeMemoryScript(app, scopedCode) {
1596
- try {
1597
- const isScopedLocation = app instanceof MicroAppModel && app.scopeLocation;
1598
- app.registerRunningApp();
1599
- new Function("window", "location", "history", scopedCode)(
1600
- app.sandBox.proxyWindow || window,
1601
- isScopedLocation ? app.iframe.contentWindow.location : window.location,
1602
- isScopedLocation ? app.iframe.contentWindow.history : window.history
1603
- );
1604
- } catch (error) {
1605
- console.error(error);
1599
+ /** 沙箱激活状态标识 */
1600
+ active = false;
1601
+ /** 记录在原始 window 上新增的属性键集合 */
1602
+ inRawWindowKeySet = /* @__PURE__ */ new Set();
1603
+ /** 重置文档和 body 事件的函数 */
1604
+ resetDocumentAndBodyEvent;
1605
+ /** 重置 window 函数的方法 */
1606
+ resetWindowFunction;
1607
+ /** 记录与原始 window 相同的属性键集合 */
1608
+ sameRawWindowKeySet = /* @__PURE__ */ new Set();
1609
+ /** 伪造的 window 对象 */
1610
+ fakeWindow;
1611
+ /** 代理的 document 对象 */
1612
+ proxyDocument;
1613
+ /** 代理的 window 对象 */
1614
+ proxyWindow;
1615
+ /** 原始 document 对象 */
1616
+ rawDocument;
1617
+ /** 原始 window 对象 */
1618
+ rawWindow;
1619
+ /** 在 window 上的唯一标识键 */
1620
+ windowSymbolKey;
1621
+ /** 处理代理对象的 get 操作 */
1622
+ handleProxyGet(target, key, rawWindow) {
1623
+ if (key === Symbol.unscopables || windowNativeFuncMap.has(key)) {
1624
+ return rawWindow[key];
1625
+ }
1626
+ if (DEV_MICRO_APP_WINDOW_KEY_MAP[key]) {
1627
+ return this.fakeWindow[key];
1628
+ }
1629
+ if (WINDOW_ALIAS_LIST.includes(key)) {
1630
+ return this.proxyWindow;
1631
+ }
1632
+ if (key === "document") {
1633
+ this.app.registerRunningApp();
1634
+ return this.rawDocument;
1635
+ }
1636
+ if (key === "eval") {
1637
+ this.app.registerRunningApp();
1638
+ return eval;
1639
+ }
1640
+ if (this.shouldUseIframeLocation(key)) {
1641
+ if (this.app instanceof MicroAppModel && this.app.iframe?.contentWindow) {
1642
+ return this.app.iframe.contentWindow[key];
1643
+ }
1644
+ return void 0;
1606
1645
  }
1607
- }
1608
- /** 脚本标签执行 */
1609
- executeSourceScript(scriptElement, scopedCode) {
1610
- if (this.isModule) {
1611
- scriptElement.src = `${this.url}?key=${Date.now()}`;
1612
- scriptElement.setAttribute("type", "module");
1613
- } else {
1614
- scriptElement.textContent = scopedCode;
1646
+ if (key === "hasOwnProperty") {
1647
+ return (checkKey) => Object.hasOwn(this.fakeWindow, checkKey) || Object.hasOwn(rawWindow, checkKey);
1615
1648
  }
1616
- this.url && scriptElement.setAttribute("origin-src", this.url);
1617
- }
1618
- /** 获取脚本内容 */
1619
- async getCode(app) {
1620
- if (this.code.length || !this.url) {
1621
- return this.code;
1649
+ if (key === "top" || key === "parent") {
1650
+ if (rawWindow === rawWindow.parent) {
1651
+ return this.proxyWindow;
1652
+ }
1653
+ return Reflect.get(rawWindow, key);
1622
1654
  }
1623
- let code = "";
1624
- if (app?.source?.getScript(this.url)) {
1625
- code = app.source.getScript(this.url)?.code || "";
1655
+ if (key === "getComputedStyle") {
1656
+ return this.createGetComputedStyleProxy(rawWindow);
1626
1657
  }
1627
- if (!code && appCache.getCacheScript(this.url)) {
1628
- code = appCache.getCacheScript(this.url)?.code || "";
1658
+ if (Reflect.has(target, key) || BK_WEWEB_INJECT_KEY_LIST.includes(key)) {
1659
+ return Reflect.get(target, key);
1629
1660
  }
1630
- if (!code) {
1631
- code = await fetchSource(this.url, {}, app).catch((error) => {
1632
- console.error(`fetch script ${this.url} error`, error);
1633
- return "";
1634
- });
1661
+ const rawValue = Reflect.get(rawWindow, key);
1662
+ return bindFunctionToRawWindow(rawWindow, rawValue);
1663
+ }
1664
+ /**
1665
+ * 处理代理对象的 set 操作
1666
+ * @description 统一处理代理对象属性设置的复杂逻辑
1667
+ * @param target - 目标对象
1668
+ * @param key - 属性键
1669
+ * @param value - 属性值
1670
+ * @param rawWindow - 原始 window 对象
1671
+ * @returns boolean - 设置是否成功
1672
+ * @private
1673
+ */
1674
+ handleProxySet(target, key, value, rawWindow) {
1675
+ if (!this.active) {
1676
+ return true;
1635
1677
  }
1636
- code = code.replace(STRICT_MODE_REGEX, "");
1637
- this.code = code;
1638
- return code;
1678
+ if (this.shouldUseIframeLocation(key)) {
1679
+ const iframe = this.app instanceof MicroAppModel ? this.app.iframe : null;
1680
+ return iframe?.contentWindow ? Reflect.set(iframe.contentWindow, key, value) : true;
1681
+ }
1682
+ if (key === "location") {
1683
+ Reflect.set(rawWindow, key, value);
1684
+ } else if (this.shouldSetOnTarget(target, key, rawWindow)) {
1685
+ this.setPropertyOnTarget(target, key, value, rawWindow);
1686
+ } else {
1687
+ Reflect.set(target, key, value);
1688
+ this.sameRawWindowKeySet.add(key);
1689
+ }
1690
+ this.handleWhiteListProperty(key, value, rawWindow);
1691
+ return true;
1639
1692
  }
1640
- setCode(code) {
1641
- this.code = code;
1693
+ /**
1694
+ * 判断是否应该使用 iframe 的 location
1695
+ * @description 检查是否在 iframe 模式下访问 location 相关属性
1696
+ * @param key - 属性键
1697
+ * @returns boolean - 是否使用 iframe location
1698
+ * @private
1699
+ */
1700
+ shouldUseIframeLocation(key) {
1701
+ return !!(BK_WEWEB_LOCATION_KEY_LIST.includes(key) && this.app instanceof MicroAppModel && this.app.iframe && this.app.scopeLocation);
1642
1702
  }
1643
- /** 转换脚本内容 */
1644
- transformCode(app) {
1645
- const sourceMapUrl = "";
1646
- if (app.sandBox) {
1647
- if (this.isModule) {
1648
- return ` with(window.${app.sandBox.windowSymbolKey}){
1649
- ;${this.code}
1650
-
1651
- ${sourceMapUrl}
1652
- }`;
1653
- }
1654
- if (app.showSourceCode) {
1655
- return `;(function(window, self, globalThis){
1656
- with(window){
1657
- ${getGlobalContextCode()}
1658
-
1659
- ${this.code}
1660
-
1661
- ${sourceMapUrl}
1662
- }
1663
- }).call(window.${app.sandBox.windowSymbolKey},
1664
- window.${app.sandBox.windowSymbolKey}, window.${app.sandBox.windowSymbolKey}, window.${app.sandBox.windowSymbolKey});`;
1703
+ /**
1704
+ * 创建 getComputedStyle 方法的代理
1705
+ * @description getComputedStyle 方法创建安全的代理实现
1706
+ * @param rawWindow - 原始 window 对象
1707
+ * @returns Function - 代理后的 getComputedStyle 方法
1708
+ * @private
1709
+ */
1710
+ createGetComputedStyleProxy(rawWindow) {
1711
+ return (element, pseudoElt) => {
1712
+ if (element instanceof Element) {
1713
+ return rawWindow.getComputedStyle(element, pseudoElt);
1665
1714
  }
1666
- return `
1667
- with(window) {
1668
- try {
1669
- ${getGlobalContextCode()}
1670
-
1671
- ${this.code}
1672
-
1673
- ${sourceMapUrl}
1674
- }
1675
- catch(e) {
1676
- console.error(e)
1677
- }
1678
- }
1679
- `;
1680
- }
1681
- return this.code;
1715
+ return rawWindow.getComputedStyle(document.body, pseudoElt);
1716
+ };
1682
1717
  }
1683
- };
1684
- function shouldSkipProperty(global, property) {
1685
- const globalWindow = global;
1686
- return (
1687
- // biome-ignore lint/suspicious/noPrototypeBuiltins: <explanation>
1688
- !global.hasOwnProperty(property) || !Number.isNaN(property) && property < global.length || typeof navigator !== "undefined" && navigator.userAgent.indexOf("Trident") !== -1 && Boolean(globalWindow[property]) && typeof window !== "undefined" && globalWindow[property]?.parent === window
1689
- );
1690
- }
1691
- function getGlobalProp(global, useFirstGlobalProp) {
1692
- let cnt = 0;
1693
- let foundLastProp;
1694
- let result;
1695
- for (const property in global) {
1696
- if (shouldSkipProperty(global, property)) continue;
1697
- if (cnt === 0 && property !== firstGlobalProp || cnt === 1 && property !== secondGlobalProp) {
1698
- return property;
1718
+ /**
1719
+ * 判断是否应该在目标对象上设置属性
1720
+ * @description 检查属性设置的逻辑条件
1721
+ * @param target - 目标对象
1722
+ * @param key - 属性键
1723
+ * @param rawWindow - 原始 window 对象
1724
+ * @returns boolean - 是否在目标对象上设置
1725
+ * @private
1726
+ */
1727
+ shouldSetOnTarget(target, key, rawWindow) {
1728
+ return !Object.hasOwn(target, key) && Object.hasOwn(rawWindow, key) && !BK_WEWEB_INJECT_KEY_LIST.includes(key);
1729
+ }
1730
+ /**
1731
+ * 在目标对象上设置属性
1732
+ * @description 安全地在目标对象上设置属性,保持描述符特性
1733
+ * @param target - 目标对象
1734
+ * @param key - 属性键
1735
+ * @param value - 属性值
1736
+ * @param rawWindow - 原始 window 对象
1737
+ * @private
1738
+ */
1739
+ setPropertyOnTarget(target, key, value, rawWindow) {
1740
+ const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
1741
+ if (!descriptor) {
1742
+ return;
1699
1743
  }
1700
- if (foundLastProp) {
1701
- lastGlobalProp = property;
1702
- result = useFirstGlobalProp && result || property;
1703
- } else {
1704
- foundLastProp = property === lastGlobalProp;
1744
+ const { configurable, enumerable, writable } = descriptor;
1745
+ if (writable) {
1746
+ Object.defineProperty(target, key, {
1747
+ configurable,
1748
+ enumerable,
1749
+ value,
1750
+ writable
1751
+ });
1752
+ this.sameRawWindowKeySet.add(key);
1705
1753
  }
1706
- cnt += 1;
1707
1754
  }
1708
- return result;
1709
- }
1710
- function noteGlobalProps(global) {
1711
- secondGlobalProp = void 0;
1712
- firstGlobalProp = secondGlobalProp;
1713
- for (const property in global) {
1714
- if (shouldSkipProperty(global, property)) continue;
1715
- if (!firstGlobalProp) {
1716
- firstGlobalProp = property;
1717
- } else if (!secondGlobalProp) {
1718
- secondGlobalProp = property;
1755
+ /**
1756
+ * 处理白名单属性
1757
+ * @description 处理需要在原始 window 上设置的白名单属性
1758
+ * @param key - 属性键
1759
+ * @param value - 属性值
1760
+ * @param rawWindow - 原始 window 对象
1761
+ * @private
1762
+ */
1763
+ handleWhiteListProperty(key, value, rawWindow) {
1764
+ if (WINDOW_WHITE_LIST.includes(key) && !Reflect.has(rawWindow, key) && !BK_WEWEB_INJECT_KEY_LIST.includes(key)) {
1765
+ Reflect.set(rawWindow, key, value);
1766
+ this.inRawWindowKeySet.add(key);
1719
1767
  }
1720
- lastGlobalProp = property;
1721
1768
  }
1722
- return lastGlobalProp;
1723
- }
1724
- async function execAppScripts(app) {
1725
- const appScriptList = Array.from(app.source.scripts.values()).filter((script) => script.fromHtml || script.initial);
1726
- const commonList = appScriptList.filter((script) => !script.async && !script.defer || script.isModule);
1727
- await Promise.all(commonList.map((script) => script.getCode(app)));
1728
- await Promise.all(commonList.map((script) => script.executeCode(app)));
1729
- const deferScriptList = [];
1730
- const asyncScriptList = [];
1731
- for (const script of appScriptList) {
1732
- if (script.defer || script.async) {
1733
- if (!script.code && script.defer) {
1734
- deferScriptList.push(script.executeCode(app));
1735
- } else {
1736
- asyncScriptList.push(script.executeCode(app));
1737
- }
1769
+ /**
1770
+ * 激活沙箱
1771
+ * @description 启动沙箱环境,初始化代理对象和事件处理
1772
+ * @param data - 传递给沙箱的数据(可选)
1773
+ */
1774
+ activated(data) {
1775
+ if (!this.active) {
1776
+ this.active = true;
1777
+ this.rawDocument = createProxyDocument(document, this.app);
1778
+ this.fakeWindow.__BK_WEWEB_DATA__ = data ?? {};
1779
+ const { resetDocumentAndBodyEvent } = rewriteDocumentAndBodyEvent();
1780
+ this.resetDocumentAndBodyEvent = resetDocumentAndBodyEvent;
1738
1781
  }
1739
1782
  }
1740
- await Promise.all([...asyncScriptList, ...deferScriptList]).catch((error) => {
1741
- console.error(error);
1742
- });
1743
- }
1783
+ /**
1784
+ * 停用沙箱
1785
+ * @description 关闭沙箱环境,清理所有副作用和修改
1786
+ */
1787
+ deactivated() {
1788
+ if (!this.active) return;
1789
+ this.active = false;
1790
+ this.resetWindowFunction();
1791
+ for (const key of this.inRawWindowKeySet) {
1792
+ Reflect.deleteProperty(window, key);
1793
+ }
1794
+ this.inRawWindowKeySet.clear();
1795
+ this.resetDocumentAndBodyEvent?.();
1796
+ }
1797
+ };
1744
1798
 
1745
1799
  // src/mode/app.ts
1746
1800
  var IFRAME_CONSTANTS = {
@@ -2683,7 +2737,11 @@ function createOverriddenAppendChild() {
2683
2737
  if (keepAliveResult) {
2684
2738
  return keepAliveResult;
2685
2739
  }
2686
- return elementAppendHandler(this, newChild, bodyAppendChild2);
2740
+ const node = elementAppendHandler(this, newChild, bodyAppendChild2);
2741
+ if (node?.tagName === "STYLE") {
2742
+ node.insertAdjacentElement = createOverriddenInsertAdjacentElement();
2743
+ }
2744
+ return node;
2687
2745
  };
2688
2746
  }
2689
2747
  function createOverriddenAppend() {
@@ -2714,6 +2772,16 @@ function createOverriddenRemoveChild() {
2714
2772
  return oldChild;
2715
2773
  };
2716
2774
  }
2775
+ var insertAdjacentElement = createOverriddenInsertAdjacentElement();
2776
+ function createOverriddenInsertAdjacentElement() {
2777
+ return function(where, element) {
2778
+ const node = elementAppendHandler(this, element, headAppendChild);
2779
+ if (node?.tagName === "STYLE") {
2780
+ node.insertAdjacentElement = insertAdjacentElement;
2781
+ }
2782
+ return node;
2783
+ };
2784
+ }
2717
2785
  function rewriteBodyAndHeaderMethods() {
2718
2786
  if (hasRewrite) {
2719
2787
  return;
@@ -2862,7 +2930,7 @@ var BkWewebElement = class extends HTMLElement {
2862
2930
  */
2863
2931
  async handleAttributeChanged() {
2864
2932
  if (!this.appKey) return;
2865
- if (this.getBooleanAttr("setShodowDom" /* setShodowDom */)) {
2933
+ if (this.setShadowDomAttr) {
2866
2934
  this.attachShadow({ mode: "open" });
2867
2935
  }
2868
2936
  const app = appCache.getApp(this.appKey);
@@ -2887,13 +2955,21 @@ var BkWewebElement = class extends HTMLElement {
2887
2955
  this.handleAttributeChanged();
2888
2956
  }
2889
2957
  }
2958
+ /**
2959
+ * 获取是否启用 Shadow DOM 属性
2960
+ * @description 获取是否启用 Shadow DOM 属性 兼容旧版使用
2961
+ * @returns boolean | undefined - 是否启用 Shadow DOM 属性
2962
+ */
2963
+ get setShadowDomAttr() {
2964
+ return this.getBooleanAttr("setShadowDom" /* setShadowDom */) ?? this.getBooleanAttr("setShodowDom" /* setShodowDom */);
2965
+ }
2890
2966
  /**
2891
2967
  * 组件连接到 DOM 时的回调
2892
2968
  * @description 当自定义元素被插入到 DOM 时触发
2893
2969
  * @returns Promise<void>
2894
2970
  */
2895
2971
  async connectedCallback() {
2896
- if (this.getBooleanAttr("setShodowDom" /* setShodowDom */) && !this.shadowRoot) {
2972
+ if (this.setShadowDomAttr && !this.shadowRoot) {
2897
2973
  this.attachShadow({ delegatesFocus: false, mode: "open" });
2898
2974
  }
2899
2975
  await load(this.appProps);
@@ -2954,14 +3030,14 @@ var BkWewebElement = class extends HTMLElement {
2954
3030
  return {
2955
3031
  ...commonProps,
2956
3032
  mode: "js" /* INSTANCE */,
2957
- scopeCss: this.getBooleanAttr("scopeCss" /* scopeCss */) && !this.getBooleanAttr("setShodowDom" /* setShodowDom */),
3033
+ scopeCss: this.getBooleanAttr("scopeCss" /* scopeCss */) && !this.setShadowDomAttr,
2958
3034
  scopeJs: this.getBooleanAttr("scopeJs" /* scopeJs */)
2959
3035
  };
2960
3036
  }
2961
3037
  return {
2962
3038
  ...commonProps,
2963
3039
  mode: "app" /* APP */,
2964
- scopeCss: !this.getBooleanAttr("setShodowDom" /* setShodowDom */),
3040
+ scopeCss: !this.setShadowDomAttr,
2965
3041
  scopeJs: !this.getBooleanAttr("scopeJs" /* scopeJs */),
2966
3042
  scopeLocation: this.getBooleanAttr("scopeLocation" /* scopeLocation */)
2967
3043
  };