bippy 0.7.3-dev.a6ed02c → 0.7.3-dev.a9e42c2

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.
@@ -2,15 +2,6 @@ import type { Fiber } from "../react-internals/index.js";
2
2
  import { getDisplayName } from "../core.js";
3
3
 
4
4
  import type { FiberSource } from "./types.js";
5
- import {
6
- SCHEME_REGEX,
7
- INTERNAL_SCHEME_PREFIXES,
8
- ABOUT_REACT_PREFIX,
9
- ANONYMOUS_FILE_PATTERNS,
10
- SOURCE_FILE_EXTENSION_REGEX,
11
- BUNDLED_FILE_PATTERN_REGEX,
12
- QUERY_PARAMETER_PATTERN_REGEX,
13
- } from "./constants.js";
14
5
  import { getDefinitionFrameFromOwnedChild, getParentStack, hasDebugStack } from "./owner-stack.js";
15
6
  import { parseDebugStack } from "./parse-debug-stack.js";
16
7
  import { parseStack, type StackFrame } from "./parse-stack.js";
@@ -165,130 +156,4 @@ export const getSource = async (
165
156
  return getSourceByComponentName(fiber, shouldUseCache, sourceFetch, requestOptions);
166
157
  };
167
158
 
168
- const getPathSegmentCount = (path: string): number => path.split("/").filter(Boolean).length;
169
-
170
- const stripSingleBasePathPrefix = (path: string): string => {
171
- const firstSlashIndex = path.indexOf("/", 1);
172
- if (firstSlashIndex === -1) {
173
- return path;
174
- }
175
-
176
- const basePath = path.slice(0, firstSlashIndex);
177
- if (getPathSegmentCount(basePath) !== 1) {
178
- return path;
179
- }
180
-
181
- const remainderPath = path.slice(firstSlashIndex);
182
- if (!SOURCE_FILE_EXTENSION_REGEX.test(remainderPath)) {
183
- return path;
184
- }
185
-
186
- const remainderSegments = remainderPath.split("/").filter(Boolean);
187
- if (remainderSegments.length < 2) {
188
- return path;
189
- }
190
-
191
- const firstRemainderSegment = remainderSegments[0];
192
- if (firstRemainderSegment.startsWith("@")) {
193
- return path;
194
- }
195
-
196
- if (firstRemainderSegment.length > 4) {
197
- return path;
198
- }
199
-
200
- return remainderPath;
201
- };
202
-
203
- export const normalizeFileName = (fileName: string): string => {
204
- if (!fileName) {
205
- return "";
206
- }
207
-
208
- if (ANONYMOUS_FILE_PATTERNS.some((pattern) => pattern === fileName)) {
209
- return "";
210
- }
211
-
212
- let normalizedFileName = fileName;
213
- const isWindowsDrivePath = /^[a-zA-Z]:[\\/]/.test(normalizedFileName);
214
-
215
- const isHttpUrl =
216
- normalizedFileName.startsWith("http://") || normalizedFileName.startsWith("https://");
217
- if (isHttpUrl) {
218
- try {
219
- const parsedUrl = new URL(normalizedFileName);
220
- normalizedFileName = parsedUrl.pathname;
221
- } catch {}
222
- normalizedFileName = stripSingleBasePathPrefix(normalizedFileName);
223
- }
224
-
225
- if (normalizedFileName.startsWith(ABOUT_REACT_PREFIX)) {
226
- const remainder = normalizedFileName.slice(ABOUT_REACT_PREFIX.length);
227
- const slashIndex = remainder.indexOf("/");
228
- const colonIndex = remainder.indexOf(":");
229
-
230
- if (slashIndex !== -1 && (colonIndex === -1 || slashIndex < colonIndex)) {
231
- normalizedFileName = remainder.slice(slashIndex + 1);
232
- } else {
233
- normalizedFileName = remainder;
234
- }
235
- }
236
-
237
- let didStripPrefix = true;
238
- while (didStripPrefix) {
239
- didStripPrefix = false;
240
- for (const prefix of INTERNAL_SCHEME_PREFIXES) {
241
- if (normalizedFileName.startsWith(prefix)) {
242
- normalizedFileName = normalizedFileName.slice(prefix.length);
243
-
244
- if (prefix === "file:///") {
245
- normalizedFileName = `/${normalizedFileName.replace(/^\/+/, "")}`;
246
- }
247
-
248
- didStripPrefix = true;
249
- break;
250
- }
251
- }
252
- }
253
-
254
- if (!isWindowsDrivePath) {
255
- const schemeMatch = normalizedFileName.match(SCHEME_REGEX);
256
- if (schemeMatch) {
257
- normalizedFileName = normalizedFileName.slice(schemeMatch[0].length);
258
- }
259
- }
260
-
261
- if (normalizedFileName.startsWith("//")) {
262
- const firstPathSlashIndex = normalizedFileName.indexOf("/", 2);
263
- normalizedFileName =
264
- firstPathSlashIndex === -1 ? "" : normalizedFileName.slice(firstPathSlashIndex);
265
- }
266
-
267
- const queryParameterIndex = normalizedFileName.indexOf("?");
268
- if (queryParameterIndex !== -1) {
269
- const potentialQueryParameters = normalizedFileName.slice(queryParameterIndex);
270
- if (QUERY_PARAMETER_PATTERN_REGEX.test(potentialQueryParameters)) {
271
- normalizedFileName = normalizedFileName.slice(0, queryParameterIndex);
272
- }
273
- }
274
-
275
- return normalizedFileName;
276
- };
277
-
278
- export const isSourceFile = (fileName: string): boolean => {
279
- const normalizedFileName = normalizeFileName(fileName);
280
-
281
- if (!normalizedFileName) {
282
- return false;
283
- }
284
-
285
- if (!SOURCE_FILE_EXTENSION_REGEX.test(normalizedFileName)) {
286
- return false;
287
- }
288
-
289
- if (BUNDLED_FILE_PATTERN_REGEX.test(normalizedFileName)) {
290
- return false;
291
- }
292
-
293
- return true;
294
- };
159
+ export { isSourceFile, normalizeFileName } from "./normalize-file-name.js";
@@ -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
- let memoCacheSlots = memoCache.data[memoCache.index];
327
- if (memoCacheSlots === undefined) {
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 = parseErrorStack(hook.stackError);
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
  }