bippy 0.6.0 → 0.6.1-dev.03b829a
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 +126 -136
- package/dist/core.cjs +1 -1
- package/dist/core.d.cts +45 -60
- package/dist/core.d.ts +45 -60
- package/dist/core.js +1 -1
- package/dist/core2.cjs +9 -0
- package/dist/core2.d.cts +11 -3
- package/dist/core2.d.ts +11 -3
- package/dist/core2.js +9 -0
- package/dist/errors.d.cts +447 -0
- package/dist/errors.d.ts +447 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +11 -3
- package/dist/index.d.ts +11 -3
- package/dist/index.iife.js +1 -1
- 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.iife.js +1 -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 -4
- package/dist/source.d.cts +27 -9
- package/dist/source.d.ts +27 -9
- package/dist/source.js +14 -4
- package/package.json +32 -22
- package/src/core.ts +368 -314
- package/src/errors.ts +34 -0
- package/src/install-hook-only.ts +2 -2
- package/src/rdt-hook.ts +150 -153
- package/src/react-internals/generated/react-work-tags.ts +318 -0
- package/src/react-internals/index.ts +67 -0
- package/src/react-internals/semver.ts +78 -0
- package/src/react-internals/types.ts +269 -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 +1 -1
- package/src/source/get-source.ts +4 -3
- package/src/source/index.ts +9 -1
- package/src/source/inspect-hooks.ts +221 -169
- package/src/source/owner-stack.ts +88 -114
- package/src/source/parse-debug-stack.ts +4 -4
- package/src/source/parse-hook-names.ts +1 -1
- package/src/source/parse-stack.ts +1 -1
- package/src/source/symbolication.ts +488 -88
- package/dist/get-source.cjs +0 -19
- package/dist/get-source.js +0 -19
- 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
package/src/core.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
//
|
|
2
|
-
// since it will be executed before the react devtools hook is created
|
|
1
|
+
// React must remain a type-only import because this module loads immediately after the DevTools hook.
|
|
3
2
|
|
|
4
3
|
import type * as React from "react";
|
|
5
4
|
|
|
@@ -7,65 +6,82 @@ import type {
|
|
|
7
6
|
ContextDependency,
|
|
8
7
|
Fiber,
|
|
9
8
|
FiberRoot,
|
|
9
|
+
HostFiber,
|
|
10
10
|
MemoizedState,
|
|
11
11
|
ReactDevToolsGlobalHook,
|
|
12
12
|
ReactRenderer,
|
|
13
|
-
} from "./
|
|
13
|
+
} from "./react-internals/index.js";
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
16
|
_onActiveListeners,
|
|
17
17
|
BIPPY_INSTRUMENTATION_STRING,
|
|
18
|
+
createUnsubscribe,
|
|
18
19
|
getRDTHook,
|
|
19
20
|
hasRDTHook,
|
|
20
|
-
isReactRefresh,
|
|
21
21
|
isRealReactDevtools,
|
|
22
|
+
onRDTHookReplace,
|
|
22
23
|
onRendererInject,
|
|
23
24
|
} from "./rdt-hook.js";
|
|
24
|
-
import {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
export
|
|
35
|
-
export
|
|
36
|
-
export
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
25
|
+
import type { Unsubscribe } from "./rdt-hook.js";
|
|
26
|
+
import {
|
|
27
|
+
getReactWorkTagsForFiber,
|
|
28
|
+
MutationMask,
|
|
29
|
+
ReactBuildType,
|
|
30
|
+
ReactFiberFlags,
|
|
31
|
+
ReactSymbols,
|
|
32
|
+
setReactWorkTagsForFiber,
|
|
33
|
+
} from "./react-internals/index.js";
|
|
34
|
+
|
|
35
|
+
export { getReactWorkTags, ReactSymbols } from "./react-internals/index.js";
|
|
36
|
+
export type { ReactWorkTagMap, ReactWorkTagVersion } from "./react-internals/index.js";
|
|
37
|
+
export {
|
|
38
|
+
BippyError,
|
|
39
|
+
BippyHookInspectionError,
|
|
40
|
+
BippyHookRenderError,
|
|
41
|
+
BippySourceMapError,
|
|
42
|
+
BippyUnsupportedHookError,
|
|
43
|
+
} from "./errors.js";
|
|
44
|
+
|
|
45
|
+
const isComponentType = (value: unknown): value is React.ComponentType<unknown> =>
|
|
46
|
+
typeof value === "function";
|
|
47
|
+
|
|
48
|
+
const getPropertyValue = (value: object, key: PropertyKey): unknown => {
|
|
49
|
+
let currentValue: object | null = value;
|
|
50
|
+
while (currentValue) {
|
|
51
|
+
const descriptor = Object.getOwnPropertyDescriptor(currentValue, key);
|
|
52
|
+
if (descriptor) {
|
|
53
|
+
if ("value" in descriptor) return descriptor.value;
|
|
54
|
+
return descriptor.get?.call(value);
|
|
55
|
+
}
|
|
56
|
+
currentValue = Object.getPrototypeOf(currentValue);
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const getTypeName = (value: object): string | null => {
|
|
62
|
+
const displayName = getPropertyValue(value, "displayName");
|
|
63
|
+
if (typeof displayName === "string" && displayName) return displayName;
|
|
64
|
+
const name = getPropertyValue(value, "name");
|
|
65
|
+
return typeof name === "string" && name ? name : null;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
interface FiberSelector {
|
|
69
|
+
(node: Fiber): boolean | Promise<boolean | void> | void;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface FiberTimings {
|
|
73
|
+
selfTime: number;
|
|
74
|
+
totalTime: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface RenderHandler {
|
|
78
|
+
<State>(fiber: Fiber, phase: RenderPhase, state?: State): unknown;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface ValueWrite {
|
|
82
|
+
path: string[];
|
|
83
|
+
value: unknown;
|
|
84
|
+
}
|
|
69
85
|
|
|
70
86
|
/**
|
|
71
87
|
* Returns `true` if object is a React Element.
|
|
@@ -74,19 +90,17 @@ const MutationMask =
|
|
|
74
90
|
*/
|
|
75
91
|
export const isValidElement = (element: unknown): element is React.ReactElement =>
|
|
76
92
|
typeof element === "object" &&
|
|
77
|
-
element
|
|
93
|
+
element !== null &&
|
|
78
94
|
"$$typeof" in element &&
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
String(element.$$typeof),
|
|
82
|
-
);
|
|
95
|
+
(String(element.$$typeof) === ReactSymbols.LEGACY_ELEMENT_SYMBOL_STRING ||
|
|
96
|
+
String(element.$$typeof) === ReactSymbols.ELEMENT_SYMBOL_STRING);
|
|
83
97
|
|
|
84
98
|
/**
|
|
85
99
|
* Returns `true` if object is a React Fiber.
|
|
86
100
|
*/
|
|
87
101
|
export const isValidFiber = (fiber: unknown): fiber is Fiber =>
|
|
88
102
|
typeof fiber === "object" &&
|
|
89
|
-
fiber
|
|
103
|
+
fiber !== null &&
|
|
90
104
|
"tag" in fiber &&
|
|
91
105
|
"stateNode" in fiber &&
|
|
92
106
|
"return" in fiber &&
|
|
@@ -94,21 +108,27 @@ export const isValidFiber = (fiber: unknown): fiber is Fiber =>
|
|
|
94
108
|
"sibling" in fiber &&
|
|
95
109
|
"flags" in fiber;
|
|
96
110
|
|
|
111
|
+
const isFiberRoot = (fiberRoot: unknown): fiberRoot is FiberRoot =>
|
|
112
|
+
typeof fiberRoot === "object" &&
|
|
113
|
+
fiberRoot !== null &&
|
|
114
|
+
"current" in fiberRoot &&
|
|
115
|
+
isValidFiber(fiberRoot.current);
|
|
116
|
+
|
|
97
117
|
/**
|
|
98
118
|
* Returns `true` if fiber is a host fiber. Host fibers are DOM nodes in react-dom, `View` in react-native, etc.
|
|
99
119
|
*
|
|
100
120
|
* @see https://reactnative.dev/architecture/glossary#host-view-tree-and-host-view
|
|
101
121
|
*/
|
|
102
|
-
export const isHostFiber = (fiber: Fiber):
|
|
122
|
+
export const isHostFiber = (fiber: Fiber): fiber is HostFiber => {
|
|
123
|
+
const workTags = getReactWorkTagsForFiber(fiber);
|
|
103
124
|
switch (fiber.tag) {
|
|
104
|
-
case
|
|
105
|
-
|
|
106
|
-
case
|
|
107
|
-
|
|
108
|
-
case HostSingletonTag:
|
|
125
|
+
case workTags.HostComponent:
|
|
126
|
+
case workTags.HostText:
|
|
127
|
+
case workTags.HostHoistable:
|
|
128
|
+
case workTags.HostSingleton:
|
|
109
129
|
return true;
|
|
110
130
|
default:
|
|
111
|
-
return
|
|
131
|
+
return false;
|
|
112
132
|
}
|
|
113
133
|
};
|
|
114
134
|
|
|
@@ -118,12 +138,13 @@ export const isHostFiber = (fiber: Fiber): boolean => {
|
|
|
118
138
|
* @see https://reactnative.dev/architecture/glossary#react-composite-components
|
|
119
139
|
*/
|
|
120
140
|
export const isCompositeFiber = (fiber: Fiber): boolean => {
|
|
141
|
+
const workTags = getReactWorkTagsForFiber(fiber);
|
|
121
142
|
switch (fiber.tag) {
|
|
122
|
-
case
|
|
123
|
-
case
|
|
124
|
-
case
|
|
125
|
-
case
|
|
126
|
-
case
|
|
143
|
+
case workTags.ClassComponent:
|
|
144
|
+
case workTags.ForwardRef:
|
|
145
|
+
case workTags.FunctionComponent:
|
|
146
|
+
case workTags.MemoComponent:
|
|
147
|
+
case workTags.SimpleMemoComponent:
|
|
127
148
|
return true;
|
|
128
149
|
default:
|
|
129
150
|
return false;
|
|
@@ -230,16 +251,17 @@ export const traverseProps = (
|
|
|
230
251
|
export const didFiberRender = (fiber: Fiber): boolean => {
|
|
231
252
|
const nextProps = fiber.memoizedProps;
|
|
232
253
|
const prevProps = fiber.alternate?.memoizedProps || {};
|
|
233
|
-
const flags = fiber.flags ??
|
|
254
|
+
const flags = fiber.flags ?? fiber.effectTag ?? 0;
|
|
255
|
+
const workTags = getReactWorkTagsForFiber(fiber);
|
|
234
256
|
|
|
235
257
|
switch (fiber.tag) {
|
|
236
|
-
case
|
|
237
|
-
case
|
|
238
|
-
case
|
|
239
|
-
case
|
|
240
|
-
case
|
|
241
|
-
case
|
|
242
|
-
return (flags & PerformedWork) === PerformedWork;
|
|
258
|
+
case workTags.ClassComponent:
|
|
259
|
+
case workTags.ContextConsumer:
|
|
260
|
+
case workTags.ForwardRef:
|
|
261
|
+
case workTags.FunctionComponent:
|
|
262
|
+
case workTags.MemoComponent:
|
|
263
|
+
case workTags.SimpleMemoComponent: {
|
|
264
|
+
return (flags & ReactFiberFlags.PerformedWork) === ReactFiberFlags.PerformedWork;
|
|
243
265
|
}
|
|
244
266
|
default:
|
|
245
267
|
// Host nodes (DOM, root, etc.)
|
|
@@ -257,8 +279,8 @@ export const didFiberRender = (fiber: Fiber): boolean => {
|
|
|
257
279
|
*/
|
|
258
280
|
export const didFiberCommit = (fiber: Fiber): boolean => {
|
|
259
281
|
return Boolean(
|
|
260
|
-
(fiber.flags & (MutationMask | Cloned)) !== 0 ||
|
|
261
|
-
(fiber.subtreeFlags & (MutationMask | Cloned)) !== 0,
|
|
282
|
+
(fiber.flags & (MutationMask | ReactFiberFlags.Cloned)) !== 0 ||
|
|
283
|
+
(fiber.subtreeFlags & (MutationMask | ReactFiberFlags.Cloned)) !== 0,
|
|
262
284
|
);
|
|
263
285
|
};
|
|
264
286
|
|
|
@@ -297,7 +319,8 @@ export const getFiberStack = (fiber: Fiber): Fiber[] => {
|
|
|
297
319
|
let currentFiber = fiber;
|
|
298
320
|
while (currentFiber.return) {
|
|
299
321
|
stack.push(currentFiber);
|
|
300
|
-
|
|
322
|
+
const parentFiber = currentFiber.return;
|
|
323
|
+
currentFiber = parentFiber;
|
|
301
324
|
}
|
|
302
325
|
return stack;
|
|
303
326
|
};
|
|
@@ -306,8 +329,9 @@ export const getFiberStack = (fiber: Fiber): Fiber[] => {
|
|
|
306
329
|
* Returns `true` if the {@link Fiber} should be filtered out during reconciliation.
|
|
307
330
|
*/
|
|
308
331
|
const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
332
|
+
const workTags = getReactWorkTagsForFiber(fiber);
|
|
309
333
|
switch (fiber.tag) {
|
|
310
|
-
case
|
|
334
|
+
case workTags.DehydratedSuspenseComponent:
|
|
311
335
|
// TODO: ideally we would show dehydrated Suspense immediately.
|
|
312
336
|
// However, it has some special behavior (like disconnecting
|
|
313
337
|
// an alternate and turning into real Suspense) which breaks DevTools.
|
|
@@ -315,13 +339,13 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
|
315
339
|
// https://github.com/bvaughn/react-devtools-experimental/issues/197
|
|
316
340
|
return true;
|
|
317
341
|
|
|
318
|
-
case
|
|
319
|
-
case
|
|
320
|
-
case
|
|
321
|
-
case
|
|
342
|
+
case workTags.Fragment:
|
|
343
|
+
case workTags.HostText:
|
|
344
|
+
case workTags.LegacyHiddenComponent:
|
|
345
|
+
case workTags.OffscreenComponent:
|
|
322
346
|
return true;
|
|
323
347
|
|
|
324
|
-
case
|
|
348
|
+
case workTags.HostRoot:
|
|
325
349
|
// It is never valid to filter the root element.
|
|
326
350
|
return false;
|
|
327
351
|
|
|
@@ -331,15 +355,15 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
|
331
355
|
|
|
332
356
|
if (typeof symbolOrNumber === "symbol") {
|
|
333
357
|
return (
|
|
334
|
-
symbolOrNumber.description === CONCURRENT_MODE_SYMBOL_DESCRIPTION ||
|
|
335
|
-
symbolOrNumber.description === DEPRECATED_ASYNC_MODE_SYMBOL_DESCRIPTION
|
|
358
|
+
symbolOrNumber.description === ReactSymbols.CONCURRENT_MODE_SYMBOL_DESCRIPTION ||
|
|
359
|
+
symbolOrNumber.description === ReactSymbols.DEPRECATED_ASYNC_MODE_SYMBOL_DESCRIPTION
|
|
336
360
|
);
|
|
337
361
|
}
|
|
338
362
|
|
|
339
363
|
switch (symbolOrNumber) {
|
|
340
|
-
case CONCURRENT_MODE_NUMBER:
|
|
341
|
-
case CONCURRENT_MODE_SYMBOL_STRING:
|
|
342
|
-
case DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
|
|
364
|
+
case ReactSymbols.CONCURRENT_MODE_NUMBER:
|
|
365
|
+
case ReactSymbols.CONCURRENT_MODE_SYMBOL_STRING:
|
|
366
|
+
case ReactSymbols.DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
|
|
343
367
|
return true;
|
|
344
368
|
|
|
345
369
|
default:
|
|
@@ -405,75 +429,65 @@ export function traverseFiber(
|
|
|
405
429
|
): Promise<Fiber | null>;
|
|
406
430
|
export function traverseFiber(
|
|
407
431
|
fiber: Fiber | null,
|
|
408
|
-
selector:
|
|
432
|
+
selector: FiberSelector,
|
|
433
|
+
ascending?: boolean,
|
|
434
|
+
): Fiber | null | Promise<Fiber | null>;
|
|
435
|
+
export function traverseFiber(
|
|
436
|
+
fiber: Fiber | null,
|
|
437
|
+
selector: FiberSelector,
|
|
409
438
|
ascending = false,
|
|
410
439
|
): Fiber | null | Promise<Fiber | null> {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
const firstResult = selector(fiber);
|
|
414
|
-
if (firstResult instanceof Promise) {
|
|
415
|
-
return (async () => {
|
|
416
|
-
if ((await firstResult) === true) return fiber;
|
|
417
|
-
|
|
418
|
-
let child = ascending ? fiber.return : fiber.child;
|
|
419
|
-
while (child) {
|
|
420
|
-
const match = await traverseFiberAsync(
|
|
421
|
-
child,
|
|
422
|
-
selector as (node: Fiber) => Promise<boolean | void>,
|
|
423
|
-
ascending,
|
|
424
|
-
);
|
|
425
|
-
if (match) return match;
|
|
426
|
-
child = ascending ? null : child.sibling;
|
|
427
|
-
}
|
|
428
|
-
return null;
|
|
429
|
-
})();
|
|
430
|
-
}
|
|
440
|
+
return traverseFiberInternal(fiber, selector, ascending);
|
|
441
|
+
}
|
|
431
442
|
|
|
432
|
-
|
|
443
|
+
const isPromiseLike = <Result>(value: unknown): value is PromiseLike<Result> =>
|
|
444
|
+
(typeof value === "object" || typeof value === "function") &&
|
|
445
|
+
value !== null &&
|
|
446
|
+
"then" in value &&
|
|
447
|
+
typeof value.then === "function";
|
|
433
448
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
return
|
|
441
|
-
}
|
|
449
|
+
const traverseFiberChildren = (
|
|
450
|
+
fiber: Fiber,
|
|
451
|
+
selector: FiberSelector,
|
|
452
|
+
ascending: boolean,
|
|
453
|
+
): Fiber | null | Promise<Fiber | null> => {
|
|
454
|
+
const firstChild = ascending ? fiber.return : fiber.child;
|
|
455
|
+
return traverseFiberSiblings(firstChild, selector, ascending);
|
|
456
|
+
};
|
|
442
457
|
|
|
443
|
-
const
|
|
458
|
+
const traverseFiberSiblings = (
|
|
444
459
|
fiber: Fiber | null,
|
|
445
|
-
selector:
|
|
446
|
-
ascending
|
|
447
|
-
): Fiber | null => {
|
|
460
|
+
selector: FiberSelector,
|
|
461
|
+
ascending: boolean,
|
|
462
|
+
): Fiber | null | Promise<Fiber | null> => {
|
|
448
463
|
if (!fiber) return null;
|
|
449
|
-
if (selector(fiber) === true) return fiber;
|
|
450
|
-
|
|
451
|
-
let child = ascending ? fiber.return : fiber.child;
|
|
452
|
-
while (child) {
|
|
453
|
-
const match = traverseFiberSync(child, selector, ascending);
|
|
454
|
-
if (match) return match;
|
|
455
464
|
|
|
456
|
-
|
|
465
|
+
const nextSibling = ascending ? null : fiber.sibling;
|
|
466
|
+
const match = traverseFiberInternal(fiber, selector, ascending);
|
|
467
|
+
if (isPromiseLike<Fiber | null>(match)) {
|
|
468
|
+
return Promise.resolve(match).then(
|
|
469
|
+
(resolvedMatch) => resolvedMatch ?? traverseFiberSiblings(nextSibling, selector, ascending),
|
|
470
|
+
);
|
|
457
471
|
}
|
|
458
|
-
return
|
|
472
|
+
return match ?? traverseFiberSiblings(nextSibling, selector, ascending);
|
|
459
473
|
};
|
|
460
474
|
|
|
461
|
-
const
|
|
475
|
+
const traverseFiberInternal = (
|
|
462
476
|
fiber: Fiber | null,
|
|
463
|
-
selector:
|
|
464
|
-
ascending
|
|
465
|
-
): Promise<Fiber | null> => {
|
|
477
|
+
selector: FiberSelector,
|
|
478
|
+
ascending: boolean,
|
|
479
|
+
): Fiber | null | Promise<Fiber | null> => {
|
|
466
480
|
if (!fiber) return null;
|
|
467
|
-
if ((await selector(fiber)) === true) return fiber;
|
|
468
481
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
child = ascending ? null : child.sibling;
|
|
482
|
+
const selection = selector(fiber);
|
|
483
|
+
if (isPromiseLike<boolean | void>(selection)) {
|
|
484
|
+
return Promise.resolve(selection).then((didSelectFiber) =>
|
|
485
|
+
didSelectFiber === true ? fiber : traverseFiberChildren(fiber, selector, ascending),
|
|
486
|
+
);
|
|
475
487
|
}
|
|
476
|
-
return
|
|
488
|
+
if (selection === true) return fiber;
|
|
489
|
+
|
|
490
|
+
return traverseFiberChildren(fiber, selector, ascending);
|
|
477
491
|
};
|
|
478
492
|
|
|
479
493
|
/**
|
|
@@ -485,12 +499,12 @@ const traverseFiberAsync = async (
|
|
|
485
499
|
* console.log(selfTime, totalTime);
|
|
486
500
|
* ```
|
|
487
501
|
*/
|
|
488
|
-
export const getTimings = (fiber?: Fiber | null):
|
|
502
|
+
export const getTimings = (fiber?: Fiber | null): FiberTimings => {
|
|
489
503
|
const totalTime = fiber?.actualDuration ?? 0;
|
|
490
504
|
let selfTime = totalTime;
|
|
491
505
|
// TODO: calculate a DOM time, which is just host component summed up
|
|
492
506
|
let child = fiber?.child ?? null;
|
|
493
|
-
while (totalTime > 0 && child
|
|
507
|
+
while (totalTime > 0 && child !== null) {
|
|
494
508
|
selfTime -= child.actualDuration ?? 0;
|
|
495
509
|
child = child.sibling;
|
|
496
510
|
}
|
|
@@ -501,48 +515,29 @@ export const getTimings = (fiber?: Fiber | null): { selfTime: number; totalTime:
|
|
|
501
515
|
* Returns `true` if the {@link Fiber} uses React Compiler's memo cache.
|
|
502
516
|
*/
|
|
503
517
|
export const hasMemoCache = (fiber: Fiber): boolean => {
|
|
504
|
-
return Boolean(
|
|
518
|
+
return Boolean(fiber.updateQueue?.memoCache);
|
|
505
519
|
};
|
|
506
520
|
|
|
507
|
-
type FiberType =
|
|
508
|
-
| React.ComponentType<unknown>
|
|
509
|
-
| React.ForwardRefExoticComponent<unknown>
|
|
510
|
-
| React.MemoExoticComponent<React.ComponentType<unknown>>;
|
|
511
|
-
|
|
512
521
|
/**
|
|
513
522
|
* Returns the type (e.g. component definition) of the {@link Fiber}
|
|
514
523
|
*/
|
|
515
524
|
export const getType = (type: unknown): null | React.ComponentType<unknown> => {
|
|
516
|
-
|
|
517
|
-
if (typeof
|
|
518
|
-
|
|
519
|
-
}
|
|
520
|
-
if (typeof currentType === "object" && currentType) {
|
|
521
|
-
// memo / forwardRef case
|
|
522
|
-
return getType(
|
|
523
|
-
(currentType as React.MemoExoticComponent<React.ComponentType<unknown>>).type ||
|
|
524
|
-
(currentType as { render: React.ComponentType<unknown> }).render,
|
|
525
|
-
);
|
|
526
|
-
}
|
|
527
|
-
return null;
|
|
525
|
+
if (isComponentType(type)) return type;
|
|
526
|
+
if (typeof type !== "object" || type === null) return null;
|
|
527
|
+
return getType(Reflect.get(type, "type") ?? Reflect.get(type, "render"));
|
|
528
528
|
};
|
|
529
529
|
|
|
530
530
|
/**
|
|
531
531
|
* Returns the display name of the {@link Fiber} type.
|
|
532
532
|
*/
|
|
533
533
|
export const getDisplayName = (type: unknown): null | string => {
|
|
534
|
-
|
|
535
|
-
if (typeof
|
|
536
|
-
|
|
537
|
-
}
|
|
538
|
-
if (typeof currentType !== "function" && !(typeof currentType === "object" && currentType)) {
|
|
539
|
-
return null;
|
|
540
|
-
}
|
|
541
|
-
const name = currentType.displayName || currentType.name || null;
|
|
534
|
+
if (typeof type === "string") return type;
|
|
535
|
+
if ((typeof type !== "function" && typeof type !== "object") || type === null) return null;
|
|
536
|
+
const name = getTypeName(type);
|
|
542
537
|
if (name) return name;
|
|
543
|
-
const unwrappedType = getType(
|
|
538
|
+
const unwrappedType = getType(type);
|
|
544
539
|
if (!unwrappedType) return null;
|
|
545
|
-
return unwrappedType
|
|
540
|
+
return getTypeName(unwrappedType);
|
|
546
541
|
};
|
|
547
542
|
|
|
548
543
|
/**
|
|
@@ -550,7 +545,10 @@ export const getDisplayName = (type: unknown): null | string => {
|
|
|
550
545
|
*/
|
|
551
546
|
export const detectReactBuildType = (renderer: ReactRenderer): "development" | "production" => {
|
|
552
547
|
try {
|
|
553
|
-
if (
|
|
548
|
+
if (
|
|
549
|
+
typeof renderer.version === "string" &&
|
|
550
|
+
renderer.bundleType === ReactBuildType.Development
|
|
551
|
+
) {
|
|
554
552
|
return "development";
|
|
555
553
|
}
|
|
556
554
|
} catch {}
|
|
@@ -562,11 +560,7 @@ export const detectReactBuildType = (renderer: ReactRenderer): "development" | "
|
|
|
562
560
|
*/
|
|
563
561
|
export const isInstrumentationActive = (): boolean => {
|
|
564
562
|
const rdtHook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
565
|
-
return (
|
|
566
|
-
Boolean(rdtHook?._instrumentationIsActive) ||
|
|
567
|
-
isRealReactDevtools(rdtHook) ||
|
|
568
|
-
isReactRefresh(rdtHook)
|
|
569
|
-
);
|
|
563
|
+
return Boolean(rdtHook?._instrumentationIsActive) || isRealReactDevtools(rdtHook);
|
|
570
564
|
};
|
|
571
565
|
|
|
572
566
|
export const _fiberRoots = new Set<FiberRoot>();
|
|
@@ -582,37 +576,35 @@ export const getLatestFiber = (fiber: Fiber): Fiber => {
|
|
|
582
576
|
}
|
|
583
577
|
for (const root of _fiberRoots) {
|
|
584
578
|
const latestFiber = traverseFiber(root.current, (innerFiber) => {
|
|
585
|
-
if (innerFiber === fiber) return true;
|
|
579
|
+
if (innerFiber === fiber || innerFiber === alternate) return true;
|
|
586
580
|
});
|
|
587
581
|
if (latestFiber) return latestFiber;
|
|
588
582
|
}
|
|
589
583
|
return fiber;
|
|
590
584
|
};
|
|
591
585
|
|
|
592
|
-
export type RenderHandler = <S>(fiber: Fiber, phase: RenderPhase, state?: S) => unknown;
|
|
593
|
-
|
|
594
586
|
export type RenderPhase = "mount" | "unmount" | "update";
|
|
595
587
|
|
|
596
|
-
let
|
|
588
|
+
let nextFiberId = 0;
|
|
597
589
|
const fiberIdMap = new WeakMap<Fiber, number>();
|
|
598
590
|
|
|
599
|
-
export const setFiberId = (fiber: Fiber,
|
|
600
|
-
fiberIdMap.set(fiber,
|
|
591
|
+
export const setFiberId = (fiber: Fiber, fiberId: number = nextFiberId++): void => {
|
|
592
|
+
fiberIdMap.set(fiber, fiberId);
|
|
593
|
+
if (Number.isSafeInteger(fiberId) && fiberId >= nextFiberId) {
|
|
594
|
+
nextFiberId = fiberId + 1;
|
|
595
|
+
}
|
|
601
596
|
};
|
|
602
597
|
|
|
603
|
-
// react fibers are double buffered, so the alternate fiber may
|
|
604
|
-
// be switched to the current fiber and vice versa.
|
|
605
|
-
// fiber === fiber.alternate.alternate
|
|
606
598
|
export const getFiberId = (fiber: Fiber): number => {
|
|
607
|
-
let
|
|
608
|
-
if (
|
|
609
|
-
|
|
599
|
+
let currentFiberId = fiberIdMap.get(fiber);
|
|
600
|
+
if (currentFiberId === undefined && fiber.alternate) {
|
|
601
|
+
currentFiberId = fiberIdMap.get(fiber.alternate);
|
|
610
602
|
}
|
|
611
|
-
if (
|
|
612
|
-
|
|
613
|
-
setFiberId(fiber,
|
|
603
|
+
if (currentFiberId === undefined) {
|
|
604
|
+
currentFiberId = nextFiberId++;
|
|
605
|
+
setFiberId(fiber, currentFiberId);
|
|
614
606
|
}
|
|
615
|
-
return
|
|
607
|
+
return currentFiberId;
|
|
616
608
|
};
|
|
617
609
|
|
|
618
610
|
const mountFiberRecursively = (
|
|
@@ -622,7 +614,7 @@ const mountFiberRecursively = (
|
|
|
622
614
|
): void => {
|
|
623
615
|
let fiber: Fiber | null = firstChild;
|
|
624
616
|
|
|
625
|
-
while (fiber
|
|
617
|
+
while (fiber !== null) {
|
|
626
618
|
if (!fiberIdMap.has(fiber)) {
|
|
627
619
|
getFiberId(fiber);
|
|
628
620
|
}
|
|
@@ -631,7 +623,7 @@ const mountFiberRecursively = (
|
|
|
631
623
|
onRender(fiber, "mount");
|
|
632
624
|
}
|
|
633
625
|
|
|
634
|
-
if (fiber.tag ===
|
|
626
|
+
if (fiber.tag === getReactWorkTagsForFiber(fiber).SuspenseComponent) {
|
|
635
627
|
const isTimedOut = fiber.memoizedState !== null;
|
|
636
628
|
if (isTimedOut) {
|
|
637
629
|
// Special case: if Suspense mounts in a timed-out state,
|
|
@@ -646,18 +638,12 @@ const mountFiberRecursively = (
|
|
|
646
638
|
}
|
|
647
639
|
}
|
|
648
640
|
} else {
|
|
649
|
-
|
|
650
|
-
const areSuspenseChildrenConditionallyWrapped = (OffscreenComponentTag as number) === -1;
|
|
651
|
-
if (areSuspenseChildrenConditionallyWrapped) {
|
|
652
|
-
primaryChild = fiber.child;
|
|
653
|
-
} else if (fiber.child !== null) {
|
|
654
|
-
primaryChild = fiber.child.child;
|
|
655
|
-
}
|
|
641
|
+
const primaryChild = fiber.child?.child ?? null;
|
|
656
642
|
if (primaryChild !== null) {
|
|
657
643
|
mountFiberRecursively(onRender, primaryChild, false);
|
|
658
644
|
}
|
|
659
645
|
}
|
|
660
|
-
} else if (fiber.child
|
|
646
|
+
} else if (fiber.child !== null) {
|
|
661
647
|
mountFiberRecursively(onRender, fiber.child, true);
|
|
662
648
|
}
|
|
663
649
|
fiber = traverseSiblings ? fiber.sibling : null;
|
|
@@ -667,7 +653,7 @@ const mountFiberRecursively = (
|
|
|
667
653
|
const updateFiberRecursively = (
|
|
668
654
|
onRender: RenderHandler,
|
|
669
655
|
nextFiber: Fiber,
|
|
670
|
-
prevFiber: Fiber,
|
|
656
|
+
prevFiber: Fiber | null,
|
|
671
657
|
parentFiber: Fiber | null,
|
|
672
658
|
): void => {
|
|
673
659
|
if (!fiberIdMap.has(nextFiber)) {
|
|
@@ -678,7 +664,7 @@ const updateFiberRecursively = (
|
|
|
678
664
|
getFiberId(prevFiber);
|
|
679
665
|
}
|
|
680
666
|
|
|
681
|
-
const isSuspense = nextFiber.tag ===
|
|
667
|
+
const isSuspense = nextFiber.tag === getReactWorkTagsForFiber(nextFiber).SuspenseComponent;
|
|
682
668
|
|
|
683
669
|
const shouldIncludeInTree = !shouldFilterFiber(nextFiber);
|
|
684
670
|
if (shouldIncludeInTree && didFiberRender(nextFiber)) {
|
|
@@ -765,7 +751,7 @@ const updateFiberRecursively = (
|
|
|
765
751
|
};
|
|
766
752
|
|
|
767
753
|
const unmountFiber = (onRender: RenderHandler, fiber: Fiber): void => {
|
|
768
|
-
const isRoot = fiber.tag ===
|
|
754
|
+
const isRoot = fiber.tag === getReactWorkTagsForFiber(fiber).HostRoot;
|
|
769
755
|
|
|
770
756
|
if (isRoot || !shouldFilterFiber(fiber)) {
|
|
771
757
|
onRender(fiber, "unmount");
|
|
@@ -774,7 +760,8 @@ const unmountFiber = (onRender: RenderHandler, fiber: Fiber): void => {
|
|
|
774
760
|
|
|
775
761
|
const unmountFiberChildrenRecursively = (onRender: RenderHandler, fiber: Fiber): void => {
|
|
776
762
|
// We might meet a nested Suspense on our way.
|
|
777
|
-
const isTimedOutSuspense =
|
|
763
|
+
const isTimedOutSuspense =
|
|
764
|
+
fiber.tag === getReactWorkTagsForFiber(fiber).SuspenseComponent && fiber.memoizedState !== null;
|
|
778
765
|
let child = fiber.child;
|
|
779
766
|
|
|
780
767
|
if (isTimedOutSuspense) {
|
|
@@ -800,7 +787,7 @@ const unmountFiberChildrenRecursively = (onRender: RenderHandler, fiber: Fiber):
|
|
|
800
787
|
|
|
801
788
|
let commitId = 0;
|
|
802
789
|
const rootInstanceMap = new WeakMap<
|
|
803
|
-
FiberRoot,
|
|
790
|
+
Fiber | FiberRoot,
|
|
804
791
|
{
|
|
805
792
|
id: number;
|
|
806
793
|
prevFiber: Fiber | null;
|
|
@@ -814,7 +801,7 @@ const rootInstanceMap = new WeakMap<
|
|
|
814
801
|
* console.log(phase)
|
|
815
802
|
* })
|
|
816
803
|
*/
|
|
817
|
-
export const traverseRenderedFibers = (root: FiberRoot, onRender: RenderHandler): void => {
|
|
804
|
+
export const traverseRenderedFibers = (root: Fiber | FiberRoot, onRender: RenderHandler): void => {
|
|
818
805
|
const fiber = "current" in root ? root.current : root;
|
|
819
806
|
|
|
820
807
|
let rootInstance = rootInstanceMap.get(root);
|
|
@@ -831,13 +818,15 @@ export const traverseRenderedFibers = (root: FiberRoot, onRender: RenderHandler)
|
|
|
831
818
|
} else if (prevFiber !== null) {
|
|
832
819
|
const wasMounted =
|
|
833
820
|
prevFiber &&
|
|
834
|
-
prevFiber.memoizedState
|
|
835
|
-
prevFiber.memoizedState.element
|
|
821
|
+
prevFiber.memoizedState !== null &&
|
|
822
|
+
prevFiber.memoizedState.element !== null &&
|
|
823
|
+
prevFiber.memoizedState.element !== undefined &&
|
|
836
824
|
// A dehydrated root is not considered mounted
|
|
837
825
|
prevFiber.memoizedState.isDehydrated !== true;
|
|
838
826
|
const isMounted =
|
|
839
|
-
fiber.memoizedState
|
|
840
|
-
fiber.memoizedState.element
|
|
827
|
+
fiber.memoizedState !== null &&
|
|
828
|
+
fiber.memoizedState.element !== null &&
|
|
829
|
+
fiber.memoizedState.element !== undefined &&
|
|
841
830
|
// A dehydrated root is not considered mounted
|
|
842
831
|
fiber.memoizedState.isDehydrated !== true;
|
|
843
832
|
|
|
@@ -859,16 +848,15 @@ const overrideRenderers = new Set<ReactRenderer>();
|
|
|
859
848
|
let areOverrideRenderersWired = false;
|
|
860
849
|
|
|
861
850
|
const wireOverrideRenderers = (): void => {
|
|
862
|
-
if (areOverrideRenderersWired) return;
|
|
863
851
|
if (!hasRDTHook()) return;
|
|
864
852
|
const rdtHook = getRDTHook();
|
|
865
|
-
if (!rdtHook?.renderers) return;
|
|
866
|
-
areOverrideRenderersWired = true;
|
|
867
853
|
|
|
868
|
-
|
|
854
|
+
setHookEventDispatchers(rdtHook);
|
|
869
855
|
for (const renderer of rdtHook.renderers.values()) {
|
|
870
856
|
overrideRenderers.add(renderer);
|
|
871
857
|
}
|
|
858
|
+
if (areOverrideRenderersWired) return;
|
|
859
|
+
areOverrideRenderersWired = true;
|
|
872
860
|
onRendererInject((renderer) => {
|
|
873
861
|
overrideRenderers.add(renderer);
|
|
874
862
|
});
|
|
@@ -880,14 +868,13 @@ const getRootRenderer = (fiber: Fiber): ReactRenderer | null => {
|
|
|
880
868
|
while (hostRootFiber.return) {
|
|
881
869
|
hostRootFiber = hostRootFiber.return;
|
|
882
870
|
}
|
|
883
|
-
const
|
|
871
|
+
const fiberRoot = hostRootFiber.stateNode;
|
|
872
|
+
if (!isFiberRoot(fiberRoot)) return null;
|
|
873
|
+
const rendererId = rootRendererIds.get(fiberRoot);
|
|
884
874
|
if (rendererId === undefined) return null;
|
|
885
|
-
return getRDTHook().renderers
|
|
875
|
+
return getRDTHook().renderers.get(rendererId) ?? null;
|
|
886
876
|
};
|
|
887
877
|
|
|
888
|
-
// dispatch to the renderer that owns the fiber's root when known; renderers
|
|
889
|
-
// that never committed through bippy's hook patch fall back to "try all",
|
|
890
|
-
// which matches the pre-ownership behavior for single-renderer apps
|
|
891
878
|
const resolveOverrideRenderers = (fiber: Fiber): ReactRenderer[] => {
|
|
892
879
|
wireOverrideRenderers();
|
|
893
880
|
const rootRenderer = getRootRenderer(fiber);
|
|
@@ -913,15 +900,14 @@ const getHookStateDispatch = (
|
|
|
913
900
|
hookIndex: number,
|
|
914
901
|
): ((value: unknown) => void) | null => {
|
|
915
902
|
let hookState = fiber.memoizedState;
|
|
916
|
-
for (let
|
|
903
|
+
for (let currentHookIndex = 0; currentHookIndex < hookIndex; currentHookIndex++) {
|
|
917
904
|
if (!hookState?.next) return null;
|
|
918
905
|
hookState = hookState.next;
|
|
919
906
|
}
|
|
920
907
|
const queue = hookState?.queue;
|
|
921
|
-
if (isPOJO(queue)
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
return null;
|
|
908
|
+
if (!isPOJO(queue)) return null;
|
|
909
|
+
const dispatch = queue.dispatch;
|
|
910
|
+
return typeof dispatch === "function" ? (value) => dispatch(value) : null;
|
|
925
911
|
};
|
|
926
912
|
|
|
927
913
|
const findContextProviderFiber = (fiber: Fiber, contextType: unknown): Fiber | null => {
|
|
@@ -947,28 +933,33 @@ const isPOJO = (maybePOJO: unknown): maybePOJO is Record<string, unknown> => {
|
|
|
947
933
|
const buildPathsFromValue = (
|
|
948
934
|
maybePOJO: Record<string, unknown>,
|
|
949
935
|
basePath: string[] = [],
|
|
950
|
-
|
|
936
|
+
ancestors = new WeakSet<object>(),
|
|
937
|
+
): ValueWrite[] => {
|
|
951
938
|
if (!isPOJO(maybePOJO)) {
|
|
952
939
|
return [{ path: basePath, value: maybePOJO }];
|
|
953
940
|
}
|
|
941
|
+
if (ancestors.has(maybePOJO)) {
|
|
942
|
+
return [{ path: basePath, value: maybePOJO }];
|
|
943
|
+
}
|
|
954
944
|
|
|
955
|
-
|
|
945
|
+
ancestors.add(maybePOJO);
|
|
946
|
+
const paths: ValueWrite[] = [];
|
|
956
947
|
|
|
957
|
-
for (const key
|
|
958
|
-
const value = maybePOJO[key];
|
|
948
|
+
for (const [key, value] of Object.entries(maybePOJO)) {
|
|
959
949
|
const path = basePath.concat(key);
|
|
960
950
|
|
|
961
951
|
if (isPOJO(value)) {
|
|
962
|
-
paths.push(...buildPathsFromValue(value, path));
|
|
952
|
+
paths.push(...buildPathsFromValue(value, path, ancestors));
|
|
963
953
|
} else {
|
|
964
954
|
paths.push({ path, value });
|
|
965
955
|
}
|
|
966
956
|
}
|
|
967
957
|
|
|
958
|
+
ancestors.delete(maybePOJO);
|
|
968
959
|
return paths;
|
|
969
960
|
};
|
|
970
961
|
|
|
971
|
-
const buildValueWrites = (partialValue: unknown):
|
|
962
|
+
const buildValueWrites = (partialValue: unknown): ValueWrite[] =>
|
|
972
963
|
isPOJO(partialValue) ? buildPathsFromValue(partialValue) : [{ path: [], value: partialValue }];
|
|
973
964
|
|
|
974
965
|
export const overrideProps = (fiber: Fiber, partialValue: Record<string, unknown>) => {
|
|
@@ -985,11 +976,10 @@ export const overrideHookState = (fiber: Fiber, id: number, partialValue: unknow
|
|
|
985
976
|
const writes = buildValueWrites(partialValue);
|
|
986
977
|
|
|
987
978
|
if (renderers.length > 0) {
|
|
988
|
-
const hookId = String(id);
|
|
989
979
|
for (const renderer of renderers) {
|
|
990
980
|
for (const { path, value } of writes) {
|
|
991
981
|
try {
|
|
992
|
-
renderer.overrideHookState?.(fiber,
|
|
982
|
+
renderer.overrideHookState?.(fiber, id, path, value);
|
|
993
983
|
} catch {}
|
|
994
984
|
}
|
|
995
985
|
}
|
|
@@ -1022,24 +1012,22 @@ export const overrideContext = (fiber: Fiber, contextType: unknown, partialValue
|
|
|
1022
1012
|
export interface InstrumentationOptions {
|
|
1023
1013
|
name?: string;
|
|
1024
1014
|
onActive?: () => unknown;
|
|
1025
|
-
onCommitFiberRoot?: (
|
|
1015
|
+
onCommitFiberRoot?: (
|
|
1016
|
+
rendererID: number,
|
|
1017
|
+
root: FiberRoot,
|
|
1018
|
+
priority: number | void,
|
|
1019
|
+
didError?: boolean,
|
|
1020
|
+
) => unknown;
|
|
1026
1021
|
onCommitFiberUnmount?: (rendererID: number, fiber: Fiber) => unknown;
|
|
1027
1022
|
onPostCommitFiberRoot?: (rendererID: number, root: FiberRoot) => unknown;
|
|
1028
1023
|
onScheduleFiberRoot?: (rendererID: number, root: FiberRoot, children: React.ReactNode) => unknown;
|
|
1029
1024
|
}
|
|
1030
1025
|
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
>();
|
|
1037
|
-
const postCommitFiberRootListeners = new Set<
|
|
1038
|
-
NonNullable<InstrumentationOptions["onPostCommitFiberRoot"]>
|
|
1039
|
-
>();
|
|
1040
|
-
const scheduleFiberRootListeners = new Set<
|
|
1041
|
-
NonNullable<InstrumentationOptions["onScheduleFiberRoot"]>
|
|
1042
|
-
>();
|
|
1026
|
+
interface InstrumentationSubscription {
|
|
1027
|
+
options: InstrumentationOptions;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
const instrumentationSubscriptions = new Set<InstrumentationSubscription>();
|
|
1043
1031
|
|
|
1044
1032
|
interface HookDispatchers {
|
|
1045
1033
|
onCommitFiberRoot: ReactDevToolsGlobalHook["onCommitFiberRoot"];
|
|
@@ -1049,6 +1037,7 @@ interface HookDispatchers {
|
|
|
1049
1037
|
}
|
|
1050
1038
|
|
|
1051
1039
|
const hookDispatchers = new WeakMap<ReactDevToolsGlobalHook, Partial<HookDispatchers>>();
|
|
1040
|
+
let didSubscribeToHookReplacements = false;
|
|
1052
1041
|
|
|
1053
1042
|
const rootRendererIds = new WeakMap<FiberRoot, number>();
|
|
1054
1043
|
|
|
@@ -1057,7 +1046,7 @@ const rootRendererIds = new WeakMap<FiberRoot, number>();
|
|
|
1057
1046
|
// next instrument() call installs a fresh wrapper over it; a superseded
|
|
1058
1047
|
// wrapper still forwards the previous chain but skips the listeners so
|
|
1059
1048
|
// they never fire twice.
|
|
1060
|
-
const
|
|
1049
|
+
const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
1061
1050
|
const dispatchers = hookDispatchers.get(rdtHook) ?? {};
|
|
1062
1051
|
hookDispatchers.set(rdtHook, dispatchers);
|
|
1063
1052
|
|
|
@@ -1070,13 +1059,29 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1070
1059
|
rendererID,
|
|
1071
1060
|
root,
|
|
1072
1061
|
priority,
|
|
1062
|
+
didError,
|
|
1073
1063
|
) => {
|
|
1074
|
-
prevOnCommitFiberRoot
|
|
1064
|
+
if (prevOnCommitFiberRoot) {
|
|
1065
|
+
prevOnCommitFiberRoot.call(rdtHook, rendererID, root, priority, didError);
|
|
1066
|
+
}
|
|
1075
1067
|
if (hookDispatchers.get(rdtHook)?.onCommitFiberRoot !== dispatchCommitFiberRoot) return;
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1068
|
+
setReactWorkTagsForFiber(root.current, rdtHook.renderers.get(rendererID));
|
|
1069
|
+
const rootMemoizedState = root.current.memoizedState;
|
|
1070
|
+
const isUnmounting =
|
|
1071
|
+
rootMemoizedState === null ||
|
|
1072
|
+
rootMemoizedState.element === null ||
|
|
1073
|
+
rootMemoizedState.element === undefined;
|
|
1074
|
+
if (isUnmounting) {
|
|
1075
|
+
_fiberRoots.delete(root);
|
|
1076
|
+
rootRendererIds.delete(root);
|
|
1077
|
+
} else {
|
|
1078
|
+
_fiberRoots.add(root);
|
|
1079
|
+
rootRendererIds.set(root, rendererID);
|
|
1080
|
+
}
|
|
1081
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
1082
|
+
if (options.onCommitFiberRoot) {
|
|
1083
|
+
options.onCommitFiberRoot(rendererID, root, priority, didError);
|
|
1084
|
+
}
|
|
1080
1085
|
}
|
|
1081
1086
|
};
|
|
1082
1087
|
dispatchers.onCommitFiberRoot = dispatchCommitFiberRoot;
|
|
@@ -1092,12 +1097,17 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1092
1097
|
rendererID,
|
|
1093
1098
|
fiber,
|
|
1094
1099
|
) => {
|
|
1095
|
-
|
|
1100
|
+
setReactWorkTagsForFiber(fiber, rdtHook.renderers.get(rendererID));
|
|
1101
|
+
if (prevOnCommitFiberUnmount) {
|
|
1102
|
+
prevOnCommitFiberUnmount.call(rdtHook, rendererID, fiber);
|
|
1103
|
+
}
|
|
1096
1104
|
if (hookDispatchers.get(rdtHook)?.onCommitFiberUnmount !== dispatchCommitFiberUnmount) {
|
|
1097
1105
|
return;
|
|
1098
1106
|
}
|
|
1099
|
-
for (const
|
|
1100
|
-
|
|
1107
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
1108
|
+
if (options.onCommitFiberUnmount) {
|
|
1109
|
+
options.onCommitFiberUnmount(rendererID, fiber);
|
|
1110
|
+
}
|
|
1101
1111
|
}
|
|
1102
1112
|
};
|
|
1103
1113
|
dispatchers.onCommitFiberUnmount = dispatchCommitFiberUnmount;
|
|
@@ -1113,12 +1123,16 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1113
1123
|
rendererID,
|
|
1114
1124
|
root,
|
|
1115
1125
|
) => {
|
|
1116
|
-
prevOnPostCommitFiberRoot
|
|
1126
|
+
if (prevOnPostCommitFiberRoot) {
|
|
1127
|
+
prevOnPostCommitFiberRoot.call(rdtHook, rendererID, root);
|
|
1128
|
+
}
|
|
1117
1129
|
if (hookDispatchers.get(rdtHook)?.onPostCommitFiberRoot !== dispatchPostCommitFiberRoot) {
|
|
1118
1130
|
return;
|
|
1119
1131
|
}
|
|
1120
|
-
for (const
|
|
1121
|
-
|
|
1132
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
1133
|
+
if (options.onPostCommitFiberRoot) {
|
|
1134
|
+
options.onPostCommitFiberRoot(rendererID, root);
|
|
1135
|
+
}
|
|
1122
1136
|
}
|
|
1123
1137
|
};
|
|
1124
1138
|
dispatchers.onPostCommitFiberRoot = dispatchPostCommitFiberRoot;
|
|
@@ -1135,10 +1149,14 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1135
1149
|
root,
|
|
1136
1150
|
children,
|
|
1137
1151
|
) => {
|
|
1138
|
-
prevOnScheduleFiberRoot
|
|
1152
|
+
if (prevOnScheduleFiberRoot) {
|
|
1153
|
+
prevOnScheduleFiberRoot.call(rdtHook, rendererID, root, children);
|
|
1154
|
+
}
|
|
1139
1155
|
if (hookDispatchers.get(rdtHook)?.onScheduleFiberRoot !== dispatchScheduleFiberRoot) return;
|
|
1140
|
-
for (const
|
|
1141
|
-
|
|
1156
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
1157
|
+
if (options.onScheduleFiberRoot) {
|
|
1158
|
+
options.onScheduleFiberRoot(rendererID, root, children);
|
|
1159
|
+
}
|
|
1142
1160
|
}
|
|
1143
1161
|
};
|
|
1144
1162
|
dispatchers.onScheduleFiberRoot = dispatchScheduleFiberRoot;
|
|
@@ -1151,8 +1169,7 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1151
1169
|
* dispatches to a set of listeners, so multiple `instrument` calls compose
|
|
1152
1170
|
* without stacking patches. Returns an unsubscribe function that removes
|
|
1153
1171
|
* exactly the handlers this call registered.
|
|
1154
|
-
* The
|
|
1155
|
-
* bippy subscriptions through `using`.
|
|
1172
|
+
* The unsubscribe function can also be disposed through `using`.
|
|
1156
1173
|
* @example
|
|
1157
1174
|
* const unsubscribe = instrument({
|
|
1158
1175
|
* onActive() {
|
|
@@ -1167,34 +1184,24 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1167
1184
|
export const instrument = (options: InstrumentationOptions): Unsubscribe => {
|
|
1168
1185
|
const rdtHook = getRDTHook(options.onActive);
|
|
1169
1186
|
|
|
1187
|
+
if (!didSubscribeToHookReplacements) {
|
|
1188
|
+
onRDTHookReplace(setHookEventDispatchers);
|
|
1189
|
+
didSubscribeToHookReplacements = true;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1170
1192
|
rdtHook._instrumentationSource = options.name ?? BIPPY_INSTRUMENTATION_STRING;
|
|
1171
1193
|
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
onScheduleFiberRoot,
|
|
1180
|
-
} = options;
|
|
1181
|
-
if (onCommitFiberRoot) commitFiberRootListeners.add(onCommitFiberRoot);
|
|
1182
|
-
if (onCommitFiberUnmount) commitFiberUnmountListeners.add(onCommitFiberUnmount);
|
|
1183
|
-
if (onPostCommitFiberRoot) postCommitFiberRootListeners.add(onPostCommitFiberRoot);
|
|
1184
|
-
if (onScheduleFiberRoot) scheduleFiberRootListeners.add(onScheduleFiberRoot);
|
|
1185
|
-
|
|
1186
|
-
return toUnsubscribe(() => {
|
|
1187
|
-
if (onActive) _onActiveListeners.delete(onActive);
|
|
1188
|
-
if (onCommitFiberRoot) commitFiberRootListeners.delete(onCommitFiberRoot);
|
|
1189
|
-
if (onCommitFiberUnmount) commitFiberUnmountListeners.delete(onCommitFiberUnmount);
|
|
1190
|
-
if (onPostCommitFiberRoot) postCommitFiberRootListeners.delete(onPostCommitFiberRoot);
|
|
1191
|
-
if (onScheduleFiberRoot) scheduleFiberRootListeners.delete(onScheduleFiberRoot);
|
|
1194
|
+
setHookEventDispatchers(rdtHook);
|
|
1195
|
+
const subscription: InstrumentationSubscription = { options };
|
|
1196
|
+
instrumentationSubscriptions.add(subscription);
|
|
1197
|
+
|
|
1198
|
+
return createUnsubscribe(() => {
|
|
1199
|
+
if (options.onActive) _onActiveListeners.delete(options.onActive);
|
|
1200
|
+
instrumentationSubscriptions.delete(subscription);
|
|
1192
1201
|
});
|
|
1193
1202
|
};
|
|
1194
1203
|
|
|
1195
|
-
// React
|
|
1196
|
-
// caching discovered keys makes repeat lookups a single property read instead of a
|
|
1197
|
-
// scan over every own key of the element.
|
|
1204
|
+
// React uses per-renderer suffixes for host-instance Fiber keys, so discovered keys are cached.
|
|
1198
1205
|
const knownFiberPropertyKeys = new Set<string>();
|
|
1199
1206
|
|
|
1200
1207
|
const isFiberPropertyKey = (key: string): boolean =>
|
|
@@ -1202,6 +1209,37 @@ const isFiberPropertyKey = (key: string): boolean =>
|
|
|
1202
1209
|
key.startsWith("__reactInternalInstance$") ||
|
|
1203
1210
|
key.startsWith("__reactFiber");
|
|
1204
1211
|
|
|
1212
|
+
const getLegacyRootFiber = (hostInstance: object): Fiber | null => {
|
|
1213
|
+
const reactRootContainer = Reflect.get(hostInstance, "_reactRootContainer");
|
|
1214
|
+
if (typeof reactRootContainer !== "object" || reactRootContainer === null) return null;
|
|
1215
|
+
const internalRoot = Reflect.get(reactRootContainer, "_internalRoot");
|
|
1216
|
+
if (typeof internalRoot !== "object" || internalRoot === null) return null;
|
|
1217
|
+
const current = Reflect.get(internalRoot, "current");
|
|
1218
|
+
if (typeof current !== "object" || current === null) return null;
|
|
1219
|
+
const child = Reflect.get(current, "child");
|
|
1220
|
+
return isValidFiber(child) ? child : null;
|
|
1221
|
+
};
|
|
1222
|
+
|
|
1223
|
+
const getInternalInstanceHandle = (hostInstance: object): Fiber | null => {
|
|
1224
|
+
const internalInstanceHandle =
|
|
1225
|
+
Reflect.get(hostInstance, "__internalInstanceHandle") ??
|
|
1226
|
+
Reflect.get(hostInstance, "_internalInstanceHandle");
|
|
1227
|
+
return isValidFiber(internalInstanceHandle) ? internalInstanceHandle : null;
|
|
1228
|
+
};
|
|
1229
|
+
|
|
1230
|
+
const getPublicHostInstance = (stateNode: unknown): unknown => {
|
|
1231
|
+
if (typeof stateNode !== "object" || stateNode === null) return stateNode;
|
|
1232
|
+
const canonical = Reflect.get(stateNode, "canonical");
|
|
1233
|
+
if (typeof canonical === "object" && canonical !== null) {
|
|
1234
|
+
const publicInstance = Reflect.get(canonical, "publicInstance");
|
|
1235
|
+
if (typeof publicInstance === "object" && publicInstance !== null) {
|
|
1236
|
+
return publicInstance;
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
const nativeTag = Reflect.get(stateNode, "_nativeTag");
|
|
1240
|
+
return typeof nativeTag === "number" ? nativeTag : stateNode;
|
|
1241
|
+
};
|
|
1242
|
+
|
|
1205
1243
|
export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
|
|
1206
1244
|
const rdtHook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
1207
1245
|
if (rdtHook?.renderers) {
|
|
@@ -1213,40 +1251,56 @@ export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
|
|
|
1213
1251
|
}
|
|
1214
1252
|
}
|
|
1215
1253
|
|
|
1216
|
-
if (typeof hostInstance === "object" && hostInstance
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
return (hostInstance._reactRootContainer as any)?._internalRoot?.current?.child;
|
|
1220
|
-
}
|
|
1254
|
+
if (typeof hostInstance === "object" && hostInstance !== null) {
|
|
1255
|
+
const legacyRootFiber = getLegacyRootFiber(hostInstance);
|
|
1256
|
+
if (legacyRootFiber) return legacyRootFiber;
|
|
1221
1257
|
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
// handle" instead of a __reactFiber$-prefixed key
|
|
1225
|
-
const fabricInstanceHandle =
|
|
1226
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1227
|
-
(hostInstance as any).__internalInstanceHandle ??
|
|
1228
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1229
|
-
(hostInstance as any)._internalInstanceHandle;
|
|
1230
|
-
if (isFiber(fabricInstanceHandle)) {
|
|
1231
|
-
return fabricInstanceHandle;
|
|
1232
|
-
}
|
|
1258
|
+
const internalInstanceHandle = getInternalInstanceHandle(hostInstance);
|
|
1259
|
+
if (internalInstanceHandle) return internalInstanceHandle;
|
|
1233
1260
|
|
|
1234
|
-
const hostInstanceRecord = hostInstance as Record<string, unknown>;
|
|
1235
1261
|
for (const knownKey of knownFiberPropertyKeys) {
|
|
1236
|
-
const fiber =
|
|
1237
|
-
if (fiber) return fiber
|
|
1262
|
+
const fiber = Reflect.get(hostInstance, knownKey);
|
|
1263
|
+
if (isValidFiber(fiber)) return fiber;
|
|
1238
1264
|
}
|
|
1239
1265
|
|
|
1240
|
-
for (const key of Object.keys(
|
|
1266
|
+
for (const key of Object.keys(hostInstance)) {
|
|
1241
1267
|
if (isFiberPropertyKey(key)) {
|
|
1242
1268
|
knownFiberPropertyKeys.add(key);
|
|
1243
|
-
|
|
1269
|
+
const fiber = Reflect.get(hostInstance, key);
|
|
1270
|
+
if (isValidFiber(fiber)) return fiber;
|
|
1244
1271
|
}
|
|
1245
1272
|
}
|
|
1246
1273
|
}
|
|
1274
|
+
|
|
1275
|
+
if (
|
|
1276
|
+
hostInstance !== null &&
|
|
1277
|
+
hostInstance !== undefined &&
|
|
1278
|
+
(typeof hostInstance === "object" || typeof hostInstance === "number")
|
|
1279
|
+
) {
|
|
1280
|
+
for (const fiberRoot of _fiberRoots) {
|
|
1281
|
+
const fiber = traverseFiber(
|
|
1282
|
+
fiberRoot.current,
|
|
1283
|
+
(candidateFiber) =>
|
|
1284
|
+
isHostFiber(candidateFiber) &&
|
|
1285
|
+
getPublicHostInstance(candidateFiber.stateNode) === hostInstance,
|
|
1286
|
+
);
|
|
1287
|
+
if (fiber) return fiber;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1247
1290
|
return null;
|
|
1248
1291
|
};
|
|
1249
1292
|
|
|
1250
|
-
export
|
|
1251
|
-
|
|
1252
|
-
|
|
1293
|
+
export {
|
|
1294
|
+
BIPPY_INSTRUMENTATION_STRING,
|
|
1295
|
+
_onActiveListeners,
|
|
1296
|
+
_renderers,
|
|
1297
|
+
getRDTHook,
|
|
1298
|
+
hasRDTHook,
|
|
1299
|
+
installRDTHook,
|
|
1300
|
+
isRealReactDevtools,
|
|
1301
|
+
onRendererInject,
|
|
1302
|
+
patchRDTHook,
|
|
1303
|
+
version,
|
|
1304
|
+
} from "./rdt-hook.js";
|
|
1305
|
+
export type { Unsubscribe } from "./rdt-hook.js";
|
|
1306
|
+
export type * from "./react-internals/index.js";
|