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

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 (64) 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 -59
  5. package/dist/core.d.ts +45 -59
  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 +465 -0
  12. package/dist/errors.d.ts +465 -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/rdt-hook.cjs +1 -1
  23. package/dist/rdt-hook.js +1 -1
  24. package/dist/source.cjs +14 -4
  25. package/dist/source.d.cts +27 -9
  26. package/dist/source.d.ts +27 -9
  27. package/dist/source.js +14 -4
  28. package/package.json +22 -16
  29. package/src/core.ts +412 -316
  30. package/src/errors.ts +99 -0
  31. package/src/rdt-hook.ts +163 -151
  32. package/src/react-internals/generated/react-work-tags.ts +314 -0
  33. package/src/react-internals/index.ts +67 -0
  34. package/src/react-internals/semver.ts +78 -0
  35. package/src/react-internals/types.ts +269 -0
  36. package/src/source/constants.ts +1 -1
  37. package/src/source/error-stack.ts +11 -0
  38. package/src/source/get-display-name-from-source.ts +1 -1
  39. package/src/source/get-source.ts +4 -3
  40. package/src/source/index.ts +9 -1
  41. package/src/source/inspect-hooks.ts +199 -165
  42. package/src/source/owner-stack.ts +87 -113
  43. package/src/source/parse-debug-stack.ts +4 -4
  44. package/src/source/parse-hook-names.ts +1 -1
  45. package/src/source/parse-stack.ts +1 -1
  46. package/src/source/symbolication.ts +476 -88
  47. package/dist/get-source.cjs +0 -19
  48. package/dist/get-source.js +0 -19
  49. package/dist/react-refresh.cjs +0 -9
  50. package/dist/react-refresh.d.cts +0 -66
  51. package/dist/react-refresh.d.ts +0 -66
  52. package/dist/react-refresh.js +0 -9
  53. package/dist/unsubscribe.d.cts +0 -298
  54. package/dist/unsubscribe.d.ts +0 -298
  55. package/src/react-refresh/constants.ts +0 -9
  56. package/src/react-refresh/detect-hmr-transport.ts +0 -33
  57. package/src/react-refresh/index.ts +0 -173
  58. package/src/react-refresh/metro-hmr-transport.ts +0 -188
  59. package/src/react-refresh/next-webpack-hmr-transport.ts +0 -72
  60. package/src/react-refresh/normalize-hmr-file-path.ts +0 -24
  61. package/src/react-refresh/types.ts +0 -7
  62. package/src/react-refresh/vite-hmr-transport.ts +0 -116
  63. package/src/types.ts +0 -438
  64. package/src/unsubscribe.ts +0 -17
@@ -1,33 +1,26 @@
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 {
22
10
  SERVER_FRAME_MARKER,
23
11
  SERVER_ENV_PATTERN,
24
12
  SERVER_COMPONENT_URL_PREFIXES,
25
13
  } from "./constants.js";
26
-
14
+ import { getPrepareStackTrace, setPrepareStackTrace } from "./error-stack.js";
27
15
  import { parseDebugStack } from "./parse-debug-stack.js";
28
16
  import { parseStack, StackFrame } from "./parse-stack.js";
29
17
  import { symbolicateStack } from "./symbolication.js";
30
18
 
19
+ interface RendererDispatcherSnapshot {
20
+ currentDispatcherRef: RendererDispatcherRef;
21
+ value: unknown;
22
+ }
23
+
31
24
  export const hasDebugStack = (
32
25
  fiber: Fiber,
33
26
  ): fiber is Fiber & {
@@ -37,13 +30,10 @@ export const hasDebugStack = (
37
30
  };
38
31
 
39
32
  const isFiberOwner = (owner: Fiber | ServerComponentInfo): owner is Fiber =>
40
- typeof (owner as Fiber).tag === "number";
33
+ "tag" in owner && typeof owner.tag === "number";
41
34
 
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
35
  const getDebugOwner = (fiber: Fiber): Fiber | ServerComponentInfo | undefined =>
46
- fiber._debugOwner as Fiber | ServerComponentInfo | undefined;
36
+ fiber._debugOwner ?? undefined;
47
37
 
48
38
  /**
49
39
  * Locates a frame inside the fiber's own function body without invoking it:
@@ -90,26 +80,41 @@ export const getDefinitionFrameFromOwnedChild = (fiber: Fiber): StackFrame | nul
90
80
  return null;
91
81
  };
92
82
 
93
- const getCurrentDispatcher = (): null | React.RefObject<unknown> => {
83
+ const getRendererDispatcherRefs = (): RendererDispatcherRef[] => {
94
84
  const rdtHook = getRDTHook();
95
- for (const renderer of [...Array.from(_renderers), ...Array.from(rdtHook.renderers.values())]) {
85
+ const renderers = new Set([..._renderers, ...rdtHook.renderers.values()]);
86
+ const currentDispatcherRefs: RendererDispatcherRef[] = [];
87
+ const seenCurrentDispatcherRefs = new Set<object>();
88
+ for (const renderer of renderers) {
96
89
  const currentDispatcherRef = renderer.currentDispatcherRef;
97
- if (currentDispatcherRef && typeof currentDispatcherRef === "object") {
98
- return "H" in currentDispatcherRef ? currentDispatcherRef.H : currentDispatcherRef.current;
90
+ if (!currentDispatcherRef || seenCurrentDispatcherRefs.has(currentDispatcherRef)) continue;
91
+ seenCurrentDispatcherRefs.add(currentDispatcherRef);
92
+ currentDispatcherRefs.push(currentDispatcherRef);
93
+ }
94
+ return currentDispatcherRefs;
95
+ };
96
+
97
+ const clearRendererDispatchers = (): RendererDispatcherSnapshot[] => {
98
+ const dispatcherSnapshots: RendererDispatcherSnapshot[] = [];
99
+ for (const currentDispatcherRef of getRendererDispatcherRefs()) {
100
+ const value =
101
+ "H" in currentDispatcherRef ? currentDispatcherRef.H : currentDispatcherRef.current;
102
+ dispatcherSnapshots.push({ currentDispatcherRef, value });
103
+ if ("H" in currentDispatcherRef) {
104
+ currentDispatcherRef.H = null;
105
+ } else {
106
+ currentDispatcherRef.current = null;
99
107
  }
100
108
  }
101
- return null;
109
+ return dispatcherSnapshots;
102
110
  };
103
111
 
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
- }
112
+ const restoreRendererDispatchers = (dispatcherSnapshots: RendererDispatcherSnapshot[]): void => {
113
+ for (const { currentDispatcherRef, value } of dispatcherSnapshots) {
114
+ if ("H" in currentDispatcherRef) {
115
+ currentDispatcherRef.H = value;
116
+ } else {
117
+ currentDispatcherRef.current = value;
113
118
  }
114
119
  }
115
120
  };
@@ -146,49 +151,31 @@ const describeNativeComponentFrame = (
146
151
  return cachedFrame;
147
152
  }
148
153
 
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;
154
+ const previousPrepareStackTrace = getPrepareStackTrace();
155
+ setPrepareStackTrace(undefined);
152
156
  reEntry = true;
153
157
 
154
- const previousDispatcher = getCurrentDispatcher();
155
- setCurrentDispatcher(null);
158
+ const dispatcherSnapshots = clearRendererDispatchers();
156
159
  const previousConsoleError = console.error;
157
160
  const previousConsoleWarn = console.warn;
158
161
  console.error = () => {};
159
162
  console.warn = () => {};
160
163
  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
- */
164
+ // HACK: Run the control and sample under the same property name so their stacks share a frame.
172
165
  const RunInRootFrame = {
173
- DetermineComponentFrameRoot() {
166
+ DetermineComponentFrameRoot(this: void) {
174
167
  let control: unknown;
175
168
  try {
176
- // This should throw.
177
169
  if (construct) {
178
- // Something should be setting the props in the constructor.
179
170
  const ThrowingConstructor = function () {
180
171
  throw Error();
181
172
  };
182
173
  Object.defineProperty(ThrowingConstructor.prototype, "props", {
183
174
  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
175
  throw Error();
187
176
  },
188
177
  });
189
178
  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
179
  try {
193
180
  Reflect.construct(ThrowingConstructor, []);
194
181
  } catch (caughtError) {
@@ -197,13 +184,11 @@ const describeNativeComponentFrame = (
197
184
  Reflect.construct(component, [], ThrowingConstructor);
198
185
  } else {
199
186
  try {
200
- // @ts-expect-error -- ThrowingConstructor is a constructor function
201
- ThrowingConstructor.call();
187
+ Function.prototype.apply.call(ThrowingConstructor, undefined, []);
202
188
  } catch (caughtError) {
203
189
  control = caughtError;
204
190
  }
205
- // @ts-expect-error -- ThrowingConstructor is a constructor function
206
- component.call(ThrowingConstructor.prototype);
191
+ Function.prototype.apply.call(component, ThrowingConstructor.prototype, []);
207
192
  }
208
193
  } else {
209
194
  try {
@@ -211,22 +196,17 @@ const describeNativeComponentFrame = (
211
196
  } catch (caughtError) {
212
197
  control = caughtError;
213
198
  }
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(() => {});
199
+ const maybePromise = Function.prototype.apply.call(component, undefined, []);
200
+ if (
201
+ typeof maybePromise === "object" &&
202
+ maybePromise !== null &&
203
+ "catch" in maybePromise &&
204
+ typeof maybePromise.catch === "function"
205
+ ) {
206
+ maybePromise.catch(() => undefined);
226
207
  }
227
208
  }
228
209
  } catch (sample: unknown) {
229
- // This is inlined manually because closure doesn't do it for us.
230
210
  if (
231
211
  sample instanceof Error &&
232
212
  control instanceof Error &&
@@ -239,21 +219,18 @@ const describeNativeComponentFrame = (
239
219
  },
240
220
  };
241
221
 
242
- // @ts-expect-error --- displayName is not a property of the function
243
- RunInRootFrame.DetermineComponentFrameRoot.displayName = "DetermineComponentFrameRoot";
222
+ Object.defineProperty(RunInRootFrame.DetermineComponentFrameRoot, "displayName", {
223
+ value: "DetermineComponentFrameRoot",
224
+ });
244
225
  const namePropDescriptor = Object.getOwnPropertyDescriptor(
245
226
  // eslint-disable-next-line @typescript-eslint/unbound-method
246
227
  RunInRootFrame.DetermineComponentFrameRoot,
247
228
  "name",
248
229
  );
249
- // Before ES6, the `name` property was not configurable.
250
230
  if (namePropDescriptor?.configurable) {
251
- // V8 utilizes a function's `name` property when generating a stack trace.
252
231
  Object.defineProperty(
253
232
  // eslint-disable-next-line @typescript-eslint/unbound-method
254
233
  RunInRootFrame.DetermineComponentFrameRoot,
255
- // Configurable properties can be updated even if its writable descriptor
256
- // is set to `false`.
257
234
  "name",
258
235
  { value: "DetermineComponentFrameRoot" },
259
236
  );
@@ -338,9 +315,9 @@ const describeNativeComponentFrame = (
338
315
  } finally {
339
316
  reEntry = false;
340
317
 
341
- Error.prepareStackTrace = previousPrepareStackTrace;
318
+ setPrepareStackTrace(previousPrepareStackTrace);
342
319
 
343
- setCurrentDispatcher(previousDispatcher);
320
+ restoreRendererDispatchers(dispatcherSnapshots);
344
321
  console.error = previousConsoleError;
345
322
  console.warn = previousConsoleWarn;
346
323
  }
@@ -353,35 +330,34 @@ const describeNativeComponentFrame = (
353
330
 
354
331
  // https://github.com/facebook/react/blob/ac3e705a18696168acfcaed39dce0cfaa6be8836/packages/react-reconciler/src/ReactFiberComponentStack.js#L180
355
332
  export const describeFiber = (fiber: Fiber, childFiber: Fiber | null): string => {
356
- const tag = fiber.tag as number;
357
333
  let stackFrame = "";
358
- switch (tag) {
359
- case ActivityComponentTag:
334
+ const workTags = getReactWorkTagsForFiber(fiber);
335
+ switch (fiber.tag) {
336
+ case workTags.ActivityComponent:
360
337
  stackFrame = describeBuiltInComponentFrame("Activity");
361
338
  break;
362
- case ClassComponentTag:
339
+ case workTags.ClassComponent:
363
340
  stackFrame = describeNativeComponentFrame(fiber.type, true);
364
341
  break;
365
- case ForwardRefTag:
366
- stackFrame = describeNativeComponentFrame(
367
- (fiber.type as { render: React.ComponentType<unknown> }).render,
368
- false,
369
- );
342
+ case workTags.ForwardRef:
343
+ stackFrame = describeNativeComponentFrame(getType(fiber.type) ?? fiber.type, false);
370
344
  break;
371
- case FunctionComponentTag:
372
- case SimpleMemoComponentTag:
345
+ case workTags.FunctionComponent:
346
+ case workTags.SimpleMemoComponent:
373
347
  stackFrame = describeNativeComponentFrame(fiber.type, false);
374
348
  break;
375
- case HostComponentTag:
376
- case HostHoistableTag:
377
- case HostSingletonTag:
378
- stackFrame = describeBuiltInComponentFrame(fiber.type as string);
349
+ case workTags.HostComponent:
350
+ case workTags.HostHoistable:
351
+ case workTags.HostSingleton:
352
+ if (typeof fiber.type === "string") {
353
+ stackFrame = describeBuiltInComponentFrame(fiber.type);
354
+ }
379
355
  break;
380
- case LazyComponentTag:
356
+ case workTags.LazyComponent:
381
357
  // TODO: When we support Thenables as component types we should rename this.
382
358
  stackFrame = describeBuiltInComponentFrame("Lazy");
383
359
  break;
384
- case SuspenseComponentTag:
360
+ case workTags.SuspenseComponent:
385
361
  if (fiber.child !== childFiber && childFiber !== null) {
386
362
  // If we came from the second Fiber then we're in the Suspense Fallback.
387
363
  stackFrame = describeBuiltInComponentFrame("Suspense Fallback");
@@ -389,10 +365,10 @@ export const describeFiber = (fiber: Fiber, childFiber: Fiber | null): string =>
389
365
  stackFrame = describeBuiltInComponentFrame("Suspense");
390
366
  }
391
367
  break;
392
- case SuspenseListComponentTag:
368
+ case workTags.SuspenseListComponent:
393
369
  stackFrame = describeBuiltInComponentFrame("SuspenseList");
394
370
  break;
395
- case ViewTransitionComponentTag:
371
+ case workTags.ViewTransitionComponent:
396
372
  // Note: enableViewTransition feature flag is not available in this codebase,
397
373
  // so we'll always include ViewTransition
398
374
  stackFrame = describeBuiltInComponentFrame("ViewTransition");
@@ -421,8 +397,8 @@ export const getFallbackParentStack = (thisFiber: Fiber): string => {
421
397
  // Since we don't have __DEV__ in this codebase, we'll check for _debugInfo
422
398
  const debugInfo = currentFiber._debugInfo;
423
399
  if (debugInfo && Array.isArray(debugInfo)) {
424
- for (let i = debugInfo.length - 1; i >= 0; i--) {
425
- const debugEntry = debugInfo[i];
400
+ for (let debugInfoIndex = debugInfo.length - 1; debugInfoIndex >= 0; debugInfoIndex--) {
401
+ const debugEntry = debugInfo[debugInfoIndex];
426
402
  if (typeof debugEntry.name === "string") {
427
403
  componentStack += describeDebugInfoFrame(debugEntry.name, debugEntry.env);
428
404
  }
@@ -460,14 +436,10 @@ export const getFallbackParentStack = (thisFiber: Fiber): string => {
460
436
  * @see https://github.com/facebook/react/blob/main/packages/react-devtools-shared/src/backend/shared/DevToolsOwnerStack.js#L12
461
437
  */
462
438
  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
439
  let formattedStack = stack;
467
440
  if (!formattedStack) {
468
441
  return "";
469
442
  }
470
- Error.prepareStackTrace = prevPrepareStackTrace;
471
443
 
472
444
  if (formattedStack.startsWith("Error: react-stack-top-frame\n")) {
473
445
  // V8's default formatting prefixes with the error message which we
@@ -660,7 +632,9 @@ export const getParentStack = async (
660
632
  const enrichedStackFrames = fallbackStackFrames.map((stackFrame): StackFrame => {
661
633
  const isServerFrame =
662
634
  (stackFrame.source?.includes(SERVER_FRAME_MARKER) ?? false) ||
663
- (stackFrame.source != null && SERVER_ENV_PATTERN.test(stackFrame.source));
635
+ (stackFrame.source !== null &&
636
+ stackFrame.source !== undefined &&
637
+ SERVER_ENV_PATTERN.test(stackFrame.source));
664
638
 
665
639
  if (isServerFrame) {
666
640
  return getEnrichedServerStackFrame(
@@ -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;
@@ -67,7 +67,7 @@ export const extractLocation = (
67
67
  };
68
68
 
69
69
  const applySlice = <T>(lines: T[], options?: ParseOptions): T[] => {
70
- if (options && options.slice != null) {
70
+ if (options && options.slice !== null && options.slice !== undefined) {
71
71
  if (Array.isArray(options.slice)) return lines.slice(options.slice[0], options.slice[1]);
72
72
  return lines.slice(0, options.slice);
73
73
  }