@lark-apaas/coding-preset-vite-react 0.1.1-alpha.0
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.ts +320 -0
- package/dist/index.js +1941 -0
- package/dist/index.js.map +1 -0
- package/package.json +69 -0
- package/src/empty.css +1 -0
- package/src/inspector-stub.js +6 -0
- package/src/module-alias/clsx.mjs +8 -0
- package/src/module-alias/echarts-for-react.mjs +130 -0
- package/src/module-alias/echarts.mjs +43 -0
- package/src/module-alias/registry_echarts_theme.mjs +390 -0
- package/src/overlay/components.js +94 -0
- package/src/overlay/index.js +443 -0
- package/src/overlay/vite-client.js +555 -0
- package/src/polyfills/index.ts +35 -0
- package/src/runtime/ws-watchdog-client.mjs +148 -0
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/**
|
|
3
|
+
* Vite Error Overlay Client
|
|
4
|
+
*
|
|
5
|
+
* 自定义错误 overlay,替换 Vite 原生 overlay,与 rspack-preset 功能对齐。
|
|
6
|
+
*
|
|
7
|
+
* 功能:
|
|
8
|
+
* - 编译错误展示:监听 vite:error 事件,显示编译错误 overlay
|
|
9
|
+
* - 运行时错误展示:监听 window.error 和 unhandledrejection 事件
|
|
10
|
+
* - 错误修复检测:监听 vite:afterUpdate 事件,修复后自动刷新页面
|
|
11
|
+
* - PostMessage 通信:向父窗口发送 PreviewReady、RenderError 等消息
|
|
12
|
+
* - 错误上报:通过 __RUNTIME_LOGGER__ 或 postMessage 上报错误日志
|
|
13
|
+
*
|
|
14
|
+
* 实现原理:
|
|
15
|
+
* - 使用 iframe 隔离样式,避免影响应用样式
|
|
16
|
+
* - 通过 createHotContext 创建 HMR context 监听 Vite 事件
|
|
17
|
+
* - 使用 debounce 防止运行时错误频繁触发渲染
|
|
18
|
+
*
|
|
19
|
+
* 暴露的全局 API (window.__VITE_ERROR_OVERLAY__):
|
|
20
|
+
* - showCompileError(message: string): 显示编译错误
|
|
21
|
+
* - clearCompileError(shouldReload?: boolean): 清除编译错误
|
|
22
|
+
* - showRuntimeErrors(errors: Error[]): 显示运行时错误
|
|
23
|
+
* - clearRuntimeErrors(dismissOverlay?: boolean): 清除运行时错误
|
|
24
|
+
* - handleRuntimeError(error: Error): 处理单个运行时错误
|
|
25
|
+
*
|
|
26
|
+
* @see https://vite.dev/guide/api-hmr.html
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
// 防止重复初始化
|
|
30
|
+
if (!window.__VITE_ERROR_OVERLAY_INITIALIZED__) {
|
|
31
|
+
window.__VITE_ERROR_OVERLAY_INITIALIZED__ = true;
|
|
32
|
+
|
|
33
|
+
/* ===== Overlay Core (iframe) ===== */
|
|
34
|
+
|
|
35
|
+
let iframeRoot = null;
|
|
36
|
+
let rootDocument = null;
|
|
37
|
+
let root = null;
|
|
38
|
+
let scheduledRenderFn = null;
|
|
39
|
+
|
|
40
|
+
// Overlay State
|
|
41
|
+
let currentCompileErrorMessage = '';
|
|
42
|
+
let currentRuntimeErrors = [];
|
|
43
|
+
let currentMode = null;
|
|
44
|
+
|
|
45
|
+
// ===== HMR API State (用于 toolkit 的统一 HMR API) =====
|
|
46
|
+
let hasErrorInCurrentUpdate = false;
|
|
47
|
+
const hmrBeforeApplyCallbacks = new Set();
|
|
48
|
+
const hmrSuccessCallbacks = new Set();
|
|
49
|
+
const hmrErrorCallbacks = new Set();
|
|
50
|
+
|
|
51
|
+
// ===== Components =====
|
|
52
|
+
function ErrorContainer(doc, parentRoot, errorTitle) {
|
|
53
|
+
const container = doc.createElement('div');
|
|
54
|
+
container.className = 'overlay-container';
|
|
55
|
+
|
|
56
|
+
const content = doc.createElement('div');
|
|
57
|
+
content.className = 'overlay-content';
|
|
58
|
+
container.appendChild(content);
|
|
59
|
+
|
|
60
|
+
const img = doc.createElement('img');
|
|
61
|
+
img.src =
|
|
62
|
+
'https://lf3-static.bytednsdoc.com/obj/eden-cn/ylcylz_fsph_ryhs/ljhwZthlaukjlkulzlp/feisuda/template/illustration_empty_negative_error.svg';
|
|
63
|
+
img.alt = 'Error';
|
|
64
|
+
img.width = 100;
|
|
65
|
+
img.height = 100;
|
|
66
|
+
img.className = 'overlay-error-image';
|
|
67
|
+
content.appendChild(img);
|
|
68
|
+
|
|
69
|
+
const title = doc.createElement('p');
|
|
70
|
+
title.className = 'overlay-title';
|
|
71
|
+
title.textContent = errorTitle;
|
|
72
|
+
content.appendChild(title);
|
|
73
|
+
|
|
74
|
+
parentRoot.appendChild(container);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function RootStyle(doc, parentRoot) {
|
|
78
|
+
const style = doc.createElement('style');
|
|
79
|
+
style.id = 'vite-error-overlay-style';
|
|
80
|
+
style.textContent = `
|
|
81
|
+
* {
|
|
82
|
+
margin: 0;
|
|
83
|
+
padding: 0;
|
|
84
|
+
box-sizing: border-box;
|
|
85
|
+
}
|
|
86
|
+
body {
|
|
87
|
+
font-family: "PingFang SC", -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
88
|
+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
89
|
+
sans-serif;
|
|
90
|
+
-webkit-font-smoothing: antialiased;
|
|
91
|
+
-moz-osx-font-smoothing: grayscale;
|
|
92
|
+
}
|
|
93
|
+
.overlay-container {
|
|
94
|
+
min-height: 100vh;
|
|
95
|
+
display: flex;
|
|
96
|
+
align-items: center;
|
|
97
|
+
justify-content: center;
|
|
98
|
+
background-color: #fff;
|
|
99
|
+
}
|
|
100
|
+
.overlay-content {
|
|
101
|
+
display: flex;
|
|
102
|
+
flex-direction: column;
|
|
103
|
+
justify-content: center;
|
|
104
|
+
align-items: center;
|
|
105
|
+
text-align: center;
|
|
106
|
+
}
|
|
107
|
+
.overlay-error-image {
|
|
108
|
+
margin-bottom: 12px;
|
|
109
|
+
width: 100px;
|
|
110
|
+
height: auto;
|
|
111
|
+
}
|
|
112
|
+
.overlay-title {
|
|
113
|
+
color: #1F2329;
|
|
114
|
+
text-align: center;
|
|
115
|
+
font-size: 14px;
|
|
116
|
+
font-style: normal;
|
|
117
|
+
font-weight: 500;
|
|
118
|
+
line-height: 22px;
|
|
119
|
+
}
|
|
120
|
+
`;
|
|
121
|
+
parentRoot.appendChild(style);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ===== PostMessage 通信 (与 rspack 对齐) =====
|
|
125
|
+
function getParentOriginFromParams() {
|
|
126
|
+
try {
|
|
127
|
+
var params = new URLSearchParams(window.location.search);
|
|
128
|
+
var origin = params.get('__parentOrigin');
|
|
129
|
+
if (origin) {
|
|
130
|
+
sessionStorage.setItem('__parentOrigin', origin);
|
|
131
|
+
return origin;
|
|
132
|
+
}
|
|
133
|
+
} catch (e) {
|
|
134
|
+
// ignore
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
return sessionStorage.getItem('__parentOrigin') || '';
|
|
138
|
+
} catch (e) {
|
|
139
|
+
return '';
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function getLegacyParentOrigin() {
|
|
144
|
+
const currentOrigin = window.location?.origin || '';
|
|
145
|
+
if (currentOrigin.includes('force.feishuapp.net')) {
|
|
146
|
+
return 'https://force.feishu.cn';
|
|
147
|
+
}
|
|
148
|
+
if (currentOrigin.includes('force-pre.feishuapp.net')) {
|
|
149
|
+
return 'https://force.feishu-pre.cn';
|
|
150
|
+
}
|
|
151
|
+
if (currentOrigin.includes('force.byted.org')) {
|
|
152
|
+
return 'https://force.feishu-boe.cn';
|
|
153
|
+
}
|
|
154
|
+
if (currentOrigin.includes('feishuapp.cn') || currentOrigin.includes('miaoda.feishuapp.net')) {
|
|
155
|
+
return 'https://miaoda.feishu.cn';
|
|
156
|
+
}
|
|
157
|
+
if (currentOrigin.includes('fsapp.kundou.cn') || currentOrigin.includes('miaoda-pre.feishuapp.net')) {
|
|
158
|
+
return 'https://miaoda.feishu-pre.cn';
|
|
159
|
+
}
|
|
160
|
+
return 'https://miaoda.feishu-boe.cn';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function sendPostMessage(message, targetOrigin) {
|
|
164
|
+
try {
|
|
165
|
+
const parentOrigin = getParentOriginFromParams() || process.env.FORCE_FRAMEWORK_DOMAIN_MAIN || getLegacyParentOrigin();
|
|
166
|
+
const origin = targetOrigin || parentOrigin;
|
|
167
|
+
window.parent.postMessage(message, origin);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
// ignore postMessage errors
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ===== 错误上报 (与 rspack 对齐) =====
|
|
174
|
+
function logError(messages, mode) {
|
|
175
|
+
const logMeta = {
|
|
176
|
+
noStacktrace: mode === 'compileError',
|
|
177
|
+
stacktrace: mode === 'compileError' ? [] : undefined,
|
|
178
|
+
type: mode === 'compileError' ? 'compile-error' : 'uncaught-render-error',
|
|
179
|
+
};
|
|
180
|
+
try {
|
|
181
|
+
// 优先使用暴露的 __RUNTIME_LOGGER__ 上报错误
|
|
182
|
+
if (window.__RUNTIME_LOGGER__) {
|
|
183
|
+
window.__RUNTIME_LOGGER__.get().log({
|
|
184
|
+
level: 'error',
|
|
185
|
+
args: messages,
|
|
186
|
+
meta: logMeta,
|
|
187
|
+
});
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
// 首次执行编译报错会走此处逻辑
|
|
191
|
+
const parts = [];
|
|
192
|
+
for (const m of messages) {
|
|
193
|
+
if (m instanceof Error) {
|
|
194
|
+
parts.push(m.message, m);
|
|
195
|
+
} else {
|
|
196
|
+
parts.push(m);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const logJSON = {
|
|
200
|
+
id: 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
201
|
+
const r = Math.random() * 16 | 0;
|
|
202
|
+
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
203
|
+
return v.toString(16);
|
|
204
|
+
}),
|
|
205
|
+
type: 'typedLogV2',
|
|
206
|
+
level: 'error',
|
|
207
|
+
args: parts,
|
|
208
|
+
meta: logMeta,
|
|
209
|
+
};
|
|
210
|
+
sendPostMessage({ type: 'SELECTED_LOG', payload: JSON.stringify(logJSON) });
|
|
211
|
+
} catch (e) {
|
|
212
|
+
// ignore log errors
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ===== Iframe Management =====
|
|
217
|
+
function IframeRoot(doc, parent, props) {
|
|
218
|
+
const iframe = doc.createElement('iframe');
|
|
219
|
+
iframe.id = 'vite-error-overlay';
|
|
220
|
+
iframe.src = 'about:blank';
|
|
221
|
+
|
|
222
|
+
iframe.style.border = 'none';
|
|
223
|
+
iframe.style.height = '100%';
|
|
224
|
+
iframe.style.left = '0';
|
|
225
|
+
iframe.style.minHeight = '100vh';
|
|
226
|
+
iframe.style.minHeight = '-webkit-fill-available';
|
|
227
|
+
iframe.style.position = 'fixed';
|
|
228
|
+
iframe.style.top = '0';
|
|
229
|
+
iframe.style.width = '100vw';
|
|
230
|
+
iframe.style.zIndex = '2147483647';
|
|
231
|
+
|
|
232
|
+
iframe.addEventListener('load', function onLoad() {
|
|
233
|
+
try {
|
|
234
|
+
iframe.contentDocument.body.style.margin = '0';
|
|
235
|
+
props.onIframeLoad();
|
|
236
|
+
} catch (error) {
|
|
237
|
+
// ignore
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
return iframe;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function OverlayRoot(doc, parent) {
|
|
245
|
+
const div = doc.createElement('div');
|
|
246
|
+
div.id = 'container';
|
|
247
|
+
parent.appendChild(div);
|
|
248
|
+
return div;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function ensureRootExists(renderFn) {
|
|
252
|
+
if (root) {
|
|
253
|
+
renderFn();
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
scheduledRenderFn = renderFn;
|
|
258
|
+
|
|
259
|
+
if (iframeRoot) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
iframeRoot = IframeRoot(document, document.body, {
|
|
264
|
+
onIframeLoad: function onIframeLoad() {
|
|
265
|
+
rootDocument = iframeRoot.contentDocument;
|
|
266
|
+
RootStyle(rootDocument, rootDocument.head);
|
|
267
|
+
root = OverlayRoot(rootDocument, rootDocument.body);
|
|
268
|
+
scheduledRenderFn();
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
document.body.appendChild(iframeRoot);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function removeAllChildren(element) {
|
|
276
|
+
const childList = Array.prototype.slice.call(element.childNodes, 0);
|
|
277
|
+
for (let i = 0; i < childList.length; i += 1) {
|
|
278
|
+
element.removeChild(childList[i]);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// ===== Render =====
|
|
283
|
+
function render() {
|
|
284
|
+
ensureRootExists(function () {
|
|
285
|
+
removeAllChildren(root);
|
|
286
|
+
|
|
287
|
+
// 通知前端,渲染错误页面已准备就绪
|
|
288
|
+
sendPostMessage({ type: 'PreviewReady' });
|
|
289
|
+
|
|
290
|
+
// 通知主应用存在自定义 overlay
|
|
291
|
+
sendPostMessage({
|
|
292
|
+
type: 'app-features',
|
|
293
|
+
payload: [{ feature: 'error-overlay', enable: true }],
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
if (currentCompileErrorMessage) {
|
|
297
|
+
currentMode = 'compileError';
|
|
298
|
+
|
|
299
|
+
// 通知主应用,当前出现渲染报错
|
|
300
|
+
sendPostMessage({ type: 'RenderError', data: currentMode });
|
|
301
|
+
|
|
302
|
+
// 发送编译错误
|
|
303
|
+
logError(['Compile Error', currentCompileErrorMessage], currentMode);
|
|
304
|
+
ErrorContainer(rootDocument, root, '编译出错');
|
|
305
|
+
} else if (currentRuntimeErrors.length) {
|
|
306
|
+
currentMode = 'runtimeError';
|
|
307
|
+
|
|
308
|
+
// 通知主应用,当前出现渲染报错
|
|
309
|
+
sendPostMessage({ type: 'RenderError', data: currentMode });
|
|
310
|
+
|
|
311
|
+
// 发送运行错误
|
|
312
|
+
logError(['Uncaught Render Error', ...currentRuntimeErrors], currentMode);
|
|
313
|
+
ErrorContainer(rootDocument, root, '页面出错了');
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function cleanup() {
|
|
319
|
+
try {
|
|
320
|
+
if (iframeRoot && iframeRoot.parentNode) {
|
|
321
|
+
iframeRoot.parentNode.removeChild(iframeRoot);
|
|
322
|
+
}
|
|
323
|
+
} catch (e) {
|
|
324
|
+
// ignore
|
|
325
|
+
}
|
|
326
|
+
scheduledRenderFn = null;
|
|
327
|
+
root = null;
|
|
328
|
+
iframeRoot = null;
|
|
329
|
+
rootDocument = null;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ===== Debounce (与 rspack 对齐) =====
|
|
333
|
+
function debounce(fn, wait) {
|
|
334
|
+
let timer;
|
|
335
|
+
function debounced() {
|
|
336
|
+
const context = this;
|
|
337
|
+
const args = arguments;
|
|
338
|
+
clearTimeout(timer);
|
|
339
|
+
timer = setTimeout(function () {
|
|
340
|
+
return fn.apply(context, args);
|
|
341
|
+
}, wait);
|
|
342
|
+
}
|
|
343
|
+
return debounced;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// ===== Public API =====
|
|
347
|
+
function clearCompileError(shouldReload) {
|
|
348
|
+
if (!root || currentMode !== 'compileError') {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
currentCompileErrorMessage = '';
|
|
352
|
+
currentMode = null;
|
|
353
|
+
cleanup();
|
|
354
|
+
|
|
355
|
+
// 编译错误修复后需要刷新页面,因为错误期间模块可能没有正确加载
|
|
356
|
+
if (shouldReload) {
|
|
357
|
+
window.location.reload();
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function clearRuntimeErrors(dismissOverlay) {
|
|
362
|
+
if (!root || currentMode !== 'runtimeError') {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
currentRuntimeErrors = [];
|
|
366
|
+
if (typeof dismissOverlay === 'undefined' || dismissOverlay) {
|
|
367
|
+
currentMode = null;
|
|
368
|
+
cleanup();
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function showCompileError(message) {
|
|
373
|
+
if (!message) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
currentCompileErrorMessage = message;
|
|
377
|
+
render();
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function showRuntimeErrors(errors) {
|
|
381
|
+
if (!errors || !errors.length) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
currentRuntimeErrors = errors;
|
|
385
|
+
render();
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Debounced version to prevent frequent renders
|
|
389
|
+
const debouncedShowRuntimeErrors = debounce(showRuntimeErrors, 30);
|
|
390
|
+
|
|
391
|
+
// 检测是否为编译/模块加载错误(适配 Vite 错误格式)
|
|
392
|
+
function isCompileError(error) {
|
|
393
|
+
const message = error.message || '';
|
|
394
|
+
return (
|
|
395
|
+
// Vite 模块加载错误
|
|
396
|
+
/Failed to fetch dynamically imported module/.test(message) ||
|
|
397
|
+
/Failed to load module script/.test(message) ||
|
|
398
|
+
/Unable to preload CSS/.test(message) ||
|
|
399
|
+
// 通用模块错误
|
|
400
|
+
/Cannot find module/.test(message) ||
|
|
401
|
+
/Module not found/.test(message) ||
|
|
402
|
+
// Webpack 格式(兼容)
|
|
403
|
+
/Module [A-z ]+\(from/.test(message)
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// 处理运行时错误(与 rspack 对齐)
|
|
408
|
+
function handleRuntimeError(error) {
|
|
409
|
+
// 如果是编译相关的错误,忽略它(会由 vite:error 事件处理)
|
|
410
|
+
if (!error || isCompileError(error)) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (currentRuntimeErrors.indexOf(error) === -1) {
|
|
415
|
+
currentRuntimeErrors = currentRuntimeErrors.concat(error);
|
|
416
|
+
}
|
|
417
|
+
debouncedShowRuntimeErrors(currentRuntimeErrors);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// ===== Vite HMR Integration (using createHotContext) =====
|
|
421
|
+
const CLIENT_BASE_PATH = process.env.CLIENT_BASE_PATH || '';
|
|
422
|
+
|
|
423
|
+
async function setupViteHMRListener() {
|
|
424
|
+
try {
|
|
425
|
+
const { createHotContext } = await import(CLIENT_BASE_PATH + '/@vite/client');
|
|
426
|
+
const hot = createHotContext(CLIENT_BASE_PATH + '/@error-overlay.js');
|
|
427
|
+
|
|
428
|
+
// ===== HMR API: beforeUpdate - 通知 onBeforeApply 回调 + 重置错误标志 =====
|
|
429
|
+
hot.on('vite:beforeUpdate', () => {
|
|
430
|
+
hmrBeforeApplyCallbacks.forEach((cb) => {
|
|
431
|
+
try {
|
|
432
|
+
cb();
|
|
433
|
+
} catch (e) {
|
|
434
|
+
console.warn('[VITE_HMR] onBeforeApply callback error:', e);
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
hasErrorInCurrentUpdate = false;
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
// ===== HMR API: error - 设置错误标志并通知回调 =====
|
|
441
|
+
hot.on('vite:error', (payload) => {
|
|
442
|
+
hasErrorInCurrentUpdate = true;
|
|
443
|
+
|
|
444
|
+
// 通知 HMR 错误回调
|
|
445
|
+
hmrErrorCallbacks.forEach((cb) => {
|
|
446
|
+
try {
|
|
447
|
+
cb(payload.err);
|
|
448
|
+
} catch (e) {
|
|
449
|
+
// ignore callback errors
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// 显示错误 overlay
|
|
454
|
+
if (payload.err) {
|
|
455
|
+
const msg = payload.err.message + (payload.err.frame ? '\n\n' + payload.err.frame : '');
|
|
456
|
+
showCompileError(msg);
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// ===== HMR API: afterUpdate - 使用 queueMicrotask 等待 error 事件 =====
|
|
461
|
+
hot.on('vite:afterUpdate', () => {
|
|
462
|
+
// 使用 queueMicrotask 延迟执行,确保 vite:error 事件先处理
|
|
463
|
+
// Vite 事件顺序: beforeUpdate -> afterUpdate -> error
|
|
464
|
+
// microtask 会在所有同步事件处理完后执行
|
|
465
|
+
queueMicrotask(() => {
|
|
466
|
+
if (!hasErrorInCurrentUpdate) {
|
|
467
|
+
// 没有错误,通知成功回调
|
|
468
|
+
hmrSuccessCallbacks.forEach((cb) => {
|
|
469
|
+
try {
|
|
470
|
+
cb();
|
|
471
|
+
} catch (e) {
|
|
472
|
+
// ignore callback errors
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
// 清除编译错误 overlay(如果之前有的话)
|
|
479
|
+
clearCompileError(true);
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
// 自定义事件
|
|
483
|
+
hot.on('fullstack:error', (data) => {
|
|
484
|
+
showCompileError(data?.message || 'Unknown error');
|
|
485
|
+
});
|
|
486
|
+
} catch (e) {
|
|
487
|
+
// Failed to setup HMR listener
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// ===== Global Error Listeners =====
|
|
492
|
+
function setupGlobalErrorListeners() {
|
|
493
|
+
window.addEventListener('error', (event) => {
|
|
494
|
+
// 忽略编译错误模式下的运行时错误
|
|
495
|
+
if (currentMode === 'compileError') return;
|
|
496
|
+
|
|
497
|
+
if (event.error) {
|
|
498
|
+
handleRuntimeError(event.error);
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
window.addEventListener('unhandledrejection', (event) => {
|
|
503
|
+
if (currentMode === 'compileError') return;
|
|
504
|
+
|
|
505
|
+
if (event.reason instanceof Error) {
|
|
506
|
+
handleRuntimeError(event.reason);
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// ===== Expose Error Overlay API =====
|
|
512
|
+
window.__VITE_ERROR_OVERLAY__ = {
|
|
513
|
+
showCompileError,
|
|
514
|
+
clearCompileError,
|
|
515
|
+
showRuntimeErrors,
|
|
516
|
+
clearRuntimeErrors,
|
|
517
|
+
handleRuntimeError,
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
// ===== Expose HMR API (用于 toolkit 的统一 HMR API) =====
|
|
521
|
+
// @see docs/RFC_HMR_API.md
|
|
522
|
+
window.__VITE_HMR__ = {
|
|
523
|
+
/**
|
|
524
|
+
* 注册 HMR 更新前回调(vite:beforeUpdate 阶段,模块替换之前)
|
|
525
|
+
* @param {Function} callback 回调函数
|
|
526
|
+
* @returns {Function} cleanup 函数
|
|
527
|
+
*/
|
|
528
|
+
onBeforeApply(callback) {
|
|
529
|
+
hmrBeforeApplyCallbacks.add(callback);
|
|
530
|
+
return () => hmrBeforeApplyCallbacks.delete(callback);
|
|
531
|
+
},
|
|
532
|
+
/**
|
|
533
|
+
* 注册 HMR 成功回调
|
|
534
|
+
* @param {Function} callback 成功回调函数
|
|
535
|
+
* @returns {Function} cleanup 函数
|
|
536
|
+
*/
|
|
537
|
+
onSuccess(callback) {
|
|
538
|
+
hmrSuccessCallbacks.add(callback);
|
|
539
|
+
return () => hmrSuccessCallbacks.delete(callback);
|
|
540
|
+
},
|
|
541
|
+
/**
|
|
542
|
+
* 注册 HMR 失败回调
|
|
543
|
+
* @param {Function} callback 失败回调函数
|
|
544
|
+
* @returns {Function} cleanup 函数
|
|
545
|
+
*/
|
|
546
|
+
onError(callback) {
|
|
547
|
+
hmrErrorCallbacks.add(callback);
|
|
548
|
+
return () => hmrErrorCallbacks.delete(callback);
|
|
549
|
+
},
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
// ===== Initialize =====
|
|
553
|
+
setupViteHMRListener();
|
|
554
|
+
setupGlobalErrorListeners();
|
|
555
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* iOS 兼容性 Polyfills
|
|
3
|
+
*
|
|
4
|
+
* 使用 core-js 提供 iOS 15.4 以下版本需要的 polyfill
|
|
5
|
+
* 此文件会被单独打包成 polyfills.js,按需加载
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ECMAScript polyfills (core-js)
|
|
9
|
+
import 'core-js/actual/array/at';
|
|
10
|
+
import 'core-js/actual/string/at';
|
|
11
|
+
import 'core-js/actual/array/find-last';
|
|
12
|
+
import 'core-js/actual/array/find-last-index';
|
|
13
|
+
import 'core-js/actual/object/has-own';
|
|
14
|
+
import 'core-js/actual/promise/any';
|
|
15
|
+
import 'core-js/actual/aggregate-error';
|
|
16
|
+
import 'core-js/actual/structured-clone';
|
|
17
|
+
|
|
18
|
+
// Web Crypto API polyfill (不在 core-js 范围内)
|
|
19
|
+
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID !== 'function') {
|
|
20
|
+
Object.defineProperty(crypto, 'randomUUID', {
|
|
21
|
+
value: function randomUUID(): string {
|
|
22
|
+
const bytes = new Uint8Array(16);
|
|
23
|
+
crypto.getRandomValues(bytes);
|
|
24
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
25
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
26
|
+
let hex = '';
|
|
27
|
+
for (let i = 0; i < 16; i++) {
|
|
28
|
+
hex += (bytes[i] < 16 ? '0' : '') + bytes[i].toString(16);
|
|
29
|
+
}
|
|
30
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
31
|
+
},
|
|
32
|
+
writable: true,
|
|
33
|
+
configurable: true,
|
|
34
|
+
});
|
|
35
|
+
}
|