bippy 0.7.3-dev.a6ed02c → 0.7.3-dev.c0e872e
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/LICENSE +1 -0
- package/README.md +4 -11
- package/dist/core.cjs +1 -1
- package/dist/core.js +1 -1
- package/dist/errors.d.cts +4 -1
- package/dist/errors.d.ts +4 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +32 -29
- package/dist/index.d.ts +34 -31
- package/dist/index.js +1 -1
- package/dist/rdt-hook.cjs +1 -1
- package/dist/rdt-hook.js +1 -1
- package/dist/source.cjs +11 -12
- package/dist/source.d.cts +22 -22
- package/dist/source.d.ts +22 -22
- package/dist/source.js +12 -13
- package/package.json +6 -34
- package/src/call-listener.ts +13 -0
- package/src/core.ts +248 -241
- package/src/rdt-hook.ts +45 -29
- package/src/react-internals/current-fiber.ts +84 -0
- package/src/react-internals/generated/react-work-tags.ts +3 -0
- package/src/react-internals/index.ts +38 -11
- package/src/react.ts +141 -56
- package/src/source/inspect-hooks.ts +29 -21
- package/src/source/parse-stack.ts +111 -63
- package/src/source/symbolication.ts +15 -4
|
@@ -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,
|
|
@@ -87,6 +87,16 @@ let currentHook: MemoizedState | null = null;
|
|
|
87
87
|
let currentContextDependency: ContextDependency<unknown> | null = null;
|
|
88
88
|
let currentThenableIndex = 0;
|
|
89
89
|
let currentThenableState: unknown[] | null = null;
|
|
90
|
+
let currentMemoCacheIndex = 0;
|
|
91
|
+
let isInspectingHooks = false;
|
|
92
|
+
|
|
93
|
+
const assertNotInspectingHooks = (): void => {
|
|
94
|
+
if (isInspectingHooks) {
|
|
95
|
+
throw new BippyHookInspectionError(
|
|
96
|
+
"Hook inspection cannot be called during another inspection.",
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
90
100
|
|
|
91
101
|
const SuspenseException: unknown = new Error(
|
|
92
102
|
"Suspense interrupted this render. This error is an internal implementation detail of `use`.",
|
|
@@ -323,16 +333,8 @@ const dispatcherUseMemoCache = (size: number): unknown[] => {
|
|
|
323
333
|
const memoCache = fiber.updateQueue?.memoCache;
|
|
324
334
|
if (memoCache === null || memoCache === undefined) return [];
|
|
325
335
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
memoCacheSlots = memoCache.data[memoCache.index] = Array.from(
|
|
329
|
-
{ length: size },
|
|
330
|
-
() => REACT_MEMO_CACHE_SENTINEL,
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
memoCache.index++;
|
|
335
|
-
return memoCacheSlots;
|
|
336
|
+
const memoCacheSlots = memoCache.data[currentMemoCacheIndex++];
|
|
337
|
+
return memoCacheSlots?.slice() ?? Array.from({ length: size }, () => REACT_MEMO_CACHE_SENTINEL);
|
|
336
338
|
};
|
|
337
339
|
|
|
338
340
|
const dispatcherUseOptimistic = (passthrough: unknown): [unknown, () => void] => {
|
|
@@ -602,8 +604,9 @@ const findPrimitiveIndex = (hookStack: StackFrame[], hook: HookLogEntry): number
|
|
|
602
604
|
const parseTrimmedStack = (
|
|
603
605
|
rootStack: StackFrame[],
|
|
604
606
|
hook: HookLogEntry,
|
|
607
|
+
parseHookStack: (stack: string) => StackFrame[],
|
|
605
608
|
): [StackFrame | null, StackFrame[] | null] => {
|
|
606
|
-
const hookStack =
|
|
609
|
+
const hookStack = parseHookStack(hook.stackError.stack || "");
|
|
607
610
|
const rootIndex = findCommonAncestorIndex(rootStack, hookStack);
|
|
608
611
|
const primitiveIndex = findPrimitiveIndex(hookStack, hook);
|
|
609
612
|
if (rootIndex === -1 || primitiveIndex === -1 || rootIndex - primitiveIndex < 2) {
|
|
@@ -624,13 +627,14 @@ const NON_ID_HOOK_PRIMITIVES = new Set([
|
|
|
624
627
|
|
|
625
628
|
const buildTree = (rootStack: StackFrame[], capturedHookLog: HookLogEntry[]): HooksTree => {
|
|
626
629
|
const rootChildren: HooksNode[] = [];
|
|
630
|
+
const parseHookStack = createStackParser();
|
|
627
631
|
let previousStack: StackFrame[] | null = null;
|
|
628
632
|
let levelChildren = rootChildren;
|
|
629
633
|
let nativeHookID = 0;
|
|
630
634
|
const childrenStack: HooksNode[][] = [];
|
|
631
635
|
|
|
632
636
|
for (const hook of capturedHookLog) {
|
|
633
|
-
const [primitiveFrame, stack] = parseTrimmedStack(rootStack, hook);
|
|
637
|
+
const [primitiveFrame, stack] = parseTrimmedStack(rootStack, hook, parseHookStack);
|
|
634
638
|
let displayName = hook.displayName;
|
|
635
639
|
if (displayName === null && primitiveFrame !== null) {
|
|
636
640
|
const primitiveName = parseHookName(primitiveFrame.functionName);
|
|
@@ -834,6 +838,7 @@ const performDispatcherInspection = <TArgs extends unknown[]>(
|
|
|
834
838
|
): HooksTree => {
|
|
835
839
|
const previousDispatcher = readDispatcher(dispatcherRef);
|
|
836
840
|
writeDispatcher(dispatcherRef, dispatcherProxy);
|
|
841
|
+
isInspectingHooks = true;
|
|
837
842
|
|
|
838
843
|
let capturedHookLog: HookLogEntry[] = [];
|
|
839
844
|
let ancestorStackError: Error | undefined;
|
|
@@ -846,6 +851,7 @@ const performDispatcherInspection = <TArgs extends unknown[]>(
|
|
|
846
851
|
} finally {
|
|
847
852
|
capturedHookLog = hookLog;
|
|
848
853
|
hookLog = [];
|
|
854
|
+
isInspectingHooks = false;
|
|
849
855
|
writeDispatcher(dispatcherRef, previousDispatcher);
|
|
850
856
|
}
|
|
851
857
|
|
|
@@ -938,6 +944,7 @@ export const inspectHooks = (
|
|
|
938
944
|
props: Record<string, unknown>,
|
|
939
945
|
target: ReactDevToolsTarget = globalThis,
|
|
940
946
|
): HooksTree => {
|
|
947
|
+
assertNotInspectingHooks();
|
|
941
948
|
const dispatcherRef = requireDispatcherRef(undefined, target);
|
|
942
949
|
getPrimitiveStackCache();
|
|
943
950
|
currentHook = null;
|
|
@@ -961,6 +968,7 @@ export const inspectHooks = (
|
|
|
961
968
|
};
|
|
962
969
|
|
|
963
970
|
export const getFiberHooks = (fiber: Fiber): HooksTree => {
|
|
971
|
+
assertNotInspectingHooks();
|
|
964
972
|
const dispatcherRef = requireDispatcherRef(fiber);
|
|
965
973
|
const workTags = getReactWorkTagsForFiber(fiber);
|
|
966
974
|
|
|
@@ -976,6 +984,7 @@ export const getFiberHooks = (fiber: Fiber): HooksTree => {
|
|
|
976
984
|
|
|
977
985
|
currentHook = fiber.memoizedState;
|
|
978
986
|
currentFiber = fiber;
|
|
987
|
+
currentMemoCacheIndex = 0;
|
|
979
988
|
|
|
980
989
|
const debugThenableState = fiber.dependencies?._debugThenableState;
|
|
981
990
|
const usedThenables = Array.isArray(debugThenableState)
|
|
@@ -984,18 +993,16 @@ export const getFiberHooks = (fiber: Fiber): HooksTree => {
|
|
|
984
993
|
currentThenableState = Array.isArray(usedThenables) ? usedThenables : null;
|
|
985
994
|
currentThenableIndex = 0;
|
|
986
995
|
|
|
987
|
-
resolveContextDependency(fiber);
|
|
988
|
-
|
|
989
|
-
const type = fiber.type;
|
|
990
|
-
let props = fiber.memoizedProps;
|
|
991
|
-
if (type !== fiber.elementType) {
|
|
992
|
-
props = resolveDefaultProps(type, props);
|
|
993
|
-
}
|
|
994
|
-
|
|
995
996
|
const originalConsoleMethods = suppressConsole();
|
|
996
997
|
const contextMap = new Map<ReactContext<unknown>, unknown>();
|
|
997
998
|
|
|
998
999
|
try {
|
|
1000
|
+
resolveContextDependency(fiber);
|
|
1001
|
+
const type = fiber.type;
|
|
1002
|
+
const props =
|
|
1003
|
+
type !== fiber.elementType
|
|
1004
|
+
? resolveDefaultProps(type, fiber.memoizedProps)
|
|
1005
|
+
: fiber.memoizedProps;
|
|
999
1006
|
if (
|
|
1000
1007
|
currentContextDependency !== null &&
|
|
1001
1008
|
!Object.hasOwn(currentContextDependency, "memoizedValue")
|
|
@@ -1030,6 +1037,7 @@ export const getFiberHooks = (fiber: Fiber): HooksTree => {
|
|
|
1030
1037
|
currentContextDependency = null;
|
|
1031
1038
|
currentThenableState = null;
|
|
1032
1039
|
currentThenableIndex = 0;
|
|
1040
|
+
currentMemoCacheIndex = 0;
|
|
1033
1041
|
restoreContexts(contextMap);
|
|
1034
1042
|
restoreConsole(originalConsoleMethods);
|
|
1035
1043
|
}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
|
225
|
+
const functionNameIndex = getStringIndex(names, functionName);
|
|
219
226
|
if (functionNameIndex === -1) return null;
|
|
220
227
|
|
|
221
228
|
let ignoredSource: StackFrame | null = null;
|
|
222
|
-
for (
|
|
223
|
-
|
|
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
|
|
281
|
+
const sourceIndex = getStringIndex(sources, fileName);
|
|
271
282
|
return sourceIndex === -1 ? null : (sourcesContent[sourceIndex] ?? null);
|
|
272
283
|
};
|
|
273
284
|
|