bippy 0.6.1-dev.d7876ea → 0.6.1-dev.f9a65c0

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 (50) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +211 -445
  3. package/dist/core.cjs +1 -1
  4. package/dist/core.d.cts +25 -75
  5. package/dist/core.d.ts +25 -75
  6. package/dist/core.js +1 -1
  7. package/dist/core2.cjs +1 -1
  8. package/dist/core2.d.cts +11 -3
  9. package/dist/core2.d.ts +11 -3
  10. package/dist/core2.js +1 -1
  11. package/dist/errors.d.cts +33 -59
  12. package/dist/errors.d.ts +33 -59
  13. package/dist/index.cjs +1 -1
  14. package/dist/index.d.cts +14 -3
  15. package/dist/index.d.ts +14 -3
  16. package/dist/index.js +1 -1
  17. package/dist/install-hook-only.cjs +1 -1
  18. package/dist/install-hook-only.d.cts +8 -0
  19. package/dist/install-hook-only.d.ts +8 -0
  20. package/dist/install-hook-only.js +1 -1
  21. package/dist/rdt-hook.cjs +1 -1
  22. package/dist/rdt-hook.js +1 -1
  23. package/dist/source.cjs +12 -13
  24. package/dist/source.d.cts +85 -77
  25. package/dist/source.d.ts +85 -77
  26. package/dist/source.js +12 -13
  27. package/package.json +6 -3
  28. package/src/core.ts +146 -579
  29. package/src/errors.ts +0 -65
  30. package/src/index.ts +1 -0
  31. package/src/install-hook-only.ts +2 -2
  32. package/src/rdt-hook.ts +39 -45
  33. package/src/{generated/react-work-tags.js → react-internals/generated/react-work-tags.ts} +85 -7
  34. package/src/{react-internals.ts → react-internals/index.ts} +6 -4
  35. package/src/{semver.ts → react-internals/semver.ts} +2 -0
  36. package/src/{types.ts → react-internals/types.ts} +1 -84
  37. package/src/react.ts +76 -0
  38. package/src/source/get-display-name-from-source.ts +44 -40
  39. package/src/source/get-source.ts +42 -26
  40. package/src/source/index.ts +2 -0
  41. package/src/source/inspect-hooks.ts +74 -96
  42. package/src/source/owner-stack.ts +66 -91
  43. package/src/source/parse-hook-names.ts +14 -53
  44. package/src/source/parse-stack.ts +14 -31
  45. package/src/source/renderer-dispatchers.ts +30 -0
  46. package/src/source/symbolication.ts +421 -108
  47. package/dist/index.iife.js +0 -9
  48. package/dist/install-hook-only.iife.js +0 -9
  49. package/src/generated/react-work-tags.d.ts +0 -261
  50. package/src/unsubscribe.ts +0 -17
@@ -1,16 +1,25 @@
1
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";
2
+ import { getReactWorkTagsForFiber } from "../react-internals/index.js";
3
+ import type {
4
+ Fiber,
5
+ RendererDispatcherRef,
6
+ ServerComponentInfo,
7
+ } from "../react-internals/index.js";
5
8
  import {
9
+ REACT_STACK_BOTTOM_FRAME_PATTERNS,
6
10
  SERVER_FRAME_MARKER,
7
11
  SERVER_ENV_PATTERN,
8
12
  SERVER_COMPONENT_URL_PREFIXES,
9
13
  } from "./constants.js";
10
14
  import { getPrepareStackTrace, setPrepareStackTrace } from "./error-stack.js";
11
15
  import { parseDebugStack } from "./parse-debug-stack.js";
12
- import { parseStack, StackFrame } from "./parse-stack.js";
13
- 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";
14
23
 
15
24
  interface RendererDispatcherSnapshot {
16
25
  currentDispatcherRef: RendererDispatcherRef;
@@ -28,9 +37,6 @@ export const hasDebugStack = (
28
37
  const isFiberOwner = (owner: Fiber | ServerComponentInfo): owner is Fiber =>
29
38
  "tag" in owner && typeof owner.tag === "number";
30
39
 
31
- const getDebugOwner = (fiber: Fiber): Fiber | ServerComponentInfo | undefined =>
32
- fiber._debugOwner ?? undefined;
33
-
34
40
  /**
35
41
  * Locates a frame inside the fiber's own function body without invoking it:
36
42
  * any child fiber owned by this fiber was created by JSX inside its body, so
@@ -76,42 +82,18 @@ export const getDefinitionFrameFromOwnedChild = (fiber: Fiber): StackFrame | nul
76
82
  return null;
77
83
  };
78
84
 
79
- const getRendererDispatcherRefs = (): RendererDispatcherRef[] => {
80
- const rdtHook = getRDTHook();
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) {
85
- const currentDispatcherRef = renderer.currentDispatcherRef;
86
- if (!currentDispatcherRef || seenCurrentDispatcherRefs.has(currentDispatcherRef)) continue;
87
- seenCurrentDispatcherRefs.add(currentDispatcherRef);
88
- currentDispatcherRefs.push(currentDispatcherRef);
89
- }
90
- return currentDispatcherRefs;
91
- };
92
-
93
85
  const clearRendererDispatchers = (): RendererDispatcherSnapshot[] => {
94
86
  const dispatcherSnapshots: RendererDispatcherSnapshot[] = [];
95
87
  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;
103
- }
88
+ dispatcherSnapshots.push({ currentDispatcherRef, value: readDispatcher(currentDispatcherRef) });
89
+ writeDispatcher(currentDispatcherRef, null);
104
90
  }
105
91
  return dispatcherSnapshots;
106
92
  };
107
93
 
108
94
  const restoreRendererDispatchers = (dispatcherSnapshots: RendererDispatcherSnapshot[]): void => {
109
95
  for (const { currentDispatcherRef, value } of dispatcherSnapshots) {
110
- if ("H" in currentDispatcherRef) {
111
- currentDispatcherRef.H = value;
112
- } else {
113
- currentDispatcherRef.current = value;
114
- }
96
+ writeDispatcher(currentDispatcherRef, value);
115
97
  }
116
98
  };
117
99
 
@@ -290,6 +272,10 @@ const describeNativeComponentFrame = (
290
272
  if (controlIndex < 0 || sampleLines[sampleIndex] !== controlLines[controlIndex]) {
291
273
  // V8 adds a "new" prefix for native classes. Let's remove it to make it prettier.
292
274
  let stackFrame = `\n${sampleLines[sampleIndex].replace(" at new ", " at ")}`;
275
+ const [parsedStackFrame] = parseStack(stackFrame);
276
+ if (!parsedStackFrame?.fileName) {
277
+ continue;
278
+ }
293
279
 
294
280
  const displayName = getDisplayName(component);
295
281
  // If our component frame is labeled "<anonymous>"
@@ -318,7 +304,7 @@ const describeNativeComponentFrame = (
318
304
  console.warn = previousConsoleWarn;
319
305
  }
320
306
 
321
- const componentName = component ? getDisplayName(component) : "";
307
+ const componentName = getDisplayName(component);
322
308
  const syntheticFrame = componentName ? describeBuiltInComponentFrame(componentName) : "";
323
309
  componentFrameCache.set(component, syntheticFrame);
324
310
  return syntheticFrame;
@@ -340,7 +326,7 @@ export const describeFiber = (fiber: Fiber, childFiber: Fiber | null): string =>
340
326
  break;
341
327
  case workTags.FunctionComponent:
342
328
  case workTags.SimpleMemoComponent:
343
- stackFrame = describeNativeComponentFrame(fiber.type, false);
329
+ stackFrame = describeNativeComponentFrame(getType(fiber.type) ?? fiber.type, false);
344
330
  break;
345
331
  case workTags.HostComponent:
346
332
  case workTags.HostHoistable:
@@ -407,7 +393,7 @@ export const getFallbackParentStack = (thisFiber: Fiber): string => {
407
393
  return componentStack;
408
394
  } catch (error) {
409
395
  if (error instanceof Error) {
410
- return `\nError generating stack: ${error.message}\n${error.stack}`;
396
+ return `\nBippy couldn’t generate the stack: ${error.message}\n${error.stack}`;
411
397
  }
412
398
  return "";
413
399
  }
@@ -448,8 +434,7 @@ export const formatOwnerStack = (stack: string): string => {
448
434
  formattedStack = formattedStack.slice(firstNewlineIndex + 1);
449
435
  }
450
436
  let bottomFrameIndex = Math.max(
451
- formattedStack.indexOf("react_stack_bottom_frame"),
452
- formattedStack.indexOf("react-stack-bottom-frame"),
437
+ ...REACT_STACK_BOTTOM_FRAME_PATTERNS.map((pattern) => formattedStack.indexOf(pattern)),
453
438
  );
454
439
  if (bottomFrameIndex !== -1) {
455
440
  bottomFrameIndex = formattedStack.lastIndexOf("\n", bottomFrameIndex);
@@ -466,11 +451,6 @@ export const formatOwnerStack = (stack: string): string => {
466
451
  return formattedStack;
467
452
  };
468
453
 
469
- interface DebugStackEntry {
470
- componentName: string;
471
- stackFrames: StackFrame[];
472
- }
473
-
474
454
  const isReactServerComponentFrame = (stackFrame: StackFrame): boolean =>
475
455
  Boolean(
476
456
  stackFrame.functionName && stackFrame.fileName && isServerComponentUrl(stackFrame.fileName),
@@ -482,24 +462,22 @@ const areStackFramesEqual = (firstFrame: StackFrame, secondFrame: StackFrame): b
482
462
  firstFrame.columnNumber === secondFrame.columnNumber;
483
463
 
484
464
  const buildFunctionNameToRscFramesMap = (
485
- debugStackEntries: DebugStackEntry[],
465
+ debugStackFrames: StackFrame[],
486
466
  ): Map<string, StackFrame[]> => {
487
467
  const functionNameToRscFrames = new Map<string, StackFrame[]>();
488
468
 
489
- for (const debugStackEntry of debugStackEntries) {
490
- for (const stackFrame of debugStackEntry.stackFrames) {
491
- if (!isReactServerComponentFrame(stackFrame)) continue;
469
+ for (const stackFrame of debugStackFrames) {
470
+ if (!isReactServerComponentFrame(stackFrame)) continue;
492
471
 
493
- const functionName = stackFrame.functionName!;
494
- const framesForFunction = functionNameToRscFrames.get(functionName) ?? [];
495
- const isDuplicateFrame = framesForFunction.some((existingFrame) =>
496
- areStackFramesEqual(existingFrame, stackFrame),
497
- );
472
+ const functionName = stackFrame.functionName!;
473
+ const framesForFunction = functionNameToRscFrames.get(functionName) ?? [];
474
+ const isDuplicateFrame = framesForFunction.some((existingFrame) =>
475
+ areStackFramesEqual(existingFrame, stackFrame),
476
+ );
498
477
 
499
- if (!isDuplicateFrame) {
500
- framesForFunction.push(stackFrame);
501
- functionNameToRscFrames.set(functionName, framesForFunction);
502
- }
478
+ if (!isDuplicateFrame) {
479
+ framesForFunction.push(stackFrame);
480
+ functionNameToRscFrames.set(functionName, framesForFunction);
503
481
  }
504
482
  }
505
483
 
@@ -560,7 +538,7 @@ const getOwnerStackFromDebugStacks = (fiber: Fiber): StackFrame[] => {
560
538
  while (owner) {
561
539
  if (isFiberOwner(owner)) {
562
540
  const ownerFiber: Fiber = owner;
563
- owner = getDebugOwner(ownerFiber);
541
+ owner = ownerFiber._debugOwner;
564
542
  if (owner && hasDebugStack(ownerFiber)) {
565
543
  const { frames, isTrusted } = parseDebugStack(ownerFiber._debugStack);
566
544
  if (isTrusted) {
@@ -584,53 +562,39 @@ const getOwnerStackFromDebugStacks = (fiber: Fiber): StackFrame[] => {
584
562
  return ownerStackFrames;
585
563
  };
586
564
 
587
- const getDebugStackEntries = (rootFiber: Fiber): DebugStackEntry[] => {
588
- const debugStackEntries: DebugStackEntry[] = [];
565
+ const getDebugStackFrames = (rootFiber: Fiber): StackFrame[] => {
566
+ const debugStackFrames: StackFrame[] = [];
589
567
 
590
568
  traverseFiber(
591
569
  rootFiber,
592
570
  (currentFiber) => {
593
571
  if (!hasDebugStack(currentFiber)) return;
594
572
 
595
- const componentName =
596
- typeof currentFiber.type !== "string"
597
- ? getDisplayName(currentFiber.type) || "<anonymous>"
598
- : currentFiber.type;
599
-
600
- debugStackEntries.push({
601
- componentName,
602
- stackFrames: parseStack(formatOwnerStack(currentFiber._debugStack?.stack)),
603
- });
573
+ const { frames, isTrusted } = parseDebugStack(currentFiber._debugStack);
574
+ if (isTrusted) {
575
+ debugStackFrames.push(...frames);
576
+ }
604
577
  },
605
578
  true,
606
579
  );
607
580
 
608
- return debugStackEntries;
581
+ return debugStackFrames;
609
582
  };
610
583
 
611
584
  /**
612
- * Returns a stack of ALL ancestor components in the render tree (the fiber's
613
- * `return` chain), including wrappers that render `{children}` without having
614
- * created this fiber's JSX. Locations come from re-invoking each component
615
- * with a throwing dispatcher; server frames are enriched from debug stacks by
616
- * 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.
617
588
  */
618
- export const getParentStack = async (
619
- fiber: Fiber,
620
- shouldCache = true,
621
- fetchFunction?: (url: string) => Promise<Response>,
622
- ): Promise<StackFrame[]> => {
623
- const debugStackEntries = getDebugStackEntries(fiber);
589
+ export const getRawParentStack = (fiber: Fiber): StackFrame[] => {
590
+ const debugStackFrames = getDebugStackFrames(fiber);
624
591
  const fallbackStackFrames = parseStack(getFallbackParentStack(fiber));
625
- const functionNameToRscFrames = buildFunctionNameToRscFramesMap(debugStackEntries);
592
+ const functionNameToRscFrames = buildFunctionNameToRscFramesMap(debugStackFrames);
626
593
  const functionNameToUsageIndex = new Map<string, number>();
627
594
 
628
595
  const enrichedStackFrames = fallbackStackFrames.map((stackFrame): StackFrame => {
629
596
  const isServerFrame =
630
- (stackFrame.source?.includes(SERVER_FRAME_MARKER) ?? false) ||
631
- (stackFrame.source !== null &&
632
- stackFrame.source !== undefined &&
633
- SERVER_ENV_PATTERN.test(stackFrame.source));
597
+ stackFrame.source !== undefined && SERVER_ENV_PATTERN.test(stackFrame.source);
634
598
 
635
599
  if (isServerFrame) {
636
600
  return getEnrichedServerStackFrame(
@@ -643,15 +607,26 @@ export const getParentStack = async (
643
607
  return stackFrame;
644
608
  });
645
609
 
646
- const deduplicatedStackFrames = enrichedStackFrames.filter((stackFrame, index, frames) => {
610
+ return enrichedStackFrames.filter((stackFrame, index, frames) => {
647
611
  if (index === 0) return true;
648
612
  const previousFrame = frames[index - 1];
649
613
  return stackFrame.functionName !== previousFrame.functionName;
650
614
  });
651
-
652
- return symbolicateStack(deduplicatedStackFrames, shouldCache, fetchFunction);
653
615
  };
654
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
+
655
630
  // an owner frame is only actionable if it can point an editor somewhere:
656
631
  // it needs a file location and must not be ignore-listed bundler/framework code
657
632
  const isLocatableFrame = (stackFrame: StackFrame): boolean =>
@@ -670,7 +645,7 @@ const isLocatableFrame = (stackFrame: StackFrame): boolean =>
670
645
  export const getOwnerStack = async (
671
646
  fiber: Fiber,
672
647
  shouldCache = true,
673
- fetchFunction?: (url: string) => Promise<Response>,
648
+ fetchFunction?: SourceFetch,
674
649
  ): Promise<StackFrame[]> => {
675
650
  const debugStackFrames = getOwnerStackFromDebugStacks(fiber);
676
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: string[] | 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
+ };