react-spot 0.0.4 → 0.0.6
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/package.json +1 -1
- package/src/core/fiber-utils.ts +58 -11
package/package.json
CHANGED
package/src/core/fiber-utils.ts
CHANGED
|
@@ -110,9 +110,13 @@ function getComponentNameFromType(type: unknown): string {
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
|
|
113
|
+
// React 19 会在 _debugStack 中注入一个以组件自身名称命名的"自身帧",
|
|
114
|
+
// 用于让 Error 调用栈更可读。对于源码导航,我们需要跳过这个自身帧,
|
|
115
|
+
// 取其后面的帧(即组件 JSX 在父组件中的使用位置)。
|
|
116
|
+
// 但 workspace 包通过 HMR 模块加载时,自身帧可能不存在或已被过滤,
|
|
117
|
+
// 此时 meaningful[0] 就已经是正确的使用位置。
|
|
118
|
+
// 因此不能使用固定索引,需要通过函数名匹配来动态判断。
|
|
119
|
+
const STACK_FRAME_INDEX_FALLBACK = 1;
|
|
116
120
|
|
|
117
121
|
/**
|
|
118
122
|
* 判断栈帧行是否为 React 运行时内部帧(无法解析为用户源码)。
|
|
@@ -217,10 +221,45 @@ export function getStackFrame(fiber: Fiber): string | undefined {
|
|
|
217
221
|
meaningfulLines.push(line);
|
|
218
222
|
}
|
|
219
223
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
+
|
|
225
|
+
if (meaningfulLines.length === 0) return undefined;
|
|
226
|
+
|
|
227
|
+
// 原生 DOM 元素直接取第一个帧(JSX 标签创建点)
|
|
228
|
+
if (typeof fiber.type === 'string') {
|
|
229
|
+
return meaningfulLines[0];
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// 函数组件:检测 meaningful[0] 是否为 React 注入的"自身帧"。
|
|
233
|
+
// React 19 会注入一个以组件名命名的帧,使调用栈更可读。
|
|
234
|
+
// 判断方法:检查帧中的函数名是否与当前 fiber 的组件名匹配。
|
|
235
|
+
// 若匹配,说明是自身帧,应跳过取 meaningful[1](实际使用位置)。
|
|
236
|
+
// 若不匹配(如 workspace 包组件的自身帧被 HMR 过滤掉),meaningful[0] 已是使用位置。
|
|
237
|
+
const componentName = getComponentName(fiber);
|
|
238
|
+
const firstFrame = meaningfulLines[0];
|
|
239
|
+
if (isSelfFrame(firstFrame, componentName)) {
|
|
240
|
+
return meaningfulLines[STACK_FRAME_INDEX_FALLBACK] || meaningfulLines[0];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return firstFrame;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* 检测栈帧行是否为 React 注入的组件"自身帧"。
|
|
248
|
+
*
|
|
249
|
+
* React 19 在 _debugStack 中注入形如 `at ComponentName (url:line:col)` 的帧,
|
|
250
|
+
* 函数名与当前组件名一致。通过比较帧中的函数名与 fiber 的组件名来判断。
|
|
251
|
+
*/
|
|
252
|
+
function isSelfFrame(frameLine: string, componentName: string): boolean {
|
|
253
|
+
// 提取帧中的函数名:匹配 "at FuncName (" 或 "FuncName@" 格式
|
|
254
|
+
const atMatch = frameLine.match(/^at\s+([^\s(]+)/);
|
|
255
|
+
if (atMatch) {
|
|
256
|
+
return atMatch[1] === componentName;
|
|
257
|
+
}
|
|
258
|
+
const firefoxMatch = frameLine.match(/^([^@]+)@/);
|
|
259
|
+
if (firefoxMatch) {
|
|
260
|
+
return firefoxMatch[1] === componentName;
|
|
261
|
+
}
|
|
262
|
+
return false;
|
|
224
263
|
}
|
|
225
264
|
|
|
226
265
|
/**
|
|
@@ -251,14 +290,22 @@ export function getAllMeaningfulFrames(fiber: Fiber): string[] {
|
|
|
251
290
|
|
|
252
291
|
if (meaningfulLines.length === 0) return [];
|
|
253
292
|
|
|
254
|
-
//
|
|
255
|
-
|
|
256
|
-
|
|
293
|
+
// 确定首选帧索引:与 getStackFrame 保持一致的智能检测逻辑
|
|
294
|
+
let preferredIndex = 0;
|
|
295
|
+
if (typeof fiber.type !== 'string' && meaningfulLines.length > 1) {
|
|
296
|
+
const componentName = getComponentName(fiber);
|
|
297
|
+
if (isSelfFrame(meaningfulLines[0], componentName)) {
|
|
298
|
+
preferredIndex = STACK_FRAME_INDEX_FALLBACK;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const preferred = meaningfulLines[preferredIndex];
|
|
257
303
|
if (!preferred) return meaningfulLines;
|
|
258
304
|
|
|
305
|
+
// 将首选帧放到数组头部,其余按原始顺序跟随
|
|
259
306
|
const result = [preferred];
|
|
260
307
|
for (let i = 0; i < meaningfulLines.length; i++) {
|
|
261
|
-
if (i !==
|
|
308
|
+
if (i !== preferredIndex) {
|
|
262
309
|
result.push(meaningfulLines[i]);
|
|
263
310
|
}
|
|
264
311
|
}
|