bippy 0.6.1-dev.fe75557 → 0.7.0-dev.07e6032
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 -1
- package/README.md +223 -461
- package/dist/core.cjs +1 -1
- package/dist/core.js +1 -1
- package/dist/errors.d.cts +410 -0
- package/dist/errors.d.ts +410 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +154 -3
- package/dist/index.d.ts +154 -3
- package/dist/index.js +1 -1
- package/dist/install-hook-only.cjs +1 -1
- package/dist/install-hook-only.d.cts +9 -1
- package/dist/install-hook-only.d.ts +9 -1
- package/dist/install-hook-only.js +1 -1
- package/dist/rdt-hook.cjs +1 -1
- package/dist/rdt-hook.js +1 -1
- package/dist/source.cjs +14 -5
- package/dist/source.d.cts +86 -69
- package/dist/source.d.ts +86 -69
- package/dist/source.js +14 -5
- package/package.json +26 -27
- package/src/core.ts +367 -685
- package/src/errors.ts +34 -0
- package/src/index.ts +1 -0
- package/src/install-hook-only.ts +6 -2
- package/src/rdt-hook.ts +177 -156
- package/src/react-internals/generated/react-work-tags.ts +318 -0
- package/src/react-internals/index.ts +73 -0
- package/src/react-internals/semver.ts +80 -0
- package/src/react-internals/types.ts +186 -0
- package/src/react.ts +76 -0
- package/src/source/constants.ts +1 -1
- package/src/source/error-stack.ts +11 -0
- package/src/source/get-display-name-from-source.ts +44 -40
- package/src/source/get-source.ts +43 -26
- package/src/source/index.ts +11 -1
- package/src/source/inspect-hooks.ts +220 -207
- package/src/source/owner-stack.ts +121 -174
- package/src/source/parse-debug-stack.ts +4 -4
- package/src/source/parse-hook-names.ts +14 -53
- package/src/source/parse-stack.ts +14 -31
- package/src/source/renderer-dispatchers.ts +30 -0
- package/src/source/symbolication.ts +709 -120
- package/dist/core.d.cts +0 -214
- package/dist/core.d.ts +0 -214
- package/dist/core2.d.cts +0 -3
- package/dist/core2.d.ts +0 -3
- package/dist/get-source.cjs +0 -19
- package/dist/get-source.js +0 -19
- package/dist/index.iife.js +0 -9
- package/dist/install-hook-only.iife.js +0 -9
- package/dist/react-refresh.cjs +0 -9
- package/dist/react-refresh.d.cts +0 -66
- package/dist/react-refresh.d.ts +0 -66
- package/dist/react-refresh.js +0 -9
- package/dist/unsubscribe.d.cts +0 -298
- package/dist/unsubscribe.d.ts +0 -298
- package/src/react-refresh/constants.ts +0 -9
- package/src/react-refresh/detect-hmr-transport.ts +0 -33
- package/src/react-refresh/index.ts +0 -173
- package/src/react-refresh/metro-hmr-transport.ts +0 -188
- package/src/react-refresh/next-webpack-hmr-transport.ts +0 -72
- package/src/react-refresh/normalize-hmr-file-path.ts +0 -24
- package/src/react-refresh/types.ts +0 -7
- package/src/react-refresh/vite-hmr-transport.ts +0 -116
- package/src/types.ts +0 -438
- package/src/unsubscribe.ts +0 -17
|
@@ -1,15 +1,26 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
Fiber,
|
|
3
|
+
ContextDependency,
|
|
4
|
+
MemoizedState,
|
|
5
|
+
ReactContext,
|
|
6
|
+
RendererDispatcherRef,
|
|
7
|
+
} from "../react-internals/index.js";
|
|
8
|
+
import {
|
|
9
|
+
BippyHookInspectionError,
|
|
10
|
+
BippyHookRenderError,
|
|
11
|
+
BippyUnsupportedHookError,
|
|
12
|
+
} from "../errors.js";
|
|
13
|
+
import { getReactWorkTagsForFiber } from "../react-internals/index.js";
|
|
2
14
|
import { parseStack, type StackFrame } from "./parse-stack.js";
|
|
3
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
getRendererDispatcherRefs,
|
|
17
|
+
readDispatcher,
|
|
18
|
+
writeDispatcher,
|
|
19
|
+
} from "./renderer-dispatchers.js";
|
|
4
20
|
|
|
5
21
|
const REACT_CONTEXT_TYPE = Symbol.for("react.context");
|
|
6
22
|
const REACT_MEMO_CACHE_SENTINEL = Symbol.for("react.memo_cache_sentinel");
|
|
7
23
|
|
|
8
|
-
const FUNCTION_COMPONENT_TAG = 0;
|
|
9
|
-
const CONTEXT_PROVIDER_TAG = 10;
|
|
10
|
-
const FORWARD_REF_TAG = 11;
|
|
11
|
-
const SIMPLE_MEMO_COMPONENT_TAG = 15;
|
|
12
|
-
|
|
13
24
|
export interface HookSource {
|
|
14
25
|
lineNumber: number | null;
|
|
15
26
|
columnNumber: number | null;
|
|
@@ -26,8 +37,7 @@ export interface HooksNode {
|
|
|
26
37
|
hookSource: HookSource | null;
|
|
27
38
|
}
|
|
28
39
|
|
|
29
|
-
|
|
30
|
-
export interface HooksTree extends Array<HooksNode> {}
|
|
40
|
+
export type HooksTree = HooksNode[];
|
|
31
41
|
|
|
32
42
|
interface HookLogEntry {
|
|
33
43
|
displayName: string | null;
|
|
@@ -37,10 +47,29 @@ interface HookLogEntry {
|
|
|
37
47
|
dispatcherHookName: string;
|
|
38
48
|
}
|
|
39
49
|
|
|
40
|
-
interface
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
interface InspectableReactContext {
|
|
51
|
+
_currentValue: unknown;
|
|
52
|
+
displayName?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface InspectableRef {
|
|
56
|
+
current: unknown;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface InspectableThenable {
|
|
60
|
+
reason?: unknown;
|
|
61
|
+
status?: unknown;
|
|
62
|
+
then: (...arguments_: unknown[]) => unknown;
|
|
63
|
+
value?: unknown;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface ForwardRefRenderType {
|
|
67
|
+
render: (props: Record<string, unknown>, ref: unknown) => unknown;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface InspectedActionState {
|
|
71
|
+
error: unknown;
|
|
72
|
+
value: unknown;
|
|
44
73
|
}
|
|
45
74
|
|
|
46
75
|
let hookLog: HookLogEntry[] = [];
|
|
@@ -49,11 +78,10 @@ let currentFiber: Fiber | null = null;
|
|
|
49
78
|
let currentHook: MemoizedState | null = null;
|
|
50
79
|
let currentContextDependency: ContextDependency<unknown> | null = null;
|
|
51
80
|
let currentThenableIndex = 0;
|
|
52
|
-
|
|
53
|
-
let currentThenableState: any[] | null = null;
|
|
81
|
+
let currentThenableState: unknown[] | null = null;
|
|
54
82
|
|
|
55
83
|
const SuspenseException: unknown = new Error(
|
|
56
|
-
"Suspense
|
|
84
|
+
"Suspense interrupted this render. This error is an internal implementation detail of `use`.",
|
|
57
85
|
);
|
|
58
86
|
|
|
59
87
|
const parseErrorStack = (error: Error): StackFrame[] =>
|
|
@@ -65,40 +93,40 @@ const nextHook = (): MemoizedState | null => {
|
|
|
65
93
|
return hook;
|
|
66
94
|
};
|
|
67
95
|
|
|
68
|
-
const
|
|
96
|
+
const isInspectableThenable = (value: unknown): value is InspectableThenable =>
|
|
97
|
+
typeof value === "object" &&
|
|
98
|
+
value !== null &&
|
|
99
|
+
"then" in value &&
|
|
100
|
+
typeof value.then === "function";
|
|
101
|
+
|
|
102
|
+
const isInspectableRef = (value: unknown): value is InspectableRef =>
|
|
103
|
+
typeof value === "object" && value !== null && "current" in value;
|
|
104
|
+
|
|
105
|
+
const isReactContext = (value: unknown): value is ReactContext<unknown> =>
|
|
106
|
+
typeof value === "object" && value !== null && "_currentValue" in value;
|
|
107
|
+
|
|
108
|
+
const isContextDependency = (value: unknown): value is ContextDependency<unknown> =>
|
|
109
|
+
typeof value === "object" && value !== null && "context" in value && "next" in value;
|
|
110
|
+
|
|
111
|
+
const isForwardRefRenderType = (value: unknown): value is ForwardRefRenderType =>
|
|
112
|
+
typeof value === "object" &&
|
|
113
|
+
value !== null &&
|
|
114
|
+
"render" in value &&
|
|
115
|
+
typeof value.render === "function";
|
|
116
|
+
|
|
117
|
+
const readContext = (context: InspectableReactContext): unknown => {
|
|
69
118
|
if (currentFiber === null) return context._currentValue;
|
|
70
119
|
if (currentContextDependency === null) {
|
|
71
|
-
throw new
|
|
120
|
+
throw new BippyHookInspectionError("Context reads don’t match context dependencies.");
|
|
72
121
|
}
|
|
73
|
-
if (Object.
|
|
74
|
-
const value = currentContextDependency.memoizedValue
|
|
122
|
+
if (Object.hasOwn(currentContextDependency, "memoizedValue")) {
|
|
123
|
+
const value = currentContextDependency.memoizedValue;
|
|
75
124
|
currentContextDependency = currentContextDependency.next;
|
|
76
125
|
return value;
|
|
77
126
|
}
|
|
78
127
|
return context._currentValue;
|
|
79
128
|
};
|
|
80
129
|
|
|
81
|
-
const getDispatcherRef = (): DispatcherRefContainer | null => {
|
|
82
|
-
const rdtHook = getRDTHook();
|
|
83
|
-
const allRenderers = [..._renderers, ...rdtHook.renderers.values()];
|
|
84
|
-
for (const renderer of allRenderers) {
|
|
85
|
-
const ref = renderer.currentDispatcherRef;
|
|
86
|
-
if (ref && typeof ref === "object") return ref as DispatcherRefContainer;
|
|
87
|
-
}
|
|
88
|
-
return null;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const getDispatcherFromRef = (ref: DispatcherRefContainer): unknown =>
|
|
92
|
-
"H" in ref ? ref.H : ref.current;
|
|
93
|
-
|
|
94
|
-
const setDispatcherOnRef = (ref: DispatcherRefContainer, dispatcher: unknown): void => {
|
|
95
|
-
if ("H" in ref) {
|
|
96
|
-
ref.H = dispatcher;
|
|
97
|
-
} else {
|
|
98
|
-
ref.current = dispatcher;
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
|
|
102
130
|
const pushHookLogEntry = (
|
|
103
131
|
primitive: string,
|
|
104
132
|
value: unknown,
|
|
@@ -115,14 +143,13 @@ const pushHookLogEntry = (
|
|
|
115
143
|
};
|
|
116
144
|
|
|
117
145
|
const dispatcherUse = (usable: unknown): unknown => {
|
|
118
|
-
if (usable
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (typeof asThenable.then === "function") {
|
|
122
|
-
const thenable =
|
|
146
|
+
if (typeof usable === "object" && usable !== null) {
|
|
147
|
+
if (isInspectableThenable(usable)) {
|
|
148
|
+
const cachedThenable =
|
|
123
149
|
currentThenableState !== null && currentThenableIndex < currentThenableState.length
|
|
124
150
|
? currentThenableState[currentThenableIndex++]
|
|
125
|
-
:
|
|
151
|
+
: usable;
|
|
152
|
+
const thenable = isInspectableThenable(cachedThenable) ? cachedThenable : usable;
|
|
126
153
|
|
|
127
154
|
switch (thenable.status) {
|
|
128
155
|
case "fulfilled": {
|
|
@@ -135,19 +162,22 @@ const dispatcherUse = (usable: unknown): unknown => {
|
|
|
135
162
|
pushHookLogEntry("Unresolved", thenable, "Use");
|
|
136
163
|
throw SuspenseException;
|
|
137
164
|
}
|
|
138
|
-
if (
|
|
139
|
-
const context:
|
|
165
|
+
if ("$$typeof" in usable && usable.$$typeof === REACT_CONTEXT_TYPE && isReactContext(usable)) {
|
|
166
|
+
const context: InspectableReactContext = {
|
|
167
|
+
_currentValue: usable._currentValue,
|
|
168
|
+
displayName: typeof usable.displayName === "string" ? usable.displayName : undefined,
|
|
169
|
+
};
|
|
140
170
|
const value = readContext(context);
|
|
141
|
-
pushHookLogEntry("Context (use)", value, "Use", context.displayName
|
|
171
|
+
pushHookLogEntry("Context (use)", value, "Use", context.displayName ?? "Context");
|
|
142
172
|
return value;
|
|
143
173
|
}
|
|
144
174
|
}
|
|
145
|
-
throw new
|
|
175
|
+
throw new BippyHookInspectionError("use() received an unsupported value: " + String(usable));
|
|
146
176
|
};
|
|
147
177
|
|
|
148
|
-
const dispatcherUseContext = (context:
|
|
178
|
+
const dispatcherUseContext = (context: InspectableReactContext): unknown => {
|
|
149
179
|
const value = readContext(context);
|
|
150
|
-
pushHookLogEntry("Context", value, "Context", context.displayName
|
|
180
|
+
pushHookLogEntry("Context", value, "Context", context.displayName ?? null);
|
|
151
181
|
return value;
|
|
152
182
|
};
|
|
153
183
|
|
|
@@ -157,7 +187,7 @@ const dispatcherUseState = (initialState: unknown): [unknown, () => void] => {
|
|
|
157
187
|
hook !== null
|
|
158
188
|
? hook.memoizedState
|
|
159
189
|
: typeof initialState === "function"
|
|
160
|
-
?
|
|
190
|
+
? initialState()
|
|
161
191
|
: initialState;
|
|
162
192
|
pushHookLogEntry("State", state, "State");
|
|
163
193
|
return [state, () => {}];
|
|
@@ -175,11 +205,13 @@ const dispatcherUseReducer = (
|
|
|
175
205
|
return [state, () => {}];
|
|
176
206
|
};
|
|
177
207
|
|
|
178
|
-
const dispatcherUseRef = (initialValue: unknown):
|
|
208
|
+
const dispatcherUseRef = (initialValue: unknown): InspectableRef => {
|
|
179
209
|
const hook = nextHook();
|
|
180
|
-
const ref = hook
|
|
181
|
-
|
|
182
|
-
|
|
210
|
+
const ref = isInspectableRef(hook?.memoizedState)
|
|
211
|
+
? hook.memoizedState
|
|
212
|
+
: { current: initialValue };
|
|
213
|
+
pushHookLogEntry("Ref", ref.current, "Ref");
|
|
214
|
+
return ref;
|
|
183
215
|
};
|
|
184
216
|
|
|
185
217
|
const dispatcherUseCacheRefresh = (): (() => void) => {
|
|
@@ -205,10 +237,7 @@ const dispatcherUseEffect = (create: () => void): void => {
|
|
|
205
237
|
|
|
206
238
|
const dispatcherUseImperativeHandle = (ref: unknown): void => {
|
|
207
239
|
nextHook();
|
|
208
|
-
|
|
209
|
-
if (ref !== null && typeof ref === "object" && "current" in ref) {
|
|
210
|
-
instance = ref.current;
|
|
211
|
-
}
|
|
240
|
+
const instance = isInspectableRef(ref) ? ref.current : undefined;
|
|
212
241
|
pushHookLogEntry("ImperativeHandle", instance, "ImperativeHandle");
|
|
213
242
|
};
|
|
214
243
|
|
|
@@ -227,7 +256,7 @@ const dispatcherUseCallback = (callback: unknown): unknown => {
|
|
|
227
256
|
const hook = nextHook();
|
|
228
257
|
pushHookLogEntry(
|
|
229
258
|
"Callback",
|
|
230
|
-
hook
|
|
259
|
+
Array.isArray(hook?.memoizedState) ? hook.memoizedState[0] : callback,
|
|
231
260
|
"Callback",
|
|
232
261
|
);
|
|
233
262
|
return callback;
|
|
@@ -235,7 +264,7 @@ const dispatcherUseCallback = (callback: unknown): unknown => {
|
|
|
235
264
|
|
|
236
265
|
const dispatcherUseMemo = (nextCreate: () => unknown): unknown => {
|
|
237
266
|
const hook = nextHook();
|
|
238
|
-
const value = hook
|
|
267
|
+
const value = Array.isArray(hook?.memoizedState) ? hook.memoizedState[0] : nextCreate();
|
|
239
268
|
pushHookLogEntry("Memo", value, "Memo");
|
|
240
269
|
return value;
|
|
241
270
|
};
|
|
@@ -254,7 +283,7 @@ const dispatcherUseSyncExternalStore = (
|
|
|
254
283
|
const dispatcherUseTransition = (): [boolean, () => void] => {
|
|
255
284
|
const stateHook = nextHook();
|
|
256
285
|
nextHook();
|
|
257
|
-
const isPending = stateHook
|
|
286
|
+
const isPending = stateHook?.memoizedState === true;
|
|
258
287
|
pushHookLogEntry("Transition", isPending, "Transition");
|
|
259
288
|
return [isPending, () => {}];
|
|
260
289
|
};
|
|
@@ -268,7 +297,7 @@ const dispatcherUseDeferredValue = (value: unknown): unknown => {
|
|
|
268
297
|
|
|
269
298
|
const dispatcherUseId = (): string => {
|
|
270
299
|
const hook = nextHook();
|
|
271
|
-
const identifier = hook
|
|
300
|
+
const identifier = typeof hook?.memoizedState === "string" ? hook.memoizedState : "";
|
|
272
301
|
pushHookLogEntry("Id", identifier, "Id");
|
|
273
302
|
return identifier;
|
|
274
303
|
};
|
|
@@ -277,9 +306,7 @@ const dispatcherUseMemoCache = (size: number): unknown[] => {
|
|
|
277
306
|
const fiber = currentFiber;
|
|
278
307
|
if (fiber === null || fiber === undefined) return [];
|
|
279
308
|
|
|
280
|
-
const memoCache =
|
|
281
|
-
fiber.updateQueue as { memoCache?: { data: unknown[][]; index: number } } | null
|
|
282
|
-
)?.memoCache;
|
|
309
|
+
const memoCache = fiber.updateQueue?.memoCache;
|
|
283
310
|
if (memoCache === null || memoCache === undefined) return [];
|
|
284
311
|
|
|
285
312
|
let memoCacheSlots = memoCache.data[memoCache.index];
|
|
@@ -304,28 +331,22 @@ const dispatcherUseOptimistic = (passthrough: unknown): [unknown, () => void] =>
|
|
|
304
331
|
const inspectActionStateHook = (
|
|
305
332
|
hook: MemoizedState | null,
|
|
306
333
|
initialState: unknown,
|
|
307
|
-
):
|
|
334
|
+
): InspectedActionState => {
|
|
308
335
|
let value: unknown;
|
|
309
336
|
let error: unknown = null;
|
|
310
337
|
if (hook !== null) {
|
|
311
338
|
const actionResult = hook.memoizedState;
|
|
312
|
-
if (
|
|
313
|
-
|
|
314
|
-
actionResult !== null &&
|
|
315
|
-
"then" in actionResult &&
|
|
316
|
-
typeof actionResult.then === "function"
|
|
317
|
-
) {
|
|
318
|
-
const thenable = actionResult as { status?: string; value?: unknown; reason?: unknown };
|
|
319
|
-
switch (thenable.status) {
|
|
339
|
+
if (isInspectableThenable(actionResult)) {
|
|
340
|
+
switch (actionResult.status) {
|
|
320
341
|
case "fulfilled":
|
|
321
|
-
value =
|
|
342
|
+
value = actionResult.value;
|
|
322
343
|
break;
|
|
323
344
|
case "rejected":
|
|
324
|
-
error =
|
|
345
|
+
error = actionResult.reason;
|
|
325
346
|
break;
|
|
326
347
|
default:
|
|
327
348
|
error = SuspenseException;
|
|
328
|
-
value =
|
|
349
|
+
value = actionResult;
|
|
329
350
|
}
|
|
330
351
|
} else {
|
|
331
352
|
value = actionResult;
|
|
@@ -342,15 +363,8 @@ const createActionStateDispatcher =
|
|
|
342
363
|
const hook = nextHook();
|
|
343
364
|
nextHook();
|
|
344
365
|
nextHook();
|
|
345
|
-
const stackError = new Error();
|
|
346
366
|
const { value, error } = inspectActionStateHook(hook, initialState);
|
|
347
|
-
|
|
348
|
-
displayName: null,
|
|
349
|
-
primitive,
|
|
350
|
-
stackError,
|
|
351
|
-
value,
|
|
352
|
-
dispatcherHookName: primitive,
|
|
353
|
-
});
|
|
367
|
+
pushHookLogEntry(primitive, value, primitive);
|
|
354
368
|
if (error !== null) throw error;
|
|
355
369
|
return [value, () => {}, false];
|
|
356
370
|
};
|
|
@@ -360,7 +374,7 @@ const dispatcherUseFormState = createActionStateDispatcher("FormState");
|
|
|
360
374
|
|
|
361
375
|
const dispatcherUseHostTransitionStatus = (): unknown => {
|
|
362
376
|
// HACK: creating a minimal fake context because useHostTransitionStatus reads from an internal context not available outside React
|
|
363
|
-
const status = readContext({ _currentValue: null }
|
|
377
|
+
const status = readContext({ _currentValue: null });
|
|
364
378
|
pushHookLogEntry("HostTransitionStatus", status, "HostTransitionStatus");
|
|
365
379
|
return status;
|
|
366
380
|
};
|
|
@@ -371,8 +385,7 @@ const dispatcherUseEffectEvent = (callback: (...args: unknown[]) => unknown): ty
|
|
|
371
385
|
return callback;
|
|
372
386
|
};
|
|
373
387
|
|
|
374
|
-
|
|
375
|
-
const Dispatcher: Record<string, (...args: any[]) => any> = {
|
|
388
|
+
const dispatcher = {
|
|
376
389
|
readContext,
|
|
377
390
|
use: dispatcherUse,
|
|
378
391
|
useCallback: dispatcherUseCallback,
|
|
@@ -399,17 +412,12 @@ const Dispatcher: Record<string, (...args: any[]) => any> = {
|
|
|
399
412
|
useEffectEvent: dispatcherUseEffectEvent,
|
|
400
413
|
};
|
|
401
414
|
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
const error = new Error("Missing method in Dispatcher: " + prop);
|
|
409
|
-
error.name = "ReactDebugToolsUnsupportedHookError";
|
|
410
|
-
throw error;
|
|
411
|
-
},
|
|
412
|
-
});
|
|
415
|
+
const dispatcherProxy = new Proxy(dispatcher, {
|
|
416
|
+
get(target, propertyName: string) {
|
|
417
|
+
if (Object.hasOwn(target, propertyName)) return Reflect.get(target, propertyName);
|
|
418
|
+
throw new BippyUnsupportedHookError("The React dispatcher is missing method " + propertyName);
|
|
419
|
+
},
|
|
420
|
+
});
|
|
413
421
|
|
|
414
422
|
const getPrimitiveStackCache = (): Map<string, StackFrame[]> => {
|
|
415
423
|
if (primitiveStackCache !== null) return primitiveStackCache;
|
|
@@ -418,41 +426,38 @@ const getPrimitiveStackCache = (): Map<string, StackFrame[]> => {
|
|
|
418
426
|
let capturedHookLog: HookLogEntry[];
|
|
419
427
|
|
|
420
428
|
try {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
429
|
+
dispatcher.useContext({ _currentValue: null });
|
|
430
|
+
dispatcher.useState(null);
|
|
431
|
+
dispatcher.useReducer((state: unknown) => state, null);
|
|
432
|
+
dispatcher.useRef(null);
|
|
433
|
+
dispatcher.useCacheRefresh();
|
|
434
|
+
dispatcher.useLayoutEffect(() => {});
|
|
435
|
+
dispatcher.useInsertionEffect(() => {});
|
|
436
|
+
dispatcher.useEffect(() => {});
|
|
437
|
+
dispatcher.useImperativeHandle(undefined);
|
|
438
|
+
dispatcher.useDebugValue(null);
|
|
439
|
+
dispatcher.useCallback(() => {});
|
|
440
|
+
dispatcher.useTransition();
|
|
441
|
+
dispatcher.useSyncExternalStore(
|
|
434
442
|
() => () => {},
|
|
435
443
|
() => null,
|
|
436
|
-
() => null,
|
|
437
444
|
);
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
}
|
|
454
|
-
Dispatcher.useId();
|
|
455
|
-
if (typeof Dispatcher.useEffectEvent === "function") Dispatcher.useEffectEvent(() => {});
|
|
445
|
+
dispatcher.useDeferredValue(null);
|
|
446
|
+
dispatcher.useMemo(() => null);
|
|
447
|
+
dispatcher.useOptimistic(null);
|
|
448
|
+
dispatcher.useFormState((state: unknown) => state, null);
|
|
449
|
+
dispatcher.useActionState((state: unknown) => state, null);
|
|
450
|
+
dispatcher.useHostTransitionStatus();
|
|
451
|
+
dispatcher.use({ $$typeof: REACT_CONTEXT_TYPE, _currentValue: null });
|
|
452
|
+
const fulfilledPromise = Promise.resolve(null);
|
|
453
|
+
Reflect.set(fulfilledPromise, "status", "fulfilled");
|
|
454
|
+
Reflect.set(fulfilledPromise, "value", null);
|
|
455
|
+
dispatcher.use(fulfilledPromise);
|
|
456
|
+
try {
|
|
457
|
+
dispatcher.use(new Promise<never>(() => {}));
|
|
458
|
+
} catch {}
|
|
459
|
+
dispatcher.useId();
|
|
460
|
+
dispatcher.useEffectEvent(() => {});
|
|
456
461
|
} finally {
|
|
457
462
|
capturedHookLog = hookLog;
|
|
458
463
|
hookLog = [];
|
|
@@ -681,14 +686,20 @@ const processDebugValues = (hooksTree: HooksTree, parentHooksNode: HooksNode | n
|
|
|
681
686
|
const setupContexts = (contextMap: Map<ReactContext<unknown>, unknown>, fiber: Fiber): void => {
|
|
682
687
|
let current: Fiber | null = fiber;
|
|
683
688
|
while (current) {
|
|
684
|
-
if (current.tag ===
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
689
|
+
if (current.tag === getReactWorkTagsForFiber(current).ContextProvider) {
|
|
690
|
+
const providerType = current.type;
|
|
691
|
+
const nestedContext =
|
|
692
|
+
typeof providerType === "object" && providerType !== null && "_context" in providerType
|
|
693
|
+
? providerType._context
|
|
694
|
+
: undefined;
|
|
695
|
+
const context = isReactContext(nestedContext)
|
|
696
|
+
? nestedContext
|
|
697
|
+
: isReactContext(providerType)
|
|
698
|
+
? providerType
|
|
699
|
+
: null;
|
|
700
|
+
if (context && !contextMap.has(context)) {
|
|
690
701
|
contextMap.set(context, context._currentValue);
|
|
691
|
-
context._currentValue =
|
|
702
|
+
context._currentValue = current.memoizedProps.value;
|
|
692
703
|
}
|
|
693
704
|
}
|
|
694
705
|
current = current.return;
|
|
@@ -703,28 +714,26 @@ const restoreContexts = (contextMap: Map<ReactContext<unknown>, unknown>): void
|
|
|
703
714
|
|
|
704
715
|
const handleRenderFunctionError = (error: unknown): void => {
|
|
705
716
|
if (error === SuspenseException) return;
|
|
706
|
-
if (error instanceof
|
|
707
|
-
|
|
708
|
-
wrapperError.name = "ReactDebugToolsRenderError";
|
|
709
|
-
(wrapperError as { cause: unknown }).cause = error;
|
|
710
|
-
throw wrapperError;
|
|
717
|
+
if (error instanceof BippyUnsupportedHookError) throw error;
|
|
718
|
+
throw new BippyHookRenderError("Bippy couldn’t render the inspected component", error);
|
|
711
719
|
};
|
|
712
720
|
|
|
713
721
|
const resolveDefaultProps = (
|
|
714
|
-
|
|
722
|
+
component: unknown,
|
|
715
723
|
baseProps: Record<string, unknown>,
|
|
716
724
|
): Record<string, unknown> => {
|
|
717
725
|
if (
|
|
718
|
-
|
|
719
|
-
typeof
|
|
720
|
-
"defaultProps" in
|
|
721
|
-
|
|
726
|
+
component &&
|
|
727
|
+
typeof component === "object" &&
|
|
728
|
+
"defaultProps" in component &&
|
|
729
|
+
typeof component.defaultProps === "object" &&
|
|
730
|
+
component.defaultProps !== null
|
|
722
731
|
) {
|
|
723
732
|
const props = { ...baseProps };
|
|
724
|
-
const defaultProps =
|
|
725
|
-
for (const propName
|
|
733
|
+
const defaultProps = component.defaultProps;
|
|
734
|
+
for (const [propName, value] of Object.entries(defaultProps)) {
|
|
726
735
|
if (props[propName] === undefined) {
|
|
727
|
-
props[propName] =
|
|
736
|
+
props[propName] = value;
|
|
728
737
|
}
|
|
729
738
|
}
|
|
730
739
|
return props;
|
|
@@ -736,11 +745,9 @@ const suppressConsole = (): Record<string, unknown> => {
|
|
|
736
745
|
const originalMethods: Record<string, unknown> = {};
|
|
737
746
|
for (const method in console) {
|
|
738
747
|
try {
|
|
739
|
-
originalMethods[method] = (console
|
|
740
|
-
(console
|
|
741
|
-
} catch {
|
|
742
|
-
/* noop */
|
|
743
|
-
}
|
|
748
|
+
originalMethods[method] = Reflect.get(console, method);
|
|
749
|
+
Reflect.set(console, method, () => {});
|
|
750
|
+
} catch {}
|
|
744
751
|
}
|
|
745
752
|
return originalMethods;
|
|
746
753
|
};
|
|
@@ -748,19 +755,17 @@ const suppressConsole = (): Record<string, unknown> => {
|
|
|
748
755
|
const restoreConsole = (originalMethods: Record<string, unknown>): void => {
|
|
749
756
|
for (const method in originalMethods) {
|
|
750
757
|
try {
|
|
751
|
-
(console
|
|
752
|
-
} catch {
|
|
753
|
-
/* noop */
|
|
754
|
-
}
|
|
758
|
+
Reflect.set(console, method, originalMethods[method]);
|
|
759
|
+
} catch {}
|
|
755
760
|
}
|
|
756
761
|
};
|
|
757
762
|
|
|
758
763
|
const performDispatcherInspection = (
|
|
759
|
-
dispatcherRef:
|
|
764
|
+
dispatcherRef: RendererDispatcherRef,
|
|
760
765
|
renderFn: () => void,
|
|
761
766
|
): HooksTree => {
|
|
762
|
-
const previousDispatcher =
|
|
763
|
-
|
|
767
|
+
const previousDispatcher = readDispatcher(dispatcherRef);
|
|
768
|
+
writeDispatcher(dispatcherRef, dispatcherProxy);
|
|
764
769
|
|
|
765
770
|
let capturedHookLog: HookLogEntry[] = [];
|
|
766
771
|
let ancestorStackError: Error | undefined;
|
|
@@ -773,56 +778,61 @@ const performDispatcherInspection = (
|
|
|
773
778
|
} finally {
|
|
774
779
|
capturedHookLog = hookLog;
|
|
775
780
|
hookLog = [];
|
|
776
|
-
|
|
781
|
+
writeDispatcher(dispatcherRef, previousDispatcher);
|
|
777
782
|
}
|
|
778
783
|
|
|
779
784
|
const rootStack = ancestorStackError !== undefined ? parseErrorStack(ancestorStackError) : [];
|
|
780
785
|
return buildTree(rootStack, capturedHookLog);
|
|
781
786
|
};
|
|
782
787
|
|
|
783
|
-
const requireDispatcherRef = ():
|
|
784
|
-
const dispatcherRef =
|
|
788
|
+
const requireDispatcherRef = (): RendererDispatcherRef => {
|
|
789
|
+
const dispatcherRef = getRendererDispatcherRefs()[0];
|
|
785
790
|
if (!dispatcherRef) {
|
|
786
|
-
throw new
|
|
787
|
-
"
|
|
791
|
+
throw new BippyHookInspectionError(
|
|
792
|
+
"Bippy couldn’t find a React renderer. Load React and install Bippy’s hook.",
|
|
788
793
|
);
|
|
789
794
|
}
|
|
790
795
|
return dispatcherRef;
|
|
791
796
|
};
|
|
792
797
|
|
|
793
798
|
const resolveContextDependency = (fiber: Fiber): void => {
|
|
794
|
-
|
|
799
|
+
const legacyDependenciesKey = ["dependencies_old", "dependencies_new"].find((key) =>
|
|
800
|
+
Object.hasOwn(fiber, key),
|
|
801
|
+
);
|
|
802
|
+
if (Object.hasOwn(fiber, "dependencies")) {
|
|
795
803
|
const dependencies = fiber.dependencies;
|
|
796
804
|
currentContextDependency = dependencies !== null ? dependencies.firstContext : null;
|
|
797
|
-
} else if (
|
|
798
|
-
const dependencies = (fiber
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
const
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
805
|
+
} else if (legacyDependenciesKey) {
|
|
806
|
+
const dependencies: unknown = Reflect.get(fiber, legacyDependenciesKey);
|
|
807
|
+
const firstContext =
|
|
808
|
+
typeof dependencies === "object" && dependencies !== null && "firstContext" in dependencies
|
|
809
|
+
? dependencies.firstContext
|
|
810
|
+
: null;
|
|
811
|
+
currentContextDependency = isContextDependency(firstContext) ? firstContext : null;
|
|
812
|
+
} else if (Object.hasOwn(fiber, "contextDependencies")) {
|
|
813
|
+
const contextDependencies: unknown = Reflect.get(fiber, "contextDependencies");
|
|
814
|
+
const firstContext =
|
|
815
|
+
typeof contextDependencies === "object" &&
|
|
816
|
+
contextDependencies !== null &&
|
|
817
|
+
"first" in contextDependencies
|
|
818
|
+
? contextDependencies.first
|
|
819
|
+
: null;
|
|
820
|
+
currentContextDependency = isContextDependency(firstContext) ? firstContext : null;
|
|
812
821
|
} else {
|
|
813
|
-
throw new
|
|
822
|
+
throw new BippyHookInspectionError("Bippy doesn’t support this React version.");
|
|
814
823
|
}
|
|
815
824
|
};
|
|
816
825
|
|
|
817
826
|
export const getFiberHooks = (fiber: Fiber): HooksTree => {
|
|
818
827
|
const dispatcherRef = requireDispatcherRef();
|
|
828
|
+
const workTags = getReactWorkTagsForFiber(fiber);
|
|
819
829
|
|
|
820
830
|
if (
|
|
821
|
-
fiber.tag !==
|
|
822
|
-
fiber.tag !==
|
|
823
|
-
fiber.tag !==
|
|
831
|
+
fiber.tag !== workTags.FunctionComponent &&
|
|
832
|
+
fiber.tag !== workTags.SimpleMemoComponent &&
|
|
833
|
+
fiber.tag !== workTags.ForwardRef
|
|
824
834
|
) {
|
|
825
|
-
throw new
|
|
835
|
+
throw new BippyHookInspectionError("Hook inspection requires a function component Fiber.");
|
|
826
836
|
}
|
|
827
837
|
|
|
828
838
|
getPrimitiveStackCache();
|
|
@@ -830,19 +840,17 @@ export const getFiberHooks = (fiber: Fiber): HooksTree => {
|
|
|
830
840
|
currentHook = fiber.memoizedState;
|
|
831
841
|
currentFiber = fiber;
|
|
832
842
|
|
|
833
|
-
const debugThenableState =
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
? debugThenableState.thenables || debugThenableState
|
|
838
|
-
: null;
|
|
843
|
+
const debugThenableState = fiber.dependencies?._debugThenableState;
|
|
844
|
+
const usedThenables = Array.isArray(debugThenableState)
|
|
845
|
+
? debugThenableState
|
|
846
|
+
: debugThenableState?.thenables;
|
|
839
847
|
currentThenableState = Array.isArray(usedThenables) ? usedThenables : null;
|
|
840
848
|
currentThenableIndex = 0;
|
|
841
849
|
|
|
842
850
|
resolveContextDependency(fiber);
|
|
843
851
|
|
|
844
852
|
const type = fiber.type;
|
|
845
|
-
let props = fiber.memoizedProps
|
|
853
|
+
let props = fiber.memoizedProps;
|
|
846
854
|
if (type !== fiber.elementType) {
|
|
847
855
|
props = resolveDefaultProps(type, props);
|
|
848
856
|
}
|
|
@@ -853,22 +861,27 @@ export const getFiberHooks = (fiber: Fiber): HooksTree => {
|
|
|
853
861
|
try {
|
|
854
862
|
if (
|
|
855
863
|
currentContextDependency !== null &&
|
|
856
|
-
!Object.
|
|
864
|
+
!Object.hasOwn(currentContextDependency, "memoizedValue")
|
|
857
865
|
) {
|
|
858
866
|
setupContexts(contextMap, fiber);
|
|
859
867
|
}
|
|
860
868
|
|
|
861
|
-
if (fiber.tag ===
|
|
869
|
+
if (fiber.tag === workTags.ForwardRef) {
|
|
870
|
+
if (!isForwardRefRenderType(type)) {
|
|
871
|
+
throw new BippyHookInspectionError("ForwardRef fiber is missing its render function.");
|
|
872
|
+
}
|
|
862
873
|
return performDispatcherInspection(dispatcherRef, () => {
|
|
863
|
-
|
|
864
|
-
props,
|
|
865
|
-
fiber.ref,
|
|
866
|
-
);
|
|
874
|
+
type.render(props, fiber.ref);
|
|
867
875
|
});
|
|
868
876
|
}
|
|
869
877
|
|
|
878
|
+
if (typeof type !== "function") {
|
|
879
|
+
throw new BippyHookInspectionError(
|
|
880
|
+
"Function component fiber is missing its component function.",
|
|
881
|
+
);
|
|
882
|
+
}
|
|
870
883
|
return performDispatcherInspection(dispatcherRef, () => {
|
|
871
|
-
|
|
884
|
+
type(props);
|
|
872
885
|
});
|
|
873
886
|
} finally {
|
|
874
887
|
currentFiber = null;
|