bippy 0.7.3-dev.8ba0705 → 0.7.3-dev.9f2850b

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.
@@ -1,26 +1,17 @@
1
- import type { Fiber } from "../react-internals/index.js";
2
- import { getDisplayName } from "../core.js";
1
+ import type { Fiber } from "../react-internals/index";
2
+ import { getDisplayName } from "../core";
3
3
 
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
- import { getDefinitionFrameFromOwnedChild, getParentStack, hasDebugStack } from "./owner-stack.js";
15
- import { parseDebugStack } from "./parse-debug-stack.js";
16
- import { parseStack, type StackFrame } from "./parse-stack.js";
4
+ import type { FiberSource } from "./types";
5
+ import { getDefinitionFrameFromOwnedChild, getParentStack, hasDebugStack } from "./owner-stack";
6
+ import { parseDebugStack } from "./parse-debug-stack";
7
+ import { parseStack, type StackFrame } from "./parse-stack";
17
8
  import {
18
9
  getSourceFromSourceMapByFunctionName,
19
10
  getSourceMap,
20
11
  symbolicateStack,
21
12
  type SourceFetch,
22
13
  type SourceMapRequestOptions,
23
- } from "./symbolication.js";
14
+ } from "./symbolication";
24
15
 
25
16
  export const hasDebugSource = (
26
17
  fiber: Fiber,
@@ -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";
@@ -6,8 +6,8 @@ export {
6
6
  getParentStack,
7
7
  getRawOwnerStack,
8
8
  hasDebugStack,
9
- } from "./owner-stack.js";
10
- export { getRawSource, getSource, isSourceFile, normalizeFileName } from "./get-source.js";
9
+ } from "./owner-stack";
10
+ export { getRawSource, getSource, isSourceFile, normalizeFileName } from "./get-source";
11
11
  export {
12
12
  getSourceContentFromSourceMap,
13
13
  getSourceFromSourceMap,
@@ -20,22 +20,22 @@ export {
20
20
  type SourceMap,
21
21
  type SourceMapRequestOptions,
22
22
  type StandardSourceMap,
23
- } from "./symbolication.js";
24
- export type { FiberSource } from "./types.js";
25
- export { parseStack, type ParseOptions, type StackFrame } from "./parse-stack.js";
26
- export { getDisplayNameFromSource } from "./get-display-name-from-source.js";
23
+ } from "./symbolication";
24
+ export type { FiberSource } from "./types";
25
+ export { parseStack, type ParseOptions, type StackFrame } from "./parse-stack";
26
+ export { getDisplayNameFromSource } from "./get-display-name-from-source";
27
27
  export {
28
28
  BippyError,
29
29
  BippyHookInspectionError,
30
30
  BippyHookRenderError,
31
31
  BippySourceMapError,
32
32
  BippyUnsupportedHookError,
33
- } from "../errors.js";
33
+ } from "../errors";
34
34
  export {
35
35
  getFiberHooks,
36
36
  inspectHooks,
37
37
  type HookSource,
38
38
  type HooksNode,
39
39
  type HooksTree,
40
- } from "./inspect-hooks.js";
41
- export { parseHookNames, type HookNames } from "./parse-hook-names.js";
40
+ } from "./inspect-hooks";
41
+ export { parseHookNames, type HookNames } from "./parse-hook-names";
@@ -1,5 +1,5 @@
1
- import { getRenderer } from "../core.js";
2
- import type { ReactDevToolsTarget } from "../rdt-hook.js";
1
+ import { getRenderer } from "../core";
2
+ import type { ReactDevToolsTarget } from "../rdt-hook";
3
3
  import type {
4
4
  Fiber,
5
5
  ContextDependency,
@@ -7,19 +7,15 @@ import type {
7
7
  ReactContext,
8
8
  ReactDebugInfo,
9
9
  RendererDispatcherRef,
10
- } from "../react-internals/index.js";
10
+ } from "../react-internals/index";
11
11
  import {
12
12
  BippyHookInspectionError,
13
13
  BippyHookRenderError,
14
14
  BippyUnsupportedHookError,
15
- } from "../errors.js";
16
- import { getReactWorkTagsForFiber } from "../react-internals/index.js";
17
- import { parseStack, type StackFrame } from "./parse-stack.js";
18
- import {
19
- getRendererDispatcherRefs,
20
- readDispatcher,
21
- writeDispatcher,
22
- } from "./renderer-dispatchers.js";
15
+ } from "../errors";
16
+ import { getReactWorkTagsForFiber } from "../react-internals/index";
17
+ import { createStackParser, parseStack, type StackFrame } from "./parse-stack";
18
+ import { getRendererDispatcherRefs, readDispatcher, writeDispatcher } from "./renderer-dispatchers";
23
19
 
24
20
  const REACT_CONTEXT_TYPE = Symbol.for("react.context");
25
21
  const REACT_MEMO_CACHE_SENTINEL = Symbol.for("react.memo_cache_sentinel");
@@ -87,6 +83,16 @@ let currentHook: MemoizedState | null = null;
87
83
  let currentContextDependency: ContextDependency<unknown> | null = null;
88
84
  let currentThenableIndex = 0;
89
85
  let currentThenableState: unknown[] | null = null;
86
+ let currentMemoCacheIndex = 0;
87
+ let isInspectingHooks = false;
88
+
89
+ const assertNotInspectingHooks = (): void => {
90
+ if (isInspectingHooks) {
91
+ throw new BippyHookInspectionError(
92
+ "Hook inspection cannot be called during another inspection.",
93
+ );
94
+ }
95
+ };
90
96
 
91
97
  const SuspenseException: unknown = new Error(
92
98
  "Suspense interrupted this render. This error is an internal implementation detail of `use`.",
@@ -323,16 +329,8 @@ const dispatcherUseMemoCache = (size: number): unknown[] => {
323
329
  const memoCache = fiber.updateQueue?.memoCache;
324
330
  if (memoCache === null || memoCache === undefined) return [];
325
331
 
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;
332
+ const memoCacheSlots = memoCache.data[currentMemoCacheIndex++];
333
+ return memoCacheSlots?.slice() ?? Array.from({ length: size }, () => REACT_MEMO_CACHE_SENTINEL);
336
334
  };
337
335
 
338
336
  const dispatcherUseOptimistic = (passthrough: unknown): [unknown, () => void] => {
@@ -602,8 +600,9 @@ const findPrimitiveIndex = (hookStack: StackFrame[], hook: HookLogEntry): number
602
600
  const parseTrimmedStack = (
603
601
  rootStack: StackFrame[],
604
602
  hook: HookLogEntry,
603
+ parseHookStack: (stack: string) => StackFrame[],
605
604
  ): [StackFrame | null, StackFrame[] | null] => {
606
- const hookStack = parseErrorStack(hook.stackError);
605
+ const hookStack = parseHookStack(hook.stackError.stack || "");
607
606
  const rootIndex = findCommonAncestorIndex(rootStack, hookStack);
608
607
  const primitiveIndex = findPrimitiveIndex(hookStack, hook);
609
608
  if (rootIndex === -1 || primitiveIndex === -1 || rootIndex - primitiveIndex < 2) {
@@ -624,13 +623,14 @@ const NON_ID_HOOK_PRIMITIVES = new Set([
624
623
 
625
624
  const buildTree = (rootStack: StackFrame[], capturedHookLog: HookLogEntry[]): HooksTree => {
626
625
  const rootChildren: HooksNode[] = [];
626
+ const parseHookStack = createStackParser();
627
627
  let previousStack: StackFrame[] | null = null;
628
628
  let levelChildren = rootChildren;
629
629
  let nativeHookID = 0;
630
630
  const childrenStack: HooksNode[][] = [];
631
631
 
632
632
  for (const hook of capturedHookLog) {
633
- const [primitiveFrame, stack] = parseTrimmedStack(rootStack, hook);
633
+ const [primitiveFrame, stack] = parseTrimmedStack(rootStack, hook, parseHookStack);
634
634
  let displayName = hook.displayName;
635
635
  if (displayName === null && primitiveFrame !== null) {
636
636
  const primitiveName = parseHookName(primitiveFrame.functionName);
@@ -834,6 +834,7 @@ const performDispatcherInspection = <TArgs extends unknown[]>(
834
834
  ): HooksTree => {
835
835
  const previousDispatcher = readDispatcher(dispatcherRef);
836
836
  writeDispatcher(dispatcherRef, dispatcherProxy);
837
+ isInspectingHooks = true;
837
838
 
838
839
  let capturedHookLog: HookLogEntry[] = [];
839
840
  let ancestorStackError: Error | undefined;
@@ -846,6 +847,7 @@ const performDispatcherInspection = <TArgs extends unknown[]>(
846
847
  } finally {
847
848
  capturedHookLog = hookLog;
848
849
  hookLog = [];
850
+ isInspectingHooks = false;
849
851
  writeDispatcher(dispatcherRef, previousDispatcher);
850
852
  }
851
853
 
@@ -938,6 +940,7 @@ export const inspectHooks = (
938
940
  props: Record<string, unknown>,
939
941
  target: ReactDevToolsTarget = globalThis,
940
942
  ): HooksTree => {
943
+ assertNotInspectingHooks();
941
944
  const dispatcherRef = requireDispatcherRef(undefined, target);
942
945
  getPrimitiveStackCache();
943
946
  currentHook = null;
@@ -961,6 +964,7 @@ export const inspectHooks = (
961
964
  };
962
965
 
963
966
  export const getFiberHooks = (fiber: Fiber): HooksTree => {
967
+ assertNotInspectingHooks();
964
968
  const dispatcherRef = requireDispatcherRef(fiber);
965
969
  const workTags = getReactWorkTagsForFiber(fiber);
966
970
 
@@ -976,6 +980,7 @@ export const getFiberHooks = (fiber: Fiber): HooksTree => {
976
980
 
977
981
  currentHook = fiber.memoizedState;
978
982
  currentFiber = fiber;
983
+ currentMemoCacheIndex = 0;
979
984
 
980
985
  const debugThenableState = fiber.dependencies?._debugThenableState;
981
986
  const usedThenables = Array.isArray(debugThenableState)
@@ -984,18 +989,16 @@ export const getFiberHooks = (fiber: Fiber): HooksTree => {
984
989
  currentThenableState = Array.isArray(usedThenables) ? usedThenables : null;
985
990
  currentThenableIndex = 0;
986
991
 
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
992
  const originalConsoleMethods = suppressConsole();
996
993
  const contextMap = new Map<ReactContext<unknown>, unknown>();
997
994
 
998
995
  try {
996
+ resolveContextDependency(fiber);
997
+ const type = fiber.type;
998
+ const props =
999
+ type !== fiber.elementType
1000
+ ? resolveDefaultProps(type, fiber.memoizedProps)
1001
+ : fiber.memoizedProps;
999
1002
  if (
1000
1003
  currentContextDependency !== null &&
1001
1004
  !Object.hasOwn(currentContextDependency, "memoizedValue")
@@ -1030,6 +1033,7 @@ export const getFiberHooks = (fiber: Fiber): HooksTree => {
1030
1033
  currentContextDependency = null;
1031
1034
  currentThenableState = null;
1032
1035
  currentThenableIndex = 0;
1036
+ currentMemoCacheIndex = 0;
1033
1037
  restoreContexts(contextMap);
1034
1038
  restoreConsole(originalConsoleMethods);
1035
1039
  }