bippy 0.7.3-dev.1d4f35b → 0.7.3-dev.699994d
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 +3 -0
- package/dist/errors.d.ts +3 -0
- 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 +163 -205
- package/src/rdt-hook.ts +7 -4
- 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
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Adapted from ReactFiberTreeReflection.js. Copyright (c) Meta Platforms, Inc. and affiliates. MIT licensed.
|
|
2
|
+
import type { Fiber } from "./types.js";
|
|
3
|
+
import { getReactWorkTagsForFiber } from "./index.js";
|
|
4
|
+
|
|
5
|
+
export const getCurrentFiberFromRoot = (fiber: Fiber): Fiber | null => {
|
|
6
|
+
const alternate = fiber.alternate;
|
|
7
|
+
if (!alternate) return null;
|
|
8
|
+
let firstBranch = fiber;
|
|
9
|
+
let secondBranch = alternate;
|
|
10
|
+
|
|
11
|
+
while (firstBranch.return) {
|
|
12
|
+
const firstParent = firstBranch.return;
|
|
13
|
+
const secondParent = firstParent.alternate;
|
|
14
|
+
if (!secondParent) {
|
|
15
|
+
const nextParent = firstParent.return;
|
|
16
|
+
if (!nextParent) return null;
|
|
17
|
+
firstBranch = secondBranch = nextParent;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (firstParent.child === secondParent.child) {
|
|
22
|
+
let child = firstParent.child;
|
|
23
|
+
while (child) {
|
|
24
|
+
if (child === firstBranch || child === secondBranch) {
|
|
25
|
+
let root = firstParent;
|
|
26
|
+
while (root.return) root = root.return;
|
|
27
|
+
if (root.tag !== getReactWorkTagsForFiber(root).HostRoot) return null;
|
|
28
|
+
return child === firstBranch ? fiber : alternate;
|
|
29
|
+
}
|
|
30
|
+
child = child.sibling;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (firstBranch.return !== secondBranch.return) {
|
|
36
|
+
firstBranch = firstParent;
|
|
37
|
+
secondBranch = secondParent;
|
|
38
|
+
} else {
|
|
39
|
+
let didFindChild = false;
|
|
40
|
+
let child = firstParent.child;
|
|
41
|
+
while (child) {
|
|
42
|
+
if (child === firstBranch) {
|
|
43
|
+
firstBranch = firstParent;
|
|
44
|
+
secondBranch = secondParent;
|
|
45
|
+
didFindChild = true;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
if (child === secondBranch) {
|
|
49
|
+
firstBranch = secondParent;
|
|
50
|
+
secondBranch = firstParent;
|
|
51
|
+
didFindChild = true;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
child = child.sibling;
|
|
55
|
+
}
|
|
56
|
+
if (!didFindChild) {
|
|
57
|
+
child = secondParent.child;
|
|
58
|
+
while (child) {
|
|
59
|
+
if (child === firstBranch) {
|
|
60
|
+
firstBranch = secondParent;
|
|
61
|
+
secondBranch = firstParent;
|
|
62
|
+
didFindChild = true;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
if (child === secondBranch) {
|
|
66
|
+
firstBranch = firstParent;
|
|
67
|
+
secondBranch = secondParent;
|
|
68
|
+
didFindChild = true;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
child = child.sibling;
|
|
72
|
+
}
|
|
73
|
+
if (!didFindChild) return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (firstBranch.alternate !== secondBranch) return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (firstBranch.tag !== getReactWorkTagsForFiber(firstBranch).HostRoot) return null;
|
|
80
|
+
const root = firstBranch.stateNode;
|
|
81
|
+
if (typeof root !== "object" || root === null || !("current" in root)) return null;
|
|
82
|
+
if (root.current === firstBranch) return fiber;
|
|
83
|
+
return root.current === secondBranch ? alternate : null;
|
|
84
|
+
};
|
|
@@ -25,6 +25,9 @@ export const ReactSymbols = {
|
|
|
25
25
|
DEPRECATED_ASYNC_MODE_SYMBOL_STRING: "Symbol(react.async_mode)",
|
|
26
26
|
ELEMENT_SYMBOL_STRING: "Symbol(react.transitional.element)",
|
|
27
27
|
LEGACY_ELEMENT_SYMBOL_STRING: "Symbol(react.element)",
|
|
28
|
+
STRICT_MODE_NUMBER: 60108,
|
|
29
|
+
STRICT_MODE_SYMBOL_DESCRIPTION: "react.strict_mode",
|
|
30
|
+
STRICT_MODE_SYMBOL_STRING: "Symbol(react.strict_mode)",
|
|
28
31
|
} as const;
|
|
29
32
|
|
|
30
33
|
export interface ReactWorkTagMap {
|
|
@@ -18,8 +18,24 @@ export type {
|
|
|
18
18
|
} from "./generated/react-work-tags.js";
|
|
19
19
|
export * from "./types.js";
|
|
20
20
|
|
|
21
|
+
interface InheritedWorkTags {
|
|
22
|
+
workTags: Readonly<ReactWorkTagMap>;
|
|
23
|
+
generation: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
const defaultReactWorkTags = getReactWorkTags();
|
|
22
27
|
const fiberReactWorkTags = new WeakMap<Fiber, Readonly<ReactWorkTagMap>>();
|
|
28
|
+
const inheritedFiberWorkTags = new WeakMap<Fiber, InheritedWorkTags>();
|
|
29
|
+
let workTagGeneration = 0;
|
|
30
|
+
|
|
31
|
+
const getCachedWorkTags = (fiber: Fiber): Readonly<ReactWorkTagMap> | undefined => {
|
|
32
|
+
const assignedWorkTags = fiberReactWorkTags.get(fiber);
|
|
33
|
+
if (assignedWorkTags) return assignedWorkTags;
|
|
34
|
+
const inheritedWorkTags = inheritedFiberWorkTags.get(fiber);
|
|
35
|
+
return inheritedWorkTags?.generation === workTagGeneration
|
|
36
|
+
? inheritedWorkTags.workTags
|
|
37
|
+
: undefined;
|
|
38
|
+
};
|
|
23
39
|
|
|
24
40
|
// React's experimental channel historically reported "0.0.0-experimental-<sha>"
|
|
25
41
|
// as the runtime version; those builds use modern work tags, not the 16.x rows
|
|
@@ -39,25 +55,36 @@ export const getReactWorkTagsForRenderer = (
|
|
|
39
55
|
|
|
40
56
|
export const setReactWorkTagsForFiber = (fiber: Fiber, renderer?: ReactRenderer): void => {
|
|
41
57
|
const workTags = getReactWorkTagsForRenderer(renderer);
|
|
58
|
+
if (
|
|
59
|
+
getCachedWorkTags(fiber) !== workTags ||
|
|
60
|
+
(fiber.alternate && getCachedWorkTags(fiber.alternate) !== workTags)
|
|
61
|
+
) {
|
|
62
|
+
workTagGeneration++;
|
|
63
|
+
}
|
|
42
64
|
fiberReactWorkTags.set(fiber, workTags);
|
|
43
65
|
if (fiber.alternate) fiberReactWorkTags.set(fiber.alternate, workTags);
|
|
44
66
|
};
|
|
45
67
|
|
|
46
68
|
export const getReactWorkTagsForFiber = (fiber: Fiber): Readonly<ReactWorkTagMap> => {
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
69
|
+
let workTags = getCachedWorkTags(fiber);
|
|
70
|
+
if (workTags) return workTags;
|
|
71
|
+
const traversedFibers: Fiber[] = [];
|
|
72
|
+
let ancestor = fiber;
|
|
73
|
+
while (!workTags) {
|
|
74
|
+
traversedFibers.push(ancestor);
|
|
75
|
+
const parent = ancestor.return;
|
|
76
|
+
if (!parent) {
|
|
77
|
+
workTags = inheritedFiberWorkTags.get(ancestor)?.workTags ?? defaultReactWorkTags;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
ancestor = parent;
|
|
81
|
+
workTags = getCachedWorkTags(ancestor);
|
|
55
82
|
}
|
|
56
|
-
const
|
|
83
|
+
const inheritedWorkTags = { workTags, generation: workTagGeneration };
|
|
57
84
|
for (const traversedFiber of traversedFibers) {
|
|
58
|
-
|
|
85
|
+
inheritedFiberWorkTags.set(traversedFiber, inheritedWorkTags);
|
|
59
86
|
if (traversedFiber.alternate) {
|
|
60
|
-
|
|
87
|
+
inheritedFiberWorkTags.set(traversedFiber.alternate, inheritedWorkTags);
|
|
61
88
|
}
|
|
62
89
|
}
|
|
63
90
|
return workTags;
|
package/src/react.ts
CHANGED
|
@@ -1,77 +1,162 @@
|
|
|
1
1
|
import "./install-hook-only.js";
|
|
2
|
-
import
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const subscribeToEmptyStore = (): (() => void) => unsubscribeFromEmptyStore;
|
|
12
|
-
const useSyncExternalStore: unknown = Reflect.get(React, "useSyncExternalStore");
|
|
10
|
+
interface RenderMarker {
|
|
11
|
+
(state: undefined): undefined;
|
|
12
|
+
}
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
interface ReducerQueue {
|
|
15
|
+
lastRenderedReducer: unknown;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface FiberCapture {
|
|
19
|
+
fiber: Fiber;
|
|
20
|
+
queue: ReducerQueue | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const isReducerQueue = (value: unknown): value is ReducerQueue =>
|
|
24
|
+
typeof value === "object" && value !== null && "lastRenderedReducer" in value;
|
|
25
|
+
|
|
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;
|
|
31
|
+
};
|
|
32
|
+
|
|
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;
|
|
39
|
+
};
|
|
40
|
+
|
|
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;
|
|
49
|
+
};
|
|
50
|
+
|
|
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
|
+
}
|
|
65
|
+
|
|
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.
|
|
18
69
|
const bindProxy = new Proxy(originalBind, {
|
|
19
|
-
apply: (bind,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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 {}
|
|
23
84
|
}
|
|
24
|
-
return Reflect.apply(bind,
|
|
85
|
+
return Reflect.apply(bind, callback, boundArguments);
|
|
25
86
|
},
|
|
26
87
|
});
|
|
27
|
-
Reflect.set(Function.prototype, "bind", bindProxy);
|
|
28
88
|
|
|
29
89
|
try {
|
|
30
|
-
|
|
90
|
+
try {
|
|
91
|
+
Reflect.set(Function.prototype, "bind", bindProxy);
|
|
92
|
+
} catch {}
|
|
93
|
+
React.useReducer(renderMarker, undefined);
|
|
94
|
+
return capture;
|
|
31
95
|
} finally {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
96
|
+
isCapturing = false;
|
|
97
|
+
capture = null;
|
|
98
|
+
try {
|
|
99
|
+
if (Function.prototype.bind === bindProxy) {
|
|
100
|
+
Reflect.set(Function.prototype, "bind", originalBind);
|
|
101
|
+
}
|
|
102
|
+
} catch {}
|
|
35
103
|
}
|
|
36
|
-
|
|
37
|
-
return capturedFiber;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const useExternalStoreCapture = (): void => {
|
|
41
|
-
if (typeof useSyncExternalStore !== "function") return;
|
|
42
|
-
Reflect.apply(useSyncExternalStore, React, [
|
|
43
|
-
subscribeToEmptyStore,
|
|
44
|
-
readEmptySnapshot,
|
|
45
|
-
readEmptySnapshot,
|
|
46
|
-
]);
|
|
47
104
|
};
|
|
48
105
|
|
|
49
|
-
|
|
50
|
-
|
|
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;
|
|
117
|
+
}
|
|
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
|
+
});
|
|
51
131
|
};
|
|
52
132
|
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
const
|
|
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;
|
|
60
150
|
const fiber =
|
|
61
|
-
|
|
62
|
-
(
|
|
63
|
-
?
|
|
64
|
-
:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}, [fiber]);
|
|
72
|
-
|
|
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
|
+
}
|
|
73
161
|
return fiber ?? undefined;
|
|
74
162
|
};
|
|
75
|
-
|
|
76
|
-
export const useFiber =
|
|
77
|
-
typeof useSyncExternalStore === "function" ? useFiberWithExternalStore : useFiberWithReducer;
|
|
@@ -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
|
}
|