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

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