bippy 0.6.1-dev.61475ed → 0.6.1-dev.a3b3942

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.
Files changed (66) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +32 -48
  3. package/dist/core.cjs +1 -1
  4. package/dist/core.d.cts +45 -60
  5. package/dist/core.d.ts +45 -60
  6. package/dist/core.js +1 -1
  7. package/dist/core2.cjs +9 -0
  8. package/dist/core2.d.cts +11 -3
  9. package/dist/core2.d.ts +11 -3
  10. package/dist/core2.js +9 -0
  11. package/dist/errors.d.cts +420 -0
  12. package/dist/errors.d.ts +420 -0
  13. package/dist/index.cjs +1 -1
  14. package/dist/index.d.cts +11 -3
  15. package/dist/index.d.ts +11 -3
  16. package/dist/index.iife.js +1 -1
  17. package/dist/index.js +1 -1
  18. package/dist/install-hook-only.cjs +1 -1
  19. package/dist/install-hook-only.d.cts +9 -1
  20. package/dist/install-hook-only.d.ts +9 -1
  21. package/dist/install-hook-only.iife.js +1 -1
  22. package/dist/install-hook-only.js +1 -1
  23. package/dist/rdt-hook.cjs +1 -1
  24. package/dist/rdt-hook.js +1 -1
  25. package/dist/source.cjs +14 -5
  26. package/dist/source.d.cts +27 -11
  27. package/dist/source.d.ts +27 -11
  28. package/dist/source.js +14 -5
  29. package/package.json +22 -16
  30. package/src/core.ts +349 -328
  31. package/src/errors.ts +34 -0
  32. package/src/install-hook-only.ts +2 -2
  33. package/src/rdt-hook.ts +160 -161
  34. package/src/react-internals/generated/react-work-tags.ts +318 -0
  35. package/src/react-internals/index.ts +67 -0
  36. package/src/react-internals/semver.ts +80 -0
  37. package/src/react-internals/types.ts +197 -0
  38. package/src/source/constants.ts +1 -1
  39. package/src/source/error-stack.ts +11 -0
  40. package/src/source/get-display-name-from-source.ts +32 -25
  41. package/src/source/get-source.ts +5 -5
  42. package/src/source/index.ts +9 -1
  43. package/src/source/inspect-hooks.ts +215 -182
  44. package/src/source/owner-stack.ts +127 -166
  45. package/src/source/parse-debug-stack.ts +4 -4
  46. package/src/source/parse-hook-names.ts +1 -1
  47. package/src/source/parse-stack.ts +1 -3
  48. package/src/source/symbolication.ts +490 -90
  49. package/dist/get-source.cjs +0 -19
  50. package/dist/get-source.js +0 -19
  51. package/dist/react-refresh.cjs +0 -9
  52. package/dist/react-refresh.d.cts +0 -66
  53. package/dist/react-refresh.d.ts +0 -66
  54. package/dist/react-refresh.js +0 -9
  55. package/dist/unsubscribe.d.cts +0 -298
  56. package/dist/unsubscribe.d.ts +0 -298
  57. package/src/react-refresh/constants.ts +0 -9
  58. package/src/react-refresh/detect-hmr-transport.ts +0 -33
  59. package/src/react-refresh/index.ts +0 -173
  60. package/src/react-refresh/metro-hmr-transport.ts +0 -188
  61. package/src/react-refresh/next-webpack-hmr-transport.ts +0 -72
  62. package/src/react-refresh/normalize-hmr-file-path.ts +0 -24
  63. package/src/react-refresh/types.ts +0 -7
  64. package/src/react-refresh/vite-hmr-transport.ts +0 -116
  65. package/src/types.ts +0 -438
  66. package/src/unsubscribe.ts +0 -17
@@ -1,33 +1,27 @@
1
- import {
2
- _renderers,
3
- ActivityComponentTag,
4
- ClassComponentTag,
1
+ import { getDisplayName, getType, traverseFiber } from "../core.js";
2
+ import { _renderers, getRDTHook } from "../rdt-hook.js";
3
+ import { getReactWorkTagsForFiber } from "../react-internals/index.js";
4
+ import type {
5
5
  Fiber,
6
- ForwardRefTag,
7
- FunctionComponentTag,
8
- getRDTHook,
9
- HostComponentTag,
10
- HostHoistableTag,
11
- HostSingletonTag,
12
- LazyComponentTag,
13
- SimpleMemoComponentTag,
14
- SuspenseComponentTag,
15
- SuspenseListComponentTag,
16
- ViewTransitionComponentTag,
17
- getDisplayName,
18
- traverseFiber,
19
- } from "../core.js";
20
- import { ServerComponentInfo } from "../types.js";
6
+ RendererDispatcherRef,
7
+ ServerComponentInfo,
8
+ } from "../react-internals/index.js";
21
9
  import {
10
+ REACT_STACK_BOTTOM_FRAME_PATTERNS,
22
11
  SERVER_FRAME_MARKER,
23
12
  SERVER_ENV_PATTERN,
24
13
  SERVER_COMPONENT_URL_PREFIXES,
25
14
  } from "./constants.js";
26
-
15
+ import { getPrepareStackTrace, setPrepareStackTrace } from "./error-stack.js";
27
16
  import { parseDebugStack } from "./parse-debug-stack.js";
28
17
  import { parseStack, StackFrame } from "./parse-stack.js";
29
18
  import { symbolicateStack } from "./symbolication.js";
30
19
 
20
+ interface RendererDispatcherSnapshot {
21
+ currentDispatcherRef: RendererDispatcherRef;
22
+ value: unknown;
23
+ }
24
+
31
25
  export const hasDebugStack = (
32
26
  fiber: Fiber,
33
27
  ): fiber is Fiber & {
@@ -37,13 +31,7 @@ export const hasDebugStack = (
37
31
  };
38
32
 
39
33
  const isFiberOwner = (owner: Fiber | ServerComponentInfo): owner is Fiber =>
40
- typeof (owner as Fiber).tag === "number";
41
-
42
- // react's typings (and bippy's Fiber, which mirrors them) declare _debugOwner
43
- // as a Fiber, but react 19 flight sets a ReactComponentInfo object for server
44
- // component owners
45
- const getDebugOwner = (fiber: Fiber): Fiber | ServerComponentInfo | undefined =>
46
- fiber._debugOwner as Fiber | ServerComponentInfo | undefined;
34
+ "tag" in owner && typeof owner.tag === "number";
47
35
 
48
36
  /**
49
37
  * Locates a frame inside the fiber's own function body without invoking it:
@@ -90,26 +78,41 @@ export const getDefinitionFrameFromOwnedChild = (fiber: Fiber): StackFrame | nul
90
78
  return null;
91
79
  };
92
80
 
93
- const getCurrentDispatcher = (): null | React.RefObject<unknown> => {
81
+ const getRendererDispatcherRefs = (): RendererDispatcherRef[] => {
94
82
  const rdtHook = getRDTHook();
95
- for (const renderer of [...Array.from(_renderers), ...Array.from(rdtHook.renderers.values())]) {
83
+ const renderers = new Set([..._renderers, ...rdtHook.renderers.values()]);
84
+ const currentDispatcherRefs: RendererDispatcherRef[] = [];
85
+ const seenCurrentDispatcherRefs = new Set<object>();
86
+ for (const renderer of renderers) {
96
87
  const currentDispatcherRef = renderer.currentDispatcherRef;
97
- if (currentDispatcherRef && typeof currentDispatcherRef === "object") {
98
- return "H" in currentDispatcherRef ? currentDispatcherRef.H : currentDispatcherRef.current;
88
+ if (!currentDispatcherRef || seenCurrentDispatcherRefs.has(currentDispatcherRef)) continue;
89
+ seenCurrentDispatcherRefs.add(currentDispatcherRef);
90
+ currentDispatcherRefs.push(currentDispatcherRef);
91
+ }
92
+ return currentDispatcherRefs;
93
+ };
94
+
95
+ const clearRendererDispatchers = (): RendererDispatcherSnapshot[] => {
96
+ const dispatcherSnapshots: RendererDispatcherSnapshot[] = [];
97
+ for (const currentDispatcherRef of getRendererDispatcherRefs()) {
98
+ const value =
99
+ "H" in currentDispatcherRef ? currentDispatcherRef.H : currentDispatcherRef.current;
100
+ dispatcherSnapshots.push({ currentDispatcherRef, value });
101
+ if ("H" in currentDispatcherRef) {
102
+ currentDispatcherRef.H = null;
103
+ } else {
104
+ currentDispatcherRef.current = null;
99
105
  }
100
106
  }
101
- return null;
107
+ return dispatcherSnapshots;
102
108
  };
103
109
 
104
- const setCurrentDispatcher = (value: null | React.RefObject<unknown>): void => {
105
- for (const renderer of _renderers) {
106
- const currentDispatcherRef = renderer.currentDispatcherRef;
107
- if (currentDispatcherRef && typeof currentDispatcherRef === "object") {
108
- if ("H" in currentDispatcherRef) {
109
- currentDispatcherRef.H = value;
110
- } else {
111
- currentDispatcherRef.current = value;
112
- }
110
+ const restoreRendererDispatchers = (dispatcherSnapshots: RendererDispatcherSnapshot[]): void => {
111
+ for (const { currentDispatcherRef, value } of dispatcherSnapshots) {
112
+ if ("H" in currentDispatcherRef) {
113
+ currentDispatcherRef.H = value;
114
+ } else {
115
+ currentDispatcherRef.current = value;
113
116
  }
114
117
  }
115
118
  };
@@ -146,49 +149,31 @@ const describeNativeComponentFrame = (
146
149
  return cachedFrame;
147
150
  }
148
151
 
149
- const previousPrepareStackTrace = Error.prepareStackTrace;
150
- // HACK: V8 API allows undefined but bun-types declares it as non-optional
151
- (Error as { prepareStackTrace?: typeof Error.prepareStackTrace }).prepareStackTrace = undefined;
152
+ const previousPrepareStackTrace = getPrepareStackTrace();
153
+ setPrepareStackTrace(undefined);
152
154
  reEntry = true;
153
155
 
154
- const previousDispatcher = getCurrentDispatcher();
155
- setCurrentDispatcher(null);
156
+ const dispatcherSnapshots = clearRendererDispatchers();
156
157
  const previousConsoleError = console.error;
157
158
  const previousConsoleWarn = console.warn;
158
159
  console.error = () => {};
159
160
  console.warn = () => {};
160
161
  try {
161
- /**
162
- * Finding a common stack frame between sample and control errors can be
163
- * tricky given the different types and levels of stack trace truncation from
164
- * different JS VMs. So instead we'll attempt to control what that common
165
- * frame should be through this object method:
166
- * Having both the sample and control errors be in the function under the
167
- * `DescribeNativeComponentFrameRoot` property, + setting the `name` and
168
- * `displayName` properties of the function ensures that a stack
169
- * frame exists that has the method name `DescribeNativeComponentFrameRoot` in
170
- * it for both control and sample stacks.
171
- */
162
+ // HACK: Run the control and sample under the same property name so their stacks share a frame.
172
163
  const RunInRootFrame = {
173
- DetermineComponentFrameRoot() {
164
+ DetermineComponentFrameRoot(this: void) {
174
165
  let control: unknown;
175
166
  try {
176
- // This should throw.
177
167
  if (construct) {
178
- // Something should be setting the props in the constructor.
179
168
  const ThrowingConstructor = function () {
180
169
  throw Error();
181
170
  };
182
171
  Object.defineProperty(ThrowingConstructor.prototype, "props", {
183
172
  set: function () {
184
- // We use a throwing setter instead of frozen or non-writable props
185
- // because that won't throw in a non-strict mode function.
186
173
  throw Error();
187
174
  },
188
175
  });
189
176
  if (typeof Reflect === "object" && Reflect.construct) {
190
- // We construct a different control for this case to include any extra
191
- // frames added by the construct call.
192
177
  try {
193
178
  Reflect.construct(ThrowingConstructor, []);
194
179
  } catch (caughtError) {
@@ -197,13 +182,11 @@ const describeNativeComponentFrame = (
197
182
  Reflect.construct(component, [], ThrowingConstructor);
198
183
  } else {
199
184
  try {
200
- // @ts-expect-error -- ThrowingConstructor is a constructor function
201
- ThrowingConstructor.call();
185
+ Function.prototype.apply.call(ThrowingConstructor, undefined, []);
202
186
  } catch (caughtError) {
203
187
  control = caughtError;
204
188
  }
205
- // @ts-expect-error -- ThrowingConstructor is a constructor function
206
- component.call(ThrowingConstructor.prototype);
189
+ Function.prototype.apply.call(component, ThrowingConstructor.prototype, []);
207
190
  }
208
191
  } else {
209
192
  try {
@@ -211,22 +194,17 @@ const describeNativeComponentFrame = (
211
194
  } catch (caughtError) {
212
195
  control = caughtError;
213
196
  }
214
- // TODO(luna): This will currently only throw if the function component
215
- // tries to access React/ReactDOM/props. We should probably make this throw
216
- // in simple components too
217
- const maybePromise = (component as () => Promise<unknown>)();
218
-
219
- // If the function component returns a promise, it's likely an async
220
- // component, which we don't yet support. Attach a noop catch handler to
221
- // silence the error.
222
- // TODO: Implement component stacks for async client components?
223
- // eslint-disable-next-line @typescript-eslint/no-misused-promises -- we literally check if this is a promise here
224
- if (maybePromise && typeof maybePromise.catch === "function") {
225
- maybePromise.catch(() => {});
197
+ const maybePromise = Function.prototype.apply.call(component, undefined, []);
198
+ if (
199
+ typeof maybePromise === "object" &&
200
+ maybePromise !== null &&
201
+ "catch" in maybePromise &&
202
+ typeof maybePromise.catch === "function"
203
+ ) {
204
+ maybePromise.catch(() => undefined);
226
205
  }
227
206
  }
228
207
  } catch (sample: unknown) {
229
- // This is inlined manually because closure doesn't do it for us.
230
208
  if (
231
209
  sample instanceof Error &&
232
210
  control instanceof Error &&
@@ -239,21 +217,18 @@ const describeNativeComponentFrame = (
239
217
  },
240
218
  };
241
219
 
242
- // @ts-expect-error --- displayName is not a property of the function
243
- RunInRootFrame.DetermineComponentFrameRoot.displayName = "DetermineComponentFrameRoot";
220
+ Object.defineProperty(RunInRootFrame.DetermineComponentFrameRoot, "displayName", {
221
+ value: "DetermineComponentFrameRoot",
222
+ });
244
223
  const namePropDescriptor = Object.getOwnPropertyDescriptor(
245
224
  // eslint-disable-next-line @typescript-eslint/unbound-method
246
225
  RunInRootFrame.DetermineComponentFrameRoot,
247
226
  "name",
248
227
  );
249
- // Before ES6, the `name` property was not configurable.
250
228
  if (namePropDescriptor?.configurable) {
251
- // V8 utilizes a function's `name` property when generating a stack trace.
252
229
  Object.defineProperty(
253
230
  // eslint-disable-next-line @typescript-eslint/unbound-method
254
231
  RunInRootFrame.DetermineComponentFrameRoot,
255
- // Configurable properties can be updated even if its writable descriptor
256
- // is set to `false`.
257
232
  "name",
258
233
  { value: "DetermineComponentFrameRoot" },
259
234
  );
@@ -338,14 +313,14 @@ const describeNativeComponentFrame = (
338
313
  } finally {
339
314
  reEntry = false;
340
315
 
341
- Error.prepareStackTrace = previousPrepareStackTrace;
316
+ setPrepareStackTrace(previousPrepareStackTrace);
342
317
 
343
- setCurrentDispatcher(previousDispatcher);
318
+ restoreRendererDispatchers(dispatcherSnapshots);
344
319
  console.error = previousConsoleError;
345
320
  console.warn = previousConsoleWarn;
346
321
  }
347
322
 
348
- const componentName = component ? getDisplayName(component) : "";
323
+ const componentName = getDisplayName(component);
349
324
  const syntheticFrame = componentName ? describeBuiltInComponentFrame(componentName) : "";
350
325
  componentFrameCache.set(component, syntheticFrame);
351
326
  return syntheticFrame;
@@ -353,35 +328,34 @@ const describeNativeComponentFrame = (
353
328
 
354
329
  // https://github.com/facebook/react/blob/ac3e705a18696168acfcaed39dce0cfaa6be8836/packages/react-reconciler/src/ReactFiberComponentStack.js#L180
355
330
  export const describeFiber = (fiber: Fiber, childFiber: Fiber | null): string => {
356
- const tag = fiber.tag as number;
357
331
  let stackFrame = "";
358
- switch (tag) {
359
- case ActivityComponentTag:
332
+ const workTags = getReactWorkTagsForFiber(fiber);
333
+ switch (fiber.tag) {
334
+ case workTags.ActivityComponent:
360
335
  stackFrame = describeBuiltInComponentFrame("Activity");
361
336
  break;
362
- case ClassComponentTag:
337
+ case workTags.ClassComponent:
363
338
  stackFrame = describeNativeComponentFrame(fiber.type, true);
364
339
  break;
365
- case ForwardRefTag:
366
- stackFrame = describeNativeComponentFrame(
367
- (fiber.type as { render: React.ComponentType<unknown> }).render,
368
- false,
369
- );
340
+ case workTags.ForwardRef:
341
+ stackFrame = describeNativeComponentFrame(getType(fiber.type) ?? fiber.type, false);
370
342
  break;
371
- case FunctionComponentTag:
372
- case SimpleMemoComponentTag:
343
+ case workTags.FunctionComponent:
344
+ case workTags.SimpleMemoComponent:
373
345
  stackFrame = describeNativeComponentFrame(fiber.type, false);
374
346
  break;
375
- case HostComponentTag:
376
- case HostHoistableTag:
377
- case HostSingletonTag:
378
- stackFrame = describeBuiltInComponentFrame(fiber.type as string);
347
+ case workTags.HostComponent:
348
+ case workTags.HostHoistable:
349
+ case workTags.HostSingleton:
350
+ if (typeof fiber.type === "string") {
351
+ stackFrame = describeBuiltInComponentFrame(fiber.type);
352
+ }
379
353
  break;
380
- case LazyComponentTag:
354
+ case workTags.LazyComponent:
381
355
  // TODO: When we support Thenables as component types we should rename this.
382
356
  stackFrame = describeBuiltInComponentFrame("Lazy");
383
357
  break;
384
- case SuspenseComponentTag:
358
+ case workTags.SuspenseComponent:
385
359
  if (fiber.child !== childFiber && childFiber !== null) {
386
360
  // If we came from the second Fiber then we're in the Suspense Fallback.
387
361
  stackFrame = describeBuiltInComponentFrame("Suspense Fallback");
@@ -389,10 +363,10 @@ export const describeFiber = (fiber: Fiber, childFiber: Fiber | null): string =>
389
363
  stackFrame = describeBuiltInComponentFrame("Suspense");
390
364
  }
391
365
  break;
392
- case SuspenseListComponentTag:
366
+ case workTags.SuspenseListComponent:
393
367
  stackFrame = describeBuiltInComponentFrame("SuspenseList");
394
368
  break;
395
- case ViewTransitionComponentTag:
369
+ case workTags.ViewTransitionComponent:
396
370
  // Note: enableViewTransition feature flag is not available in this codebase,
397
371
  // so we'll always include ViewTransition
398
372
  stackFrame = describeBuiltInComponentFrame("ViewTransition");
@@ -421,8 +395,8 @@ export const getFallbackParentStack = (thisFiber: Fiber): string => {
421
395
  // Since we don't have __DEV__ in this codebase, we'll check for _debugInfo
422
396
  const debugInfo = currentFiber._debugInfo;
423
397
  if (debugInfo && Array.isArray(debugInfo)) {
424
- for (let i = debugInfo.length - 1; i >= 0; i--) {
425
- const debugEntry = debugInfo[i];
398
+ for (let debugInfoIndex = debugInfo.length - 1; debugInfoIndex >= 0; debugInfoIndex--) {
399
+ const debugEntry = debugInfo[debugInfoIndex];
426
400
  if (typeof debugEntry.name === "string") {
427
401
  componentStack += describeDebugInfoFrame(debugEntry.name, debugEntry.env);
428
402
  }
@@ -435,7 +409,7 @@ export const getFallbackParentStack = (thisFiber: Fiber): string => {
435
409
  return componentStack;
436
410
  } catch (error) {
437
411
  if (error instanceof Error) {
438
- return `\nError generating stack: ${error.message}\n${error.stack}`;
412
+ return `\nBippy couldn’t generate the stack: ${error.message}\n${error.stack}`;
439
413
  }
440
414
  return "";
441
415
  }
@@ -460,14 +434,10 @@ export const getFallbackParentStack = (thisFiber: Fiber): string => {
460
434
  * @see https://github.com/facebook/react/blob/main/packages/react-devtools-shared/src/backend/shared/DevToolsOwnerStack.js#L12
461
435
  */
462
436
  export const formatOwnerStack = (stack: string): string => {
463
- const prevPrepareStackTrace = Error.prepareStackTrace;
464
- // HACK: V8 API allows undefined but bun-types declares it as non-optional
465
- (Error as { prepareStackTrace?: typeof Error.prepareStackTrace }).prepareStackTrace = undefined;
466
437
  let formattedStack = stack;
467
438
  if (!formattedStack) {
468
439
  return "";
469
440
  }
470
- Error.prepareStackTrace = prevPrepareStackTrace;
471
441
 
472
442
  if (formattedStack.startsWith("Error: react-stack-top-frame\n")) {
473
443
  // V8's default formatting prefixes with the error message which we
@@ -480,8 +450,7 @@ export const formatOwnerStack = (stack: string): string => {
480
450
  formattedStack = formattedStack.slice(firstNewlineIndex + 1);
481
451
  }
482
452
  let bottomFrameIndex = Math.max(
483
- formattedStack.indexOf("react_stack_bottom_frame"),
484
- formattedStack.indexOf("react-stack-bottom-frame"),
453
+ ...REACT_STACK_BOTTOM_FRAME_PATTERNS.map((pattern) => formattedStack.indexOf(pattern)),
485
454
  );
486
455
  if (bottomFrameIndex !== -1) {
487
456
  bottomFrameIndex = formattedStack.lastIndexOf("\n", bottomFrameIndex);
@@ -498,11 +467,6 @@ export const formatOwnerStack = (stack: string): string => {
498
467
  return formattedStack;
499
468
  };
500
469
 
501
- interface DebugStackEntry {
502
- componentName: string;
503
- stackFrames: StackFrame[];
504
- }
505
-
506
470
  const isReactServerComponentFrame = (stackFrame: StackFrame): boolean =>
507
471
  Boolean(
508
472
  stackFrame.functionName && stackFrame.fileName && isServerComponentUrl(stackFrame.fileName),
@@ -514,24 +478,22 @@ const areStackFramesEqual = (firstFrame: StackFrame, secondFrame: StackFrame): b
514
478
  firstFrame.columnNumber === secondFrame.columnNumber;
515
479
 
516
480
  const buildFunctionNameToRscFramesMap = (
517
- debugStackEntries: DebugStackEntry[],
481
+ debugStackFrames: StackFrame[],
518
482
  ): Map<string, StackFrame[]> => {
519
483
  const functionNameToRscFrames = new Map<string, StackFrame[]>();
520
484
 
521
- for (const debugStackEntry of debugStackEntries) {
522
- for (const stackFrame of debugStackEntry.stackFrames) {
523
- if (!isReactServerComponentFrame(stackFrame)) continue;
485
+ for (const stackFrame of debugStackFrames) {
486
+ if (!isReactServerComponentFrame(stackFrame)) continue;
524
487
 
525
- const functionName = stackFrame.functionName!;
526
- const framesForFunction = functionNameToRscFrames.get(functionName) ?? [];
527
- const isDuplicateFrame = framesForFunction.some((existingFrame) =>
528
- areStackFramesEqual(existingFrame, stackFrame),
529
- );
488
+ const functionName = stackFrame.functionName!;
489
+ const framesForFunction = functionNameToRscFrames.get(functionName) ?? [];
490
+ const isDuplicateFrame = framesForFunction.some((existingFrame) =>
491
+ areStackFramesEqual(existingFrame, stackFrame),
492
+ );
530
493
 
531
- if (!isDuplicateFrame) {
532
- framesForFunction.push(stackFrame);
533
- functionNameToRscFrames.set(functionName, framesForFunction);
534
- }
494
+ if (!isDuplicateFrame) {
495
+ framesForFunction.push(stackFrame);
496
+ functionNameToRscFrames.set(functionName, framesForFunction);
535
497
  }
536
498
  }
537
499
 
@@ -592,7 +554,7 @@ const getOwnerStackFromDebugStacks = (fiber: Fiber): StackFrame[] => {
592
554
  while (owner) {
593
555
  if (isFiberOwner(owner)) {
594
556
  const ownerFiber: Fiber = owner;
595
- owner = getDebugOwner(ownerFiber);
557
+ owner = ownerFiber._debugOwner;
596
558
  if (owner && hasDebugStack(ownerFiber)) {
597
559
  const { frames, isTrusted } = parseDebugStack(ownerFiber._debugStack);
598
560
  if (isTrusted) {
@@ -616,51 +578,39 @@ const getOwnerStackFromDebugStacks = (fiber: Fiber): StackFrame[] => {
616
578
  return ownerStackFrames;
617
579
  };
618
580
 
619
- const getDebugStackEntries = (rootFiber: Fiber): DebugStackEntry[] => {
620
- const debugStackEntries: DebugStackEntry[] = [];
581
+ const getDebugStackFrames = (rootFiber: Fiber): StackFrame[] => {
582
+ const debugStackFrames: StackFrame[] = [];
621
583
 
622
584
  traverseFiber(
623
585
  rootFiber,
624
586
  (currentFiber) => {
625
587
  if (!hasDebugStack(currentFiber)) return;
626
588
 
627
- const componentName =
628
- typeof currentFiber.type !== "string"
629
- ? getDisplayName(currentFiber.type) || "<anonymous>"
630
- : currentFiber.type;
631
-
632
- debugStackEntries.push({
633
- componentName,
634
- stackFrames: parseStack(formatOwnerStack(currentFiber._debugStack?.stack)),
635
- });
589
+ const { frames, isTrusted } = parseDebugStack(currentFiber._debugStack);
590
+ if (isTrusted) {
591
+ debugStackFrames.push(...frames);
592
+ }
636
593
  },
637
594
  true,
638
595
  );
639
596
 
640
- return debugStackEntries;
597
+ return debugStackFrames;
641
598
  };
642
599
 
643
600
  /**
644
- * Returns a stack of ALL ancestor components in the render tree (the fiber's
645
- * `return` chain), including wrappers that render `{children}` without having
646
- * created this fiber's JSX. Locations come from re-invoking each component
647
- * with a throwing dispatcher; server frames are enriched from debug stacks by
648
- * name matching. Works on every React version.
601
+ * The unsymbolicated frames behind {@link getParentStack}: bundle-space
602
+ * locations from re-invoking each component with a throwing dispatcher,
603
+ * with server frames enriched from debug stacks by name matching.
649
604
  */
650
- export const getParentStack = async (
651
- fiber: Fiber,
652
- shouldCache = true,
653
- fetchFunction?: (url: string) => Promise<Response>,
654
- ): Promise<StackFrame[]> => {
655
- const debugStackEntries = getDebugStackEntries(fiber);
605
+ export const getRawParentStack = (fiber: Fiber): StackFrame[] => {
606
+ const debugStackFrames = getDebugStackFrames(fiber);
656
607
  const fallbackStackFrames = parseStack(getFallbackParentStack(fiber));
657
- const functionNameToRscFrames = buildFunctionNameToRscFramesMap(debugStackEntries);
608
+ const functionNameToRscFrames = buildFunctionNameToRscFramesMap(debugStackFrames);
658
609
  const functionNameToUsageIndex = new Map<string, number>();
659
610
 
660
611
  const enrichedStackFrames = fallbackStackFrames.map((stackFrame): StackFrame => {
661
612
  const isServerFrame =
662
- (stackFrame.source?.includes(SERVER_FRAME_MARKER) ?? false) ||
663
- (stackFrame.source != null && SERVER_ENV_PATTERN.test(stackFrame.source));
613
+ stackFrame.source !== undefined && SERVER_ENV_PATTERN.test(stackFrame.source);
664
614
 
665
615
  if (isServerFrame) {
666
616
  return getEnrichedServerStackFrame(
@@ -673,15 +623,26 @@ export const getParentStack = async (
673
623
  return stackFrame;
674
624
  });
675
625
 
676
- const deduplicatedStackFrames = enrichedStackFrames.filter((stackFrame, index, frames) => {
626
+ return enrichedStackFrames.filter((stackFrame, index, frames) => {
677
627
  if (index === 0) return true;
678
628
  const previousFrame = frames[index - 1];
679
629
  return stackFrame.functionName !== previousFrame.functionName;
680
630
  });
681
-
682
- return symbolicateStack(deduplicatedStackFrames, shouldCache, fetchFunction);
683
631
  };
684
632
 
633
+ /**
634
+ * Returns a stack of ALL ancestor components in the render tree (the fiber's
635
+ * `return` chain), including wrappers that render `{children}` without having
636
+ * created this fiber's JSX. Locations come from re-invoking each component
637
+ * with a throwing dispatcher; server frames are enriched from debug stacks by
638
+ * name matching. Works on every React version.
639
+ */
640
+ export const getParentStack = async (
641
+ fiber: Fiber,
642
+ shouldCache = true,
643
+ fetchFunction?: (url: string) => Promise<Response>,
644
+ ): Promise<StackFrame[]> => symbolicateStack(getRawParentStack(fiber), shouldCache, fetchFunction);
645
+
685
646
  // an owner frame is only actionable if it can point an editor somewhere:
686
647
  // it needs a file location and must not be ignore-listed bundler/framework code
687
648
  const isLocatableFrame = (stackFrame: StackFrame): boolean =>
@@ -1,4 +1,5 @@
1
1
  import { JSX_FACTORY_FRAME_COUNT, REACT_STACK_BOTTOM_FRAME_PATTERNS } from "./constants.js";
2
+ import { getPrepareStackTrace, setPrepareStackTrace } from "./error-stack.js";
2
3
  import { parseStack, StackFrame } from "./parse-stack.js";
3
4
 
4
5
  interface V8CallSite {
@@ -117,14 +118,13 @@ export const parseDebugStack = (debugStack: Error): ParsedDebugStack => {
117
118
  }
118
119
  return stackString;
119
120
  };
120
- const previousPrepareStackTrace = Error.prepareStackTrace;
121
- // node's CallSite typings disagree with browser-safe optional methods
122
- Error.prepareStackTrace = collectFramesAndFormatStack as typeof Error.prepareStackTrace;
121
+ const previousPrepareStackTrace = getPrepareStackTrace();
122
+ setPrepareStackTrace(collectFramesAndFormatStack);
123
123
  let stackString: string;
124
124
  try {
125
125
  stackString = String(debugStack.stack);
126
126
  } finally {
127
- Error.prepareStackTrace = previousPrepareStackTrace;
127
+ setPrepareStackTrace(previousPrepareStackTrace);
128
128
  }
129
129
 
130
130
  const result = structuredResult ?? parseMaterializedStack(stackString);
@@ -35,7 +35,7 @@ const flattenHooksTree = (hooksTree: HooksTree): HooksNode[] => {
35
35
 
36
36
  const findSourceContentByFileName = (
37
37
  sources: string[],
38
- sourcesContent: string[] | undefined,
38
+ sourcesContent: Array<string | null> | undefined,
39
39
  fileName: string,
40
40
  ): string | null => {
41
41
  if (!sourcesContent) return null;
@@ -1,5 +1,4 @@
1
1
  export interface StackFrame {
2
- args?: unknown[];
3
2
  columnNumber?: number;
4
3
  lineNumber?: number;
5
4
  // start of the enclosing function (the definition, not the call site);
@@ -17,7 +16,6 @@ export interface StackFrame {
17
16
 
18
17
  export interface ParseOptions {
19
18
  slice?: number | [number, number];
20
- allowEmpty?: boolean;
21
19
  includeInElement?: boolean;
22
20
  }
23
21
 
@@ -67,7 +65,7 @@ export const extractLocation = (
67
65
  };
68
66
 
69
67
  const applySlice = <T>(lines: T[], options?: ParseOptions): T[] => {
70
- if (options && options.slice != null) {
68
+ if (options && options.slice !== null && options.slice !== undefined) {
71
69
  if (Array.isArray(options.slice)) return lines.slice(options.slice[0], options.slice[1]);
72
70
  return lines.slice(0, options.slice);
73
71
  }