@blueking/bk-weweb 0.0.35 → 0.0.36-beta.17
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.d.mts +652 -107
- package/dist/index.esm.js +2249 -1450
- package/dist/index.esm.js.map +1 -1
- package/dist/vite/helper.d.mts +11 -0
- package/dist/vite/helper.esm.js +50 -0
- package/dist/vite/helper.esm.js.map +1 -0
- package/package.json +3 -2
- package/readme.md +13 -13
package/dist/index.esm.js
CHANGED
|
@@ -1,13 +1,33 @@
|
|
|
1
|
+
// src/common/state.ts
|
|
2
|
+
var AppState = {
|
|
3
|
+
/** 未设置状态 */
|
|
4
|
+
UNSET: "UNSET",
|
|
5
|
+
/** 加载中 */
|
|
6
|
+
LOADING: "LOADING",
|
|
7
|
+
/** 加载完成 */
|
|
8
|
+
LOADED: "LOADED",
|
|
9
|
+
/** 挂载中 */
|
|
10
|
+
MOUNTING: "MOUNTING",
|
|
11
|
+
/** 挂载完成 */
|
|
12
|
+
MOUNTED: "MOUNTED",
|
|
13
|
+
/** 激活状态 */
|
|
14
|
+
ACTIVATED: "ACTIVATED",
|
|
15
|
+
/** 未激活状态 */
|
|
16
|
+
DEACTIVATED: "DEACTIVATED",
|
|
17
|
+
/** 卸载状态 */
|
|
18
|
+
UNMOUNT: "UNMOUNT",
|
|
19
|
+
/** 错误状态 */
|
|
20
|
+
ERROR: "ERROR"
|
|
21
|
+
};
|
|
22
|
+
|
|
1
23
|
// src/context/cache.ts
|
|
2
24
|
var currentRunningApp = null;
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
}
|
|
6
|
-
function setCurrentRunningApp(appInstance) {
|
|
25
|
+
var getCurrentRunningApp = () => currentRunningApp;
|
|
26
|
+
var setCurrentRunningApp = (appInstance) => {
|
|
7
27
|
currentRunningApp = appInstance;
|
|
8
|
-
}
|
|
28
|
+
};
|
|
9
29
|
var windowNativeFuncMap = /* @__PURE__ */ new Map();
|
|
10
|
-
var
|
|
30
|
+
var GLOBAL_CONTEXT_VARIABLES = [
|
|
11
31
|
"Array",
|
|
12
32
|
"ArrayBuffer",
|
|
13
33
|
"Boolean",
|
|
@@ -64,15 +84,25 @@ var globalContextCode = `const { ${[
|
|
|
64
84
|
"URIError",
|
|
65
85
|
"valueOf",
|
|
66
86
|
"WeakMap",
|
|
67
|
-
"WeakSet"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
87
|
+
"WeakSet",
|
|
88
|
+
"requestAnimationFrame",
|
|
89
|
+
"cancelAnimationFrame",
|
|
90
|
+
"requestIdleCallback",
|
|
91
|
+
"cancelIdleCallback"
|
|
92
|
+
];
|
|
93
|
+
var getGlobalContextCode = () => `const { ${GLOBAL_CONTEXT_VARIABLES.join(",")} } = this;`;
|
|
94
|
+
var isNativeFunction = (func) => {
|
|
95
|
+
if (typeof func !== "function") return false;
|
|
96
|
+
try {
|
|
97
|
+
return Function.prototype.toString.call(func).includes("[native code]");
|
|
98
|
+
} catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
71
101
|
};
|
|
72
102
|
var collectNativeWindowFunc = () => {
|
|
73
|
-
const
|
|
74
|
-
for (const key of
|
|
75
|
-
if (!windowNativeFuncMap.has(key) &&
|
|
103
|
+
const windowKeys = Object.getOwnPropertyNames(window);
|
|
104
|
+
for (const key of windowKeys) {
|
|
105
|
+
if (!windowNativeFuncMap.has(key) && /^[A-Z]/.test(key) && isNativeFunction(window[key])) {
|
|
76
106
|
windowNativeFuncMap.set(key, true);
|
|
77
107
|
}
|
|
78
108
|
}
|
|
@@ -93,10 +123,11 @@ var WewebMode = /* @__PURE__ */ ((WewebMode2) => {
|
|
|
93
123
|
// src/typings/sandbox.ts
|
|
94
124
|
var WINDOW_WHITE_LIST = [
|
|
95
125
|
"System",
|
|
96
|
-
// SystemJS
|
|
126
|
+
// SystemJS 模块加载器
|
|
97
127
|
"__cjsWrapper",
|
|
98
|
-
// SystemJS CommonJS
|
|
99
|
-
|
|
128
|
+
// SystemJS CommonJS 包装器
|
|
129
|
+
// 开发环境下允许 React DevTools
|
|
130
|
+
...false ? ["__REACT_DEVTOOLS_GLOBAL_HOOK__"] : []
|
|
100
131
|
];
|
|
101
132
|
var BK_WEWEB_INJECT_KEY_LIST = [
|
|
102
133
|
"__POWERED_BY_BK_WEWEB__",
|
|
@@ -105,909 +136,474 @@ var BK_WEWEB_INJECT_KEY_LIST = [
|
|
|
105
136
|
];
|
|
106
137
|
var WINDOW_ALIAS_LIST = ["window", "self", "globalThis"];
|
|
107
138
|
var BK_WEWEB_LOCATION_KEY_LIST = ["location", "history"];
|
|
108
|
-
var
|
|
139
|
+
var COMMON_MICRO_APP_WINDOW_KEY_MAP = {
|
|
109
140
|
__bk_pop_manager: true,
|
|
110
141
|
__bk_zIndex_manager: true,
|
|
111
142
|
i18n: true
|
|
112
143
|
};
|
|
113
|
-
var
|
|
144
|
+
var DEV_MICRO_APP_WINDOW_KEY_MAP = false ? {
|
|
145
|
+
// 开发环境标识
|
|
114
146
|
__DEV__: true,
|
|
147
|
+
// Vue DevTools 相关
|
|
115
148
|
__VUE_DEVTOOLS_GLOBAL_HOOK__: true,
|
|
116
149
|
__VUE_DEVTOOLS_HOOK_REPLAY__: true,
|
|
117
150
|
__VUE_DEVTOOLS_PLUGINS__: true,
|
|
151
|
+
// Vue I18n 相关
|
|
118
152
|
__VUE_I18N_FULL_INSTALL__: true,
|
|
119
153
|
__VUE_I18N_LEGACY_API__: true,
|
|
154
|
+
// Vue 配置相关
|
|
120
155
|
__VUE_OPTIONS_API__: true,
|
|
156
|
+
// Core-js 相关
|
|
121
157
|
"__core-js_shared__": true,
|
|
158
|
+
// Webpack 相关
|
|
122
159
|
webpackChunkapm: true,
|
|
123
160
|
webpackChunkpc: true,
|
|
124
161
|
webpackChunktrace: true,
|
|
125
162
|
webpackJsonp: true,
|
|
126
|
-
|
|
127
|
-
|
|
163
|
+
// 包含通用配置
|
|
164
|
+
...COMMON_MICRO_APP_WINDOW_KEY_MAP
|
|
165
|
+
} : COMMON_MICRO_APP_WINDOW_KEY_MAP;
|
|
128
166
|
|
|
129
167
|
// src/utils/common.ts
|
|
168
|
+
var PROTOCOL_REGEX = /^((((ht|f)tps?)|file):)?\/\//;
|
|
169
|
+
var DATA_BLOB_REGEX = /^(data|blob):/;
|
|
170
|
+
var FILE_EXTENSION_REGEX = /\.(\w+)$/;
|
|
171
|
+
var JS_EXTENSION_REGEX = /\.js$/;
|
|
172
|
+
var RANDOM_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
130
173
|
var nextTask = (cb) => Promise.resolve().then(cb);
|
|
131
|
-
|
|
174
|
+
var addUrlProtocol = (url) => {
|
|
132
175
|
if (url.startsWith("//")) return `${location.protocol}${url}`;
|
|
133
176
|
if (!url.startsWith("http")) return `${location.protocol}//${url}`;
|
|
134
177
|
return url;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (!path ||
|
|
178
|
+
};
|
|
179
|
+
var fillUpPath = (path, baseURI) => {
|
|
180
|
+
if (!path || PROTOCOL_REGEX.test(path) || DATA_BLOB_REGEX.test(path)) {
|
|
181
|
+
return path;
|
|
182
|
+
}
|
|
138
183
|
const { origin, pathname } = new URL(addUrlProtocol(baseURI));
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
184
|
+
const basePath = `${origin}${pathname}`.replace(FILE_EXTENSION_REGEX, "/");
|
|
185
|
+
return new URL(path, basePath).toString();
|
|
186
|
+
};
|
|
187
|
+
var random = (length, chars = RANDOM_CHARS) => {
|
|
188
|
+
const charsLength = chars.length;
|
|
189
|
+
return Array.from({ length }, () => chars[Math.floor(Math.random() * charsLength)]).join("");
|
|
190
|
+
};
|
|
191
|
+
var randomUrl = () => `inline-${random(16)}`;
|
|
192
|
+
var requestIdleCallback = window.requestIdleCallback ?? ((cb) => {
|
|
145
193
|
const start = Date.now();
|
|
146
194
|
return setTimeout(() => {
|
|
147
195
|
cb({
|
|
148
196
|
didTimeout: false,
|
|
149
|
-
timeRemaining()
|
|
150
|
-
return Math.max(0, 50 - (Date.now() - start));
|
|
151
|
-
}
|
|
197
|
+
timeRemaining: () => Math.max(0, 50 - (Date.now() - start))
|
|
152
198
|
});
|
|
153
199
|
}, 1);
|
|
154
200
|
});
|
|
155
|
-
var cancelIdleCallback = window.cancelIdleCallback
|
|
156
|
-
clearTimeout(id);
|
|
157
|
-
});
|
|
158
|
-
var random = (n, str = "abcdefghijklmnopqrstuvwxyz0123456789") => {
|
|
159
|
-
let result = "";
|
|
160
|
-
for (let i = 0; i < n; i++) {
|
|
161
|
-
result += str[Number.parseInt((Math.random() * str.length).toString(), 10)];
|
|
162
|
-
}
|
|
163
|
-
return result;
|
|
164
|
-
};
|
|
201
|
+
var cancelIdleCallback = window.cancelIdleCallback ?? ((id) => clearTimeout(id));
|
|
165
202
|
var isJsonpUrl = (url) => {
|
|
166
203
|
if (!url) return false;
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
204
|
+
try {
|
|
205
|
+
const { pathname } = new URL(addUrlProtocol(url));
|
|
206
|
+
return !JS_EXTENSION_REGEX.test(pathname);
|
|
207
|
+
} catch {
|
|
208
|
+
return !JS_EXTENSION_REGEX.test(url);
|
|
209
|
+
}
|
|
170
210
|
};
|
|
171
211
|
|
|
172
|
-
// src/
|
|
173
|
-
var
|
|
174
|
-
var
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
break;
|
|
191
|
-
case "afterend":
|
|
192
|
-
elements.forEach((el) => shadow.host.parentNode?.insertBefore(el, shadow.host.nextSibling));
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
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);
|
|
224
|
+
};
|
|
225
|
+
var dispatchLinkOrScriptLoad = (element) => {
|
|
226
|
+
const loadEvent = defineEventSourceElement(element, "load");
|
|
227
|
+
if (typeof element.onload === "function") {
|
|
228
|
+
element.onload.call(element, loadEvent);
|
|
229
|
+
return;
|
|
195
230
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
const value2 = app.container[key];
|
|
205
|
-
if (typeof value2 === "function") {
|
|
206
|
-
return value2.bind(app.container);
|
|
207
|
-
}
|
|
208
|
-
if (value2 !== void 0) {
|
|
209
|
-
return value2;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
const value = Reflect.get(rawDocument.body, key);
|
|
213
|
-
return typeof value === "function" ? value.bind(rawDocument.body) : value;
|
|
214
|
-
},
|
|
215
|
-
set(_, key, value) {
|
|
216
|
-
if (app.container instanceof ShadowRoot) {
|
|
217
|
-
app.container[key] = value;
|
|
218
|
-
return true;
|
|
219
|
-
}
|
|
220
|
-
Reflect.set(rawDocument.body, key, value);
|
|
221
|
-
return true;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
);
|
|
225
|
-
function createElement(tagName, options) {
|
|
226
|
-
const element = rawDocument.createElement(tagName, options);
|
|
227
|
-
element.__BK_WEWEB_APP_KEY__ = app.appCacheKey;
|
|
228
|
-
return element;
|
|
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;
|
|
229
238
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
return app?.container;
|
|
237
|
-
}
|
|
238
|
-
return rawDocument.querySelector.call(this, selectors);
|
|
239
|
-
}
|
|
239
|
+
element.dispatchEvent(errorEvent);
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// src/utils/fetch.ts
|
|
243
|
+
var fetchSource = async (url, options = {}, app) => {
|
|
244
|
+
if (typeof app?.fetchSource === "function") {
|
|
240
245
|
try {
|
|
241
|
-
return app
|
|
246
|
+
return await app.fetchSource(url, options);
|
|
242
247
|
} catch {
|
|
243
|
-
return
|
|
248
|
+
return "";
|
|
244
249
|
}
|
|
245
250
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (app?.container instanceof ShadowRoot) {
|
|
249
|
-
return app?.container;
|
|
250
|
-
}
|
|
251
|
-
return rawDocument.querySelector(selectors);
|
|
252
|
-
}
|
|
253
|
-
return app?.container?.querySelectorAll(selectors) ?? [];
|
|
251
|
+
if (src_default.fetchSource) {
|
|
252
|
+
return src_default.fetchSource(url, options);
|
|
254
253
|
}
|
|
255
|
-
|
|
256
|
-
|
|
254
|
+
try {
|
|
255
|
+
const response = await window.fetch(url, options);
|
|
256
|
+
return await response.text();
|
|
257
|
+
} catch {
|
|
258
|
+
return "";
|
|
257
259
|
}
|
|
258
|
-
|
|
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;
|
|
260
300
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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);
|
|
264
311
|
}
|
|
265
|
-
|
|
312
|
+
this.scoped = true;
|
|
266
313
|
}
|
|
267
|
-
|
|
268
|
-
|
|
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;
|
|
321
|
+
}
|
|
322
|
+
return styleElement;
|
|
269
323
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
if (
|
|
282
|
-
|
|
283
|
-
}
|
|
284
|
-
if (key === "getElementsByClassName") {
|
|
285
|
-
return getElementsByClassName.bind(rawDocument);
|
|
286
|
-
}
|
|
287
|
-
if (key === "getElementsByTagName") {
|
|
288
|
-
return getElementsByTagName.bind(rawDocument);
|
|
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);
|
|
289
337
|
}
|
|
290
|
-
|
|
291
|
-
|
|
338
|
+
styleElement = this.scopedStyleCSS(app, styleElement);
|
|
339
|
+
this.scoped = true;
|
|
340
|
+
} catch (error) {
|
|
341
|
+
console.error("scoped style error", error);
|
|
342
|
+
}
|
|
343
|
+
return styleElement;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* 获取样式代码
|
|
347
|
+
*/
|
|
348
|
+
async getCode(app) {
|
|
349
|
+
if (this.code.length || !this.url) {
|
|
350
|
+
return this.code;
|
|
351
|
+
}
|
|
352
|
+
const code = this.getCodeFromAppSource(app) || this.getCodeFromCache() || await this.fetchCodeFromRemote(app);
|
|
353
|
+
this.code = code;
|
|
354
|
+
return code;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* 检查并链接基础应用样式
|
|
358
|
+
* @param styleElement 样式元素
|
|
359
|
+
* @param app 应用实例
|
|
360
|
+
* @returns 是否已链接基础样式
|
|
361
|
+
*/
|
|
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");
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
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;
|
|
292
385
|
}
|
|
293
|
-
if (
|
|
294
|
-
|
|
386
|
+
if (CSS_SELECTORS.RELATIVE_PATH.test(matchedUrl) && linkPath) {
|
|
387
|
+
baseURI = this.buildBaseURI(linkPath);
|
|
295
388
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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;
|
|
299
411
|
}
|
|
300
|
-
return result;
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
};
|
|
304
|
-
|
|
305
|
-
// src/context/event.ts
|
|
306
|
-
function rewriteDocumentAndBodyEvent() {
|
|
307
|
-
const { addEventListener, removeEventListener } = window.document;
|
|
308
|
-
const { addEventListener: bodyAddEventListener, removeEventListener: bodyRemoveEventListener } = window.document.body;
|
|
309
|
-
const documentListenerMap = /* @__PURE__ */ new Map();
|
|
310
|
-
document.addEventListener = function(type, listener, options) {
|
|
311
|
-
const app = getCurrentRunningApp();
|
|
312
|
-
if (app?.keepAlive) {
|
|
313
|
-
const listeners = documentListenerMap.get(type) || [];
|
|
314
|
-
documentListenerMap.set(type, [...listeners, listener]);
|
|
315
412
|
}
|
|
316
|
-
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
}
|
|
413
|
+
return result.replace(/^\s+/, "");
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* style rule 处理
|
|
417
|
+
*/
|
|
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);
|
|
326
422
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
document.body.removeEventListener = document.removeEventListener;
|
|
330
|
-
function resetDocumentAndBodyEvent() {
|
|
331
|
-
const app = getCurrentRunningApp();
|
|
332
|
-
if (app?.keepAlive && documentListenerMap.values()) {
|
|
333
|
-
Array.from(documentListenerMap.entries()).forEach(([type, listeners]) => {
|
|
334
|
-
listeners?.forEach((listener) => {
|
|
335
|
-
document.removeEventListener.call(document, type, listener);
|
|
336
|
-
});
|
|
337
|
-
});
|
|
423
|
+
if (selectorText === "*") {
|
|
424
|
+
return cssText.replace("*", `${prefix} *`);
|
|
338
425
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
+
);
|
|
344
435
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
const constructable = COMMON_CONTRUCT_FU_REGEX.test(fn.toString()) || CLASS_REGEX.test(fn.toString());
|
|
362
|
-
ConstructFunCacheMap.set(fn, constructable);
|
|
363
|
-
return constructable;
|
|
364
|
-
}
|
|
365
|
-
var functionBoundedValueMap = /* @__PURE__ */ new WeakMap();
|
|
366
|
-
function bindFunctionToRawWindow(rawWindow, value) {
|
|
367
|
-
if (functionBoundedValueMap.has(value)) {
|
|
368
|
-
return functionBoundedValueMap.get(value);
|
|
369
|
-
}
|
|
370
|
-
if (typeof value === "function" && !isConstructFun(value)) {
|
|
371
|
-
const boundValue = Function.prototype.bind.call(value, rawWindow);
|
|
372
|
-
for (const key in value) {
|
|
373
|
-
boundValue[key] = value[key];
|
|
374
|
-
}
|
|
375
|
-
if (value.hasOwnProperty("prototype") && !boundValue.hasOwnProperty("prototype")) {
|
|
376
|
-
Object.defineProperty(boundValue, "prototype", { enumerable: false, value: value.prototype, writable: true });
|
|
377
|
-
}
|
|
378
|
-
if (typeof value.toString === "function") {
|
|
379
|
-
const valueHasInstanceToString = value.hasOwnProperty("toString") && !boundValue.hasOwnProperty("toString");
|
|
380
|
-
const boundValueHasPrototypeToString = boundValue.toString === Function.prototype.toString;
|
|
381
|
-
if (valueHasInstanceToString || boundValueHasPrototypeToString) {
|
|
382
|
-
const originToStringDescriptor = Object.getOwnPropertyDescriptor(
|
|
383
|
-
valueHasInstanceToString ? value : Function.prototype,
|
|
384
|
-
"toString"
|
|
385
|
-
);
|
|
386
|
-
Object.defineProperty(boundValue, "toString", {
|
|
387
|
-
...originToStringDescriptor,
|
|
388
|
-
...originToStringDescriptor?.get ? null : { value: () => value.toString() }
|
|
389
|
-
});
|
|
436
|
+
/**
|
|
437
|
+
* link style 处理
|
|
438
|
+
*/
|
|
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);
|
|
390
452
|
}
|
|
453
|
+
} catch {
|
|
454
|
+
linkElement && dispatchLinkOrScriptError(linkElement);
|
|
391
455
|
}
|
|
392
|
-
|
|
393
|
-
return boundValue;
|
|
456
|
+
return styleElement;
|
|
394
457
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
windowEventLisenerMap.set(type, [...windowEventLisenerMap.get(type) || [], listener]);
|
|
406
|
-
addEventListener.call(rawWindow, type, listener, options);
|
|
407
|
-
};
|
|
408
|
-
fakeWindow.removeEventListener = (type, listener, options) => {
|
|
409
|
-
const listenerList = windowEventLisenerMap.get(type);
|
|
410
|
-
if (listenerList?.length) {
|
|
411
|
-
const index = listenerList.indexOf(listener);
|
|
412
|
-
index > -1 && listenerList.splice(index, 1);
|
|
413
|
-
}
|
|
414
|
-
removeEventListener.call(rawWindow, type, listener, options);
|
|
415
|
-
};
|
|
416
|
-
fakeWindow.setInterval = (handler, timeout, ...args) => {
|
|
417
|
-
const timer = setInterval2.call(rawWindow, handler, timeout, ...args);
|
|
418
|
-
intervalTimerList.push(timer);
|
|
419
|
-
return timer;
|
|
420
|
-
};
|
|
421
|
-
fakeWindow.clearInterval = (timer) => {
|
|
422
|
-
const index = intervalTimerList.indexOf(timer);
|
|
423
|
-
index > -1 && intervalTimerList.splice(index, 1);
|
|
424
|
-
clearInterval2.call(rawWindow, timer);
|
|
425
|
-
};
|
|
426
|
-
function resetWindowFunction() {
|
|
427
|
-
if (windowEventLisenerMap.size) {
|
|
428
|
-
windowEventLisenerMap.forEach((listenerList, type) => {
|
|
429
|
-
listenerList.forEach((listener) => removeEventListener.call(rawWindow, type, listener));
|
|
430
|
-
});
|
|
431
|
-
windowEventLisenerMap.clear();
|
|
458
|
+
/**
|
|
459
|
+
* 隔离 style
|
|
460
|
+
*/
|
|
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);
|
|
432
468
|
}
|
|
433
|
-
if (
|
|
434
|
-
|
|
435
|
-
clearInterval2.call(rawWindow, timer);
|
|
436
|
-
});
|
|
469
|
+
if (this.url) {
|
|
470
|
+
styleElement.setAttribute(STYLE_ATTRIBUTES.ORIGIN_SRC, this.url);
|
|
437
471
|
}
|
|
472
|
+
return styleElement;
|
|
438
473
|
}
|
|
439
|
-
return {
|
|
440
|
-
resetWindowFunction
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// src/context/sandbox.ts
|
|
445
|
-
var SandBox = class {
|
|
446
|
-
constructor(app) {
|
|
447
|
-
this.app = app;
|
|
448
|
-
const windowDescriptorSet = /* @__PURE__ */ new Set();
|
|
449
|
-
const rawWindow = window;
|
|
450
|
-
this.rawWindow = rawWindow;
|
|
451
|
-
this.rawDocument = createProxyDocument(document, app);
|
|
452
|
-
const fakeWindow = /* @__PURE__ */ Object.create({});
|
|
453
|
-
fakeWindow.__BK_WEWEB_APP_KEY__ = app.appCacheKey;
|
|
454
|
-
fakeWindow.__POWERED_BY_BK_WEWEB__ = true;
|
|
455
|
-
fakeWindow.rawDocument = document;
|
|
456
|
-
fakeWindow.rawWindow = rawWindow;
|
|
457
|
-
fakeWindow.__proto__ = Window;
|
|
458
|
-
this.fakeWindow = fakeWindow;
|
|
459
|
-
const { resetWindowFunction } = rewriteWindowFunction(this.fakeWindow);
|
|
460
|
-
this.resetWindowFunction = resetWindowFunction;
|
|
461
|
-
this.windowSymbolKey = `__${(app.name || app.appCacheKey).replace(/(-|,|:|~|'|")/gim, "_")}_${random(
|
|
462
|
-
10
|
|
463
|
-
)}__`;
|
|
464
|
-
this.proxyWindow = new Proxy(this.fakeWindow, {
|
|
465
|
-
// Object.defineProperty(window, key, Descriptor)
|
|
466
|
-
defineProperty: (target, key, value) => {
|
|
467
|
-
if (windowDescriptorSet.has(key)) {
|
|
468
|
-
return Reflect.defineProperty(rawWindow, key, value);
|
|
469
|
-
}
|
|
470
|
-
return Reflect.defineProperty(target, key, value);
|
|
471
|
-
},
|
|
472
|
-
deleteProperty: (target, key) => {
|
|
473
|
-
if (Object.hasOwn(target, key)) {
|
|
474
|
-
this.sameRawWindowKeySet.has(key) && this.sameRawWindowKeySet.delete(key);
|
|
475
|
-
this.inRawWindowKeySet.has(key) && Reflect.deleteProperty(rawWindow, key);
|
|
476
|
-
return Reflect.deleteProperty(target, key);
|
|
477
|
-
}
|
|
478
|
-
return true;
|
|
479
|
-
},
|
|
480
|
-
get: (target, key) => {
|
|
481
|
-
if (key === Symbol.unscopables || windowNativeFuncMap.has(key)) return rawWindow[key];
|
|
482
|
-
if (DEV_MICRO_APP_WINDOE_KEY_MAP[key]) return this.fakeWindow[key];
|
|
483
|
-
if (WINDOW_ALIAS_LIST.includes(key)) return this.proxyWindow;
|
|
484
|
-
if (key === "document") {
|
|
485
|
-
app.registerRunningApp();
|
|
486
|
-
return this.rawDocument;
|
|
487
|
-
}
|
|
488
|
-
if (key === "eval") {
|
|
489
|
-
app.registerRunningApp();
|
|
490
|
-
return eval;
|
|
491
|
-
}
|
|
492
|
-
if (BK_WEWEB_LOCATION_KEY_LIST.includes(key) && this.app instanceof MicroAppModel && this.app.iframe && this.app.scopeLocation) {
|
|
493
|
-
return this.app.iframe.contentWindow?.[key];
|
|
494
|
-
}
|
|
495
|
-
if (key === "hasOwnProperty")
|
|
496
|
-
return (key2) => Object.hasOwn(this.fakeWindow, key2) || Object.hasOwn(rawWindow, key2);
|
|
497
|
-
if (key === "top" || key === "parent") {
|
|
498
|
-
if (rawWindow === rawWindow.parent) {
|
|
499
|
-
return this.proxyWindow;
|
|
500
|
-
}
|
|
501
|
-
return Reflect.get(rawWindow, key);
|
|
502
|
-
}
|
|
503
|
-
if (key === "getComputedStyle") {
|
|
504
|
-
return (element, pseudoElt) => {
|
|
505
|
-
if (element instanceof Element) {
|
|
506
|
-
return rawWindow.getComputedStyle(element, pseudoElt);
|
|
507
|
-
}
|
|
508
|
-
return rawWindow.getComputedStyle(document.body, pseudoElt);
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
if (Reflect.has(target, key) || BK_WEWEB_INJECT_KEY_LIST.includes(key)) return Reflect.get(target, key);
|
|
512
|
-
const rawValue = Reflect.get(rawWindow, key);
|
|
513
|
-
return bindFunctionToRawWindow(rawWindow, rawValue);
|
|
514
|
-
},
|
|
515
|
-
getOwnPropertyDescriptor: (target, key) => {
|
|
516
|
-
if (Object.hasOwn(target, key)) {
|
|
517
|
-
return Object.getOwnPropertyDescriptor(target, key);
|
|
518
|
-
}
|
|
519
|
-
if (Object.hasOwn(rawWindow, key)) {
|
|
520
|
-
windowDescriptorSet.add(key);
|
|
521
|
-
const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
|
|
522
|
-
if (descriptor && !descriptor.configurable) {
|
|
523
|
-
descriptor.configurable = true;
|
|
524
|
-
}
|
|
525
|
-
return descriptor;
|
|
526
|
-
}
|
|
527
|
-
return void 0;
|
|
528
|
-
},
|
|
529
|
-
has: (target, key) => windowNativeFuncMap.has(key) || key in target || key in rawWindow,
|
|
530
|
-
// Object.getOwnPropertyNames(window)
|
|
531
|
-
ownKeys: (target) => Array.from(new Set(Reflect.ownKeys(rawWindow).concat(Reflect.ownKeys(target)))),
|
|
532
|
-
set: (target, key, value) => {
|
|
533
|
-
if (this.active) {
|
|
534
|
-
if (BK_WEWEB_LOCATION_KEY_LIST.includes(key) && this.app instanceof MicroAppModel && this.app.iframe && this.app.scopeLocation) {
|
|
535
|
-
return Reflect.set(this.app.iframe.contentWindow, key, value);
|
|
536
|
-
}
|
|
537
|
-
if (key === "location") {
|
|
538
|
-
Reflect.set(rawWindow, key, value);
|
|
539
|
-
} else if (!Object.hasOwn(target, key) && Object.hasOwn(rawWindow, key) && !BK_WEWEB_INJECT_KEY_LIST.includes(key)) {
|
|
540
|
-
const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key);
|
|
541
|
-
const { configurable, enumerable, writable } = descriptor;
|
|
542
|
-
if (writable) {
|
|
543
|
-
Object.defineProperty(target, key, {
|
|
544
|
-
configurable,
|
|
545
|
-
enumerable,
|
|
546
|
-
value,
|
|
547
|
-
writable
|
|
548
|
-
});
|
|
549
|
-
this.sameRawWindowKeySet.add(key);
|
|
550
|
-
}
|
|
551
|
-
} else {
|
|
552
|
-
Reflect.set(target, key, value);
|
|
553
|
-
this.sameRawWindowKeySet.add(key);
|
|
554
|
-
}
|
|
555
|
-
if (WINDOW_WHITE_LIST.includes(key) && !Reflect.has(rawWindow, key) && !BK_WEWEB_INJECT_KEY_LIST.includes(key)) {
|
|
556
|
-
Reflect.set(rawWindow, key, value);
|
|
557
|
-
this.inRawWindowKeySet.add(key);
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
return true;
|
|
561
|
-
}
|
|
562
|
-
});
|
|
563
|
-
rawWindow[this.windowSymbolKey] = this.proxyWindow;
|
|
564
|
-
}
|
|
565
|
-
active = false;
|
|
566
|
-
inRawWindowKeySet = /* @__PURE__ */ new Set();
|
|
567
|
-
resetDocumentAndBodyEvent;
|
|
568
|
-
resetWindowFunction;
|
|
569
|
-
sameRawWindowKeySet = /* @__PURE__ */ new Set();
|
|
570
|
-
fakeWindow;
|
|
571
|
-
proxyDocument;
|
|
572
|
-
proxyWindow;
|
|
573
|
-
rawDocument;
|
|
574
|
-
rawWindow;
|
|
575
|
-
windowSymbolKey;
|
|
576
474
|
/**
|
|
577
|
-
*
|
|
578
|
-
* @param data data for sandbox
|
|
579
|
-
* @description active hook for sandbox
|
|
475
|
+
* 应用隔离 style
|
|
580
476
|
*/
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
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);
|
|
491
|
+
}
|
|
492
|
+
styleElement.textContent = cssText.replace(CSS_SELECTORS.ROOT_HOST_PATTERN, ":host");
|
|
589
493
|
}
|
|
590
494
|
/**
|
|
591
|
-
*
|
|
592
|
-
* @description decativated hook for sandbox
|
|
495
|
+
* 处理ShadowRoot中的字体
|
|
593
496
|
*/
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
}
|
|
601
|
-
this.inRawWindowKeySet.clear();
|
|
602
|
-
this.resetDocumentAndBodyEvent?.();
|
|
603
|
-
}
|
|
604
|
-
};
|
|
605
|
-
|
|
606
|
-
// src/utils/custom.ts
|
|
607
|
-
var ELEMENT_TARGET_NAME = ["currentTarget", "srcElement", "target"];
|
|
608
|
-
function defineEventSourceElement(element, eventName = "custom") {
|
|
609
|
-
return Object.defineProperties(
|
|
610
|
-
new CustomEvent(eventName),
|
|
611
|
-
ELEMENT_TARGET_NAME.reduce((props, name) => {
|
|
612
|
-
props[name] = {
|
|
613
|
-
get() {
|
|
614
|
-
return element;
|
|
615
|
-
}
|
|
616
|
-
};
|
|
617
|
-
return props;
|
|
618
|
-
}, {})
|
|
619
|
-
);
|
|
620
|
-
}
|
|
621
|
-
function dispatchLinkOrScriptLoad(element) {
|
|
622
|
-
const event = defineEventSourceElement(element, "load");
|
|
623
|
-
if (typeof element.onload === "function") {
|
|
624
|
-
element.onload(event);
|
|
625
|
-
return;
|
|
626
|
-
}
|
|
627
|
-
element.dispatchEvent(event);
|
|
628
|
-
}
|
|
629
|
-
function dispatchLinkOrScriptError(element) {
|
|
630
|
-
const event = defineEventSourceElement(element, "error");
|
|
631
|
-
if (typeof element.onerror === "function") {
|
|
632
|
-
element.onerror(event);
|
|
633
|
-
return;
|
|
634
|
-
}
|
|
635
|
-
element.dispatchEvent(event);
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
// src/utils/element.ts
|
|
639
|
-
var { appendChild: bodyAppendChild } = HTMLBodyElement.prototype;
|
|
640
|
-
function resetNewElement(parent, child, app) {
|
|
641
|
-
if (child instanceof HTMLStyleElement) {
|
|
642
|
-
if (child.hasAttribute("exclude")) {
|
|
643
|
-
return document.createComment("\u3010bk-weweb\u3011style with exclude attribute is ignored");
|
|
644
|
-
}
|
|
645
|
-
if (child.textContent) {
|
|
646
|
-
if (!(app.container instanceof ShadowRoot) && appCache.getBaseAppStyle(child.textContent)) {
|
|
647
|
-
return document.createComment("\u3010bk-weweb\u3011style is effective in base app");
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
if (!child.hasAttribute("ignore")) {
|
|
651
|
-
const styleInstance = new Style({
|
|
652
|
-
code: child.textContent || "",
|
|
653
|
-
fromHtml: false,
|
|
654
|
-
url: ""
|
|
655
|
-
});
|
|
656
|
-
app.source?.setStyle(randomUrl(), styleInstance);
|
|
657
|
-
styleInstance.scopedStyleCSS(app, child);
|
|
658
|
-
}
|
|
659
|
-
return child;
|
|
660
|
-
}
|
|
661
|
-
if (child instanceof HTMLLinkElement) {
|
|
662
|
-
const result = app.source?.collectLink(child, parent, true);
|
|
663
|
-
if (!result) return child;
|
|
664
|
-
if (result.style) {
|
|
665
|
-
result.style.scopedLinkCSS(app, child);
|
|
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
|
+
`;
|
|
666
503
|
}
|
|
667
|
-
|
|
668
|
-
|
|
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);
|
|
669
511
|
}
|
|
670
|
-
return child;
|
|
671
512
|
}
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
return child;
|
|
676
|
-
}
|
|
677
|
-
if (replaceInfo.script) {
|
|
678
|
-
replaceInfo.script.executeCode(app);
|
|
679
|
-
}
|
|
680
|
-
if (replaceInfo.replace !== child) {
|
|
681
|
-
return replaceInfo.replace;
|
|
682
|
-
}
|
|
683
|
-
if (app.scopeJs && !child.getAttribute("src") && !child.textContent) {
|
|
684
|
-
const observer = new MutationObserver(() => {
|
|
685
|
-
if (child.getAttribute("src")) {
|
|
686
|
-
observer.disconnect();
|
|
687
|
-
const scriptInfo = app.source.collectScript(child, parent, true);
|
|
688
|
-
if (scriptInfo?.replace) {
|
|
689
|
-
bodyAppendChild.call(app.container, scriptInfo.replace);
|
|
690
|
-
}
|
|
691
|
-
if (isJsonpUrl(child.getAttribute("src"))) {
|
|
692
|
-
app.container?.append(child);
|
|
693
|
-
return;
|
|
694
|
-
}
|
|
695
|
-
if (scriptInfo?.script) {
|
|
696
|
-
scriptInfo.script.executeCode(app);
|
|
697
|
-
}
|
|
698
|
-
child.remove();
|
|
699
|
-
} else if (child.textContent) {
|
|
700
|
-
observer.disconnect();
|
|
701
|
-
const scriptInstance = new Script({
|
|
702
|
-
async: false,
|
|
703
|
-
code: child.textContent,
|
|
704
|
-
defer: child.type === "module",
|
|
705
|
-
fromHtml: false,
|
|
706
|
-
isModule: child.type === "module"
|
|
707
|
-
});
|
|
708
|
-
app.source.scripts.set(randomUrl(), scriptInstance);
|
|
709
|
-
try {
|
|
710
|
-
scriptInstance.executeCode(app);
|
|
711
|
-
} catch (e) {
|
|
712
|
-
console.error(e);
|
|
713
|
-
} finally {
|
|
714
|
-
!scriptInstance.isModule && dispatchLinkOrScriptLoad(child);
|
|
715
|
-
child.remove();
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
});
|
|
719
|
-
observer.observe(child, { attributeFilter: ["src"], childList: true, subtree: false });
|
|
720
|
-
return document.createComment("\u3010bk-weweb\u3011dynamic script or module");
|
|
513
|
+
getCodeFromAppSource(app) {
|
|
514
|
+
if (this.url && app?.source?.styles?.has(this.url)) {
|
|
515
|
+
return app.source.styles.get(this.url)?.code || "";
|
|
721
516
|
}
|
|
722
|
-
return
|
|
517
|
+
return "";
|
|
723
518
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
}
|
|
729
|
-
function elementAppendHandler(parent, newChild, rawMethod) {
|
|
730
|
-
if (newChild.__BK_WEWEB_APP_KEY__) {
|
|
731
|
-
const app = appCache.getApp(newChild.__BK_WEWEB_APP_KEY__);
|
|
732
|
-
if (app?.container) {
|
|
733
|
-
const targetChild = resetNewElement(parent, newChild, app);
|
|
734
|
-
const needKeepAlive = isSpecialElement(newChild) && !!app.keepAlive && !(app.container instanceof ShadowRoot);
|
|
735
|
-
const container = needKeepAlive ? document.head : app?.container;
|
|
736
|
-
setMarkElement(targetChild, app, needKeepAlive);
|
|
737
|
-
return rawMethod.call(container, targetChild);
|
|
519
|
+
getCodeFromCache() {
|
|
520
|
+
if (this.url && appCache.getCacheStyle(this.url)) {
|
|
521
|
+
const style = appCache.getCacheStyle(this.url);
|
|
522
|
+
return style?.code || "";
|
|
738
523
|
}
|
|
524
|
+
return "";
|
|
739
525
|
}
|
|
740
|
-
|
|
741
|
-
}
|
|
742
|
-
function elementInsertHandler(parent, newChild, passiveChild, rawMethod) {
|
|
743
|
-
if (newChild.__BK_WEWEB_APP_KEY__) {
|
|
744
|
-
const app = appCache.getApp(newChild.__BK_WEWEB_APP_KEY__);
|
|
745
|
-
if (app?.container) {
|
|
746
|
-
const needKeepAlive = isSpecialElement(newChild) && app.keepAlive && !(app.container instanceof ShadowRoot);
|
|
747
|
-
const container = needKeepAlive ? document.head : app?.container;
|
|
748
|
-
const targetChild = resetNewElement(parent, newChild, app);
|
|
749
|
-
if (needKeepAlive) {
|
|
750
|
-
setMarkElement(targetChild, app, needKeepAlive);
|
|
751
|
-
}
|
|
752
|
-
if (passiveChild && !container.contains(passiveChild)) {
|
|
753
|
-
return bodyAppendChild.call(container, targetChild);
|
|
754
|
-
}
|
|
755
|
-
return rawMethod.call(container, targetChild, passiveChild);
|
|
756
|
-
}
|
|
526
|
+
async fetchCodeFromRemote(app) {
|
|
527
|
+
return await fetchSource(this.url, {}, app).catch(() => "");
|
|
757
528
|
}
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
if (keepAlive && app) {
|
|
762
|
-
element.__KEEP_ALIVE__ = app.appCacheKey;
|
|
763
|
-
element.setAttribute("data-from", app.name);
|
|
764
|
-
element.setAttribute("data-keep-alive", "true");
|
|
529
|
+
clearStyleElement(styleElement) {
|
|
530
|
+
styleElement.textContent = "";
|
|
531
|
+
styleElement.innerHTML = "";
|
|
765
532
|
}
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
// src/utils/fetch.ts
|
|
771
|
-
function fetchSource(url, options = {}, app) {
|
|
772
|
-
if (typeof app?.fetchSource === "function") {
|
|
773
|
-
return app.fetchSource(url, options).catch(() => "");
|
|
533
|
+
buildBaseURI(linkPath) {
|
|
534
|
+
const pathArr = linkPath.split("/");
|
|
535
|
+
pathArr.pop();
|
|
536
|
+
return addUrlProtocol(`${pathArr.join("/")}/`);
|
|
774
537
|
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
// src/entry/style.ts
|
|
782
|
-
var Style = class {
|
|
783
|
-
code = "";
|
|
784
|
-
fromHtml;
|
|
785
|
-
initial;
|
|
786
|
-
prefetch = false;
|
|
787
|
-
preload = false;
|
|
788
|
-
scoped;
|
|
789
|
-
scopedCode = "";
|
|
790
|
-
url;
|
|
791
|
-
constructor({ code, fromHtml, initial, prefetch, preload, url }) {
|
|
792
|
-
this.scoped = false;
|
|
793
|
-
this.code = code;
|
|
794
|
-
this.prefetch = prefetch ?? false;
|
|
795
|
-
this.preload = preload ?? false;
|
|
796
|
-
this.url = url;
|
|
797
|
-
this.fromHtml = fromHtml;
|
|
798
|
-
this.initial = initial ?? false;
|
|
538
|
+
handleExistingCode(styleElement, app, container, linkElement) {
|
|
539
|
+
this.commonScoped(styleElement, app);
|
|
540
|
+
container?.prepend(styleElement);
|
|
541
|
+
linkElement && dispatchLinkOrScriptLoad(linkElement);
|
|
799
542
|
}
|
|
800
543
|
/**
|
|
801
|
-
*
|
|
802
|
-
* @param app 应用实例
|
|
544
|
+
* 处理href属性
|
|
803
545
|
*/
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
this.scopedCode = cssText;
|
|
814
|
-
} else {
|
|
815
|
-
const cssText = this.resetUrlHost(styleElement.textContent || this.code || "", app.url, this.url);
|
|
816
|
-
if (cssText && app.container instanceof ShadowRoot) {
|
|
817
|
-
let fontContent = "";
|
|
818
|
-
for (const fontFace of cssText.match(/@font-face\s*\{[^}]+\}/g) || []) {
|
|
819
|
-
fontContent += `${fontFace}
|
|
820
|
-
`;
|
|
821
|
-
}
|
|
822
|
-
const rawDocument = app.sandBox?.rawDocument;
|
|
823
|
-
if (rawDocument && fontContent) {
|
|
824
|
-
const fontStyle = rawDocument.createElement("style");
|
|
825
|
-
fontStyle.setAttribute("type", "text/css");
|
|
826
|
-
fontStyle.setAttribute("powered-by", "bk-weweb");
|
|
827
|
-
fontStyle.textContent = fontContent;
|
|
828
|
-
rawDocument?.head?.append(fontStyle);
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
styleElement.textContent = cssText.replace(/(:?:root|html)/gm, ":host");
|
|
832
|
-
}
|
|
833
|
-
this.scoped = true;
|
|
834
|
-
}
|
|
835
|
-
createStyleElement() {
|
|
836
|
-
const styleElement = document.createElement("style");
|
|
837
|
-
if (styleElement.__BK_WEWEB_APP_KEY__) {
|
|
838
|
-
styleElement.__BK_WEWEB_APP_KEY__ = void 0;
|
|
839
|
-
}
|
|
840
|
-
return styleElement;
|
|
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
|
+
});
|
|
841
555
|
}
|
|
842
556
|
/**
|
|
843
|
-
*
|
|
844
|
-
* @returns 返回执行后的style标签
|
|
557
|
+
* 处理缺失的href
|
|
845
558
|
*/
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
}
|
|
860
|
-
async getCode(app) {
|
|
861
|
-
if (this.code.length || !this.url) {
|
|
862
|
-
return this.code;
|
|
863
|
-
}
|
|
864
|
-
let code = "";
|
|
865
|
-
if (app?.source?.styles?.has(this.url)) {
|
|
866
|
-
code = app.source.styles.get(this.url)?.code || "";
|
|
867
|
-
}
|
|
868
|
-
if (!code && appCache.getCacheStyle(this.url)) {
|
|
869
|
-
const style = appCache.getCacheStyle(this.url);
|
|
870
|
-
code = style?.code || "";
|
|
871
|
-
}
|
|
872
|
-
if (!code) {
|
|
873
|
-
code = await fetchSource(this.url, {}, app).catch(() => "");
|
|
874
|
-
}
|
|
875
|
-
this.code = code;
|
|
876
|
-
return code;
|
|
877
|
-
}
|
|
878
|
-
// 主应用已生效的样式 不再应用在子应用
|
|
879
|
-
linkedBaseStyle(styleElement, app) {
|
|
880
|
-
if (!(app.container instanceof ShadowRoot) && styleElement.textContent && appCache.getBaseAppStyle(styleElement.textContent)) {
|
|
881
|
-
styleElement.textContent = "";
|
|
882
|
-
styleElement.innerHTML = "";
|
|
883
|
-
styleElement.setAttribute("linked-from-base", "true");
|
|
884
|
-
return true;
|
|
885
|
-
}
|
|
886
|
-
return false;
|
|
887
|
-
}
|
|
888
|
-
resetPackRule(rule, prefix, packName) {
|
|
889
|
-
const result = this.scopeRule(Array.from(rule.cssRules), prefix);
|
|
890
|
-
return `@${packName} ${rule.conditionText} {${result}}`;
|
|
891
|
-
}
|
|
892
|
-
resetUrlHost(cssText, uri, linkPath) {
|
|
893
|
-
let baseURI = uri;
|
|
894
|
-
return cssText.replace(/url\(["']?([^)"']+)["']?\)/gm, (text, $1) => {
|
|
895
|
-
if (/^(data|blob):/.test($1) || /^(https?:)?\/\//.test($1)) {
|
|
896
|
-
return text;
|
|
897
|
-
}
|
|
898
|
-
if (/^((\.\.?\/)|[^/])/.test($1) && linkPath) {
|
|
899
|
-
const pathArr = linkPath.split("/");
|
|
900
|
-
pathArr.pop();
|
|
901
|
-
baseURI = addUrlProtocol(`${pathArr.join("/")}/`);
|
|
902
|
-
}
|
|
903
|
-
return `url("${fillUpPath($1, baseURI)}")`;
|
|
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
|
+
});
|
|
904
572
|
});
|
|
573
|
+
const observerConfig = {
|
|
574
|
+
attributeFilter: ["href"],
|
|
575
|
+
childList: false,
|
|
576
|
+
subtree: false
|
|
577
|
+
};
|
|
578
|
+
observer.observe(linkElement, observerConfig);
|
|
905
579
|
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
switch (rule.type) {
|
|
910
|
-
case 1 /* STYLE_RULE */:
|
|
911
|
-
result += this.scopeStyleRule(rule, cssPrefix);
|
|
912
|
-
break;
|
|
913
|
-
case 4 /* MEDIA_RULE */:
|
|
914
|
-
result += this.resetPackRule(rule, cssPrefix, "media");
|
|
915
|
-
break;
|
|
916
|
-
case 12 /* SUPPORTS_RULE */:
|
|
917
|
-
result += this.resetPackRule(rule, cssPrefix, "supports");
|
|
918
|
-
break;
|
|
919
|
-
default:
|
|
920
|
-
result += rule.cssText;
|
|
921
|
-
break;
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
return result.replace(/^\s+/, "");
|
|
925
|
-
}
|
|
926
|
-
scopeStyleRule(rule, prefix) {
|
|
927
|
-
const { cssText, selectorText } = rule;
|
|
928
|
-
if (/^((html[\s>~,]+body)|(html|body|:root))$/.test(selectorText)) {
|
|
929
|
-
return cssText.replace(/^((html[\s>~,]+body)|(html|body|:root))/, prefix);
|
|
580
|
+
processExistingContent(styleElement, app) {
|
|
581
|
+
if (styleElement.textContent) {
|
|
582
|
+
this.clearStyleElement(styleElement);
|
|
930
583
|
}
|
|
931
|
-
if (
|
|
932
|
-
|
|
584
|
+
if (!this.linkedBaseStyle(styleElement, app)) {
|
|
585
|
+
this.commonScoped(styleElement, app);
|
|
933
586
|
}
|
|
934
|
-
const builtInRootSelectorRE = /(^|\s+)((html[\s>~]+body)|(html|body|:root))(?=[\s>~]+|$)/;
|
|
935
|
-
return cssText.replace(
|
|
936
|
-
/^[\s\S]+{/,
|
|
937
|
-
(selectors) => selectors.replace(/(^|,)([^,]+)/g, (all, $1, $2) => {
|
|
938
|
-
if (builtInRootSelectorRE.test($2)) {
|
|
939
|
-
return all.replace(builtInRootSelectorRE, prefix);
|
|
940
|
-
}
|
|
941
|
-
return `${$1} ${prefix} ${$2.replace(/^\s*/, "")}`;
|
|
942
|
-
})
|
|
943
|
-
);
|
|
944
587
|
}
|
|
945
|
-
|
|
946
|
-
const
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
const container = needKeepAlive ? document.head : app.container;
|
|
951
|
-
try {
|
|
952
|
-
if (this.code) {
|
|
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)) {
|
|
953
593
|
this.commonScoped(styleElement, app);
|
|
954
|
-
container?.prepend(styleElement);
|
|
955
|
-
linkElement && dispatchLinkOrScriptLoad(linkElement);
|
|
956
|
-
} else if (linkElement.getAttribute("href")) {
|
|
957
|
-
this.url = fillUpPath(linkElement.getAttribute("href"), app.url);
|
|
958
|
-
this.getCode(app).then(() => {
|
|
959
|
-
this.scopedStyleCSS(app, styleElement);
|
|
960
|
-
linkElement.remove();
|
|
961
|
-
container?.prepend(styleElement);
|
|
962
|
-
linkElement && dispatchLinkOrScriptLoad(linkElement);
|
|
963
|
-
this.scoped = true;
|
|
964
|
-
});
|
|
965
|
-
} else {
|
|
966
|
-
const observer = new MutationObserver(() => {
|
|
967
|
-
if (!linkElement.href) return;
|
|
968
|
-
observer.disconnect();
|
|
969
|
-
this.url = fillUpPath(linkElement.getAttribute("href"), app.url);
|
|
970
|
-
this.getCode(app).then(() => {
|
|
971
|
-
this.scopedStyleCSS(app, styleElement);
|
|
972
|
-
linkElement.remove();
|
|
973
|
-
container?.prepend(styleElement);
|
|
974
|
-
linkElement && dispatchLinkOrScriptLoad(linkElement);
|
|
975
|
-
this.scoped = true;
|
|
976
|
-
});
|
|
977
|
-
});
|
|
978
|
-
observer.observe(linkElement, { attributeFilter: ["href"], childList: false, subtree: false });
|
|
979
|
-
}
|
|
980
|
-
} catch {
|
|
981
|
-
linkElement && dispatchLinkOrScriptError(linkElement);
|
|
982
|
-
}
|
|
983
|
-
return styleElement;
|
|
984
|
-
}
|
|
985
|
-
scopedStyleCSS(app, styleElement) {
|
|
986
|
-
const needKeepAlive = !!app.keepAlive && !(app.container instanceof ShadowRoot);
|
|
987
|
-
setMarkElement(styleElement, app, needKeepAlive);
|
|
988
|
-
if (this.code || styleElement.textContent) {
|
|
989
|
-
if (styleElement.textContent) {
|
|
990
|
-
styleElement.textContent = "";
|
|
991
|
-
styleElement.innerHTML = "";
|
|
992
594
|
}
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
}
|
|
1002
|
-
});
|
|
1003
|
-
observer.observe(styleElement, { attributes: false, characterData: true, childList: true, subtree: true });
|
|
1004
|
-
}
|
|
1005
|
-
this.url && styleElement.setAttribute("origin-src", this.url);
|
|
1006
|
-
return styleElement;
|
|
595
|
+
});
|
|
596
|
+
const observerConfig = {
|
|
597
|
+
attributes: false,
|
|
598
|
+
characterData: true,
|
|
599
|
+
childList: true,
|
|
600
|
+
subtree: true
|
|
601
|
+
};
|
|
602
|
+
observer.observe(styleElement, observerConfig);
|
|
1007
603
|
}
|
|
1008
604
|
};
|
|
1009
605
|
async function executeAppStyles(app, container) {
|
|
1010
|
-
const styleList = Array.from(app.source
|
|
606
|
+
const styleList = Array.from(app.source?.styles?.values() || []);
|
|
1011
607
|
const promiseList = [];
|
|
1012
608
|
for (const style of styleList) {
|
|
1013
609
|
promiseList.push(style.executeCode(app));
|
|
@@ -1023,38 +619,26 @@ async function executeAppStyles(app, container) {
|
|
|
1023
619
|
}
|
|
1024
620
|
|
|
1025
621
|
// src/mode/instance.ts
|
|
622
|
+
var DEFAULT_RANDOM_LENGTH = 5;
|
|
623
|
+
var WRAPPER_SUFFIX = "-wrapper";
|
|
1026
624
|
var MicroInstanceModel = class {
|
|
1027
|
-
state =
|
|
1028
|
-
// 当前实例状态
|
|
625
|
+
state = AppState.UNSET;
|
|
1029
626
|
appCacheKey;
|
|
1030
|
-
// 缓存key
|
|
1031
627
|
container;
|
|
1032
|
-
// 容器
|
|
1033
628
|
data;
|
|
1034
|
-
|
|
629
|
+
fetchSource;
|
|
1035
630
|
initSource;
|
|
1036
|
-
// 初始资源
|
|
1037
631
|
isPreLoad = false;
|
|
1038
|
-
// 是否预加载
|
|
1039
632
|
keepAlive;
|
|
1040
|
-
// 是否缓存
|
|
1041
633
|
name;
|
|
1042
|
-
// 名称
|
|
1043
634
|
sandBox;
|
|
1044
|
-
// 沙箱
|
|
1045
635
|
scopeCss = true;
|
|
1046
|
-
// 是否隔离样式
|
|
1047
636
|
scopeJs = false;
|
|
1048
|
-
// 是否隔离js
|
|
1049
637
|
showSourceCode = true;
|
|
1050
|
-
// 是否显示源码
|
|
1051
638
|
source;
|
|
1052
|
-
// 入口资源
|
|
1053
639
|
url;
|
|
1054
|
-
// url
|
|
1055
|
-
fetchSource;
|
|
1056
640
|
constructor(props) {
|
|
1057
|
-
this.name = props.id !== props.url ? props.id : random(
|
|
641
|
+
this.name = props.id !== props.url ? props.id : random(DEFAULT_RANDOM_LENGTH);
|
|
1058
642
|
this.appCacheKey = props.id || this.name;
|
|
1059
643
|
this.url = props.url;
|
|
1060
644
|
this.container = props.container ?? void 0;
|
|
@@ -1064,106 +648,178 @@ var MicroInstanceModel = class {
|
|
|
1064
648
|
this.keepAlive = props.keepAlive ?? false;
|
|
1065
649
|
this.data = props.data ?? {};
|
|
1066
650
|
this.initSource = props.initSource ?? [];
|
|
1067
|
-
if (this.scopeJs) {
|
|
1068
|
-
this.sandBox = new SandBox(this);
|
|
1069
|
-
}
|
|
1070
651
|
this.fetchSource = props.fetchSource;
|
|
652
|
+
this.initializeSandBox();
|
|
1071
653
|
}
|
|
654
|
+
/** 激活微模块 */
|
|
1072
655
|
activated(container, callback) {
|
|
1073
656
|
this.isPreLoad = false;
|
|
1074
|
-
this.state =
|
|
657
|
+
this.state = AppState.ACTIVATED;
|
|
1075
658
|
if (this.container && container) {
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
for (const node of Array.from(this.container.childNodes)) {
|
|
1079
|
-
fragment.appendChild(node);
|
|
1080
|
-
}
|
|
1081
|
-
container.appendChild(fragment);
|
|
659
|
+
this.setContainerAttribute(container);
|
|
660
|
+
this.transferNodes(container);
|
|
1082
661
|
this.container = container;
|
|
662
|
+
this.initShadowRootContainer();
|
|
1083
663
|
this.sandBox?.activated();
|
|
1084
|
-
const scriptInfo = this.
|
|
664
|
+
const scriptInfo = this.getScriptInfo();
|
|
1085
665
|
callback?.(this, scriptInfo?.exportInstance);
|
|
1086
666
|
}
|
|
1087
667
|
}
|
|
668
|
+
/** 停用微模块 */
|
|
1088
669
|
deactivated() {
|
|
1089
|
-
this.state =
|
|
670
|
+
this.state = AppState.DEACTIVATED;
|
|
1090
671
|
this.sandBox?.deactivated();
|
|
1091
672
|
}
|
|
673
|
+
/** 挂载微模块 */
|
|
1092
674
|
mount(container, callback) {
|
|
1093
675
|
this.isPreLoad = false;
|
|
1094
676
|
this.container = container ?? this.container;
|
|
1095
|
-
this.
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
this.
|
|
1100
|
-
const instanceWrap = document.createElement("div");
|
|
1101
|
-
const wrapId = `${this.name}-wrapper`;
|
|
1102
|
-
instanceWrap.setAttribute("id", wrapId);
|
|
1103
|
-
if (this.source?.styles.size) {
|
|
1104
|
-
executeAppStyles(this, this.container);
|
|
1105
|
-
}
|
|
1106
|
-
this.container.appendChild(instanceWrap);
|
|
677
|
+
this.initShadowRootContainer();
|
|
678
|
+
this.state = AppState.MOUNTING;
|
|
679
|
+
this.setContainerAttribute(this.container);
|
|
680
|
+
this.setupContainer();
|
|
681
|
+
this.executeStyles();
|
|
1107
682
|
this.sandBox?.activated();
|
|
1108
683
|
execAppScripts(this).finally(() => {
|
|
1109
|
-
this.state =
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
scriptInfo.exportInstance.render(instanceWrap, this.data);
|
|
1113
|
-
}
|
|
684
|
+
this.state = AppState.MOUNTED;
|
|
685
|
+
this.renderInstance();
|
|
686
|
+
const scriptInfo = this.getScriptInfo();
|
|
1114
687
|
callback?.(this, scriptInfo?.exportInstance);
|
|
1115
688
|
});
|
|
1116
689
|
}
|
|
690
|
+
/** 错误处理 */
|
|
1117
691
|
onError() {
|
|
1118
|
-
this.state =
|
|
692
|
+
this.state = AppState.ERROR;
|
|
1119
693
|
}
|
|
694
|
+
/** 挂载处理 */
|
|
1120
695
|
onMount() {
|
|
1121
696
|
if (this.isPreLoad) return;
|
|
1122
|
-
this.state =
|
|
697
|
+
this.state = AppState.LOADED;
|
|
1123
698
|
this.mount();
|
|
1124
699
|
}
|
|
700
|
+
/** 注册运行中的应用 */
|
|
1125
701
|
registerRunningApp() {
|
|
1126
702
|
setCurrentRunningApp(this);
|
|
1127
703
|
Promise.resolve().then(() => setCurrentRunningApp(null));
|
|
1128
704
|
}
|
|
705
|
+
/** 启动微模块 */
|
|
1129
706
|
async start() {
|
|
1130
|
-
if (!this.source ||
|
|
707
|
+
if (!this.source || this.needsReload()) {
|
|
1131
708
|
this.source = new EntrySource(this.url);
|
|
1132
709
|
await this.source.importEntry(this);
|
|
1133
710
|
}
|
|
1134
711
|
}
|
|
712
|
+
/** 卸载微模块 */
|
|
1135
713
|
unmount(needDestroy) {
|
|
1136
|
-
this.state =
|
|
714
|
+
this.state = AppState.UNMOUNT;
|
|
1137
715
|
this.sandBox?.deactivated();
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
716
|
+
if (needDestroy) {
|
|
717
|
+
appCache.deleteApp(this.url);
|
|
718
|
+
}
|
|
719
|
+
if (this.container) {
|
|
720
|
+
this.container.innerHTML = "";
|
|
721
|
+
this.container = void 0;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
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
|
+
}
|
|
1141
735
|
}
|
|
1142
|
-
set status(
|
|
1143
|
-
this.state =
|
|
736
|
+
set status(value) {
|
|
737
|
+
this.state = value;
|
|
1144
738
|
}
|
|
1145
739
|
get status() {
|
|
1146
740
|
return this.state;
|
|
1147
741
|
}
|
|
742
|
+
/** 初始化沙盒 */
|
|
743
|
+
initializeSandBox() {
|
|
744
|
+
if (this.scopeJs) {
|
|
745
|
+
this.sandBox = new SandBox(this);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
/** 设置容器属性 */
|
|
749
|
+
setContainerAttribute(container) {
|
|
750
|
+
if (container instanceof HTMLElement) {
|
|
751
|
+
container.setAttribute(CSS_ATTRIBUTE_KEY, this.name);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
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);
|
|
763
|
+
}
|
|
764
|
+
/** 设置容器 */
|
|
765
|
+
setupContainer() {
|
|
766
|
+
if (this.container) {
|
|
767
|
+
this.container.innerHTML = "";
|
|
768
|
+
const instanceWrapper = this.createInstanceWrapper();
|
|
769
|
+
this.container.appendChild(instanceWrapper);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
/** 执行样式 */
|
|
773
|
+
executeStyles() {
|
|
774
|
+
if (this.source?.styles.size && this.container) {
|
|
775
|
+
executeAppStyles(this, this.container);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
/** 创建实例包装器 */
|
|
779
|
+
createInstanceWrapper() {
|
|
780
|
+
const wrapper = document.createElement("div");
|
|
781
|
+
wrapper.id = `${this.name}${WRAPPER_SUFFIX}`;
|
|
782
|
+
return wrapper;
|
|
783
|
+
}
|
|
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
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
/** 获取脚本信息 */
|
|
795
|
+
getScriptInfo() {
|
|
796
|
+
const script = this.source?.getScript(this.url);
|
|
797
|
+
return script ? { exportInstance: script.exportInstance } : void 0;
|
|
798
|
+
}
|
|
799
|
+
/** 检查是否需要重新加载 */
|
|
800
|
+
needsReload() {
|
|
801
|
+
return this.status === AppState.ERROR || this.status === AppState.UNSET;
|
|
802
|
+
}
|
|
1148
803
|
};
|
|
1149
804
|
|
|
1150
805
|
// src/entry/script.ts
|
|
1151
806
|
var firstGlobalProp;
|
|
1152
807
|
var secondGlobalProp;
|
|
1153
808
|
var lastGlobalProp;
|
|
809
|
+
var STRICT_MODE_REGEX = /^"use\sstrict";$/gim;
|
|
1154
810
|
var Script = class {
|
|
1155
811
|
async = false;
|
|
1156
812
|
code = "";
|
|
1157
813
|
defer = false;
|
|
1158
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1159
814
|
exportInstance;
|
|
1160
815
|
fromHtml;
|
|
816
|
+
blobUrl;
|
|
1161
817
|
initial;
|
|
1162
818
|
isModule = false;
|
|
1163
819
|
scoped;
|
|
1164
820
|
url;
|
|
1165
821
|
constructor({ async, code, defer, fromHtml, initial, isModule, url }) {
|
|
1166
|
-
this.code = code;
|
|
822
|
+
this.code = code?.replace(STRICT_MODE_REGEX, "");
|
|
1167
823
|
this.async = async;
|
|
1168
824
|
this.defer = defer;
|
|
1169
825
|
this.isModule = isModule;
|
|
@@ -1171,12 +827,9 @@ var Script = class {
|
|
|
1171
827
|
this.scoped = false;
|
|
1172
828
|
this.fromHtml = fromHtml ?? false;
|
|
1173
829
|
this.initial = initial ?? false;
|
|
830
|
+
this.blobUrl = void 0;
|
|
1174
831
|
}
|
|
1175
|
-
/**
|
|
1176
|
-
* @param app 应用
|
|
1177
|
-
* @param needReplaceScriptElement 是否需要替换script标签
|
|
1178
|
-
* @returns 返回执行后的script标签或注释
|
|
1179
|
-
*/
|
|
832
|
+
/** 执行脚本代码 */
|
|
1180
833
|
async executeCode(app, needReplaceScriptElement = false) {
|
|
1181
834
|
try {
|
|
1182
835
|
if (!this.code) await this.getCode(app);
|
|
@@ -1184,9 +837,29 @@ var Script = class {
|
|
|
1184
837
|
const globalWindow = app.scopeJs ? app.sandBox?.proxyWindow || window : window;
|
|
1185
838
|
noteGlobalProps(globalWindow);
|
|
1186
839
|
}
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
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) {
|
|
1190
863
|
const scriptElement = document.createElement("script");
|
|
1191
864
|
if (scriptElement.__BK_WEWEB_APP_KEY__) {
|
|
1192
865
|
scriptElement.__BK_WEWEB_APP_KEY__ = void 0;
|
|
@@ -1204,200 +877,954 @@ var Script = class {
|
|
|
1204
877
|
}
|
|
1205
878
|
if (app instanceof MicroInstanceModel) {
|
|
1206
879
|
const globalWindow = app.scopeJs ? app.sandBox?.proxyWindow || window : window;
|
|
1207
|
-
const
|
|
880
|
+
const isIifeVar = !!this.code.replace(/\/\*[\s\S]*?\*\//g, "").match(/^\s*var\s/);
|
|
881
|
+
const exportProp = getGlobalProp(globalWindow, isIifeVar);
|
|
1208
882
|
if (exportProp) {
|
|
1209
883
|
this.exportInstance = globalWindow[exportProp];
|
|
1210
884
|
if (!app.scopeJs) {
|
|
1211
|
-
|
|
885
|
+
const descriptor = Object.getOwnPropertyDescriptor(globalWindow, exportProp);
|
|
886
|
+
if (descriptor?.configurable) {
|
|
887
|
+
delete globalWindow[exportProp];
|
|
888
|
+
}
|
|
1212
889
|
}
|
|
1213
890
|
}
|
|
1214
891
|
}
|
|
1215
|
-
} catch (
|
|
1216
|
-
console.error("execute script code error",
|
|
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");
|
|
916
|
+
} else {
|
|
917
|
+
scriptElement.textContent = scopedCode;
|
|
918
|
+
}
|
|
919
|
+
this.url && scriptElement.setAttribute("origin-src", this.url);
|
|
920
|
+
}
|
|
921
|
+
/** 获取脚本内容 */
|
|
922
|
+
async getCode(app) {
|
|
923
|
+
if (this.code.length || !this.url || this.isModule) {
|
|
924
|
+
return this.code;
|
|
925
|
+
}
|
|
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;
|
|
942
|
+
}
|
|
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));
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
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");
|
|
1061
|
+
}
|
|
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");
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
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);
|
|
1075
|
+
}
|
|
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);
|
|
1093
|
+
}
|
|
1094
|
+
if (isJsonpUrl(child.getAttribute("src"))) {
|
|
1095
|
+
app.container?.append(child);
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
if (scriptInfo?.script) {
|
|
1099
|
+
scriptInfo.script.executeCode(app);
|
|
1100
|
+
}
|
|
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);
|
|
1119
|
+
}
|
|
1120
|
+
child.remove();
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
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;
|
|
1130
|
+
}
|
|
1131
|
+
if (replaceInfo.script) {
|
|
1132
|
+
replaceInfo.script.executeCode(app);
|
|
1133
|
+
}
|
|
1134
|
+
if (replaceInfo.replace !== child) {
|
|
1135
|
+
return replaceInfo.replace;
|
|
1136
|
+
}
|
|
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");
|
|
1141
|
+
}
|
|
1142
|
+
return child;
|
|
1143
|
+
};
|
|
1144
|
+
var resetNewElement = (parent, child, app) => {
|
|
1145
|
+
if (child instanceof HTMLStyleElement) {
|
|
1146
|
+
return handleStyleElement(child, app);
|
|
1147
|
+
}
|
|
1148
|
+
if (child instanceof HTMLLinkElement) {
|
|
1149
|
+
return handleLinkElement(child, parent, app);
|
|
1150
|
+
}
|
|
1151
|
+
if (child instanceof HTMLScriptElement) {
|
|
1152
|
+
return handleScriptElement(child, parent, app);
|
|
1153
|
+
}
|
|
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);
|
|
1166
|
+
}
|
|
1167
|
+
const app = appCache.getApp(newChild.__BK_WEWEB_APP_KEY__);
|
|
1168
|
+
if (!app?.container) {
|
|
1169
|
+
return rawMethod.call(parent, newChild);
|
|
1170
|
+
}
|
|
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);
|
|
1181
|
+
}
|
|
1182
|
+
const app = appCache.getApp(newChild.__BK_WEWEB_APP_KEY__);
|
|
1183
|
+
if (!app?.container) {
|
|
1184
|
+
return rawMethod.call(parent, newChild, passiveChild);
|
|
1185
|
+
}
|
|
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);
|
|
1192
|
+
}
|
|
1193
|
+
if (passiveChild && !container.contains(passiveChild)) {
|
|
1194
|
+
return bodyAppendChild.call(container, targetChild);
|
|
1195
|
+
}
|
|
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;
|
|
1238
|
+
}
|
|
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
|
+
});
|
|
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;
|
|
1297
|
+
}
|
|
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;
|
|
1381
|
+
}
|
|
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;
|
|
1387
|
+
}
|
|
1388
|
+
});
|
|
1389
|
+
};
|
|
1390
|
+
|
|
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]);
|
|
1401
|
+
}
|
|
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
|
+
}
|
|
1412
|
+
}
|
|
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
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
document.addEventListener = addEventListener;
|
|
1426
|
+
document.body.addEventListener = bodyAddEventListener;
|
|
1427
|
+
document.removeEventListener = removeEventListener;
|
|
1428
|
+
document.body.removeEventListener = bodyRemoveEventListener;
|
|
1429
|
+
documentListenerMap.clear();
|
|
1430
|
+
}
|
|
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;
|
|
1443
|
+
}
|
|
1444
|
+
if (ConstructFunCacheMap.has(fn)) {
|
|
1445
|
+
return ConstructFunCacheMap.get(fn);
|
|
1446
|
+
}
|
|
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);
|
|
1455
|
+
}
|
|
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];
|
|
1460
|
+
}
|
|
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
|
+
});
|
|
1467
|
+
}
|
|
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
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
functionBoundedValueMap.set(value, boundValue);
|
|
1483
|
+
return boundValue;
|
|
1484
|
+
}
|
|
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);
|
|
1503
|
+
}
|
|
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);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
return {
|
|
1532
|
+
resetWindowFunction
|
|
1533
|
+
};
|
|
1534
|
+
}
|
|
1535
|
+
|
|
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);
|
|
1559
|
+
}
|
|
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
|
+
}
|
|
1587
|
+
return descriptor;
|
|
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);
|
|
1595
|
+
}
|
|
1596
|
+
});
|
|
1597
|
+
rawWindow[this.windowSymbolKey] = this.proxyWindow;
|
|
1598
|
+
}
|
|
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;
|
|
1217
1645
|
}
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
// 内存脚本执行
|
|
1221
|
-
executeMemoryScript(app, scopedCode) {
|
|
1222
|
-
try {
|
|
1223
|
-
const isScopedLocation = app instanceof MicroAppModel && app.scopeLocation;
|
|
1224
|
-
app.registerRunningApp();
|
|
1225
|
-
new Function("window", "location", "history", scopedCode)(
|
|
1226
|
-
app.sandBox.proxyWindow,
|
|
1227
|
-
isScopedLocation ? app.iframe.contentWindow.location : window.location,
|
|
1228
|
-
isScopedLocation ? app.iframe.contentWindow.history : window.history
|
|
1229
|
-
);
|
|
1230
|
-
} catch (e) {
|
|
1231
|
-
console.error(e);
|
|
1646
|
+
if (key === "hasOwnProperty") {
|
|
1647
|
+
return (checkKey) => Object.hasOwn(this.fakeWindow, checkKey) || Object.hasOwn(rawWindow, checkKey);
|
|
1232
1648
|
}
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
scriptElement.setAttribute("type", "module");
|
|
1239
|
-
} else {
|
|
1240
|
-
scriptElement.textContent = scopedCode;
|
|
1649
|
+
if (key === "top" || key === "parent") {
|
|
1650
|
+
if (rawWindow === rawWindow.parent) {
|
|
1651
|
+
return this.proxyWindow;
|
|
1652
|
+
}
|
|
1653
|
+
return Reflect.get(rawWindow, key);
|
|
1241
1654
|
}
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
// 获取脚本内容
|
|
1245
|
-
async getCode(app) {
|
|
1246
|
-
if (this.code.length || !this.url) {
|
|
1247
|
-
return this.code;
|
|
1655
|
+
if (key === "getComputedStyle") {
|
|
1656
|
+
return this.createGetComputedStyleProxy(rawWindow);
|
|
1248
1657
|
}
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
code = app.source.getScript(this.url)?.code || "";
|
|
1658
|
+
if (Reflect.has(target, key) || BK_WEWEB_INJECT_KEY_LIST.includes(key)) {
|
|
1659
|
+
return Reflect.get(target, key);
|
|
1252
1660
|
}
|
|
1253
|
-
|
|
1254
|
-
|
|
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;
|
|
1255
1677
|
}
|
|
1256
|
-
if (
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
return "";
|
|
1260
|
-
});
|
|
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;
|
|
1261
1681
|
}
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
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;
|
|
1265
1692
|
}
|
|
1266
|
-
|
|
1267
|
-
|
|
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);
|
|
1268
1702
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
if (app.showSourceCode) {
|
|
1281
|
-
return `;(function(window, self, globalThis){
|
|
1282
|
-
with(window){
|
|
1283
|
-
${getGlobalContextCode()}
|
|
1284
|
-
|
|
1285
|
-
${this.code}
|
|
1286
|
-
|
|
1287
|
-
${sourceMapUrl}
|
|
1288
|
-
}
|
|
1289
|
-
}).call(window.${app.sandBox.windowSymbolKey},
|
|
1290
|
-
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);
|
|
1291
1714
|
}
|
|
1292
|
-
return
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1715
|
+
return rawWindow.getComputedStyle(document.body, pseudoElt);
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
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;
|
|
1743
|
+
}
|
|
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);
|
|
1306
1753
|
}
|
|
1307
|
-
return this.code;
|
|
1308
1754
|
}
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
if (shouldSkipProperty(global, p)) continue;
|
|
1322
|
-
if (cnt === 0 && p !== firstGlobalProp || cnt === 1 && p !== secondGlobalProp) return p;
|
|
1323
|
-
if (foundLastProp) {
|
|
1324
|
-
lastGlobalProp = p;
|
|
1325
|
-
result = useFirstGlobalProp && result || p;
|
|
1326
|
-
} else {
|
|
1327
|
-
foundLastProp = p === lastGlobalProp;
|
|
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);
|
|
1328
1767
|
}
|
|
1329
|
-
cnt += 1;
|
|
1330
1768
|
}
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
if (
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
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;
|
|
1781
|
+
}
|
|
1341
1782
|
}
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
if (script.defer || script.async) {
|
|
1353
|
-
if (!script.code && script.defer) {
|
|
1354
|
-
deferScriptList.push(script.executeCode(app));
|
|
1355
|
-
} else asyncScriptList.push(script.executeCode(app));
|
|
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);
|
|
1356
1793
|
}
|
|
1794
|
+
this.inRawWindowKeySet.clear();
|
|
1795
|
+
this.resetDocumentAndBodyEvent?.();
|
|
1357
1796
|
}
|
|
1358
|
-
|
|
1359
|
-
console.error(e);
|
|
1360
|
-
});
|
|
1361
|
-
}
|
|
1797
|
+
};
|
|
1362
1798
|
|
|
1363
1799
|
// src/mode/app.ts
|
|
1364
|
-
var
|
|
1800
|
+
var IFRAME_CONSTANTS = {
|
|
1801
|
+
BLANK_ORIGIN: "about:blank",
|
|
1802
|
+
STYLE_HIDDEN: "display: none;",
|
|
1803
|
+
CHROME_USER_AGENT: "Chrome",
|
|
1804
|
+
DEFAULT_HTML: "<head></head><body></body>",
|
|
1805
|
+
POLLING_INTERVAL: 0
|
|
1806
|
+
};
|
|
1807
|
+
var DEFAULT_RANDOM_LENGTH2 = 5;
|
|
1365
1808
|
var MicroAppModel = class {
|
|
1366
|
-
state =
|
|
1367
|
-
// 状态
|
|
1809
|
+
state = AppState.UNSET;
|
|
1368
1810
|
container;
|
|
1369
|
-
// 容器
|
|
1370
1811
|
data;
|
|
1371
|
-
// 数据
|
|
1372
1812
|
iframe = null;
|
|
1373
|
-
// scoped iframe
|
|
1374
1813
|
initSource;
|
|
1375
|
-
// 初始资源
|
|
1376
1814
|
isModuleApp = false;
|
|
1377
|
-
// 是否预加载
|
|
1378
1815
|
isPreLoad = false;
|
|
1379
|
-
// 是否缓存
|
|
1380
1816
|
keepAlive;
|
|
1381
1817
|
mode = "app" /* APP */;
|
|
1382
|
-
// 名称
|
|
1383
1818
|
name;
|
|
1384
|
-
// 沙箱
|
|
1385
1819
|
sandBox;
|
|
1386
|
-
// 是否隔离样式
|
|
1387
1820
|
scopeCss;
|
|
1388
|
-
// 是否隔离js
|
|
1389
1821
|
scopeJs;
|
|
1390
|
-
// 是否隔离location
|
|
1391
1822
|
scopeLocation;
|
|
1392
|
-
// 是否显示源码
|
|
1393
1823
|
showSourceCode;
|
|
1394
|
-
// 入口资源
|
|
1395
1824
|
source;
|
|
1396
|
-
// url
|
|
1397
1825
|
url;
|
|
1398
|
-
// 是否是esm应用
|
|
1399
1826
|
constructor(props) {
|
|
1400
|
-
this.name = props.id !== props.url ? props.id : random(
|
|
1827
|
+
this.name = props.id !== props.url ? props.id || random(DEFAULT_RANDOM_LENGTH2) : random(DEFAULT_RANDOM_LENGTH2);
|
|
1401
1828
|
this.mode = props.mode ?? "app" /* APP */;
|
|
1402
1829
|
this.container = props.container ?? void 0;
|
|
1403
1830
|
this.showSourceCode = props.showSourceCode ?? false;
|
|
@@ -1409,71 +1836,40 @@ var MicroAppModel = class {
|
|
|
1409
1836
|
this.isPreLoad = props.isPreLoad ?? false;
|
|
1410
1837
|
this.keepAlive = props.keepAlive ?? false;
|
|
1411
1838
|
this.initSource = props.initSource ?? [];
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
}
|
|
1415
|
-
if (this.container instanceof HTMLElement) {
|
|
1416
|
-
this.container.setAttribute(CSS_ATTRIBUTE_KEY, this.name);
|
|
1417
|
-
}
|
|
1839
|
+
this.initializeSandBox();
|
|
1840
|
+
this.setContainerAttribute();
|
|
1418
1841
|
}
|
|
1419
|
-
|
|
1842
|
+
/** 激活微应用 */
|
|
1420
1843
|
activated(container, callback) {
|
|
1421
1844
|
this.isPreLoad = false;
|
|
1422
|
-
this.state =
|
|
1423
|
-
const app = this;
|
|
1845
|
+
this.state = AppState.ACTIVATED;
|
|
1424
1846
|
if (container && this.container) {
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
const list = Array.from(this.container.childNodes);
|
|
1428
|
-
for (const node of list) {
|
|
1429
|
-
node.__BK_WEWEB_APP_KEY__ = this.appCacheKey;
|
|
1430
|
-
Object.defineProperties(node, {
|
|
1431
|
-
ownerDocument: {
|
|
1432
|
-
get() {
|
|
1433
|
-
return app.sandBox?.rawDocument;
|
|
1434
|
-
}
|
|
1435
|
-
}
|
|
1436
|
-
});
|
|
1437
|
-
fragment.appendChild(node);
|
|
1438
|
-
}
|
|
1439
|
-
container.innerHTML = "";
|
|
1440
|
-
container.appendChild(fragment);
|
|
1847
|
+
this.setContainerAttribute(container);
|
|
1848
|
+
this.transferNodes(container);
|
|
1441
1849
|
this.container = container;
|
|
1442
1850
|
this.initShadowRootContainer();
|
|
1443
1851
|
this.sandBox?.activated(this.data);
|
|
1444
1852
|
callback?.(this);
|
|
1445
1853
|
}
|
|
1446
1854
|
}
|
|
1447
|
-
|
|
1855
|
+
/** 创建隔离iframe */
|
|
1448
1856
|
createIframe() {
|
|
1449
1857
|
return new Promise((resolve) => {
|
|
1450
|
-
const iframe =
|
|
1451
|
-
const url = new URL(addUrlProtocol(this.url));
|
|
1452
|
-
const isChrome = navigator.userAgent.indexOf("Chrome") > -1;
|
|
1453
|
-
iframe.setAttribute(
|
|
1454
|
-
"src",
|
|
1455
|
-
`${isChrome ? BLANK_ORIGN : location.origin}${url.pathname || "/"}${url.search}${url.hash}`
|
|
1456
|
-
);
|
|
1457
|
-
iframe.style.cssText = "display: none;";
|
|
1858
|
+
const iframe = this.createIframeElement();
|
|
1458
1859
|
document.body.appendChild(iframe);
|
|
1459
|
-
if (
|
|
1460
|
-
setTimeout(() => resolve(iframe),
|
|
1860
|
+
if (this.isChromeUserAgent()) {
|
|
1861
|
+
setTimeout(() => resolve(iframe), IFRAME_CONSTANTS.POLLING_INTERVAL);
|
|
1461
1862
|
} else {
|
|
1462
|
-
|
|
1463
|
-
if (iframe.contentWindow && iframe.contentWindow.location.href !== BLANK_ORIGN) {
|
|
1464
|
-
iframe.contentWindow.stop();
|
|
1465
|
-
iframe.contentDocument.body.parentElement.innerHTML = "<head></head><body></body>";
|
|
1466
|
-
clearInterval(interval);
|
|
1467
|
-
resolve(iframe);
|
|
1468
|
-
}
|
|
1469
|
-
}, 0);
|
|
1863
|
+
this.handleNonChromeIframe(iframe, resolve);
|
|
1470
1864
|
}
|
|
1471
1865
|
});
|
|
1472
1866
|
}
|
|
1867
|
+
/** 停用微应用 */
|
|
1473
1868
|
deactivated() {
|
|
1474
|
-
this.state =
|
|
1869
|
+
this.state = AppState.DEACTIVATED;
|
|
1475
1870
|
this.sandBox?.deactivated();
|
|
1476
1871
|
}
|
|
1872
|
+
/** 初始化ShadowRoot容器 */
|
|
1477
1873
|
initShadowRootContainer() {
|
|
1478
1874
|
if (this.container instanceof ShadowRoot) {
|
|
1479
1875
|
Object.defineProperties(this.container, {
|
|
@@ -1485,51 +1881,41 @@ var MicroAppModel = class {
|
|
|
1485
1881
|
});
|
|
1486
1882
|
}
|
|
1487
1883
|
}
|
|
1884
|
+
/** 挂载微应用 */
|
|
1488
1885
|
mount(container, callback) {
|
|
1489
1886
|
this.isPreLoad = false;
|
|
1490
1887
|
this.container = container ?? this.container;
|
|
1491
1888
|
this.initShadowRootContainer();
|
|
1492
|
-
this.state =
|
|
1493
|
-
const app = this;
|
|
1889
|
+
this.state = AppState.MOUNTING;
|
|
1494
1890
|
if (this.container) {
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
const fragment = document.createDocumentFragment();
|
|
1498
|
-
for (const node of Array.from(clonedNode.childNodes)) {
|
|
1499
|
-
node.__BK_WEWEB_APP_KEY__ = this.appCacheKey;
|
|
1500
|
-
Object.defineProperties(node, {
|
|
1501
|
-
ownerDocument: {
|
|
1502
|
-
get() {
|
|
1503
|
-
return app.sandBox?.rawDocument;
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1506
|
-
});
|
|
1507
|
-
fragment.appendChild(node);
|
|
1508
|
-
}
|
|
1509
|
-
this.container.innerHTML = "";
|
|
1510
|
-
this.container.appendChild(fragment);
|
|
1891
|
+
this.setContainerAttribute(this.container);
|
|
1892
|
+
this.renderAppContent();
|
|
1511
1893
|
this.sandBox?.activated(this.data);
|
|
1512
1894
|
execAppScripts(this).finally(() => {
|
|
1513
|
-
this.state =
|
|
1895
|
+
this.state = AppState.MOUNTED;
|
|
1514
1896
|
callback?.(this);
|
|
1515
1897
|
});
|
|
1516
1898
|
}
|
|
1517
1899
|
}
|
|
1900
|
+
/** 错误处理 */
|
|
1518
1901
|
onError() {
|
|
1519
|
-
this.state =
|
|
1902
|
+
this.state = AppState.ERROR;
|
|
1520
1903
|
}
|
|
1904
|
+
/** 挂载处理 */
|
|
1521
1905
|
onMount() {
|
|
1522
1906
|
if (this.isPreLoad) return;
|
|
1523
|
-
this.state =
|
|
1907
|
+
this.state = AppState.LOADED;
|
|
1524
1908
|
this.mount();
|
|
1525
1909
|
}
|
|
1910
|
+
/** 注册运行中的微应用 */
|
|
1526
1911
|
registerRunningApp() {
|
|
1527
1912
|
setCurrentRunningApp(this);
|
|
1528
1913
|
Promise.resolve().then(() => setCurrentRunningApp(null));
|
|
1529
1914
|
}
|
|
1915
|
+
/** 启动微应用 */
|
|
1530
1916
|
async start() {
|
|
1531
|
-
if (!this.source ||
|
|
1532
|
-
this.state =
|
|
1917
|
+
if (!this.source || this.needsReload()) {
|
|
1918
|
+
this.state = AppState.LOADING;
|
|
1533
1919
|
if (this.scopeLocation || this.isModuleApp) {
|
|
1534
1920
|
const iframe = await this.createIframe();
|
|
1535
1921
|
this.iframe = iframe;
|
|
@@ -1538,12 +1924,17 @@ var MicroAppModel = class {
|
|
|
1538
1924
|
await this.source.importEntry(this);
|
|
1539
1925
|
}
|
|
1540
1926
|
}
|
|
1927
|
+
/** 卸载微应用 */
|
|
1541
1928
|
unmount(needDestroy = false) {
|
|
1542
|
-
this.state =
|
|
1929
|
+
this.state = AppState.UNMOUNT;
|
|
1543
1930
|
this.sandBox?.deactivated();
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1931
|
+
if (needDestroy) {
|
|
1932
|
+
appCache.deleteApp(this.url);
|
|
1933
|
+
}
|
|
1934
|
+
if (this.container) {
|
|
1935
|
+
this.container.innerHTML = "";
|
|
1936
|
+
this.container = void 0;
|
|
1937
|
+
}
|
|
1547
1938
|
}
|
|
1548
1939
|
get appCacheKey() {
|
|
1549
1940
|
return this.url;
|
|
@@ -1551,77 +1942,159 @@ var MicroAppModel = class {
|
|
|
1551
1942
|
get status() {
|
|
1552
1943
|
return this.state;
|
|
1553
1944
|
}
|
|
1554
|
-
set status(
|
|
1555
|
-
this.state =
|
|
1945
|
+
set status(value) {
|
|
1946
|
+
this.state = value;
|
|
1947
|
+
}
|
|
1948
|
+
/** 初始化沙盒 */
|
|
1949
|
+
initializeSandBox() {
|
|
1950
|
+
if (this.scopeJs) {
|
|
1951
|
+
this.sandBox = new SandBox(this);
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
/** 设置容器属性 */
|
|
1955
|
+
setContainerAttribute(container) {
|
|
1956
|
+
const targetContainer = container || this.container;
|
|
1957
|
+
if (targetContainer instanceof HTMLElement) {
|
|
1958
|
+
targetContainer.setAttribute(CSS_ATTRIBUTE_KEY, this.name);
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
/** 转移节点到新容器 */
|
|
1962
|
+
transferNodes(container) {
|
|
1963
|
+
if (!this.container) return;
|
|
1964
|
+
const fragment = document.createDocumentFragment();
|
|
1965
|
+
const nodeList = Array.from(this.container.childNodes);
|
|
1966
|
+
for (const node of nodeList) {
|
|
1967
|
+
this.setupNodeProperties(node);
|
|
1968
|
+
fragment.appendChild(node);
|
|
1969
|
+
}
|
|
1970
|
+
container.innerHTML = "";
|
|
1971
|
+
container.appendChild(fragment);
|
|
1972
|
+
}
|
|
1973
|
+
/** 设置节点属性 */
|
|
1974
|
+
setupNodeProperties(node) {
|
|
1975
|
+
const app = this;
|
|
1976
|
+
const nodeWithProps = node;
|
|
1977
|
+
nodeWithProps.__BK_WEWEB_APP_KEY__ = this.appCacheKey;
|
|
1978
|
+
Object.defineProperties(node, {
|
|
1979
|
+
ownerDocument: {
|
|
1980
|
+
get() {
|
|
1981
|
+
return app.sandBox?.rawDocument;
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
});
|
|
1985
|
+
}
|
|
1986
|
+
/** 创建iframe元素 */
|
|
1987
|
+
createIframeElement() {
|
|
1988
|
+
const iframe = document.createElement("iframe");
|
|
1989
|
+
const url = new URL(addUrlProtocol(this.url));
|
|
1990
|
+
iframe.src = this.buildIframeSrc(url);
|
|
1991
|
+
iframe.style.cssText = IFRAME_CONSTANTS.STYLE_HIDDEN;
|
|
1992
|
+
return iframe;
|
|
1993
|
+
}
|
|
1994
|
+
/** 构建iframe源地址 */
|
|
1995
|
+
buildIframeSrc(url) {
|
|
1996
|
+
const isChrome = this.isChromeUserAgent();
|
|
1997
|
+
return `${isChrome ? IFRAME_CONSTANTS.BLANK_ORIGIN : location.origin}${url.pathname || "/"}${url.search}${url.hash}`;
|
|
1998
|
+
}
|
|
1999
|
+
/** 检查是否为Chrome浏览器 */
|
|
2000
|
+
isChromeUserAgent() {
|
|
2001
|
+
return navigator.userAgent.includes(IFRAME_CONSTANTS.CHROME_USER_AGENT);
|
|
2002
|
+
}
|
|
2003
|
+
/** 处理非Chrome浏览器iframe */
|
|
2004
|
+
handleNonChromeIframe(iframe, resolve) {
|
|
2005
|
+
const interval = setInterval(() => {
|
|
2006
|
+
if (iframe.contentWindow && iframe.contentWindow.location.href !== IFRAME_CONSTANTS.BLANK_ORIGIN) {
|
|
2007
|
+
iframe.contentWindow.stop();
|
|
2008
|
+
iframe.contentDocument.body.parentElement.innerHTML = IFRAME_CONSTANTS.DEFAULT_HTML;
|
|
2009
|
+
clearInterval(interval);
|
|
2010
|
+
resolve(iframe);
|
|
2011
|
+
}
|
|
2012
|
+
}, IFRAME_CONSTANTS.POLLING_INTERVAL);
|
|
2013
|
+
}
|
|
2014
|
+
/** 渲染应用内容 */
|
|
2015
|
+
renderAppContent() {
|
|
2016
|
+
if (!this.source) return;
|
|
2017
|
+
const clonedNode = this.source.html.cloneNode(true);
|
|
2018
|
+
const fragment = document.createDocumentFragment();
|
|
2019
|
+
for (const node of Array.from(clonedNode.childNodes)) {
|
|
2020
|
+
this.setupNodeProperties(node);
|
|
2021
|
+
fragment.appendChild(node);
|
|
2022
|
+
}
|
|
2023
|
+
this.container.innerHTML = "";
|
|
2024
|
+
this.container.appendChild(fragment);
|
|
2025
|
+
}
|
|
2026
|
+
/** 检查是否需要重新加载 */
|
|
2027
|
+
needsReload() {
|
|
2028
|
+
return this.status === AppState.ERROR || this.status === AppState.UNSET;
|
|
1556
2029
|
}
|
|
1557
2030
|
};
|
|
1558
2031
|
|
|
1559
2032
|
// src/utils/load-source.ts
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
2033
|
+
var JS_FILE_REGEX = /\.js$/;
|
|
2034
|
+
var CSS_FILE_REGEX = /\.css$/;
|
|
2035
|
+
var createScriptInstance = (url) => {
|
|
2036
|
+
const cachedScript = appCache.getCacheScript(url);
|
|
2037
|
+
return new Script({
|
|
2038
|
+
async: false,
|
|
2039
|
+
code: cachedScript?.code || "",
|
|
2040
|
+
defer: false,
|
|
2041
|
+
fromHtml: false,
|
|
2042
|
+
initial: true,
|
|
2043
|
+
isModule: false,
|
|
2044
|
+
url
|
|
2045
|
+
});
|
|
2046
|
+
};
|
|
2047
|
+
var createStyleInstance = (url) => {
|
|
2048
|
+
const cachedStyle = appCache.getCacheStyle(url);
|
|
2049
|
+
return new Style({
|
|
2050
|
+
code: cachedStyle?.code || "",
|
|
2051
|
+
fromHtml: true,
|
|
2052
|
+
initial: true,
|
|
2053
|
+
prefetch: false,
|
|
2054
|
+
preload: false,
|
|
2055
|
+
url
|
|
2056
|
+
});
|
|
2057
|
+
};
|
|
2058
|
+
var processResourceUrl = (url, collectScript, collectStyle) => {
|
|
2059
|
+
try {
|
|
2060
|
+
const urlObj = new URL(url);
|
|
2061
|
+
const { pathname } = urlObj;
|
|
2062
|
+
if (JS_FILE_REGEX.test(pathname)) {
|
|
2063
|
+
collectScript.set(url, createScriptInstance(url));
|
|
2064
|
+
} else if (CSS_FILE_REGEX.test(pathname)) {
|
|
2065
|
+
collectStyle.set(url, createStyleInstance(url));
|
|
2066
|
+
}
|
|
2067
|
+
} catch {
|
|
2068
|
+
console.error(`\u3010bk-weweb\u3011: ${url} is invalid URL`);
|
|
1566
2069
|
}
|
|
2070
|
+
};
|
|
2071
|
+
var collectSource = async (sourceList) => {
|
|
2072
|
+
const source = typeof sourceList === "function" ? await sourceList() : sourceList || [];
|
|
1567
2073
|
if (!source.length) return {};
|
|
1568
2074
|
const collectScript = /* @__PURE__ */ new Map();
|
|
1569
2075
|
const collectStyle = /* @__PURE__ */ new Map();
|
|
1570
|
-
source
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
initial: true,
|
|
1583
|
-
isModule: false,
|
|
1584
|
-
url: str
|
|
1585
|
-
})
|
|
1586
|
-
);
|
|
1587
|
-
} else if (url.pathname.match(/\.css$/)) {
|
|
1588
|
-
const style = appCache.getCacheStyle(str);
|
|
1589
|
-
collectStyle.set(
|
|
1590
|
-
str,
|
|
1591
|
-
new Style({
|
|
1592
|
-
code: style?.code || "",
|
|
1593
|
-
fromHtml: true,
|
|
1594
|
-
initial: true,
|
|
1595
|
-
prefetch: false,
|
|
1596
|
-
preload: false,
|
|
1597
|
-
url: str
|
|
1598
|
-
})
|
|
1599
|
-
);
|
|
1600
|
-
}
|
|
1601
|
-
} catch {
|
|
1602
|
-
console.error(`\u3010bk-weweb\u3011: ${str} is invalid URL`);
|
|
2076
|
+
for (const url of source) {
|
|
2077
|
+
processResourceUrl(url, collectScript, collectStyle);
|
|
2078
|
+
}
|
|
2079
|
+
return { collectScript, collectStyle };
|
|
2080
|
+
};
|
|
2081
|
+
var loadGlobalSource = async (sourceList) => {
|
|
2082
|
+
const { collectScript, collectStyle } = await collectSource(sourceList);
|
|
2083
|
+
const loadPromises = [];
|
|
2084
|
+
if (collectScript) {
|
|
2085
|
+
for (const [url, script] of collectScript.entries()) {
|
|
2086
|
+
loadPromises.push(script.getCode());
|
|
2087
|
+
appCache.setBaseAppScript(url, script);
|
|
1603
2088
|
}
|
|
1604
|
-
}
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
collectScript && Array.from(collectScript.entries()).forEach((item) => {
|
|
1614
|
-
promiseList.push(item[1].getCode());
|
|
1615
|
-
appCache.setBaseAppScript(...item);
|
|
1616
|
-
});
|
|
1617
|
-
collectStyle && Array.from(collectStyle.entries()).forEach((item) => {
|
|
1618
|
-
promiseList.push(item[1].getCode());
|
|
1619
|
-
appCache.setBaseAppStyle(...item);
|
|
1620
|
-
});
|
|
1621
|
-
await Promise.all(promiseList).catch((e) => {
|
|
1622
|
-
throw e;
|
|
1623
|
-
});
|
|
1624
|
-
}
|
|
2089
|
+
}
|
|
2090
|
+
if (collectStyle) {
|
|
2091
|
+
for (const [url, style] of collectStyle.entries()) {
|
|
2092
|
+
loadPromises.push(style.getCode());
|
|
2093
|
+
appCache.setBaseAppStyle(url, style);
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
await Promise.all(loadPromises);
|
|
2097
|
+
};
|
|
1625
2098
|
|
|
1626
2099
|
// src/entry/entry.ts
|
|
1627
2100
|
var SCRIPT_TYPE_NAMES = [
|
|
@@ -1631,6 +2104,12 @@ var SCRIPT_TYPE_NAMES = [
|
|
|
1631
2104
|
"application/ecmascript",
|
|
1632
2105
|
"module"
|
|
1633
2106
|
];
|
|
2107
|
+
var ICON_REL_TYPES = ["apple-touch-icon", "icon", "prefetch", "preload", "prerender"];
|
|
2108
|
+
var HTML_FILTER_REGEX = {
|
|
2109
|
+
HEAD: /<\/?head>/gim,
|
|
2110
|
+
BODY: /<\/?body>/i,
|
|
2111
|
+
TS_EXTENSION: /\.ts$/
|
|
2112
|
+
};
|
|
1634
2113
|
var EntrySource = class {
|
|
1635
2114
|
constructor(url) {
|
|
1636
2115
|
this.url = url;
|
|
@@ -1641,101 +2120,211 @@ var EntrySource = class {
|
|
|
1641
2120
|
rawHtml;
|
|
1642
2121
|
scripts;
|
|
1643
2122
|
styles;
|
|
1644
|
-
|
|
2123
|
+
/** 收集链接元素 */
|
|
2124
|
+
collectLink = (link, parent, needReplaceElement = false) => {
|
|
1645
2125
|
if (link.hasAttribute("exclude")) {
|
|
1646
|
-
return {
|
|
2126
|
+
return {
|
|
2127
|
+
replace: document.createComment("\u3010bk-weweb\u3011style with exclude attribute is ignored")
|
|
2128
|
+
};
|
|
1647
2129
|
}
|
|
1648
2130
|
if (link.hasAttribute("ignore")) {
|
|
1649
2131
|
return { replace: link };
|
|
1650
2132
|
}
|
|
1651
2133
|
const rel = link.getAttribute("rel");
|
|
1652
|
-
|
|
1653
|
-
let replaceElement;
|
|
2134
|
+
const href = link.getAttribute("href");
|
|
1654
2135
|
if (rel === "stylesheet" && href) {
|
|
1655
|
-
|
|
1656
|
-
replaceElement = document.createComment(`\u3010bk-weweb\u3011style with href=${href}`);
|
|
1657
|
-
let styleInstance = this.getStyle(href);
|
|
1658
|
-
if (!styleInstance) {
|
|
1659
|
-
styleInstance = new Style({
|
|
1660
|
-
code: "",
|
|
1661
|
-
fromHtml: !needReplaceELement,
|
|
1662
|
-
prefetch: !!link.getAttribute("prefetch"),
|
|
1663
|
-
preload: !!link.getAttribute("preload"),
|
|
1664
|
-
url: href
|
|
1665
|
-
});
|
|
1666
|
-
this.styles.set(href, styleInstance);
|
|
1667
|
-
}
|
|
1668
|
-
!needReplaceELement && parent.replaceChild(replaceElement, link);
|
|
1669
|
-
return { replace: replaceElement, style: styleInstance };
|
|
2136
|
+
return this.handleStylesheetLink(link, parent, href, needReplaceElement);
|
|
1670
2137
|
}
|
|
1671
|
-
if (rel &&
|
|
1672
|
-
|
|
1673
|
-
!needReplaceELement && parent.removeChild(link);
|
|
1674
|
-
return { replace: replaceElement };
|
|
2138
|
+
if (rel && ICON_REL_TYPES.includes(rel)) {
|
|
2139
|
+
return this.handleIconLink(link, parent, rel, href, needReplaceElement);
|
|
1675
2140
|
}
|
|
1676
2141
|
if (href) {
|
|
1677
2142
|
link.setAttribute("href", fillUpPath(href, this.url));
|
|
1678
2143
|
}
|
|
1679
2144
|
return { replace: link };
|
|
2145
|
+
};
|
|
2146
|
+
/** 收集脚本元素 */
|
|
2147
|
+
collectScript = (script, parent, needReplaceElement = false) => {
|
|
2148
|
+
if (this.shouldIgnoreScript(script)) {
|
|
2149
|
+
return;
|
|
2150
|
+
}
|
|
2151
|
+
if (script.hasAttribute("exclude")) {
|
|
2152
|
+
return this.handleExcludedScript(script, parent, needReplaceElement);
|
|
2153
|
+
}
|
|
2154
|
+
const src = script.getAttribute("src");
|
|
2155
|
+
if (src) {
|
|
2156
|
+
return this.handleExternalScript(script, parent, src, needReplaceElement);
|
|
2157
|
+
}
|
|
2158
|
+
if (script.textContent) {
|
|
2159
|
+
return this.handleInlineScript(script, parent, needReplaceElement);
|
|
2160
|
+
}
|
|
2161
|
+
return { replace: script };
|
|
2162
|
+
};
|
|
2163
|
+
/** 收集样式和脚本 */
|
|
2164
|
+
collectScriptAndStyle = (parent) => {
|
|
2165
|
+
this.processLinks(parent);
|
|
2166
|
+
this.processStyles(parent);
|
|
2167
|
+
this.processScripts(parent);
|
|
2168
|
+
this.processMetas(parent);
|
|
2169
|
+
this.processImages(parent);
|
|
2170
|
+
};
|
|
2171
|
+
getScript = (url) => this.scripts.get(url);
|
|
2172
|
+
getStyle = (urlOrCode) => {
|
|
2173
|
+
return this.styles.get(urlOrCode) || Array.from(this.styles.values()).find((style) => style.code === urlOrCode);
|
|
2174
|
+
};
|
|
2175
|
+
/** html entry */
|
|
2176
|
+
async importEntry(app) {
|
|
2177
|
+
await this.loadInitialSources(app);
|
|
2178
|
+
if (app instanceof MicroAppModel) {
|
|
2179
|
+
await this.importHtmlEntry(app);
|
|
2180
|
+
} else if (app instanceof MicroInstanceModel) {
|
|
2181
|
+
await this.importInstanceEntry(app);
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
/** 微应用入口 */
|
|
2185
|
+
async importHtmlEntry(app) {
|
|
2186
|
+
const htmlStr = await this.fetchHtmlContent(app);
|
|
2187
|
+
if (!htmlStr) {
|
|
2188
|
+
console.error("load app entry error, place check");
|
|
2189
|
+
return Promise.reject();
|
|
2190
|
+
}
|
|
2191
|
+
this.rawHtml = htmlStr;
|
|
2192
|
+
const wrapElement = this.createWrapElement(htmlStr);
|
|
2193
|
+
this.collectScriptAndStyle(wrapElement);
|
|
2194
|
+
await executeAppStyles(app, wrapElement);
|
|
2195
|
+
this.html = wrapElement;
|
|
2196
|
+
}
|
|
2197
|
+
/** 微模块入口 */
|
|
2198
|
+
async importInstanceEntry(app) {
|
|
2199
|
+
const jsStr = await this.fetchJsContent(app);
|
|
2200
|
+
if (!jsStr) {
|
|
2201
|
+
console.error("load app entry error, place check");
|
|
2202
|
+
return Promise.reject();
|
|
2203
|
+
}
|
|
2204
|
+
this.scripts.set(
|
|
2205
|
+
this.url,
|
|
2206
|
+
new Script({
|
|
2207
|
+
async: false,
|
|
2208
|
+
code: jsStr,
|
|
2209
|
+
defer: false,
|
|
2210
|
+
fromHtml: true,
|
|
2211
|
+
isModule: HTML_FILTER_REGEX.TS_EXTENSION.test(this.url),
|
|
2212
|
+
url: this.url
|
|
2213
|
+
})
|
|
2214
|
+
);
|
|
1680
2215
|
}
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
2216
|
+
setScript = (url, script) => {
|
|
2217
|
+
this.scripts.set(url, script instanceof Script ? script : new Script(script));
|
|
2218
|
+
};
|
|
2219
|
+
setStyle = (url, style) => {
|
|
2220
|
+
this.styles.set(url, style);
|
|
2221
|
+
};
|
|
2222
|
+
/** 处理样式表链接 */
|
|
2223
|
+
handleStylesheetLink = (link, parent, href, needReplaceElement) => {
|
|
2224
|
+
const fullHref = fillUpPath(href, this.url);
|
|
2225
|
+
const replaceElement = document.createComment(`\u3010bk-weweb\u3011style with href=${fullHref}`);
|
|
2226
|
+
let styleInstance = this.getStyle(fullHref);
|
|
2227
|
+
if (!styleInstance) {
|
|
2228
|
+
styleInstance = new Style({
|
|
2229
|
+
code: "",
|
|
2230
|
+
fromHtml: !needReplaceElement,
|
|
2231
|
+
prefetch: !!link.getAttribute("prefetch"),
|
|
2232
|
+
preload: !!link.getAttribute("preload"),
|
|
2233
|
+
url: fullHref
|
|
2234
|
+
});
|
|
2235
|
+
this.styles.set(fullHref, styleInstance);
|
|
1684
2236
|
}
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
replaceElement = document.createComment("\u3010bk-weweb\u3011script element with exclude attribute is removed");
|
|
1688
|
-
!needReplaceELement && parent.replaceChild(replaceElement, script);
|
|
1689
|
-
return { replace: replaceElement };
|
|
2237
|
+
if (!needReplaceElement) {
|
|
2238
|
+
parent.replaceChild(replaceElement, link);
|
|
1690
2239
|
}
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
fromHtml: !needReplaceELement,
|
|
1701
|
-
isModule: script.type === "module",
|
|
1702
|
-
url: src
|
|
1703
|
-
});
|
|
1704
|
-
this.scripts.set(src, scriptInstance);
|
|
1705
|
-
}
|
|
1706
|
-
replaceElement = document.createComment(`\u3010bk-weweb\u3011script with src='${src}'`);
|
|
1707
|
-
!needReplaceELement && parent.replaceChild(replaceElement, script);
|
|
1708
|
-
return { replace: replaceElement, script: scriptInstance };
|
|
2240
|
+
return { replace: replaceElement, style: styleInstance };
|
|
2241
|
+
};
|
|
2242
|
+
/**
|
|
2243
|
+
* 处理图标链接
|
|
2244
|
+
*/
|
|
2245
|
+
handleIconLink = (link, parent, rel, href, needReplaceElement) => {
|
|
2246
|
+
const replaceElement = document.createComment(`\u3010bk-weweb\u3011style with rel=${rel}${href ? ` & href=${href}` : ""}`);
|
|
2247
|
+
if (!needReplaceElement) {
|
|
2248
|
+
parent.removeChild(link);
|
|
1709
2249
|
}
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
2250
|
+
return { replace: replaceElement };
|
|
2251
|
+
};
|
|
2252
|
+
/**
|
|
2253
|
+
* 检查是否应该忽略脚本
|
|
2254
|
+
*/
|
|
2255
|
+
shouldIgnoreScript = (script) => {
|
|
2256
|
+
return script.hasAttribute("ignore") || // (script.type !== 'module' && isJsonpUrl(script.getAttribute('src'))) ||
|
|
2257
|
+
script.hasAttribute("type") && !SCRIPT_TYPE_NAMES.includes(script.type);
|
|
2258
|
+
};
|
|
2259
|
+
/**
|
|
2260
|
+
* 处理被排除的脚本
|
|
2261
|
+
*/
|
|
2262
|
+
handleExcludedScript = (script, parent, needReplaceElement) => {
|
|
2263
|
+
const replaceElement = document.createComment("\u3010bk-weweb\u3011script element with exclude attribute is removed");
|
|
2264
|
+
if (!needReplaceElement) {
|
|
2265
|
+
parent.replaceChild(replaceElement, script);
|
|
2266
|
+
}
|
|
2267
|
+
return { replace: replaceElement };
|
|
2268
|
+
};
|
|
2269
|
+
/**
|
|
2270
|
+
* 处理外部脚本
|
|
2271
|
+
*/
|
|
2272
|
+
handleExternalScript = (script, parent, src, needReplaceElement) => {
|
|
2273
|
+
const fullSrc = fillUpPath(src, this.url);
|
|
2274
|
+
let scriptInstance = this.getScript(fullSrc);
|
|
2275
|
+
if (!scriptInstance) {
|
|
2276
|
+
scriptInstance = new Script({
|
|
2277
|
+
async: script.hasAttribute("async"),
|
|
2278
|
+
code: "",
|
|
2279
|
+
defer: script.defer || script.type === "module",
|
|
2280
|
+
fromHtml: !needReplaceElement,
|
|
1719
2281
|
isModule: script.type === "module",
|
|
1720
|
-
url:
|
|
2282
|
+
url: fullSrc
|
|
1721
2283
|
});
|
|
1722
|
-
this.scripts.set(
|
|
1723
|
-
replaceElement = document.createComment("\u3010bk-weweb\u3011script with text content");
|
|
1724
|
-
!needReplaceELement && parent.replaceChild(replaceElement, script);
|
|
1725
|
-
return {
|
|
1726
|
-
replace: replaceElement,
|
|
1727
|
-
script: scriptInstance
|
|
1728
|
-
};
|
|
2284
|
+
this.scripts.set(fullSrc, scriptInstance);
|
|
1729
2285
|
}
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
2286
|
+
const replaceElement = document.createComment(`\u3010bk-weweb\u3011script with src='${fullSrc}'`);
|
|
2287
|
+
if (!needReplaceElement) {
|
|
2288
|
+
parent.replaceChild(replaceElement, script);
|
|
2289
|
+
}
|
|
2290
|
+
return { replace: replaceElement, script: scriptInstance };
|
|
2291
|
+
};
|
|
2292
|
+
/**
|
|
2293
|
+
* 处理内联脚本
|
|
2294
|
+
*/
|
|
2295
|
+
handleInlineScript = (script, parent, needReplaceElement) => {
|
|
2296
|
+
const nonceStr = randomUrl();
|
|
2297
|
+
const scriptInstance = new Script({
|
|
2298
|
+
async: false,
|
|
2299
|
+
code: script.textContent || "",
|
|
2300
|
+
defer: script.type === "module",
|
|
2301
|
+
fromHtml: !needReplaceElement,
|
|
2302
|
+
initial: false,
|
|
2303
|
+
isModule: script.type === "module",
|
|
2304
|
+
url: nonceStr
|
|
2305
|
+
});
|
|
2306
|
+
this.scripts.set(nonceStr, scriptInstance);
|
|
2307
|
+
const replaceElement = document.createComment("\u3010bk-weweb\u3011script with text content");
|
|
2308
|
+
if (!needReplaceElement) {
|
|
2309
|
+
parent.replaceChild(replaceElement, script);
|
|
2310
|
+
}
|
|
2311
|
+
return { replace: replaceElement, script: scriptInstance };
|
|
2312
|
+
};
|
|
2313
|
+
/**
|
|
2314
|
+
* 处理链接元素
|
|
2315
|
+
*/
|
|
2316
|
+
processLinks = (parent) => {
|
|
1733
2317
|
const links = Array.from(parent.querySelectorAll("link"));
|
|
1734
|
-
for (const link of links
|
|
1735
|
-
this.collectLink(link, link.parentElement);
|
|
2318
|
+
for (const link of links) {
|
|
2319
|
+
link.parentElement && this.collectLink(link, link.parentElement);
|
|
1736
2320
|
}
|
|
2321
|
+
};
|
|
2322
|
+
/**
|
|
2323
|
+
* 处理样式元素
|
|
2324
|
+
*/
|
|
2325
|
+
processStyles = (parent) => {
|
|
1737
2326
|
const styles = Array.from(parent.querySelectorAll("style"));
|
|
1738
|
-
for (const style of styles
|
|
2327
|
+
for (const style of styles) {
|
|
1739
2328
|
if (!style.hasAttribute("exclude") && !style.hasAttribute("ignore")) {
|
|
1740
2329
|
this.styles.set(
|
|
1741
2330
|
randomUrl(),
|
|
@@ -1748,121 +2337,156 @@ var EntrySource = class {
|
|
|
1748
2337
|
style.remove();
|
|
1749
2338
|
}
|
|
1750
2339
|
}
|
|
2340
|
+
};
|
|
2341
|
+
/**
|
|
2342
|
+
* 处理脚本元素
|
|
2343
|
+
*/
|
|
2344
|
+
processScripts = (parent) => {
|
|
1751
2345
|
const scripts = Array.from(parent.querySelectorAll("script"));
|
|
1752
2346
|
for (const script of scripts) {
|
|
1753
|
-
this.collectScript(script, script.parentElement);
|
|
2347
|
+
script.parentElement && this.collectScript(script, script.parentElement);
|
|
1754
2348
|
}
|
|
2349
|
+
};
|
|
2350
|
+
/**
|
|
2351
|
+
* 处理Meta元素
|
|
2352
|
+
*/
|
|
2353
|
+
processMetas = (parent) => {
|
|
1755
2354
|
const metas = Array.from(parent.querySelectorAll("meta"));
|
|
1756
2355
|
for (const meta of metas) {
|
|
1757
|
-
meta.parentElement
|
|
2356
|
+
meta.parentElement?.removeChild(meta);
|
|
1758
2357
|
}
|
|
2358
|
+
};
|
|
2359
|
+
/**
|
|
2360
|
+
* 处理图片元素
|
|
2361
|
+
*/
|
|
2362
|
+
processImages = (parent) => {
|
|
1759
2363
|
const imgs = Array.from(parent.querySelectorAll("img"));
|
|
1760
2364
|
for (const img of imgs) {
|
|
1761
2365
|
if (img.hasAttribute("src")) {
|
|
1762
|
-
img.setAttribute("src", fillUpPath(img.getAttribute("src"), this.url));
|
|
2366
|
+
img.setAttribute("src", fillUpPath(img.getAttribute("src") || "", this.url));
|
|
1763
2367
|
}
|
|
1764
2368
|
}
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
}
|
|
1778
|
-
if (collectStyle) {
|
|
1779
|
-
this.styles = collectStyle;
|
|
1780
|
-
}
|
|
2369
|
+
};
|
|
2370
|
+
/**
|
|
2371
|
+
* 加载初始资源
|
|
2372
|
+
*/
|
|
2373
|
+
async loadInitialSources(app) {
|
|
2374
|
+
if (!app.initSource?.length) return;
|
|
2375
|
+
const { collectScript, collectStyle } = await collectSource(app.initSource);
|
|
2376
|
+
if (collectScript) {
|
|
2377
|
+
this.scripts = collectScript;
|
|
2378
|
+
}
|
|
2379
|
+
if (collectStyle) {
|
|
2380
|
+
this.styles = collectStyle;
|
|
1781
2381
|
}
|
|
1782
|
-
if (app instanceof MicroAppModel) await this.importHtmlEntry(app);
|
|
1783
|
-
else if (app instanceof MicroInstanceModel) await this.importInstanceEntry(app);
|
|
1784
2382
|
}
|
|
1785
|
-
|
|
2383
|
+
/**
|
|
2384
|
+
* 获取HTML content
|
|
2385
|
+
*/
|
|
2386
|
+
async fetchHtmlContent(app) {
|
|
1786
2387
|
let htmlStr = appCache.getCacheHtml(this.url);
|
|
1787
2388
|
if (!htmlStr) {
|
|
1788
2389
|
htmlStr = await fetchSource(addUrlProtocol(this.url), { cache: "no-cache" }, app);
|
|
1789
|
-
if (!htmlStr) {
|
|
1790
|
-
console.error("load app entry error, place check");
|
|
1791
|
-
return Promise.reject();
|
|
1792
|
-
}
|
|
1793
2390
|
}
|
|
1794
|
-
|
|
2391
|
+
return htmlStr;
|
|
2392
|
+
}
|
|
2393
|
+
/**
|
|
2394
|
+
* 创建顶层 root 元素
|
|
2395
|
+
*/
|
|
2396
|
+
createWrapElement(htmlStr) {
|
|
1795
2397
|
const wrapElement = document.createElement("div");
|
|
1796
2398
|
if (wrapElement.__BK_WEWEB_APP_KEY__) {
|
|
1797
2399
|
wrapElement.__BK_WEWEB_APP_KEY__ = void 0;
|
|
1798
2400
|
}
|
|
1799
|
-
wrapElement.innerHTML = htmlStr.replace(
|
|
1800
|
-
|
|
1801
|
-
await executeAppStyles(app, wrapElement);
|
|
1802
|
-
this.html = wrapElement;
|
|
2401
|
+
wrapElement.innerHTML = htmlStr.replace(HTML_FILTER_REGEX.HEAD, "").replace(HTML_FILTER_REGEX.BODY, "");
|
|
2402
|
+
return wrapElement;
|
|
1803
2403
|
}
|
|
1804
|
-
|
|
2404
|
+
/**
|
|
2405
|
+
* 获取JS content
|
|
2406
|
+
*/
|
|
2407
|
+
async fetchJsContent(app) {
|
|
1805
2408
|
let jsStr = appCache.getCacheScript(this.url)?.code;
|
|
1806
2409
|
if (!jsStr) {
|
|
1807
2410
|
jsStr = await fetchSource(this.url, { cache: "no-cache" }, app);
|
|
1808
2411
|
}
|
|
1809
|
-
|
|
1810
|
-
console.error("load app entry error, place check");
|
|
1811
|
-
return Promise.reject();
|
|
1812
|
-
}
|
|
1813
|
-
this.scripts.set(
|
|
1814
|
-
this.url,
|
|
1815
|
-
new Script({
|
|
1816
|
-
async: false,
|
|
1817
|
-
code: jsStr,
|
|
1818
|
-
defer: false,
|
|
1819
|
-
fromHtml: true,
|
|
1820
|
-
isModule: !!this.url.match(/\.ts$/),
|
|
1821
|
-
url: this.url
|
|
1822
|
-
})
|
|
1823
|
-
);
|
|
1824
|
-
}
|
|
1825
|
-
setScript(url, script) {
|
|
1826
|
-
this.scripts.set(url, script instanceof Script ? script : new Script(script));
|
|
1827
|
-
}
|
|
1828
|
-
setStyle(url, style) {
|
|
1829
|
-
this.styles.set(url, style);
|
|
2412
|
+
return jsStr;
|
|
1830
2413
|
}
|
|
1831
2414
|
};
|
|
1832
2415
|
|
|
1833
2416
|
// src/cache/app-cache.ts
|
|
1834
2417
|
var AppCache = class {
|
|
1835
|
-
|
|
2418
|
+
/** 基础资源源,用于主应用共享资源包 */
|
|
1836
2419
|
baseSource;
|
|
2420
|
+
/** 应用实例缓存映射表 */
|
|
1837
2421
|
cache;
|
|
2422
|
+
/**
|
|
2423
|
+
* 构造函数
|
|
2424
|
+
* @description 初始化应用缓存管理器
|
|
2425
|
+
*/
|
|
1838
2426
|
constructor() {
|
|
1839
2427
|
this.cache = /* @__PURE__ */ new Map();
|
|
1840
2428
|
this.baseSource = new EntrySource(location.href);
|
|
1841
2429
|
}
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
2430
|
+
/**
|
|
2431
|
+
* 设置应用实例到缓存
|
|
2432
|
+
* @description 将应用实例添加到缓存中,使用应用的缓存键作为标识
|
|
2433
|
+
* @param app - 要缓存的应用实例
|
|
2434
|
+
*/
|
|
2435
|
+
setApp(app) {
|
|
2436
|
+
this.cache.set(app.appCacheKey, app);
|
|
1845
2437
|
}
|
|
1846
|
-
|
|
2438
|
+
/**
|
|
2439
|
+
* 获取缓存的应用实例
|
|
2440
|
+
* @description 根据名称或ID获取已缓存的应用实例
|
|
2441
|
+
* @param name - 应用名称或ID,为空时返回 undefined
|
|
2442
|
+
* @returns BaseModel | undefined - 应用实例或 undefined
|
|
2443
|
+
*/
|
|
1847
2444
|
getApp(name) {
|
|
1848
2445
|
if (!name) return void 0;
|
|
1849
2446
|
const app = this.cache.get(name);
|
|
1850
2447
|
if (app) return app;
|
|
1851
|
-
|
|
2448
|
+
const item = Array.from(this.cache.values()).find((item2) => item2.name === name);
|
|
2449
|
+
if (item) return item;
|
|
2450
|
+
return window.__BK_WEWEB_APP_KEY__ ? window.__getAppOrInstance__()?.getApp(name) : void 0;
|
|
1852
2451
|
}
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
2452
|
+
/**
|
|
2453
|
+
* 删除缓存的应用实例
|
|
2454
|
+
* @description 从缓存中移除指定URL的应用实例
|
|
2455
|
+
* @param url - 要删除的应用URL标识
|
|
2456
|
+
*/
|
|
2457
|
+
deleteApp(url) {
|
|
2458
|
+
this.cache.delete(url);
|
|
1856
2459
|
}
|
|
2460
|
+
/**
|
|
2461
|
+
* 获取缓存的HTML内容
|
|
2462
|
+
* @description 根据URL获取已缓存的HTML内容
|
|
2463
|
+
* @param url - 应用的URL
|
|
2464
|
+
* @returns string - HTML内容,未找到时返回空字符串
|
|
2465
|
+
*/
|
|
1857
2466
|
getCacheHtml(url) {
|
|
1858
2467
|
const list = Array.from(this.cache.values());
|
|
1859
2468
|
const app = list.find((item) => item.url === url);
|
|
1860
2469
|
if (app) return app.source?.rawHtml || "";
|
|
1861
2470
|
return "";
|
|
1862
2471
|
}
|
|
2472
|
+
/**
|
|
2473
|
+
* 设置基础应用脚本
|
|
2474
|
+
* @description 将脚本添加到基础资源源中,供多个应用共享
|
|
2475
|
+
* @param url - 脚本的URL
|
|
2476
|
+
* @param script - 脚本实例
|
|
2477
|
+
*/
|
|
2478
|
+
setBaseAppScript(url, script) {
|
|
2479
|
+
this.baseSource.setScript(url, script);
|
|
2480
|
+
}
|
|
2481
|
+
/**
|
|
2482
|
+
* 获取缓存的脚本资源
|
|
2483
|
+
* @description 从基础资源源或应用缓存中获取脚本资源
|
|
2484
|
+
* @param url - 脚本的URL
|
|
2485
|
+
* @returns Script | undefined - 脚本实例或 undefined
|
|
2486
|
+
*/
|
|
1863
2487
|
getCacheScript(url) {
|
|
1864
2488
|
let script = this.baseSource.getScript(url);
|
|
1865
|
-
if (script) return;
|
|
2489
|
+
if (script) return script;
|
|
1866
2490
|
const list = Array.from(this.cache.values());
|
|
1867
2491
|
list.some((app) => {
|
|
1868
2492
|
script = app.source?.getScript(url);
|
|
@@ -1870,9 +2494,33 @@ var AppCache = class {
|
|
|
1870
2494
|
});
|
|
1871
2495
|
return script;
|
|
1872
2496
|
}
|
|
2497
|
+
/**
|
|
2498
|
+
* 设置基础应用样式
|
|
2499
|
+
* @description 将样式添加到基础资源源中,供多个应用共享
|
|
2500
|
+
* @param url - 样式的URL
|
|
2501
|
+
* @param style - 样式实例
|
|
2502
|
+
*/
|
|
2503
|
+
setBaseAppStyle(url, style) {
|
|
2504
|
+
this.baseSource.setStyle(url, style);
|
|
2505
|
+
}
|
|
2506
|
+
/**
|
|
2507
|
+
* 获取基础应用样式
|
|
2508
|
+
* @description 从基础资源源中获取样式资源
|
|
2509
|
+
* @param urlOrCode - 样式的URL或代码
|
|
2510
|
+
* @returns Style | undefined - 样式实例或 undefined
|
|
2511
|
+
*/
|
|
2512
|
+
getBaseAppStyle(urlOrCode) {
|
|
2513
|
+
return this.baseSource.getStyle(urlOrCode);
|
|
2514
|
+
}
|
|
2515
|
+
/**
|
|
2516
|
+
* 获取缓存的样式资源
|
|
2517
|
+
* @description 从基础资源源或应用缓存中获取样式资源
|
|
2518
|
+
* @param url - 样式的URL
|
|
2519
|
+
* @returns Style | undefined - 样式实例或 undefined
|
|
2520
|
+
*/
|
|
1873
2521
|
getCacheStyle(url) {
|
|
1874
2522
|
let style = this.baseSource.getStyle(url);
|
|
1875
|
-
if (style) return;
|
|
2523
|
+
if (style) return style;
|
|
1876
2524
|
const list = Array.from(this.cache.values());
|
|
1877
2525
|
list.some((app) => {
|
|
1878
2526
|
style = app.source?.getStyle(url);
|
|
@@ -1880,17 +2528,13 @@ var AppCache = class {
|
|
|
1880
2528
|
});
|
|
1881
2529
|
return style;
|
|
1882
2530
|
}
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
}
|
|
1889
|
-
setBaseAppStyle(url, style) {
|
|
1890
|
-
this.baseSource.setStyle(url, style);
|
|
1891
|
-
}
|
|
2531
|
+
/**
|
|
2532
|
+
* 检查是否存在活跃的应用
|
|
2533
|
+
* @description 判断当前是否有处于非卸载状态的应用实例
|
|
2534
|
+
* @returns boolean - 存在活跃应用时返回 true
|
|
2535
|
+
*/
|
|
1892
2536
|
get hasActiveApp() {
|
|
1893
|
-
return Array.from(this.cache.values()).some((app) => app.status !==
|
|
2537
|
+
return Array.from(this.cache.values()).some((app) => app.status !== AppState.UNMOUNT);
|
|
1894
2538
|
}
|
|
1895
2539
|
};
|
|
1896
2540
|
var appCache = new AppCache();
|
|
@@ -1908,8 +2552,8 @@ function getStyleSource(url, style, originLink) {
|
|
|
1908
2552
|
appCache.setBaseAppStyle(url, style);
|
|
1909
2553
|
replaceStyle.textContent = data;
|
|
1910
2554
|
dispatchLinkOrScriptLoad(originLink);
|
|
1911
|
-
}).catch((
|
|
1912
|
-
console.error(
|
|
2555
|
+
}).catch((error) => {
|
|
2556
|
+
console.error("Failed to load style resource:", error);
|
|
1913
2557
|
dispatchLinkOrScriptError(originLink);
|
|
1914
2558
|
});
|
|
1915
2559
|
return replaceStyle;
|
|
@@ -1924,59 +2568,67 @@ function getScriptSource(url, script, originScript) {
|
|
|
1924
2568
|
if (!url.startsWith("inline-")) {
|
|
1925
2569
|
originScript.setAttribute("origin-src", url);
|
|
1926
2570
|
}
|
|
1927
|
-
} catch (
|
|
1928
|
-
console.error(
|
|
2571
|
+
} catch (error) {
|
|
2572
|
+
console.error("Failed to set script content:", error, url);
|
|
1929
2573
|
}
|
|
1930
2574
|
dispatchLinkOrScriptLoad(originScript);
|
|
1931
|
-
}).catch((
|
|
1932
|
-
console.error(
|
|
2575
|
+
}).catch((error) => {
|
|
2576
|
+
console.error("Failed to load script resource:", error);
|
|
1933
2577
|
dispatchLinkOrScriptError(originScript);
|
|
1934
2578
|
});
|
|
1935
2579
|
return replaceScript;
|
|
1936
2580
|
}
|
|
2581
|
+
function processLinkElement(linkElement) {
|
|
2582
|
+
const rel = linkElement.getAttribute("rel");
|
|
2583
|
+
let href = linkElement.getAttribute("href");
|
|
2584
|
+
if (rel === "stylesheet" && href) {
|
|
2585
|
+
href = fillUpPath(href, location.origin);
|
|
2586
|
+
const replaceStyle = document.createElement("style");
|
|
2587
|
+
const styleInstance = new Style({
|
|
2588
|
+
code: "",
|
|
2589
|
+
fromHtml: false,
|
|
2590
|
+
url: href
|
|
2591
|
+
});
|
|
2592
|
+
getStyleSource(href, styleInstance, linkElement);
|
|
2593
|
+
return replaceStyle;
|
|
2594
|
+
}
|
|
2595
|
+
return linkElement;
|
|
2596
|
+
}
|
|
2597
|
+
function processScriptElement(scriptElement) {
|
|
2598
|
+
let src = scriptElement.getAttribute("src");
|
|
2599
|
+
if (src && scriptElement.type !== "module") {
|
|
2600
|
+
src = fillUpPath(src, location.origin);
|
|
2601
|
+
const script = new Script({
|
|
2602
|
+
async: scriptElement.hasAttribute("async"),
|
|
2603
|
+
code: "",
|
|
2604
|
+
defer: scriptElement.defer || scriptElement.type === "module",
|
|
2605
|
+
fromHtml: false,
|
|
2606
|
+
isModule: false
|
|
2607
|
+
});
|
|
2608
|
+
appCache.setBaseAppScript(src, script);
|
|
2609
|
+
const replaceElement = getScriptSource(src, script, scriptElement);
|
|
2610
|
+
return replaceElement || scriptElement;
|
|
2611
|
+
}
|
|
2612
|
+
return scriptElement;
|
|
2613
|
+
}
|
|
1937
2614
|
function createNewNode(child) {
|
|
1938
2615
|
if (child instanceof HTMLLinkElement) {
|
|
1939
|
-
|
|
1940
|
-
let href = child.getAttribute("href");
|
|
1941
|
-
if (rel === "stylesheet" && href) {
|
|
1942
|
-
href = fillUpPath(href, location.origin);
|
|
1943
|
-
const replaceStyle = document.createElement("style");
|
|
1944
|
-
const styleInstance = new Style({
|
|
1945
|
-
code: "",
|
|
1946
|
-
fromHtml: false,
|
|
1947
|
-
url: href
|
|
1948
|
-
});
|
|
1949
|
-
getStyleSource(href, styleInstance, child);
|
|
1950
|
-
return replaceStyle;
|
|
1951
|
-
}
|
|
2616
|
+
return processLinkElement(child);
|
|
1952
2617
|
}
|
|
1953
2618
|
if (child instanceof HTMLScriptElement) {
|
|
1954
|
-
|
|
1955
|
-
if (src && child.type !== "module") {
|
|
1956
|
-
src = fillUpPath(src, location.origin);
|
|
1957
|
-
const script = new Script({
|
|
1958
|
-
async: child.hasAttribute("async"),
|
|
1959
|
-
code: "",
|
|
1960
|
-
defer: child.defer || child.type === "module",
|
|
1961
|
-
fromHtml: false,
|
|
1962
|
-
isModule: false
|
|
1963
|
-
});
|
|
1964
|
-
appCache.setBaseAppScript(src, script);
|
|
1965
|
-
const replaceElement = getScriptSource(src, script, child);
|
|
1966
|
-
return replaceElement || child;
|
|
1967
|
-
}
|
|
2619
|
+
return processScriptElement(child);
|
|
1968
2620
|
}
|
|
1969
2621
|
return child;
|
|
1970
2622
|
}
|
|
1971
2623
|
function isLinkOrScript(node) {
|
|
1972
2624
|
return node instanceof HTMLLinkElement || node instanceof HTMLScriptElement;
|
|
1973
2625
|
}
|
|
1974
|
-
function baseElementInertHandle(parent, newChild,
|
|
2626
|
+
function baseElementInertHandle(parent, newChild, referenceChild, rawMethod) {
|
|
1975
2627
|
if (isLinkOrScript(newChild)) {
|
|
1976
2628
|
const targetChild = createNewNode(newChild);
|
|
1977
|
-
return rawMethod.call(parent, targetChild,
|
|
2629
|
+
return rawMethod.call(parent, targetChild, referenceChild);
|
|
1978
2630
|
}
|
|
1979
|
-
return rawMethod.call(parent, newChild,
|
|
2631
|
+
return rawMethod.call(parent, newChild, referenceChild);
|
|
1980
2632
|
}
|
|
1981
2633
|
function baseElementAppendHandle(parent, newChild, rawMethod) {
|
|
1982
2634
|
if (isLinkOrScript(newChild)) {
|
|
@@ -1987,10 +2639,15 @@ function baseElementAppendHandle(parent, newChild, rawMethod) {
|
|
|
1987
2639
|
}
|
|
1988
2640
|
|
|
1989
2641
|
// src/base-app/collect-source.ts
|
|
1990
|
-
function
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
2642
|
+
function cacheOriginalDOMMethods() {
|
|
2643
|
+
return {
|
|
2644
|
+
rawBodyAppendChild: HTMLBodyElement.prototype.appendChild,
|
|
2645
|
+
rawHeadAppendChild: HTMLHeadElement.prototype.appendChild,
|
|
2646
|
+
rawHeadInsertBefore: HTMLHeadElement.prototype.insertBefore
|
|
2647
|
+
};
|
|
2648
|
+
}
|
|
2649
|
+
function overrideDOMMethods(originalMethods) {
|
|
2650
|
+
const { rawBodyAppendChild, rawHeadAppendChild, rawHeadInsertBefore } = originalMethods;
|
|
1994
2651
|
HTMLBodyElement.prototype.appendChild = function(newChild) {
|
|
1995
2652
|
return baseElementAppendHandle(this, newChild, rawBodyAppendChild);
|
|
1996
2653
|
};
|
|
@@ -2000,22 +2657,39 @@ function collectBaseSource() {
|
|
|
2000
2657
|
HTMLHeadElement.prototype.insertBefore = function(newChild, refChild) {
|
|
2001
2658
|
return baseElementInertHandle(this, newChild, refChild, rawHeadInsertBefore);
|
|
2002
2659
|
};
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2660
|
+
}
|
|
2661
|
+
function collectExistingStyles() {
|
|
2662
|
+
const styleNodes = document.head.querySelectorAll("style");
|
|
2663
|
+
for (const styleNode of Array.from(styleNodes)) {
|
|
2664
|
+
const textContent = styleNode.textContent;
|
|
2665
|
+
if (textContent) {
|
|
2666
|
+
try {
|
|
2667
|
+
const style = new Style({
|
|
2668
|
+
code: textContent,
|
|
2010
2669
|
fromHtml: false,
|
|
2011
2670
|
url: ""
|
|
2012
|
-
})
|
|
2013
|
-
|
|
2671
|
+
});
|
|
2672
|
+
appCache.setBaseAppStyle(randomUrl(), style);
|
|
2673
|
+
} catch (error) {
|
|
2674
|
+
console.warn("Failed to collect style element:", error);
|
|
2675
|
+
}
|
|
2014
2676
|
}
|
|
2015
|
-
}
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
function setupLoadEventListener() {
|
|
2680
|
+
window.addEventListener("load", collectExistingStyles);
|
|
2681
|
+
}
|
|
2682
|
+
function collectBaseSource() {
|
|
2683
|
+
const originalMethods = cacheOriginalDOMMethods();
|
|
2684
|
+
overrideDOMMethods(originalMethods);
|
|
2685
|
+
setupLoadEventListener();
|
|
2016
2686
|
}
|
|
2017
2687
|
|
|
2018
2688
|
// src/context/element.ts
|
|
2689
|
+
var RESOURCE_ATTRIBUTES = ["src", "srcset"];
|
|
2690
|
+
var LINK_ATTRIBUTES = ["href"];
|
|
2691
|
+
var RESOURCE_TAG_NAMES = ["IMG", "SCRIPT"];
|
|
2692
|
+
var LINK_TAG_NAMES = ["LINK"];
|
|
2019
2693
|
var { setAttribute } = Element.prototype;
|
|
2020
2694
|
var {
|
|
2021
2695
|
append,
|
|
@@ -2030,47 +2704,99 @@ var {
|
|
|
2030
2704
|
} = HTMLHeadElement.prototype;
|
|
2031
2705
|
var rawHead = document.head;
|
|
2032
2706
|
var hasRewrite = false;
|
|
2033
|
-
function
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2707
|
+
function shouldProcessResourcePath(key, tagName) {
|
|
2708
|
+
const upperTagName = tagName.toUpperCase();
|
|
2709
|
+
return RESOURCE_ATTRIBUTES.includes(key) && RESOURCE_TAG_NAMES.includes(upperTagName) || LINK_ATTRIBUTES.includes(key) && LINK_TAG_NAMES.includes(upperTagName);
|
|
2710
|
+
}
|
|
2711
|
+
function getElementApp(element) {
|
|
2712
|
+
if (!element.__BK_WEWEB_APP_KEY__) {
|
|
2713
|
+
return null;
|
|
2714
|
+
}
|
|
2715
|
+
return appCache.getApp(element.__BK_WEWEB_APP_KEY__);
|
|
2716
|
+
}
|
|
2717
|
+
function createOverriddenSetAttribute() {
|
|
2718
|
+
return function(key, value) {
|
|
2719
|
+
const tagName = this.tagName.toUpperCase();
|
|
2720
|
+
if (shouldProcessResourcePath(key, tagName) && this.__BK_WEWEB_APP_KEY__) {
|
|
2721
|
+
const app = getElementApp(this);
|
|
2722
|
+
if (app) {
|
|
2723
|
+
setAttribute.call(this, key, fillUpPath(value, app.url));
|
|
2724
|
+
return;
|
|
2725
|
+
}
|
|
2042
2726
|
}
|
|
2727
|
+
setAttribute.call(this, key, value);
|
|
2043
2728
|
};
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2729
|
+
}
|
|
2730
|
+
function handleKeepAliveElement(newChild) {
|
|
2731
|
+
if (newChild.__KEEP_ALIVE__ && isSpecialElement(newChild)) {
|
|
2732
|
+
return headAppendChild.call(rawHead, newChild);
|
|
2733
|
+
}
|
|
2734
|
+
return null;
|
|
2735
|
+
}
|
|
2736
|
+
function createOverriddenAppendChild() {
|
|
2737
|
+
return function(newChild) {
|
|
2738
|
+
const keepAliveResult = handleKeepAliveElement(newChild);
|
|
2739
|
+
if (keepAliveResult) {
|
|
2740
|
+
return keepAliveResult;
|
|
2741
|
+
}
|
|
2742
|
+
const node = elementAppendHandler(this, newChild, bodyAppendChild2);
|
|
2743
|
+
if (node?.tagName === "STYLE") {
|
|
2744
|
+
node.insertAdjacentElement = createOverriddenInsertAdjacentElement();
|
|
2745
|
+
}
|
|
2746
|
+
return node;
|
|
2047
2747
|
};
|
|
2048
|
-
|
|
2748
|
+
}
|
|
2749
|
+
function createOverriddenAppend() {
|
|
2750
|
+
return function(...nodes) {
|
|
2049
2751
|
nodes.forEach((node) => {
|
|
2050
|
-
|
|
2051
|
-
|
|
2752
|
+
const keepAliveResult = handleKeepAliveElement(node);
|
|
2753
|
+
if (keepAliveResult) {
|
|
2754
|
+
return;
|
|
2052
2755
|
}
|
|
2053
2756
|
elementAppendHandler(this, node, bodyAppendChild2);
|
|
2054
2757
|
});
|
|
2055
2758
|
};
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
};
|
|
2060
|
-
HTMLBodyElement.prototype.insertBefore = function(newChild, refChild) {
|
|
2759
|
+
}
|
|
2760
|
+
function createOverriddenInsertBefore() {
|
|
2761
|
+
return function(newChild, refChild) {
|
|
2061
2762
|
return elementInsertHandler(this, newChild, refChild, headInsertBefore);
|
|
2062
2763
|
};
|
|
2063
|
-
|
|
2064
|
-
|
|
2764
|
+
}
|
|
2765
|
+
function createOverriddenRemoveChild() {
|
|
2766
|
+
return function(oldChild) {
|
|
2767
|
+
const app = oldChild.__BK_WEWEB_APP_KEY__ ? appCache.getApp(oldChild.__BK_WEWEB_APP_KEY__) : null;
|
|
2065
2768
|
if (app?.container?.contains(oldChild)) {
|
|
2066
|
-
|
|
2067
|
-
return node;
|
|
2769
|
+
return bodyRemoveChild.call(app.container, oldChild);
|
|
2068
2770
|
}
|
|
2069
2771
|
if (this.contains(oldChild)) {
|
|
2070
2772
|
return bodyRemoveChild.call(this, oldChild);
|
|
2071
2773
|
}
|
|
2072
2774
|
return oldChild;
|
|
2073
2775
|
};
|
|
2776
|
+
}
|
|
2777
|
+
var insertAdjacentElement = createOverriddenInsertAdjacentElement();
|
|
2778
|
+
function createOverriddenInsertAdjacentElement() {
|
|
2779
|
+
return function(_where, element) {
|
|
2780
|
+
const node = elementAppendHandler(this, element, headAppendChild);
|
|
2781
|
+
if (node?.tagName === "STYLE") {
|
|
2782
|
+
node.insertAdjacentElement = insertAdjacentElement;
|
|
2783
|
+
}
|
|
2784
|
+
return node;
|
|
2785
|
+
};
|
|
2786
|
+
}
|
|
2787
|
+
function rewriteBodyAndHeaderMethods() {
|
|
2788
|
+
if (hasRewrite || window.__BK_WEWEB_HAS_REWRITE__) {
|
|
2789
|
+
return;
|
|
2790
|
+
}
|
|
2791
|
+
hasRewrite = true;
|
|
2792
|
+
window.__BK_WEWEB_HAS_REWRITE__ = true;
|
|
2793
|
+
Element.prototype.setAttribute = createOverriddenSetAttribute();
|
|
2794
|
+
HTMLBodyElement.prototype.appendChild = createOverriddenAppendChild();
|
|
2795
|
+
HTMLBodyElement.prototype.append = createOverriddenAppend();
|
|
2796
|
+
HTMLBodyElement.prototype.insertBefore = createOverriddenInsertBefore();
|
|
2797
|
+
HTMLBodyElement.prototype.removeChild = createOverriddenRemoveChild();
|
|
2798
|
+
HTMLHeadElement.prototype.appendChild = HTMLBodyElement.prototype.appendChild;
|
|
2799
|
+
HTMLHeadElement.prototype.insertBefore = createOverriddenInsertBefore();
|
|
2074
2800
|
HTMLHeadElement.prototype.removeChild = HTMLBodyElement.prototype.removeChild;
|
|
2075
2801
|
}
|
|
2076
2802
|
function resetBodyAndHeaderMethods() {
|
|
@@ -2086,31 +2812,31 @@ function resetBodyAndHeaderMethods() {
|
|
|
2086
2812
|
hasRewrite = false;
|
|
2087
2813
|
}
|
|
2088
2814
|
|
|
2089
|
-
// src/
|
|
2815
|
+
// src/lifecycle/before-load.ts
|
|
2090
2816
|
function beforeLoad() {
|
|
2091
2817
|
rewriteBodyAndHeaderMethods();
|
|
2092
2818
|
}
|
|
2093
2819
|
|
|
2094
|
-
// src/
|
|
2820
|
+
// src/lifecycle/activated.ts
|
|
2095
2821
|
function activated(appKey, container, callback) {
|
|
2096
2822
|
const app = appCache.getApp(appKey);
|
|
2097
|
-
if (app?.status ===
|
|
2823
|
+
if (app?.status === AppState.DEACTIVATED && app.keepAlive) {
|
|
2098
2824
|
nextTask(() => {
|
|
2099
2825
|
beforeLoad();
|
|
2100
2826
|
app.activated(container, callback);
|
|
2101
2827
|
});
|
|
2102
|
-
} else {
|
|
2103
|
-
|
|
2828
|
+
} else if (app) {
|
|
2829
|
+
nextTask(() => {
|
|
2104
2830
|
beforeLoad();
|
|
2105
2831
|
app.mount(container, callback);
|
|
2106
2832
|
});
|
|
2107
2833
|
}
|
|
2108
2834
|
}
|
|
2109
2835
|
|
|
2110
|
-
// src/
|
|
2836
|
+
// src/lifecycle/deactivated.ts
|
|
2111
2837
|
function deactivated(appKey) {
|
|
2112
2838
|
const app = appCache.getApp(appKey);
|
|
2113
|
-
if (app && [
|
|
2839
|
+
if (app && [AppState.ACTIVATED, AppState.MOUNTED].some((status) => status === app.status)) {
|
|
2114
2840
|
app.keepAlive ? app.deactivated() : app.unmount();
|
|
2115
2841
|
}
|
|
2116
2842
|
if (!appCache.hasActiveApp) {
|
|
@@ -2118,10 +2844,13 @@ function deactivated(appKey) {
|
|
|
2118
2844
|
}
|
|
2119
2845
|
}
|
|
2120
2846
|
|
|
2121
|
-
// src/
|
|
2847
|
+
// src/lifecycle/load.ts
|
|
2848
|
+
var STATUS_CHECK_INTERVAL = 300;
|
|
2122
2849
|
async function load(props) {
|
|
2123
2850
|
beforeLoad();
|
|
2124
|
-
if (props.mode === "js" /* INSTANCE */)
|
|
2851
|
+
if (props.mode === "js" /* INSTANCE */) {
|
|
2852
|
+
return await loadInstance(props);
|
|
2853
|
+
}
|
|
2125
2854
|
return await loadApp(props);
|
|
2126
2855
|
}
|
|
2127
2856
|
async function loadApp(props) {
|
|
@@ -2130,6 +2859,8 @@ async function loadApp(props) {
|
|
|
2130
2859
|
if (!instance) {
|
|
2131
2860
|
instance = new MicroAppModel(props);
|
|
2132
2861
|
appCache.setApp(instance);
|
|
2862
|
+
} else {
|
|
2863
|
+
instance.data = props.data || instance.data || {};
|
|
2133
2864
|
}
|
|
2134
2865
|
await instance.start();
|
|
2135
2866
|
return instance;
|
|
@@ -2142,23 +2873,26 @@ function loadInstance(props) {
|
|
|
2142
2873
|
instance = new MicroInstanceModel(props);
|
|
2143
2874
|
appCache.setApp(instance);
|
|
2144
2875
|
instance.start().then(() => resolve(instance));
|
|
2145
|
-
|
|
2876
|
+
return;
|
|
2877
|
+
}
|
|
2878
|
+
if (instance.status in [AppState.MOUNTING, AppState.UNSET]) {
|
|
2146
2879
|
const timer = setInterval(() => {
|
|
2147
|
-
if (
|
|
2880
|
+
if (instance.status in [AppState.ERROR, AppState.MOUNTED]) {
|
|
2148
2881
|
resolve(instance);
|
|
2149
2882
|
clearInterval(timer);
|
|
2150
2883
|
}
|
|
2151
|
-
},
|
|
2152
|
-
|
|
2153
|
-
resolve(instance);
|
|
2884
|
+
}, STATUS_CHECK_INTERVAL);
|
|
2885
|
+
return;
|
|
2154
2886
|
}
|
|
2887
|
+
instance.data = props.data || instance.data || {};
|
|
2888
|
+
resolve(instance);
|
|
2155
2889
|
});
|
|
2156
2890
|
}
|
|
2157
2891
|
|
|
2158
|
-
// src/
|
|
2892
|
+
// src/lifecycle/unmount.ts
|
|
2159
2893
|
function unmount(appKey) {
|
|
2160
2894
|
const app = appCache.getApp(appKey);
|
|
2161
|
-
if (app && app.status !==
|
|
2895
|
+
if (app && app.status !== AppState.UNMOUNT) {
|
|
2162
2896
|
app.unmount();
|
|
2163
2897
|
}
|
|
2164
2898
|
if (!appCache.hasActiveApp) {
|
|
@@ -2166,48 +2900,102 @@ function unmount(appKey) {
|
|
|
2166
2900
|
}
|
|
2167
2901
|
}
|
|
2168
2902
|
|
|
2169
|
-
// src/component/web-
|
|
2903
|
+
// src/component/web-component.ts
|
|
2170
2904
|
var BkWewebElement = class extends HTMLElement {
|
|
2905
|
+
/** 应用 URL 缓存 */
|
|
2171
2906
|
appUrl = "";
|
|
2907
|
+
/** 组件连接状态标识 */
|
|
2172
2908
|
connected = false;
|
|
2909
|
+
/**
|
|
2910
|
+
* 观察的属性列表
|
|
2911
|
+
* @description 定义需要监听变化的属性名称列表
|
|
2912
|
+
* @returns string[] - 属性名称数组
|
|
2913
|
+
*/
|
|
2173
2914
|
static get observedAttributes() {
|
|
2174
2915
|
return ["url" /* url */];
|
|
2175
2916
|
}
|
|
2917
|
+
/**
|
|
2918
|
+
* 获取布尔类型属性值
|
|
2919
|
+
* @description 解析布尔类型的自定义属性,支持驼峰和短横线命名
|
|
2920
|
+
* @param name - 属性名称
|
|
2921
|
+
* @returns boolean | undefined - 属性值或 undefined
|
|
2922
|
+
* @private
|
|
2923
|
+
*/
|
|
2176
2924
|
getBooleanAttr(name) {
|
|
2177
|
-
|
|
2925
|
+
const hasAttr = this.hasAttribute(name) || this.hasAttribute(name.replace(/([A-Z])/g, "-$1").toLocaleLowerCase());
|
|
2926
|
+
return hasAttr ? this.getAttribute(name) !== "false" : void 0;
|
|
2178
2927
|
}
|
|
2928
|
+
/**
|
|
2929
|
+
* 处理属性变化的内部逻辑
|
|
2930
|
+
* @description 当属性发生变化时执行的处理逻辑
|
|
2931
|
+
* @returns Promise<void>
|
|
2932
|
+
* @private
|
|
2933
|
+
*/
|
|
2179
2934
|
async handleAttributeChanged() {
|
|
2180
2935
|
if (!this.appKey) return;
|
|
2181
|
-
if (this.
|
|
2936
|
+
if (this.setShadowDomAttr) {
|
|
2182
2937
|
this.attachShadow({ mode: "open" });
|
|
2183
2938
|
}
|
|
2184
2939
|
const app = appCache.getApp(this.appKey);
|
|
2185
|
-
if (app && app.url === this.appUrl && (app.isPreLoad || app.status ===
|
|
2940
|
+
if (app && app.url === this.appUrl && (app.isPreLoad || app.status === AppState.UNMOUNT)) {
|
|
2186
2941
|
activated(this.appKey, this.shadowRoot ?? this);
|
|
2187
2942
|
return;
|
|
2188
2943
|
}
|
|
2189
2944
|
await load(this.appProps);
|
|
2190
2945
|
}
|
|
2946
|
+
/**
|
|
2947
|
+
* 属性变化回调
|
|
2948
|
+
* @description 当观察的属性发生变化时触发
|
|
2949
|
+
* @param attr - 变化的属性名
|
|
2950
|
+
* @param _oldVal - 旧值(未使用)
|
|
2951
|
+
* @param newVal - 新值
|
|
2952
|
+
*/
|
|
2191
2953
|
attributeChangedCallback(attr, _oldVal, newVal) {
|
|
2192
2954
|
if (attr !== "url" /* url */ || this[attr] === newVal || !this.connected) return;
|
|
2193
2955
|
this.appUrl = newVal;
|
|
2194
2956
|
const cacheApp = appCache.getApp(this.appKey);
|
|
2195
|
-
(this.connected || cacheApp)
|
|
2957
|
+
if (this.connected || cacheApp) {
|
|
2958
|
+
this.handleAttributeChanged();
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2961
|
+
/**
|
|
2962
|
+
* 获取是否启用 Shadow DOM 属性
|
|
2963
|
+
* @description 获取是否启用 Shadow DOM 属性 兼容旧版使用
|
|
2964
|
+
* @returns boolean | undefined - 是否启用 Shadow DOM 属性
|
|
2965
|
+
*/
|
|
2966
|
+
get setShadowDomAttr() {
|
|
2967
|
+
return this.getBooleanAttr("setShadowDom" /* setShadowDom */) ?? this.getBooleanAttr("setShodowDom" /* setShodowDom */);
|
|
2196
2968
|
}
|
|
2969
|
+
/**
|
|
2970
|
+
* 组件连接到 DOM 时的回调
|
|
2971
|
+
* @description 当自定义元素被插入到 DOM 时触发
|
|
2972
|
+
* @returns Promise<void>
|
|
2973
|
+
*/
|
|
2197
2974
|
async connectedCallback() {
|
|
2198
|
-
if (this.
|
|
2975
|
+
if (this.setShadowDomAttr && !this.shadowRoot) {
|
|
2199
2976
|
this.attachShadow({ delegatesFocus: false, mode: "open" });
|
|
2200
2977
|
}
|
|
2201
2978
|
await load(this.appProps);
|
|
2202
2979
|
activated(this.appKey, this.shadowRoot ?? this);
|
|
2203
2980
|
this.connected = true;
|
|
2204
2981
|
}
|
|
2982
|
+
/**
|
|
2983
|
+
* 组件从 DOM 断开时的回调
|
|
2984
|
+
* @description 当自定义元素从 DOM 中移除时触发
|
|
2985
|
+
*/
|
|
2205
2986
|
disconnectedCallback() {
|
|
2206
2987
|
this.connected = false;
|
|
2207
2988
|
if (this.appProps.keepAlive) {
|
|
2208
2989
|
deactivated(this.appKey);
|
|
2209
|
-
} else
|
|
2990
|
+
} else {
|
|
2991
|
+
unmount(this.appKey);
|
|
2992
|
+
}
|
|
2210
2993
|
}
|
|
2994
|
+
/**
|
|
2995
|
+
* 获取应用数据
|
|
2996
|
+
* @description 解析 data 属性中的 JSON 数据
|
|
2997
|
+
* @returns Record<string, unknown> - 应用数据对象
|
|
2998
|
+
*/
|
|
2211
2999
|
get appData() {
|
|
2212
3000
|
if (this.hasAttribute("data" /* data */)) {
|
|
2213
3001
|
try {
|
|
@@ -2217,49 +3005,60 @@ var BkWewebElement = class extends HTMLElement {
|
|
|
2217
3005
|
}
|
|
2218
3006
|
return {};
|
|
2219
3007
|
}
|
|
2220
|
-
|
|
3008
|
+
/**
|
|
3009
|
+
* 获取应用标识符
|
|
3010
|
+
* @description 优先使用 id 属性,其次使用 url 属性作为应用标识
|
|
3011
|
+
* 考虑到 JS 模式下需要 JS bundle 的复用性,需用户设置 id 属性
|
|
3012
|
+
* 如果是单实例应用则可以不用配置 id
|
|
3013
|
+
* @returns string | null - 应用标识符或 null
|
|
3014
|
+
*/
|
|
2221
3015
|
get appKey() {
|
|
2222
3016
|
return this.getAttribute("id" /* id */) || this.getAttribute("url" /* url */);
|
|
2223
3017
|
}
|
|
3018
|
+
/**
|
|
3019
|
+
* 获取应用配置属性
|
|
3020
|
+
* @description 根据模式返回相应的应用配置对象
|
|
3021
|
+
* @returns IAppModelProps | IJsModelProps - 应用配置对象
|
|
3022
|
+
*/
|
|
2224
3023
|
get appProps() {
|
|
3024
|
+
const commonProps = {
|
|
3025
|
+
container: this.shadowRoot ?? this,
|
|
3026
|
+
data: this.appData,
|
|
3027
|
+
id: this.appKey,
|
|
3028
|
+
keepAlive: this.getBooleanAttr("keepAlive" /* keepAlive */),
|
|
3029
|
+
showSourceCode: this.getBooleanAttr("showSourceCode" /* showSourceCode */),
|
|
3030
|
+
url: this.getAttribute("url" /* url */)
|
|
3031
|
+
};
|
|
2225
3032
|
if (this.getAttribute("mode") === "js" /* INSTANCE */) {
|
|
2226
3033
|
return {
|
|
2227
|
-
|
|
2228
|
-
data: this.appData,
|
|
2229
|
-
id: this.appKey,
|
|
2230
|
-
keepAlive: this.getBooleanAttr("keepAlive" /* keepAlive */),
|
|
3034
|
+
...commonProps,
|
|
2231
3035
|
mode: "js" /* INSTANCE */,
|
|
2232
|
-
scopeCss: this.getBooleanAttr("scopeCss" /* scopeCss */) && !this.
|
|
2233
|
-
scopeJs: this.getBooleanAttr("scopeJs" /* scopeJs */)
|
|
2234
|
-
showSourceCode: this.getBooleanAttr("showSourceCode" /* showSourceCode */),
|
|
2235
|
-
url: this.getAttribute("url" /* url */)
|
|
3036
|
+
scopeCss: this.getBooleanAttr("scopeCss" /* scopeCss */) && !this.setShadowDomAttr,
|
|
3037
|
+
scopeJs: this.getBooleanAttr("scopeJs" /* scopeJs */)
|
|
2236
3038
|
};
|
|
2237
3039
|
}
|
|
2238
3040
|
return {
|
|
2239
|
-
|
|
2240
|
-
data: this.appData,
|
|
2241
|
-
id: this.appKey,
|
|
2242
|
-
keepAlive: this.getBooleanAttr("keepAlive" /* keepAlive */),
|
|
3041
|
+
...commonProps,
|
|
2243
3042
|
mode: "app" /* APP */,
|
|
2244
|
-
scopeCss: !this.
|
|
3043
|
+
scopeCss: !this.setShadowDomAttr,
|
|
2245
3044
|
scopeJs: !this.getBooleanAttr("scopeJs" /* scopeJs */),
|
|
2246
|
-
scopeLocation: this.getBooleanAttr("scopeLocation" /* scopeLocation */)
|
|
2247
|
-
showSourceCode: this.getBooleanAttr("showSourceCode" /* showSourceCode */),
|
|
2248
|
-
url: this.getAttribute("url" /* url */)
|
|
3045
|
+
scopeLocation: this.getBooleanAttr("scopeLocation" /* scopeLocation */)
|
|
2249
3046
|
};
|
|
2250
3047
|
}
|
|
2251
3048
|
};
|
|
2252
3049
|
|
|
2253
|
-
// src/
|
|
3050
|
+
// src/lifecycle/mount.ts
|
|
2254
3051
|
function mount(appKey, container, callback) {
|
|
2255
3052
|
const app = appCache.getApp(appKey);
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
3053
|
+
if (app) {
|
|
3054
|
+
nextTask(() => {
|
|
3055
|
+
beforeLoad();
|
|
3056
|
+
app.mount(container, callback);
|
|
3057
|
+
});
|
|
3058
|
+
}
|
|
2260
3059
|
}
|
|
2261
3060
|
|
|
2262
|
-
// src/
|
|
3061
|
+
// src/lifecycle/unload.ts
|
|
2263
3062
|
function unload(url) {
|
|
2264
3063
|
appCache.deleteApp(url);
|
|
2265
3064
|
}
|
|
@@ -2291,19 +3090,19 @@ function preLoadSource(sourceList) {
|
|
|
2291
3090
|
var CUSTOM_ELEMENT_TAG = "bk-weweb";
|
|
2292
3091
|
var WeWeb = class {
|
|
2293
3092
|
fetchSource;
|
|
2294
|
-
|
|
3093
|
+
webComponentTag = CUSTOM_ELEMENT_TAG;
|
|
2295
3094
|
constructor() {
|
|
2296
3095
|
if (!window.customElements.get(CUSTOM_ELEMENT_TAG)) {
|
|
2297
3096
|
window.customElements.define(CUSTOM_ELEMENT_TAG, BkWewebElement);
|
|
2298
3097
|
}
|
|
2299
3098
|
}
|
|
2300
|
-
|
|
3099
|
+
/** 设置自定义DOM标签名 */
|
|
2301
3100
|
setWebComponentTag() {
|
|
2302
|
-
if (!window.customElements.get(this.
|
|
2303
|
-
window.customElements.define(this.
|
|
3101
|
+
if (!window.customElements.get(this.webComponentTag)) {
|
|
3102
|
+
window.customElements.define(this.webComponentTag, BkWewebElement);
|
|
2304
3103
|
}
|
|
2305
3104
|
}
|
|
2306
|
-
|
|
3105
|
+
/** 启动WeWeb */
|
|
2307
3106
|
start(option) {
|
|
2308
3107
|
if (option?.collectBaseSource) {
|
|
2309
3108
|
collectBaseSource();
|
|
@@ -2311,7 +3110,7 @@ var WeWeb = class {
|
|
|
2311
3110
|
if (typeof option?.fetchSource === "function") {
|
|
2312
3111
|
this.fetchSource = option.fetchSource;
|
|
2313
3112
|
}
|
|
2314
|
-
this.
|
|
3113
|
+
this.webComponentTag = option?.webComponentTag || CUSTOM_ELEMENT_TAG;
|
|
2315
3114
|
this.setWebComponentTag();
|
|
2316
3115
|
}
|
|
2317
3116
|
};
|