bippy 0.7.3-dev.e2b879b → 0.7.3-dev.e6929e3

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.
package/src/react.ts CHANGED
@@ -1,98 +1,162 @@
1
1
  import "./install-hook-only.js";
2
- import * as React from "react";
3
- import { isFiber } from "./core.js";
2
+ import React from "react";
3
+ import { isFiber, traverseFiber } from "./core.js";
4
+ import { _renderers } from "./rdt-hook.js";
5
+ import { getCurrentFiberFromRoot } from "./react-internals/current-fiber.js";
4
6
  import type { Fiber } from "./react-internals/index.js";
5
7
 
6
8
  export type { Fiber } from "./react-internals/index.js";
7
9
 
8
- interface FiberHookEffect {
9
- create: unknown;
10
- next: unknown;
10
+ interface RenderMarker {
11
+ (state: undefined): undefined;
11
12
  }
12
13
 
13
- const preserveState = (state: undefined): undefined => state;
14
- const readEmptySnapshot = (): undefined => undefined;
15
- const unsubscribeFromEmptyStore = (): void => {};
16
- const subscribeToEmptyStore = (): (() => void) => unsubscribeFromEmptyStore;
17
- const useSyncExternalStore: unknown = Reflect.get(React, "useSyncExternalStore");
14
+ interface ReducerQueue {
15
+ lastRenderedReducer: unknown;
16
+ }
18
17
 
19
- const captureFiberFromHook = (useCaptureHook: () => void): Fiber | null => {
20
- const originalBind = Function.prototype.bind;
21
- let capturedFiber: Fiber | null = null;
22
- // HACK: React binds hook callbacks to the rendering Fiber in production but exposes no public API for it.
23
- const bindProxy = new Proxy(originalBind, {
24
- apply: (bind, functionToBind, boundArguments) => {
25
- const fiber = boundArguments[1];
26
- if (!capturedFiber && isFiber(fiber)) {
27
- capturedFiber = fiber;
28
- }
29
- return Reflect.apply(bind, functionToBind, boundArguments);
30
- },
31
- });
32
- Reflect.set(Function.prototype, "bind", bindProxy);
18
+ interface FiberCapture {
19
+ fiber: Fiber;
20
+ queue: ReducerQueue | null;
21
+ }
33
22
 
34
- try {
35
- useCaptureHook();
36
- } finally {
37
- if (Function.prototype.bind === bindProxy) {
38
- Reflect.set(Function.prototype, "bind", originalBind);
39
- }
40
- }
23
+ const isReducerQueue = (value: unknown): value is ReducerQueue =>
24
+ typeof value === "object" && value !== null && "lastRenderedReducer" in value;
41
25
 
42
- return capturedFiber;
26
+ const hasRenderMarker = (fiber: Fiber, renderMarker: RenderMarker): boolean => {
27
+ for (let hook = fiber.memoizedState; hook; hook = hook.next) {
28
+ if (Array.isArray(hook.memoizedState) && hook.memoizedState[0] === renderMarker) return true;
29
+ }
30
+ return false;
43
31
  };
44
32
 
45
- const useExternalStoreCapture = (): void => {
46
- if (typeof useSyncExternalStore !== "function") return;
47
- Reflect.apply(useSyncExternalStore, React, [
48
- subscribeToEmptyStore,
49
- readEmptySnapshot,
50
- readEmptySnapshot,
51
- ]);
33
+ const getReducerQueue = (fiber: Fiber, renderMarker: RenderMarker): ReducerQueue | null => {
34
+ for (let hook = fiber.memoizedState; hook; hook = hook.next) {
35
+ if (isReducerQueue(hook.queue) && hook.queue.lastRenderedReducer === renderMarker)
36
+ return hook.queue;
37
+ }
38
+ return null;
52
39
  };
53
40
 
54
- const useReducerCapture = (): void => {
55
- React.useReducer(preserveState, undefined);
41
+ const getDevToolsFiber = (renderMarker: RenderMarker): Fiber | null => {
42
+ for (const renderer of _renderers) {
43
+ try {
44
+ const fiber = renderer.getCurrentFiber?.();
45
+ if (fiber && isFiber(fiber) && hasRenderMarker(fiber, renderMarker)) return fiber;
46
+ } catch {}
47
+ }
48
+ return null;
56
49
  };
57
50
 
58
- const useFiberWithExternalStore = (): Fiber | undefined =>
59
- captureFiberFromHook(useExternalStoreCapture) ?? undefined;
51
+ const captureReducerFiber = (
52
+ renderMarker: RenderMarker,
53
+ shouldCapture: boolean,
54
+ ): FiberCapture | null => {
55
+ let originalBind: typeof Function.prototype.bind | undefined;
56
+ if (shouldCapture) {
57
+ try {
58
+ originalBind = Function.prototype.bind;
59
+ } catch {}
60
+ }
61
+ if (!originalBind) {
62
+ React.useReducer(renderMarker, undefined);
63
+ return null;
64
+ }
60
65
 
61
- const isHookEffect = (value: unknown): value is FiberHookEffect =>
62
- typeof value === "object" && value !== null && "create" in value && "next" in value;
66
+ let capture: FiberCapture | null = null;
67
+ let isCapturing = true;
68
+ // HACK: Production React binds a new reducer's Fiber and queue; validate the private reducer marker, not argument positions.
69
+ const bindProxy = new Proxy(originalBind, {
70
+ apply: (bind, callback, boundArguments) => {
71
+ if (isCapturing && !capture) {
72
+ try {
73
+ const queue = boundArguments.find(
74
+ (argument): argument is ReducerQueue =>
75
+ isReducerQueue(argument) && argument.lastRenderedReducer === renderMarker,
76
+ );
77
+ if (queue) {
78
+ const fiber = boundArguments.find(
79
+ (argument): argument is Fiber => isFiber(argument) && isFiber(argument.return),
80
+ );
81
+ if (fiber) capture = { fiber, queue };
82
+ }
83
+ } catch {}
84
+ }
85
+ return Reflect.apply(bind, callback, boundArguments);
86
+ },
87
+ });
63
88
 
64
- const hasRenderMarker = (fiber: Fiber, renderMarker: () => void): boolean => {
65
- const lastEffect = fiber.updateQueue?.lastEffect;
66
- if (!isHookEffect(lastEffect)) return false;
67
- let effect = lastEffect;
68
- do {
69
- if (effect.create === renderMarker) return true;
70
- if (!isHookEffect(effect.next)) return false;
71
- effect = effect.next;
72
- } while (effect !== lastEffect);
73
- return false;
89
+ try {
90
+ try {
91
+ Reflect.set(Function.prototype, "bind", bindProxy);
92
+ } catch {}
93
+ React.useReducer(renderMarker, undefined);
94
+ return capture;
95
+ } finally {
96
+ isCapturing = false;
97
+ capture = null;
98
+ try {
99
+ if (Function.prototype.bind === bindProxy) {
100
+ Reflect.set(Function.prototype, "bind", originalBind);
101
+ }
102
+ } catch {}
103
+ }
74
104
  };
75
105
 
76
- // HACK: React 17 only binds the rendering Fiber on mount. Later renders are identified through
77
- // the effect list instead: renderWithHooks clears `updateQueue` on the work-in-progress Fiber,
78
- // so only the Fiber that is rendering right now holds this render's marker effect.
79
- const useFiberWithReducer = (): Fiber | undefined => {
80
- const fiberRef = React.useRef<Fiber | null>(null);
81
- const renderMarker = (): void => {};
82
- React.useEffect(renderMarker, []);
83
- const mountedFiber = captureFiberFromHook(useReducerCapture);
84
- if (mountedFiber) {
85
- fiberRef.current = mountedFiber;
86
- return mountedFiber;
106
+ // HACK: React <16.13 attaches hooks after render; the shared reducer queue validates the root-walk fallback.
107
+ const getRenderingFiberFromRoot = (
108
+ { fiber, queue }: FiberCapture,
109
+ renderMarker: RenderMarker,
110
+ ): Fiber | null => {
111
+ if (queue?.lastRenderedReducer !== renderMarker) return null;
112
+ let rootFiber = fiber;
113
+ while (rootFiber.return) rootFiber = rootFiber.return;
114
+ const root = rootFiber.stateNode;
115
+ if (typeof root !== "object" || root === null || !("current" in root) || !isFiber(root.current)) {
116
+ return null;
87
117
  }
88
- const knownFiber = fiberRef.current;
89
- if (!knownFiber) return undefined;
90
- return (
91
- [knownFiber, knownFiber.alternate].find(
92
- (candidate) => candidate !== null && hasRenderMarker(candidate, renderMarker),
93
- ) ?? undefined
94
- );
118
+ const renderingRoot = root.current.alternate;
119
+ const renderingFiber = getCurrentFiberFromRoot(fiber)?.alternate;
120
+ if (renderingFiber) {
121
+ let ancestor = renderingFiber;
122
+ while (ancestor.return) ancestor = ancestor.return;
123
+ if (ancestor === renderingRoot) return renderingFiber;
124
+ }
125
+ return traverseFiber(renderingRoot, (candidate) => {
126
+ if (candidate !== fiber && candidate !== fiber.alternate) return false;
127
+ let parent = candidate;
128
+ while (parent.return) parent = parent.return;
129
+ return parent === renderingRoot;
130
+ });
95
131
  };
96
132
 
97
- export const useFiber =
98
- typeof useSyncExternalStore === "function" ? useFiberWithExternalStore : useFiberWithReducer;
133
+ export const useFiber = (): Fiber | undefined => {
134
+ "use no memo";
135
+ const fiberRef = React.useRef<FiberCapture | null>(null);
136
+ const renderMarker: RenderMarker = (state) => state;
137
+ React.useMemo(() => renderMarker, [renderMarker]);
138
+ const devToolsFiber = fiberRef.current ? null : getDevToolsFiber(renderMarker);
139
+ const capture = captureReducerFiber(renderMarker, !fiberRef.current && !devToolsFiber);
140
+ if (devToolsFiber) {
141
+ fiberRef.current = {
142
+ fiber: devToolsFiber,
143
+ queue: getReducerQueue(devToolsFiber, renderMarker),
144
+ };
145
+ return devToolsFiber;
146
+ }
147
+ const knownCapture = capture ?? fiberRef.current;
148
+ if (!knownCapture) return undefined;
149
+ const knownFiber = knownCapture.fiber;
150
+ const fiber =
151
+ capture?.fiber ??
152
+ (hasRenderMarker(knownFiber, renderMarker)
153
+ ? knownFiber
154
+ : knownFiber.alternate && hasRenderMarker(knownFiber.alternate, renderMarker)
155
+ ? knownFiber.alternate
156
+ : getRenderingFiberFromRoot(knownCapture, renderMarker));
157
+ if (fiber) {
158
+ knownCapture.fiber = fiber;
159
+ fiberRef.current = knownCapture;
160
+ }
161
+ return fiber ?? undefined;
162
+ };
@@ -14,7 +14,7 @@ import {
14
14
  BippyUnsupportedHookError,
15
15
  } from "../errors.js";
16
16
  import { getReactWorkTagsForFiber } from "../react-internals/index.js";
17
- import { parseStack, type StackFrame } from "./parse-stack.js";
17
+ import { createStackParser, parseStack, type StackFrame } from "./parse-stack.js";
18
18
  import {
19
19
  getRendererDispatcherRefs,
20
20
  readDispatcher,
@@ -604,8 +604,9 @@ const findPrimitiveIndex = (hookStack: StackFrame[], hook: HookLogEntry): number
604
604
  const parseTrimmedStack = (
605
605
  rootStack: StackFrame[],
606
606
  hook: HookLogEntry,
607
+ parseHookStack: (stack: string) => StackFrame[],
607
608
  ): [StackFrame | null, StackFrame[] | null] => {
608
- const hookStack = parseErrorStack(hook.stackError);
609
+ const hookStack = parseHookStack(hook.stackError.stack || "");
609
610
  const rootIndex = findCommonAncestorIndex(rootStack, hookStack);
610
611
  const primitiveIndex = findPrimitiveIndex(hookStack, hook);
611
612
  if (rootIndex === -1 || primitiveIndex === -1 || rootIndex - primitiveIndex < 2) {
@@ -626,13 +627,14 @@ const NON_ID_HOOK_PRIMITIVES = new Set([
626
627
 
627
628
  const buildTree = (rootStack: StackFrame[], capturedHookLog: HookLogEntry[]): HooksTree => {
628
629
  const rootChildren: HooksNode[] = [];
630
+ const parseHookStack = createStackParser();
629
631
  let previousStack: StackFrame[] | null = null;
630
632
  let levelChildren = rootChildren;
631
633
  let nativeHookID = 0;
632
634
  const childrenStack: HooksNode[][] = [];
633
635
 
634
636
  for (const hook of capturedHookLog) {
635
- const [primitiveFrame, stack] = parseTrimmedStack(rootStack, hook);
637
+ const [primitiveFrame, stack] = parseTrimmedStack(rootStack, hook, parseHookStack);
636
638
  let displayName = hook.displayName;
637
639
  if (displayName === null && primitiveFrame !== null) {
638
640
  const primitiveName = parseHookName(primitiveFrame.functionName);
@@ -28,16 +28,14 @@ export const parseStack = (stackString: string, options?: ParseOptions): StackFr
28
28
  const frames: StackFrame[] = [];
29
29
  for (const rawLine of lines) {
30
30
  if (/^\s*at\s+/.test(rawLine)) {
31
- const parsed = parseV8OrIeString(rawLine)[0];
32
- if (parsed) frames.push(parsed);
31
+ if (CHROME_IE_STACK_REGEXP.test(rawLine)) frames.push(parseV8Line(rawLine));
33
32
  } else if (/^\s*in\s+/.test(rawLine)) {
34
33
  const elementName = rawLine
35
34
  .replace(/^\s*in\s+/, "")
36
35
  .replace(/\s*(?:\(at .*\)|\[[^\]]+\])$/, "");
37
36
  frames.push({ functionName: elementName, source: rawLine });
38
37
  } else if (rawLine.match(FIREFOX_SAFARI_STACK_REGEXP)) {
39
- const parsed = parseFFOrSafariString(rawLine)[0];
40
- if (parsed) frames.push(parsed);
38
+ if (!SAFARI_NATIVE_CODE_REGEXP.test(rawLine)) frames.push(parseSafariLine(rawLine));
41
39
  }
42
40
  }
43
41
  return frames;
@@ -48,6 +46,18 @@ export const parseStack = (stackString: string, options?: ParseOptions): StackFr
48
46
  return parseFFOrSafariString(stackString);
49
47
  };
50
48
 
49
+ const getPositionIndex = (location: string, endIndex: number): number => {
50
+ let positionIndex = endIndex - 1;
51
+ while (positionIndex >= 0) {
52
+ const character = location.charCodeAt(positionIndex);
53
+ if (character < 48 || character > 57) break;
54
+ positionIndex--;
55
+ }
56
+ return positionIndex < endIndex - 1 && location.charCodeAt(positionIndex) === 58
57
+ ? positionIndex
58
+ : -1;
59
+ };
60
+
51
61
  export const extractLocation = (
52
62
  urlLike: string,
53
63
  ): [string, string | undefined, string | undefined] => {
@@ -59,77 +69,115 @@ export const extractLocation = (
59
69
  const isWrappedLocation = urlLike.startsWith("(") && /:\d+\)$/.test(urlLike);
60
70
  const sanitizedResult = isWrappedLocation ? urlLike.slice(1, -1) : urlLike;
61
71
 
62
- const regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/;
63
- const parts = regExp.exec(sanitizedResult);
64
- if (!parts) return [sanitizedResult, undefined, undefined];
65
- return [parts[1], parts[2] || undefined, parts[3] || undefined] as const;
66
- };
72
+ if (/[\n\r\u2028\u2029]/.test(sanitizedResult)) {
73
+ const parts = /(.+?)(?::(\d+))?(?::(\d+))?$/.exec(sanitizedResult);
74
+ return parts
75
+ ? [parts[1], parts[2] || undefined, parts[3] || undefined]
76
+ : [sanitizedResult, undefined, undefined];
77
+ }
67
78
 
68
- export const parseV8OrIeString = (stack: string): StackFrame[] => {
69
- const filteredLines = stack.split("\n").filter((line) => {
70
- return !!line.match(CHROME_IE_STACK_REGEXP);
71
- });
72
-
73
- return filteredLines.map((line): StackFrame => {
74
- let currentLine = line;
75
- if (currentLine.includes("(eval ")) {
76
- currentLine = currentLine
77
- .replace(/eval code/g, "eval")
78
- .replace(/(\(eval at [^()]*)|(,.*$)/g, "");
79
- }
80
- let sanitizedLine = currentLine
81
- .replace(/^\s+/, "")
82
- .replace(/\(eval code/g, "(")
83
- .replace(/^.*?\s+/, "");
79
+ const lastPositionIndex = getPositionIndex(sanitizedResult, sanitizedResult.length);
80
+ if (lastPositionIndex <= 0) return [sanitizedResult, undefined, undefined];
81
+ const previousPositionIndex = getPositionIndex(sanitizedResult, lastPositionIndex);
82
+ if (previousPositionIndex <= 0) {
83
+ return [
84
+ sanitizedResult.slice(0, lastPositionIndex),
85
+ sanitizedResult.slice(lastPositionIndex + 1),
86
+ undefined,
87
+ ];
88
+ }
89
+ return [
90
+ sanitizedResult.slice(0, previousPositionIndex),
91
+ sanitizedResult.slice(previousPositionIndex + 1, lastPositionIndex),
92
+ sanitizedResult.slice(lastPositionIndex + 1),
93
+ ];
94
+ };
84
95
 
85
- const locationMatch = sanitizedLine.match(/ (\(.+\)$)/);
96
+ const parseV8Line = (line: string): StackFrame => {
97
+ let currentLine = line;
98
+ if (currentLine.includes("(eval ")) {
99
+ currentLine = currentLine
100
+ .replace(/eval code/g, "eval")
101
+ .replace(/(\(eval at [^()]*)|(,.*$)/g, "");
102
+ }
103
+ let sanitizedLine = currentLine
104
+ .replace(/^\s+/, "")
105
+ .replace(/\(eval code/g, "(")
106
+ .replace(/^.*?\s+/, "");
107
+
108
+ const locationMatch = sanitizedLine.match(/ (\(.+\)$)/);
109
+
110
+ sanitizedLine = locationMatch ? sanitizedLine.replace(locationMatch[0], "") : sanitizedLine;
111
+
112
+ const locationParts = extractLocation(locationMatch ? locationMatch[1] : sanitizedLine);
113
+ const functionName = (locationMatch && sanitizedLine) || undefined;
114
+ const fileName = ["eval", "<anonymous>", "(native)"].includes(locationParts[0])
115
+ ? undefined
116
+ : locationParts[0];
117
+
118
+ return {
119
+ functionName,
120
+ fileName,
121
+ lineNumber: locationParts[1] ? +locationParts[1] : undefined,
122
+ columnNumber: locationParts[2] ? +locationParts[2] : undefined,
123
+ source: currentLine,
124
+ };
125
+ };
86
126
 
87
- sanitizedLine = locationMatch ? sanitizedLine.replace(locationMatch[0], "") : sanitizedLine;
127
+ const parseSafariLine = (line: string): StackFrame => {
128
+ let currentLine = line;
129
+ if (currentLine.includes(" > eval"))
130
+ currentLine = currentLine.replace(/ line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g, ":$1");
88
131
 
89
- const locationParts = extractLocation(locationMatch ? locationMatch[1] : sanitizedLine);
90
- const functionName = (locationMatch && sanitizedLine) || undefined;
91
- const fileName = ["eval", "<anonymous>", "(native)"].includes(locationParts[0])
92
- ? undefined
93
- : locationParts[0];
132
+ if (!currentLine.includes("@") && !currentLine.includes(":")) {
133
+ return {
134
+ functionName: currentLine,
135
+ };
136
+ } else {
137
+ const functionNameRegex =
138
+ /(([^\n\r"\u2028\u2029]*".[^\n\r"\u2028\u2029]*"[^\n\r@\u2028\u2029]*(?:@[^\n\r"\u2028\u2029]*"[^\n\r@\u2028\u2029]*)*(?:[\n\r\u2028\u2029][^@]*)?)?[^@]*)@/;
139
+ const matches = currentLine.match(functionNameRegex);
140
+ const functionName = matches && matches[1] ? matches[1] : undefined;
141
+ const locationParts = extractLocation(currentLine.replace(functionNameRegex, ""));
94
142
 
95
143
  return {
96
144
  functionName,
97
- fileName,
145
+ fileName: locationParts[0],
98
146
  lineNumber: locationParts[1] ? +locationParts[1] : undefined,
99
147
  columnNumber: locationParts[2] ? +locationParts[2] : undefined,
100
148
  source: currentLine,
101
149
  };
102
- });
150
+ }
103
151
  };
104
152
 
105
- export const parseFFOrSafariString = (stack: string): StackFrame[] => {
106
- const filteredLines = stack.split("\n").filter((line) => {
107
- return !line.match(SAFARI_NATIVE_CODE_REGEXP);
108
- });
109
-
110
- return filteredLines.map((line): StackFrame => {
111
- let currentLine = line;
112
- if (currentLine.includes(" > eval"))
113
- currentLine = currentLine.replace(/ line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g, ":$1");
114
-
115
- if (!currentLine.includes("@") && !currentLine.includes(":")) {
116
- return {
117
- functionName: currentLine,
118
- };
119
- } else {
120
- const functionNameRegex =
121
- /(([^\n\r"\u2028\u2029]*".[^\n\r"\u2028\u2029]*"[^\n\r@\u2028\u2029]*(?:@[^\n\r"\u2028\u2029]*"[^\n\r@\u2028\u2029]*)*(?:[\n\r\u2028\u2029][^@]*)?)?[^@]*)@/;
122
- const matches = currentLine.match(functionNameRegex);
123
- const functionName = matches && matches[1] ? matches[1] : undefined;
124
- const locationParts = extractLocation(currentLine.replace(functionNameRegex, ""));
125
-
126
- return {
127
- functionName,
128
- fileName: locationParts[0],
129
- lineNumber: locationParts[1] ? +locationParts[1] : undefined,
130
- columnNumber: locationParts[2] ? +locationParts[2] : undefined,
131
- source: currentLine,
132
- };
153
+ const parseLines = (
154
+ stack: string,
155
+ isV8: boolean,
156
+ cache?: Map<string, StackFrame>,
157
+ ): StackFrame[] => {
158
+ const frames: StackFrame[] = [];
159
+ for (const line of stack.split("\n")) {
160
+ let frame = cache?.get(line);
161
+ if (!frame) {
162
+ if (isV8 ? !CHROME_IE_STACK_REGEXP.test(line) : SAFARI_NATIVE_CODE_REGEXP.test(line))
163
+ continue;
164
+ frame = isV8 ? parseV8Line(line) : parseSafariLine(line);
165
+ cache?.set(line, frame);
133
166
  }
134
- });
167
+ frames.push(frame);
168
+ }
169
+ return frames;
170
+ };
171
+
172
+ export const parseV8OrIeString = (stack: string): StackFrame[] => parseLines(stack, true);
173
+
174
+ export const parseFFOrSafariString = (stack: string): StackFrame[] => parseLines(stack, false);
175
+
176
+ export const createStackParser = () => {
177
+ const v8Frames = new Map<string, StackFrame>();
178
+ const safariFrames = new Map<string, StackFrame>();
179
+ return (stack: string): StackFrame[] => {
180
+ const isV8 = CHROME_IE_STACK_REGEXP.test(stack);
181
+ return parseLines(stack, isV8, isV8 ? v8Frames : safariFrames);
182
+ };
135
183
  };
@@ -207,6 +207,13 @@ export const getSourceFromSourceMap = (
207
207
  );
208
208
  };
209
209
 
210
+ const getStringIndex = (values: string[], target: string): number => {
211
+ for (let valueIndex = 0; valueIndex < values.length; valueIndex++) {
212
+ if (values[valueIndex] === target) return valueIndex;
213
+ }
214
+ return -1;
215
+ };
216
+
210
217
  const getSourceFromMappingsByFunctionName = (
211
218
  mappings: SourceMapMappings,
212
219
  sources: string[],
@@ -215,13 +222,17 @@ const getSourceFromMappingsByFunctionName = (
215
222
  ignoredSourceIndices?: Set<number>,
216
223
  ): StackFrame | null => {
217
224
  if (!names) return null;
218
- const functionNameIndex = names.indexOf(functionName);
225
+ const functionNameIndex = getStringIndex(names, functionName);
219
226
  if (functionNameIndex === -1) return null;
220
227
 
221
228
  let ignoredSource: StackFrame | null = null;
222
- for (const lineMapping of mappings) {
223
- for (const segment of lineMapping) {
229
+ for (let lineIndex = 0; lineIndex < mappings.length; lineIndex++) {
230
+ const lineMapping = mappings[lineIndex];
231
+ for (let segmentIndex = 0; segmentIndex < lineMapping.length; segmentIndex++) {
232
+ const segment = lineMapping[segmentIndex];
224
233
  if (segment[4] !== functionNameIndex) continue;
234
+ if (ignoredSource && segment[1] !== undefined && ignoredSourceIndices?.has(segment[1]))
235
+ continue;
225
236
  const source = getSourceFromSegment(segment, sources, ignoredSourceIndices, names);
226
237
  if (!source) continue;
227
238
  if (!source.isIgnoreListed) return source;
@@ -267,7 +278,7 @@ const findSourceContentByFileName = (
267
278
  fileName: string,
268
279
  ): string | null => {
269
280
  if (!sourcesContent) return null;
270
- const sourceIndex = sources.indexOf(fileName);
281
+ const sourceIndex = getStringIndex(sources, fileName);
271
282
  return sourceIndex === -1 ? null : (sourcesContent[sourceIndex] ?? null);
272
283
  };
273
284