bippy 0.6.1-dev.b88fcb4 → 0.6.1-dev.f9e6c65

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 (45) hide show
  1. package/README.md +159 -487
  2. package/dist/core.cjs +1 -1
  3. package/dist/core.d.cts +8 -54
  4. package/dist/core.d.ts +8 -54
  5. package/dist/core.js +1 -1
  6. package/dist/core2.cjs +1 -1
  7. package/dist/core2.d.cts +3 -3
  8. package/dist/core2.d.ts +3 -3
  9. package/dist/core2.js +1 -1
  10. package/dist/errors.d.cts +2 -57
  11. package/dist/errors.d.ts +2 -57
  12. package/dist/index.cjs +1 -1
  13. package/dist/index.d.cts +6 -3
  14. package/dist/index.d.ts +6 -3
  15. package/dist/index.js +1 -1
  16. package/dist/install-hook-only.cjs +1 -1
  17. package/dist/install-hook-only.js +1 -1
  18. package/dist/rdt-hook.cjs +1 -1
  19. package/dist/rdt-hook.js +1 -1
  20. package/dist/source.cjs +12 -13
  21. package/dist/source.d.cts +77 -78
  22. package/dist/source.d.ts +77 -78
  23. package/dist/source.js +12 -13
  24. package/package.json +6 -3
  25. package/src/core.ts +101 -530
  26. package/src/errors.ts +0 -65
  27. package/src/index.ts +1 -0
  28. package/src/install-hook-only.ts +2 -2
  29. package/src/rdt-hook.ts +30 -43
  30. package/src/react-internals/generated/react-work-tags.ts +6 -2
  31. package/src/react-internals/index.ts +4 -4
  32. package/src/react-internals/semver.ts +2 -0
  33. package/src/react-internals/types.ts +0 -83
  34. package/src/react.ts +76 -0
  35. package/src/source/get-display-name-from-source.ts +44 -40
  36. package/src/source/get-source.ts +40 -24
  37. package/src/source/index.ts +2 -0
  38. package/src/source/inspect-hooks.ts +70 -91
  39. package/src/source/owner-stack.ts +60 -89
  40. package/src/source/parse-hook-names.ts +14 -53
  41. package/src/source/parse-stack.ts +14 -31
  42. package/src/source/renderer-dispatchers.ts +30 -0
  43. package/src/source/symbolication.ts +312 -111
  44. package/dist/index.iife.js +0 -9
  45. package/dist/install-hook-only.iife.js +0 -9
@@ -1,5 +1,4 @@
1
1
  import { getDisplayName, getType, traverseFiber } from "../core.js";
2
- import { _renderers, getRDTHook } from "../rdt-hook.js";
3
2
  import { getReactWorkTagsForFiber } from "../react-internals/index.js";
4
3
  import type {
5
4
  Fiber,
@@ -7,14 +6,20 @@ import type {
7
6
  ServerComponentInfo,
8
7
  } from "../react-internals/index.js";
9
8
  import {
9
+ REACT_STACK_BOTTOM_FRAME_PATTERNS,
10
10
  SERVER_FRAME_MARKER,
11
11
  SERVER_ENV_PATTERN,
12
12
  SERVER_COMPONENT_URL_PREFIXES,
13
13
  } from "./constants.js";
14
14
  import { getPrepareStackTrace, setPrepareStackTrace } from "./error-stack.js";
15
15
  import { parseDebugStack } from "./parse-debug-stack.js";
16
- import { parseStack, StackFrame } from "./parse-stack.js";
17
- import { symbolicateStack } from "./symbolication.js";
16
+ import { parseStack, type StackFrame } from "./parse-stack.js";
17
+ import {
18
+ getRendererDispatcherRefs,
19
+ readDispatcher,
20
+ writeDispatcher,
21
+ } from "./renderer-dispatchers.js";
22
+ import { symbolicateStack, type SourceFetch } from "./symbolication.js";
18
23
 
19
24
  interface RendererDispatcherSnapshot {
20
25
  currentDispatcherRef: RendererDispatcherRef;
@@ -32,9 +37,6 @@ export const hasDebugStack = (
32
37
  const isFiberOwner = (owner: Fiber | ServerComponentInfo): owner is Fiber =>
33
38
  "tag" in owner && typeof owner.tag === "number";
34
39
 
35
- const getDebugOwner = (fiber: Fiber): Fiber | ServerComponentInfo | undefined =>
36
- fiber._debugOwner ?? undefined;
37
-
38
40
  /**
39
41
  * Locates a frame inside the fiber's own function body without invoking it:
40
42
  * any child fiber owned by this fiber was created by JSX inside its body, so
@@ -80,42 +82,18 @@ export const getDefinitionFrameFromOwnedChild = (fiber: Fiber): StackFrame | nul
80
82
  return null;
81
83
  };
82
84
 
83
- const getRendererDispatcherRefs = (): RendererDispatcherRef[] => {
84
- const rdtHook = getRDTHook();
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) {
89
- const currentDispatcherRef = renderer.currentDispatcherRef;
90
- if (!currentDispatcherRef || seenCurrentDispatcherRefs.has(currentDispatcherRef)) continue;
91
- seenCurrentDispatcherRefs.add(currentDispatcherRef);
92
- currentDispatcherRefs.push(currentDispatcherRef);
93
- }
94
- return currentDispatcherRefs;
95
- };
96
-
97
85
  const clearRendererDispatchers = (): RendererDispatcherSnapshot[] => {
98
86
  const dispatcherSnapshots: RendererDispatcherSnapshot[] = [];
99
87
  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;
107
- }
88
+ dispatcherSnapshots.push({ currentDispatcherRef, value: readDispatcher(currentDispatcherRef) });
89
+ writeDispatcher(currentDispatcherRef, null);
108
90
  }
109
91
  return dispatcherSnapshots;
110
92
  };
111
93
 
112
94
  const restoreRendererDispatchers = (dispatcherSnapshots: RendererDispatcherSnapshot[]): void => {
113
95
  for (const { currentDispatcherRef, value } of dispatcherSnapshots) {
114
- if ("H" in currentDispatcherRef) {
115
- currentDispatcherRef.H = value;
116
- } else {
117
- currentDispatcherRef.current = value;
118
- }
96
+ writeDispatcher(currentDispatcherRef, value);
119
97
  }
120
98
  };
121
99
 
@@ -294,6 +272,10 @@ const describeNativeComponentFrame = (
294
272
  if (controlIndex < 0 || sampleLines[sampleIndex] !== controlLines[controlIndex]) {
295
273
  // V8 adds a "new" prefix for native classes. Let's remove it to make it prettier.
296
274
  let stackFrame = `\n${sampleLines[sampleIndex].replace(" at new ", " at ")}`;
275
+ const [parsedStackFrame] = parseStack(stackFrame);
276
+ if (!parsedStackFrame?.fileName) {
277
+ continue;
278
+ }
297
279
 
298
280
  const displayName = getDisplayName(component);
299
281
  // If our component frame is labeled "<anonymous>"
@@ -322,7 +304,7 @@ const describeNativeComponentFrame = (
322
304
  console.warn = previousConsoleWarn;
323
305
  }
324
306
 
325
- const componentName = component ? getDisplayName(component) : "";
307
+ const componentName = getDisplayName(component);
326
308
  const syntheticFrame = componentName ? describeBuiltInComponentFrame(componentName) : "";
327
309
  componentFrameCache.set(component, syntheticFrame);
328
310
  return syntheticFrame;
@@ -344,7 +326,7 @@ export const describeFiber = (fiber: Fiber, childFiber: Fiber | null): string =>
344
326
  break;
345
327
  case workTags.FunctionComponent:
346
328
  case workTags.SimpleMemoComponent:
347
- stackFrame = describeNativeComponentFrame(fiber.type, false);
329
+ stackFrame = describeNativeComponentFrame(getType(fiber.type) ?? fiber.type, false);
348
330
  break;
349
331
  case workTags.HostComponent:
350
332
  case workTags.HostHoistable:
@@ -411,7 +393,7 @@ export const getFallbackParentStack = (thisFiber: Fiber): string => {
411
393
  return componentStack;
412
394
  } catch (error) {
413
395
  if (error instanceof Error) {
414
- return `\nError generating stack: ${error.message}\n${error.stack}`;
396
+ return `\nBippy couldn’t generate the stack: ${error.message}\n${error.stack}`;
415
397
  }
416
398
  return "";
417
399
  }
@@ -452,8 +434,7 @@ export const formatOwnerStack = (stack: string): string => {
452
434
  formattedStack = formattedStack.slice(firstNewlineIndex + 1);
453
435
  }
454
436
  let bottomFrameIndex = Math.max(
455
- formattedStack.indexOf("react_stack_bottom_frame"),
456
- formattedStack.indexOf("react-stack-bottom-frame"),
437
+ ...REACT_STACK_BOTTOM_FRAME_PATTERNS.map((pattern) => formattedStack.indexOf(pattern)),
457
438
  );
458
439
  if (bottomFrameIndex !== -1) {
459
440
  bottomFrameIndex = formattedStack.lastIndexOf("\n", bottomFrameIndex);
@@ -470,11 +451,6 @@ export const formatOwnerStack = (stack: string): string => {
470
451
  return formattedStack;
471
452
  };
472
453
 
473
- interface DebugStackEntry {
474
- componentName: string;
475
- stackFrames: StackFrame[];
476
- }
477
-
478
454
  const isReactServerComponentFrame = (stackFrame: StackFrame): boolean =>
479
455
  Boolean(
480
456
  stackFrame.functionName && stackFrame.fileName && isServerComponentUrl(stackFrame.fileName),
@@ -486,24 +462,22 @@ const areStackFramesEqual = (firstFrame: StackFrame, secondFrame: StackFrame): b
486
462
  firstFrame.columnNumber === secondFrame.columnNumber;
487
463
 
488
464
  const buildFunctionNameToRscFramesMap = (
489
- debugStackEntries: DebugStackEntry[],
465
+ debugStackFrames: StackFrame[],
490
466
  ): Map<string, StackFrame[]> => {
491
467
  const functionNameToRscFrames = new Map<string, StackFrame[]>();
492
468
 
493
- for (const debugStackEntry of debugStackEntries) {
494
- for (const stackFrame of debugStackEntry.stackFrames) {
495
- if (!isReactServerComponentFrame(stackFrame)) continue;
469
+ for (const stackFrame of debugStackFrames) {
470
+ if (!isReactServerComponentFrame(stackFrame)) continue;
496
471
 
497
- const functionName = stackFrame.functionName!;
498
- const framesForFunction = functionNameToRscFrames.get(functionName) ?? [];
499
- const isDuplicateFrame = framesForFunction.some((existingFrame) =>
500
- areStackFramesEqual(existingFrame, stackFrame),
501
- );
472
+ const functionName = stackFrame.functionName!;
473
+ const framesForFunction = functionNameToRscFrames.get(functionName) ?? [];
474
+ const isDuplicateFrame = framesForFunction.some((existingFrame) =>
475
+ areStackFramesEqual(existingFrame, stackFrame),
476
+ );
502
477
 
503
- if (!isDuplicateFrame) {
504
- framesForFunction.push(stackFrame);
505
- functionNameToRscFrames.set(functionName, framesForFunction);
506
- }
478
+ if (!isDuplicateFrame) {
479
+ framesForFunction.push(stackFrame);
480
+ functionNameToRscFrames.set(functionName, framesForFunction);
507
481
  }
508
482
  }
509
483
 
@@ -564,7 +538,7 @@ const getOwnerStackFromDebugStacks = (fiber: Fiber): StackFrame[] => {
564
538
  while (owner) {
565
539
  if (isFiberOwner(owner)) {
566
540
  const ownerFiber: Fiber = owner;
567
- owner = getDebugOwner(ownerFiber);
541
+ owner = ownerFiber._debugOwner;
568
542
  if (owner && hasDebugStack(ownerFiber)) {
569
543
  const { frames, isTrusted } = parseDebugStack(ownerFiber._debugStack);
570
544
  if (isTrusted) {
@@ -588,53 +562,39 @@ const getOwnerStackFromDebugStacks = (fiber: Fiber): StackFrame[] => {
588
562
  return ownerStackFrames;
589
563
  };
590
564
 
591
- const getDebugStackEntries = (rootFiber: Fiber): DebugStackEntry[] => {
592
- const debugStackEntries: DebugStackEntry[] = [];
565
+ const getDebugStackFrames = (rootFiber: Fiber): StackFrame[] => {
566
+ const debugStackFrames: StackFrame[] = [];
593
567
 
594
568
  traverseFiber(
595
569
  rootFiber,
596
570
  (currentFiber) => {
597
571
  if (!hasDebugStack(currentFiber)) return;
598
572
 
599
- const componentName =
600
- typeof currentFiber.type !== "string"
601
- ? getDisplayName(currentFiber.type) || "<anonymous>"
602
- : currentFiber.type;
603
-
604
- debugStackEntries.push({
605
- componentName,
606
- stackFrames: parseStack(formatOwnerStack(currentFiber._debugStack?.stack)),
607
- });
573
+ const { frames, isTrusted } = parseDebugStack(currentFiber._debugStack);
574
+ if (isTrusted) {
575
+ debugStackFrames.push(...frames);
576
+ }
608
577
  },
609
578
  true,
610
579
  );
611
580
 
612
- return debugStackEntries;
581
+ return debugStackFrames;
613
582
  };
614
583
 
615
584
  /**
616
- * Returns a stack of ALL ancestor components in the render tree (the fiber's
617
- * `return` chain), including wrappers that render `{children}` without having
618
- * created this fiber's JSX. Locations come from re-invoking each component
619
- * with a throwing dispatcher; server frames are enriched from debug stacks by
620
- * name matching. Works on every React version.
585
+ * The unsymbolicated frames behind {@link getParentStack}: bundle-space
586
+ * locations from re-invoking each component with a throwing dispatcher,
587
+ * with server frames enriched from debug stacks by name matching.
621
588
  */
622
- export const getParentStack = async (
623
- fiber: Fiber,
624
- shouldCache = true,
625
- fetchFunction?: (url: string) => Promise<Response>,
626
- ): Promise<StackFrame[]> => {
627
- const debugStackEntries = getDebugStackEntries(fiber);
589
+ export const getRawParentStack = (fiber: Fiber): StackFrame[] => {
590
+ const debugStackFrames = getDebugStackFrames(fiber);
628
591
  const fallbackStackFrames = parseStack(getFallbackParentStack(fiber));
629
- const functionNameToRscFrames = buildFunctionNameToRscFramesMap(debugStackEntries);
592
+ const functionNameToRscFrames = buildFunctionNameToRscFramesMap(debugStackFrames);
630
593
  const functionNameToUsageIndex = new Map<string, number>();
631
594
 
632
595
  const enrichedStackFrames = fallbackStackFrames.map((stackFrame): StackFrame => {
633
596
  const isServerFrame =
634
- (stackFrame.source?.includes(SERVER_FRAME_MARKER) ?? false) ||
635
- (stackFrame.source !== null &&
636
- stackFrame.source !== undefined &&
637
- SERVER_ENV_PATTERN.test(stackFrame.source));
597
+ stackFrame.source !== undefined && SERVER_ENV_PATTERN.test(stackFrame.source);
638
598
 
639
599
  if (isServerFrame) {
640
600
  return getEnrichedServerStackFrame(
@@ -647,15 +607,26 @@ export const getParentStack = async (
647
607
  return stackFrame;
648
608
  });
649
609
 
650
- const deduplicatedStackFrames = enrichedStackFrames.filter((stackFrame, index, frames) => {
610
+ return enrichedStackFrames.filter((stackFrame, index, frames) => {
651
611
  if (index === 0) return true;
652
612
  const previousFrame = frames[index - 1];
653
613
  return stackFrame.functionName !== previousFrame.functionName;
654
614
  });
655
-
656
- return symbolicateStack(deduplicatedStackFrames, shouldCache, fetchFunction);
657
615
  };
658
616
 
617
+ /**
618
+ * Returns a stack of ALL ancestor components in the render tree (the fiber's
619
+ * `return` chain), including wrappers that render `{children}` without having
620
+ * created this fiber's JSX. Locations come from re-invoking each component
621
+ * with a throwing dispatcher; server frames are enriched from debug stacks by
622
+ * name matching. Works on every React version.
623
+ */
624
+ export const getParentStack = async (
625
+ fiber: Fiber,
626
+ shouldCache = true,
627
+ fetchFunction?: SourceFetch,
628
+ ): Promise<StackFrame[]> => symbolicateStack(getRawParentStack(fiber), shouldCache, fetchFunction);
629
+
659
630
  // an owner frame is only actionable if it can point an editor somewhere:
660
631
  // it needs a file location and must not be ignore-listed bundler/framework code
661
632
  const isLocatableFrame = (stackFrame: StackFrame): boolean =>
@@ -674,7 +645,7 @@ const isLocatableFrame = (stackFrame: StackFrame): boolean =>
674
645
  export const getOwnerStack = async (
675
646
  fiber: Fiber,
676
647
  shouldCache = true,
677
- fetchFunction?: (url: string) => Promise<Response>,
648
+ fetchFunction?: SourceFetch,
678
649
  ): Promise<StackFrame[]> => {
679
650
  const debugStackFrames = getOwnerStackFromDebugStacks(fiber);
680
651
  if (debugStackFrames.length > 0) {
@@ -1,5 +1,10 @@
1
1
  import type { HooksNode, HooksTree, HookSource } from "./inspect-hooks.js";
2
- import { getSourceMap, getSourceFromSourceMap, type SourceMap } from "./symbolication.js";
2
+ import {
3
+ getSourceContentFromSourceMap,
4
+ getSourceFromSourceMap,
5
+ getSourceMap,
6
+ type SourceFetch,
7
+ } from "./symbolication.js";
3
8
 
4
9
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
5
10
  export interface HookNames extends Map<string, string> {}
@@ -33,40 +38,6 @@ const flattenHooksTree = (hooksTree: HooksTree): HooksNode[] => {
33
38
  return hooksList;
34
39
  };
35
40
 
36
- const findSourceContentByFileName = (
37
- sources: string[],
38
- sourcesContent: Array<string | null> | undefined,
39
- fileName: string,
40
- ): string | null => {
41
- if (!sourcesContent) return null;
42
- const sourceIndex = sources.indexOf(fileName);
43
- return sourceIndex !== -1 ? (sourcesContent[sourceIndex] ?? null) : null;
44
- };
45
-
46
- const getSourceContentFromSourceMap = (
47
- sourceMap: SourceMap,
48
- originalFileName: string,
49
- ): string | null => {
50
- const directResult = findSourceContentByFileName(
51
- sourceMap.sources,
52
- sourceMap.sourcesContent,
53
- originalFileName,
54
- );
55
- if (directResult) return directResult;
56
-
57
- if (sourceMap.sections) {
58
- for (const section of sourceMap.sections) {
59
- const sectionResult = findSourceContentByFileName(
60
- section.map.sources,
61
- section.map.sourcesContent,
62
- originalFileName,
63
- );
64
- if (sectionResult) return sectionResult;
65
- }
66
- }
67
- return null;
68
- };
69
-
70
41
  const extractVariableNameFromBinding = (binding: string): string | null => {
71
42
  const trimmed = binding.trim();
72
43
  if (trimmed.startsWith("[")) {
@@ -109,9 +80,8 @@ interface ResolvedSource {
109
80
  }
110
81
 
111
82
  interface SourceResolutionContext {
112
- sourceMapsByFile: Map<string, SourceMap | null>;
113
83
  sourceContentCache: Map<string, string | null>;
114
- fetchFn?: (url: string) => Promise<Response>;
84
+ fetchFn?: SourceFetch;
115
85
  }
116
86
 
117
87
  const resolveOriginalSource = async (
@@ -120,25 +90,17 @@ const resolveOriginalSource = async (
120
90
  runtimeColumn: number,
121
91
  context: SourceResolutionContext,
122
92
  ): Promise<ResolvedSource | null> => {
123
- const { sourceMapsByFile, sourceContentCache, fetchFn } = context;
93
+ const { sourceContentCache, fetchFn } = context;
124
94
 
125
- if (!sourceMapsByFile.has(runtimeFileName)) {
126
- sourceMapsByFile.set(runtimeFileName, await getSourceMap(runtimeFileName, true, fetchFn));
127
- }
128
-
129
- const sourceMap = sourceMapsByFile.get(runtimeFileName) ?? null;
95
+ const sourceMap = await getSourceMap(runtimeFileName, true, fetchFn);
130
96
 
131
97
  if (sourceMap) {
132
98
  const originalLocation = getSourceFromSourceMap(sourceMap, runtimeLine, runtimeColumn);
133
99
  if (originalLocation?.fileName && originalLocation.lineNumber !== undefined) {
134
- const cacheKey = `sourcemap:${runtimeFileName}:${originalLocation.fileName}`;
135
- if (!sourceContentCache.has(cacheKey)) {
136
- sourceContentCache.set(
137
- cacheKey,
138
- getSourceContentFromSourceMap(sourceMap, originalLocation.fileName),
139
- );
140
- }
141
- const originalSourceCode = sourceContentCache.get(cacheKey) ?? null;
100
+ const originalSourceCode = getSourceContentFromSourceMap(
101
+ sourceMap,
102
+ originalLocation.fileName,
103
+ );
142
104
  if (originalSourceCode) {
143
105
  return {
144
106
  sourceCode: originalSourceCode,
@@ -173,7 +135,7 @@ const resolveOriginalSource = async (
173
135
 
174
136
  export const parseHookNames = async (
175
137
  hooksTree: HooksTree,
176
- fetchFn?: (url: string) => Promise<Response>,
138
+ fetchFn?: SourceFetch,
177
139
  ): Promise<HookNames> => {
178
140
  const hookNames: HookNames = new Map();
179
141
  const hooksList = flattenHooksTree(hooksTree);
@@ -181,7 +143,6 @@ export const parseHookNames = async (
181
143
  if (hooksList.length === 0) return hookNames;
182
144
 
183
145
  const resolutionContext: SourceResolutionContext = {
184
- sourceMapsByFile: new Map(),
185
146
  sourceContentCache: new Map(),
186
147
  fetchFn,
187
148
  };
@@ -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);
@@ -16,8 +15,6 @@ export interface StackFrame {
16
15
  }
17
16
 
18
17
  export interface ParseOptions {
19
- slice?: number | [number, number];
20
- allowEmpty?: boolean;
21
18
  includeInElement?: boolean;
22
19
  }
23
20
 
@@ -31,22 +28,22 @@ export const parseStack = (stackString: string, options?: ParseOptions): StackFr
31
28
  const frames: StackFrame[] = [];
32
29
  for (const rawLine of lines) {
33
30
  if (/^\s*at\s+/.test(rawLine)) {
34
- const parsed = parseV8OrIeString(rawLine, undefined)[0];
31
+ const parsed = parseV8OrIeString(rawLine)[0];
35
32
  if (parsed) frames.push(parsed);
36
33
  } else if (/^\s*in\s+/.test(rawLine)) {
37
34
  const elementName = rawLine.replace(/^\s*in\s+/, "").replace(/\s*\(at .*\)$/, "");
38
35
  frames.push({ functionName: elementName, source: rawLine });
39
36
  } else if (rawLine.match(FIREFOX_SAFARI_STACK_REGEXP)) {
40
- const parsed = parseFFOrSafariString(rawLine, undefined)[0];
37
+ const parsed = parseFFOrSafariString(rawLine)[0];
41
38
  if (parsed) frames.push(parsed);
42
39
  }
43
40
  }
44
- return applySlice(frames, options);
41
+ return frames;
45
42
  }
46
43
  if (stackString.match(CHROME_IE_STACK_REGEXP)) {
47
- return parseV8OrIeString(stackString, options);
44
+ return parseV8OrIeString(stackString);
48
45
  }
49
- return parseFFOrSafariString(stackString, options);
46
+ return parseFFOrSafariString(stackString);
50
47
  };
51
48
 
52
49
  export const extractLocation = (
@@ -66,21 +63,10 @@ export const extractLocation = (
66
63
  return [parts[1], parts[2] || undefined, parts[3] || undefined] as const;
67
64
  };
68
65
 
69
- const applySlice = <T>(lines: T[], options?: ParseOptions): T[] => {
70
- if (options && options.slice !== null && options.slice !== undefined) {
71
- if (Array.isArray(options.slice)) return lines.slice(options.slice[0], options.slice[1]);
72
- return lines.slice(0, options.slice);
73
- }
74
- return lines;
75
- };
76
-
77
- export const parseV8OrIeString = (stack: string, options?: ParseOptions): StackFrame[] => {
78
- const filteredLines = applySlice(
79
- stack.split("\n").filter((line) => {
80
- return !!line.match(CHROME_IE_STACK_REGEXP);
81
- }),
82
- options,
83
- );
66
+ export const parseV8OrIeString = (stack: string): StackFrame[] => {
67
+ const filteredLines = stack.split("\n").filter((line) => {
68
+ return !!line.match(CHROME_IE_STACK_REGEXP);
69
+ });
84
70
 
85
71
  return filteredLines.map((line): StackFrame => {
86
72
  let currentLine = line;
@@ -100,7 +86,7 @@ export const parseV8OrIeString = (stack: string, options?: ParseOptions): StackF
100
86
 
101
87
  const locationParts = extractLocation(locationMatch ? locationMatch[1] : sanitizedLine);
102
88
  const functionName = (locationMatch && sanitizedLine) || undefined;
103
- const fileName = ["eval", "<anonymous>"].includes(locationParts[0])
89
+ const fileName = ["eval", "<anonymous>", "(native)"].includes(locationParts[0])
104
90
  ? undefined
105
91
  : locationParts[0];
106
92
 
@@ -114,13 +100,10 @@ export const parseV8OrIeString = (stack: string, options?: ParseOptions): StackF
114
100
  });
115
101
  };
116
102
 
117
- export const parseFFOrSafariString = (stack: string, options?: ParseOptions): StackFrame[] => {
118
- const filteredLines = applySlice(
119
- stack.split("\n").filter((line) => {
120
- return !line.match(SAFARI_NATIVE_CODE_REGEXP);
121
- }),
122
- options,
123
- );
103
+ export const parseFFOrSafariString = (stack: string): StackFrame[] => {
104
+ const filteredLines = stack.split("\n").filter((line) => {
105
+ return !line.match(SAFARI_NATIVE_CODE_REGEXP);
106
+ });
124
107
 
125
108
  return filteredLines.map((line): StackFrame => {
126
109
  let currentLine = line;
@@ -0,0 +1,30 @@
1
+ import type { RendererDispatcherRef } from "../react-internals/index.js";
2
+ import { _renderers, getRDTHook } from "../rdt-hook.js";
3
+
4
+ export const getRendererDispatcherRefs = (): RendererDispatcherRef[] => {
5
+ const rdtHook = getRDTHook();
6
+ const renderers = new Set([..._renderers, ...rdtHook.renderers.values()]);
7
+ const currentDispatcherRefs: RendererDispatcherRef[] = [];
8
+ const seenCurrentDispatcherRefs = new Set<object>();
9
+ for (const renderer of renderers) {
10
+ const currentDispatcherRef = renderer.currentDispatcherRef;
11
+ if (!currentDispatcherRef || seenCurrentDispatcherRefs.has(currentDispatcherRef)) continue;
12
+ seenCurrentDispatcherRefs.add(currentDispatcherRef);
13
+ currentDispatcherRefs.push(currentDispatcherRef);
14
+ }
15
+ return currentDispatcherRefs;
16
+ };
17
+
18
+ export const readDispatcher = (currentDispatcherRef: RendererDispatcherRef): unknown =>
19
+ "H" in currentDispatcherRef ? currentDispatcherRef.H : currentDispatcherRef.current;
20
+
21
+ export const writeDispatcher = (
22
+ currentDispatcherRef: RendererDispatcherRef,
23
+ dispatcher: unknown,
24
+ ): void => {
25
+ if ("H" in currentDispatcherRef) {
26
+ currentDispatcherRef.H = dispatcher;
27
+ } else {
28
+ currentDispatcherRef.current = dispatcher;
29
+ }
30
+ };