bippy 0.6.1-dev.61475ed → 0.6.1-dev.a3b3942
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 +32 -48
- 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 +420 -0
- package/dist/errors.d.ts +420 -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 -5
- package/dist/source.d.cts +27 -11
- package/dist/source.d.ts +27 -11
- package/dist/source.js +14 -5
- package/package.json +22 -16
- package/src/core.ts +349 -328
- package/src/errors.ts +34 -0
- package/src/install-hook-only.ts +2 -2
- package/src/rdt-hook.ts +160 -161
- 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 +80 -0
- package/src/react-internals/types.ts +197 -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 +32 -25
- package/src/source/get-source.ts +5 -5
- package/src/source/index.ts +9 -1
- package/src/source/inspect-hooks.ts +215 -182
- package/src/source/owner-stack.ts +127 -166
- 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 -3
- package/src/source/symbolication.ts +490 -90
- 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,69 @@ 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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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 getTypeName = (value: object): string | null => {
|
|
49
|
+
const displayName = "displayName" in value ? value.displayName : null;
|
|
50
|
+
if (typeof displayName === "string" && displayName) return displayName;
|
|
51
|
+
const name = "name" in value ? value.name : null;
|
|
52
|
+
return typeof name === "string" && name ? name : null;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
interface FiberSelector {
|
|
56
|
+
(node: Fiber): boolean | Promise<boolean | void> | void;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface FiberTimings {
|
|
60
|
+
selfTime: number;
|
|
61
|
+
totalTime: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface RenderHandler {
|
|
65
|
+
(fiber: Fiber, phase: RenderPhase): unknown;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface ValueWrite {
|
|
69
|
+
path: string[];
|
|
70
|
+
value: unknown;
|
|
71
|
+
}
|
|
69
72
|
|
|
70
73
|
/**
|
|
71
74
|
* Returns `true` if object is a React Element.
|
|
@@ -74,19 +77,17 @@ const MutationMask =
|
|
|
74
77
|
*/
|
|
75
78
|
export const isValidElement = (element: unknown): element is React.ReactElement =>
|
|
76
79
|
typeof element === "object" &&
|
|
77
|
-
element
|
|
80
|
+
element !== null &&
|
|
78
81
|
"$$typeof" in element &&
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
String(element.$$typeof),
|
|
82
|
-
);
|
|
82
|
+
(String(element.$$typeof) === ReactSymbols.LEGACY_ELEMENT_SYMBOL_STRING ||
|
|
83
|
+
String(element.$$typeof) === ReactSymbols.ELEMENT_SYMBOL_STRING);
|
|
83
84
|
|
|
84
85
|
/**
|
|
85
86
|
* Returns `true` if object is a React Fiber.
|
|
86
87
|
*/
|
|
87
88
|
export const isValidFiber = (fiber: unknown): fiber is Fiber =>
|
|
88
89
|
typeof fiber === "object" &&
|
|
89
|
-
fiber
|
|
90
|
+
fiber !== null &&
|
|
90
91
|
"tag" in fiber &&
|
|
91
92
|
"stateNode" in fiber &&
|
|
92
93
|
"return" in fiber &&
|
|
@@ -94,21 +95,27 @@ export const isValidFiber = (fiber: unknown): fiber is Fiber =>
|
|
|
94
95
|
"sibling" in fiber &&
|
|
95
96
|
"flags" in fiber;
|
|
96
97
|
|
|
98
|
+
const isFiberRoot = (fiberRoot: unknown): fiberRoot is FiberRoot =>
|
|
99
|
+
typeof fiberRoot === "object" &&
|
|
100
|
+
fiberRoot !== null &&
|
|
101
|
+
"current" in fiberRoot &&
|
|
102
|
+
isValidFiber(fiberRoot.current);
|
|
103
|
+
|
|
97
104
|
/**
|
|
98
105
|
* Returns `true` if fiber is a host fiber. Host fibers are DOM nodes in react-dom, `View` in react-native, etc.
|
|
99
106
|
*
|
|
100
107
|
* @see https://reactnative.dev/architecture/glossary#host-view-tree-and-host-view
|
|
101
108
|
*/
|
|
102
|
-
export const isHostFiber = (fiber: Fiber):
|
|
109
|
+
export const isHostFiber = (fiber: Fiber): fiber is HostFiber => {
|
|
110
|
+
const workTags = getReactWorkTagsForFiber(fiber);
|
|
103
111
|
switch (fiber.tag) {
|
|
104
|
-
case
|
|
105
|
-
|
|
106
|
-
case
|
|
107
|
-
|
|
108
|
-
case HostSingletonTag:
|
|
112
|
+
case workTags.HostComponent:
|
|
113
|
+
case workTags.HostText:
|
|
114
|
+
case workTags.HostHoistable:
|
|
115
|
+
case workTags.HostSingleton:
|
|
109
116
|
return true;
|
|
110
117
|
default:
|
|
111
|
-
return
|
|
118
|
+
return false;
|
|
112
119
|
}
|
|
113
120
|
};
|
|
114
121
|
|
|
@@ -118,12 +125,13 @@ export const isHostFiber = (fiber: Fiber): boolean => {
|
|
|
118
125
|
* @see https://reactnative.dev/architecture/glossary#react-composite-components
|
|
119
126
|
*/
|
|
120
127
|
export const isCompositeFiber = (fiber: Fiber): boolean => {
|
|
128
|
+
const workTags = getReactWorkTagsForFiber(fiber);
|
|
121
129
|
switch (fiber.tag) {
|
|
122
|
-
case
|
|
123
|
-
case
|
|
124
|
-
case
|
|
125
|
-
case
|
|
126
|
-
case
|
|
130
|
+
case workTags.ClassComponent:
|
|
131
|
+
case workTags.ForwardRef:
|
|
132
|
+
case workTags.FunctionComponent:
|
|
133
|
+
case workTags.MemoComponent:
|
|
134
|
+
case workTags.SimpleMemoComponent:
|
|
127
135
|
return true;
|
|
128
136
|
default:
|
|
129
137
|
return false;
|
|
@@ -230,16 +238,17 @@ export const traverseProps = (
|
|
|
230
238
|
export const didFiberRender = (fiber: Fiber): boolean => {
|
|
231
239
|
const nextProps = fiber.memoizedProps;
|
|
232
240
|
const prevProps = fiber.alternate?.memoizedProps || {};
|
|
233
|
-
const flags = fiber.flags ??
|
|
241
|
+
const flags = fiber.flags ?? fiber.effectTag ?? 0;
|
|
242
|
+
const workTags = getReactWorkTagsForFiber(fiber);
|
|
234
243
|
|
|
235
244
|
switch (fiber.tag) {
|
|
236
|
-
case
|
|
237
|
-
case
|
|
238
|
-
case
|
|
239
|
-
case
|
|
240
|
-
case
|
|
241
|
-
case
|
|
242
|
-
return (flags & PerformedWork) === PerformedWork;
|
|
245
|
+
case workTags.ClassComponent:
|
|
246
|
+
case workTags.ContextConsumer:
|
|
247
|
+
case workTags.ForwardRef:
|
|
248
|
+
case workTags.FunctionComponent:
|
|
249
|
+
case workTags.MemoComponent:
|
|
250
|
+
case workTags.SimpleMemoComponent: {
|
|
251
|
+
return (flags & ReactFiberFlags.PerformedWork) === ReactFiberFlags.PerformedWork;
|
|
243
252
|
}
|
|
244
253
|
default:
|
|
245
254
|
// Host nodes (DOM, root, etc.)
|
|
@@ -257,8 +266,8 @@ export const didFiberRender = (fiber: Fiber): boolean => {
|
|
|
257
266
|
*/
|
|
258
267
|
export const didFiberCommit = (fiber: Fiber): boolean => {
|
|
259
268
|
return Boolean(
|
|
260
|
-
(fiber.flags & (MutationMask | Cloned)) !== 0 ||
|
|
261
|
-
(fiber.subtreeFlags & (MutationMask | Cloned)) !== 0,
|
|
269
|
+
(fiber.flags & (MutationMask | ReactFiberFlags.Cloned)) !== 0 ||
|
|
270
|
+
(fiber.subtreeFlags & (MutationMask | ReactFiberFlags.Cloned)) !== 0,
|
|
262
271
|
);
|
|
263
272
|
};
|
|
264
273
|
|
|
@@ -297,7 +306,8 @@ export const getFiberStack = (fiber: Fiber): Fiber[] => {
|
|
|
297
306
|
let currentFiber = fiber;
|
|
298
307
|
while (currentFiber.return) {
|
|
299
308
|
stack.push(currentFiber);
|
|
300
|
-
|
|
309
|
+
const parentFiber = currentFiber.return;
|
|
310
|
+
currentFiber = parentFiber;
|
|
301
311
|
}
|
|
302
312
|
return stack;
|
|
303
313
|
};
|
|
@@ -306,8 +316,9 @@ export const getFiberStack = (fiber: Fiber): Fiber[] => {
|
|
|
306
316
|
* Returns `true` if the {@link Fiber} should be filtered out during reconciliation.
|
|
307
317
|
*/
|
|
308
318
|
const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
319
|
+
const workTags = getReactWorkTagsForFiber(fiber);
|
|
309
320
|
switch (fiber.tag) {
|
|
310
|
-
case
|
|
321
|
+
case workTags.DehydratedSuspenseComponent:
|
|
311
322
|
// TODO: ideally we would show dehydrated Suspense immediately.
|
|
312
323
|
// However, it has some special behavior (like disconnecting
|
|
313
324
|
// an alternate and turning into real Suspense) which breaks DevTools.
|
|
@@ -315,13 +326,13 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
|
315
326
|
// https://github.com/bvaughn/react-devtools-experimental/issues/197
|
|
316
327
|
return true;
|
|
317
328
|
|
|
318
|
-
case
|
|
319
|
-
case
|
|
320
|
-
case
|
|
321
|
-
case
|
|
329
|
+
case workTags.Fragment:
|
|
330
|
+
case workTags.HostText:
|
|
331
|
+
case workTags.LegacyHiddenComponent:
|
|
332
|
+
case workTags.OffscreenComponent:
|
|
322
333
|
return true;
|
|
323
334
|
|
|
324
|
-
case
|
|
335
|
+
case workTags.HostRoot:
|
|
325
336
|
// It is never valid to filter the root element.
|
|
326
337
|
return false;
|
|
327
338
|
|
|
@@ -331,15 +342,15 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
|
331
342
|
|
|
332
343
|
if (typeof symbolOrNumber === "symbol") {
|
|
333
344
|
return (
|
|
334
|
-
symbolOrNumber.description === CONCURRENT_MODE_SYMBOL_DESCRIPTION ||
|
|
335
|
-
symbolOrNumber.description === DEPRECATED_ASYNC_MODE_SYMBOL_DESCRIPTION
|
|
345
|
+
symbolOrNumber.description === ReactSymbols.CONCURRENT_MODE_SYMBOL_DESCRIPTION ||
|
|
346
|
+
symbolOrNumber.description === ReactSymbols.DEPRECATED_ASYNC_MODE_SYMBOL_DESCRIPTION
|
|
336
347
|
);
|
|
337
348
|
}
|
|
338
349
|
|
|
339
350
|
switch (symbolOrNumber) {
|
|
340
|
-
case CONCURRENT_MODE_NUMBER:
|
|
341
|
-
case CONCURRENT_MODE_SYMBOL_STRING:
|
|
342
|
-
case DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
|
|
351
|
+
case ReactSymbols.CONCURRENT_MODE_NUMBER:
|
|
352
|
+
case ReactSymbols.CONCURRENT_MODE_SYMBOL_STRING:
|
|
353
|
+
case ReactSymbols.DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
|
|
343
354
|
return true;
|
|
344
355
|
|
|
345
356
|
default:
|
|
@@ -405,75 +416,65 @@ export function traverseFiber(
|
|
|
405
416
|
): Promise<Fiber | null>;
|
|
406
417
|
export function traverseFiber(
|
|
407
418
|
fiber: Fiber | null,
|
|
408
|
-
selector:
|
|
419
|
+
selector: FiberSelector,
|
|
420
|
+
ascending?: boolean,
|
|
421
|
+
): Fiber | null | Promise<Fiber | null>;
|
|
422
|
+
export function traverseFiber(
|
|
423
|
+
fiber: Fiber | null,
|
|
424
|
+
selector: FiberSelector,
|
|
409
425
|
ascending = false,
|
|
410
426
|
): 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
|
-
}
|
|
427
|
+
return traverseFiberInternal(fiber, selector, ascending);
|
|
428
|
+
}
|
|
431
429
|
|
|
432
|
-
|
|
430
|
+
const isPromiseLike = <Result>(value: unknown): value is PromiseLike<Result> =>
|
|
431
|
+
(typeof value === "object" || typeof value === "function") &&
|
|
432
|
+
value !== null &&
|
|
433
|
+
"then" in value &&
|
|
434
|
+
typeof value.then === "function";
|
|
433
435
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
return
|
|
441
|
-
}
|
|
436
|
+
const traverseFiberChildren = (
|
|
437
|
+
fiber: Fiber,
|
|
438
|
+
selector: FiberSelector,
|
|
439
|
+
ascending: boolean,
|
|
440
|
+
): Fiber | null | Promise<Fiber | null> => {
|
|
441
|
+
const firstChild = ascending ? fiber.return : fiber.child;
|
|
442
|
+
return traverseFiberSiblings(firstChild, selector, ascending);
|
|
443
|
+
};
|
|
442
444
|
|
|
443
|
-
const
|
|
445
|
+
const traverseFiberSiblings = (
|
|
444
446
|
fiber: Fiber | null,
|
|
445
|
-
selector:
|
|
446
|
-
ascending
|
|
447
|
-
): Fiber | null => {
|
|
447
|
+
selector: FiberSelector,
|
|
448
|
+
ascending: boolean,
|
|
449
|
+
): Fiber | null | Promise<Fiber | null> => {
|
|
448
450
|
if (!fiber) return null;
|
|
449
|
-
if (selector(fiber) === true) return fiber;
|
|
450
451
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
452
|
+
const nextSibling = ascending ? null : fiber.sibling;
|
|
453
|
+
const match = traverseFiberInternal(fiber, selector, ascending);
|
|
454
|
+
if (isPromiseLike<Fiber | null>(match)) {
|
|
455
|
+
return Promise.resolve(match).then(
|
|
456
|
+
(resolvedMatch) => resolvedMatch ?? traverseFiberSiblings(nextSibling, selector, ascending),
|
|
457
|
+
);
|
|
457
458
|
}
|
|
458
|
-
return
|
|
459
|
+
return match ?? traverseFiberSiblings(nextSibling, selector, ascending);
|
|
459
460
|
};
|
|
460
461
|
|
|
461
|
-
const
|
|
462
|
+
const traverseFiberInternal = (
|
|
462
463
|
fiber: Fiber | null,
|
|
463
|
-
selector:
|
|
464
|
-
ascending
|
|
465
|
-
): Promise<Fiber | null> => {
|
|
464
|
+
selector: FiberSelector,
|
|
465
|
+
ascending: boolean,
|
|
466
|
+
): Fiber | null | Promise<Fiber | null> => {
|
|
466
467
|
if (!fiber) return null;
|
|
467
|
-
if ((await selector(fiber)) === true) return fiber;
|
|
468
|
-
|
|
469
|
-
let child = ascending ? fiber.return : fiber.child;
|
|
470
|
-
while (child) {
|
|
471
|
-
const match = await traverseFiberAsync(child, selector, ascending);
|
|
472
|
-
if (match) return match;
|
|
473
468
|
|
|
474
|
-
|
|
469
|
+
const selection = selector(fiber);
|
|
470
|
+
if (isPromiseLike<boolean | void>(selection)) {
|
|
471
|
+
return Promise.resolve(selection).then((didSelectFiber) =>
|
|
472
|
+
didSelectFiber === true ? fiber : traverseFiberChildren(fiber, selector, ascending),
|
|
473
|
+
);
|
|
475
474
|
}
|
|
476
|
-
return
|
|
475
|
+
if (selection === true) return fiber;
|
|
476
|
+
|
|
477
|
+
return traverseFiberChildren(fiber, selector, ascending);
|
|
477
478
|
};
|
|
478
479
|
|
|
479
480
|
/**
|
|
@@ -485,12 +486,12 @@ const traverseFiberAsync = async (
|
|
|
485
486
|
* console.log(selfTime, totalTime);
|
|
486
487
|
* ```
|
|
487
488
|
*/
|
|
488
|
-
export const getTimings = (fiber?: Fiber | null):
|
|
489
|
+
export const getTimings = (fiber?: Fiber | null): FiberTimings => {
|
|
489
490
|
const totalTime = fiber?.actualDuration ?? 0;
|
|
490
491
|
let selfTime = totalTime;
|
|
491
492
|
// TODO: calculate a DOM time, which is just host component summed up
|
|
492
493
|
let child = fiber?.child ?? null;
|
|
493
|
-
while (totalTime > 0 && child
|
|
494
|
+
while (totalTime > 0 && child !== null) {
|
|
494
495
|
selfTime -= child.actualDuration ?? 0;
|
|
495
496
|
child = child.sibling;
|
|
496
497
|
}
|
|
@@ -501,48 +502,29 @@ export const getTimings = (fiber?: Fiber | null): { selfTime: number; totalTime:
|
|
|
501
502
|
* Returns `true` if the {@link Fiber} uses React Compiler's memo cache.
|
|
502
503
|
*/
|
|
503
504
|
export const hasMemoCache = (fiber: Fiber): boolean => {
|
|
504
|
-
return Boolean(
|
|
505
|
+
return Boolean(fiber.updateQueue?.memoCache);
|
|
505
506
|
};
|
|
506
507
|
|
|
507
|
-
type FiberType =
|
|
508
|
-
| React.ComponentType<unknown>
|
|
509
|
-
| React.ForwardRefExoticComponent<unknown>
|
|
510
|
-
| React.MemoExoticComponent<React.ComponentType<unknown>>;
|
|
511
|
-
|
|
512
508
|
/**
|
|
513
509
|
* Returns the type (e.g. component definition) of the {@link Fiber}
|
|
514
510
|
*/
|
|
515
511
|
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;
|
|
512
|
+
if (isComponentType(type)) return type;
|
|
513
|
+
if (typeof type !== "object" || type === null) return null;
|
|
514
|
+
return getType(Reflect.get(type, "type") ?? Reflect.get(type, "render"));
|
|
528
515
|
};
|
|
529
516
|
|
|
530
517
|
/**
|
|
531
518
|
* Returns the display name of the {@link Fiber} type.
|
|
532
519
|
*/
|
|
533
520
|
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;
|
|
521
|
+
if (typeof type === "string") return type;
|
|
522
|
+
if ((typeof type !== "function" && typeof type !== "object") || type === null) return null;
|
|
523
|
+
const name = getTypeName(type);
|
|
542
524
|
if (name) return name;
|
|
543
|
-
const unwrappedType = getType(
|
|
525
|
+
const unwrappedType = getType(type);
|
|
544
526
|
if (!unwrappedType) return null;
|
|
545
|
-
return unwrappedType
|
|
527
|
+
return getTypeName(unwrappedType);
|
|
546
528
|
};
|
|
547
529
|
|
|
548
530
|
/**
|
|
@@ -550,7 +532,10 @@ export const getDisplayName = (type: unknown): null | string => {
|
|
|
550
532
|
*/
|
|
551
533
|
export const detectReactBuildType = (renderer: ReactRenderer): "development" | "production" => {
|
|
552
534
|
try {
|
|
553
|
-
if (
|
|
535
|
+
if (
|
|
536
|
+
typeof renderer.version === "string" &&
|
|
537
|
+
renderer.bundleType === ReactBuildType.Development
|
|
538
|
+
) {
|
|
554
539
|
return "development";
|
|
555
540
|
}
|
|
556
541
|
} catch {}
|
|
@@ -562,11 +547,7 @@ export const detectReactBuildType = (renderer: ReactRenderer): "development" | "
|
|
|
562
547
|
*/
|
|
563
548
|
export const isInstrumentationActive = (): boolean => {
|
|
564
549
|
const rdtHook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
565
|
-
return (
|
|
566
|
-
Boolean(rdtHook?._instrumentationIsActive) ||
|
|
567
|
-
isRealReactDevtools(rdtHook) ||
|
|
568
|
-
isReactRefresh(rdtHook)
|
|
569
|
-
);
|
|
550
|
+
return Boolean(rdtHook?._instrumentationIsActive) || isRealReactDevtools(rdtHook);
|
|
570
551
|
};
|
|
571
552
|
|
|
572
553
|
export const _fiberRoots = new Set<FiberRoot>();
|
|
@@ -582,37 +563,35 @@ export const getLatestFiber = (fiber: Fiber): Fiber => {
|
|
|
582
563
|
}
|
|
583
564
|
for (const root of _fiberRoots) {
|
|
584
565
|
const latestFiber = traverseFiber(root.current, (innerFiber) => {
|
|
585
|
-
if (innerFiber === fiber) return true;
|
|
566
|
+
if (innerFiber === fiber || innerFiber === alternate) return true;
|
|
586
567
|
});
|
|
587
568
|
if (latestFiber) return latestFiber;
|
|
588
569
|
}
|
|
589
570
|
return fiber;
|
|
590
571
|
};
|
|
591
572
|
|
|
592
|
-
export type RenderHandler = <S>(fiber: Fiber, phase: RenderPhase, state?: S) => unknown;
|
|
593
|
-
|
|
594
573
|
export type RenderPhase = "mount" | "unmount" | "update";
|
|
595
574
|
|
|
596
|
-
let
|
|
575
|
+
let nextFiberId = 0;
|
|
597
576
|
const fiberIdMap = new WeakMap<Fiber, number>();
|
|
598
577
|
|
|
599
|
-
export const setFiberId = (fiber: Fiber,
|
|
600
|
-
fiberIdMap.set(fiber,
|
|
578
|
+
export const setFiberId = (fiber: Fiber, fiberId: number = nextFiberId++): void => {
|
|
579
|
+
fiberIdMap.set(fiber, fiberId);
|
|
580
|
+
if (Number.isSafeInteger(fiberId) && fiberId >= nextFiberId) {
|
|
581
|
+
nextFiberId = fiberId + 1;
|
|
582
|
+
}
|
|
601
583
|
};
|
|
602
584
|
|
|
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
585
|
export const getFiberId = (fiber: Fiber): number => {
|
|
607
|
-
let
|
|
608
|
-
if (
|
|
609
|
-
|
|
586
|
+
let currentFiberId = fiberIdMap.get(fiber);
|
|
587
|
+
if (currentFiberId === undefined && fiber.alternate) {
|
|
588
|
+
currentFiberId = fiberIdMap.get(fiber.alternate);
|
|
610
589
|
}
|
|
611
|
-
if (
|
|
612
|
-
|
|
613
|
-
setFiberId(fiber,
|
|
590
|
+
if (currentFiberId === undefined) {
|
|
591
|
+
currentFiberId = nextFiberId++;
|
|
592
|
+
setFiberId(fiber, currentFiberId);
|
|
614
593
|
}
|
|
615
|
-
return
|
|
594
|
+
return currentFiberId;
|
|
616
595
|
};
|
|
617
596
|
|
|
618
597
|
const mountFiberRecursively = (
|
|
@@ -622,7 +601,7 @@ const mountFiberRecursively = (
|
|
|
622
601
|
): void => {
|
|
623
602
|
let fiber: Fiber | null = firstChild;
|
|
624
603
|
|
|
625
|
-
while (fiber
|
|
604
|
+
while (fiber !== null) {
|
|
626
605
|
if (!fiberIdMap.has(fiber)) {
|
|
627
606
|
getFiberId(fiber);
|
|
628
607
|
}
|
|
@@ -631,7 +610,7 @@ const mountFiberRecursively = (
|
|
|
631
610
|
onRender(fiber, "mount");
|
|
632
611
|
}
|
|
633
612
|
|
|
634
|
-
if (fiber.tag ===
|
|
613
|
+
if (fiber.tag === getReactWorkTagsForFiber(fiber).SuspenseComponent) {
|
|
635
614
|
const isTimedOut = fiber.memoizedState !== null;
|
|
636
615
|
if (isTimedOut) {
|
|
637
616
|
// Special case: if Suspense mounts in a timed-out state,
|
|
@@ -646,18 +625,12 @@ const mountFiberRecursively = (
|
|
|
646
625
|
}
|
|
647
626
|
}
|
|
648
627
|
} 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
|
-
}
|
|
628
|
+
const primaryChild = fiber.child?.child ?? null;
|
|
656
629
|
if (primaryChild !== null) {
|
|
657
630
|
mountFiberRecursively(onRender, primaryChild, false);
|
|
658
631
|
}
|
|
659
632
|
}
|
|
660
|
-
} else if (fiber.child
|
|
633
|
+
} else if (fiber.child !== null) {
|
|
661
634
|
mountFiberRecursively(onRender, fiber.child, true);
|
|
662
635
|
}
|
|
663
636
|
fiber = traverseSiblings ? fiber.sibling : null;
|
|
@@ -667,8 +640,7 @@ const mountFiberRecursively = (
|
|
|
667
640
|
const updateFiberRecursively = (
|
|
668
641
|
onRender: RenderHandler,
|
|
669
642
|
nextFiber: Fiber,
|
|
670
|
-
prevFiber: Fiber,
|
|
671
|
-
parentFiber: Fiber | null,
|
|
643
|
+
prevFiber: Fiber | null,
|
|
672
644
|
): void => {
|
|
673
645
|
if (!fiberIdMap.has(nextFiber)) {
|
|
674
646
|
getFiberId(nextFiber);
|
|
@@ -678,7 +650,7 @@ const updateFiberRecursively = (
|
|
|
678
650
|
getFiberId(prevFiber);
|
|
679
651
|
}
|
|
680
652
|
|
|
681
|
-
const isSuspense = nextFiber.tag ===
|
|
653
|
+
const isSuspense = nextFiber.tag === getReactWorkTagsForFiber(nextFiber).SuspenseComponent;
|
|
682
654
|
|
|
683
655
|
const shouldIncludeInTree = !shouldFilterFiber(nextFiber);
|
|
684
656
|
if (shouldIncludeInTree && didFiberRender(nextFiber)) {
|
|
@@ -707,7 +679,7 @@ const updateFiberRecursively = (
|
|
|
707
679
|
const prevFallbackChildSet = prevFiber.child?.sibling ?? null;
|
|
708
680
|
|
|
709
681
|
if (nextFallbackChildSet !== null && prevFallbackChildSet !== null) {
|
|
710
|
-
updateFiberRecursively(onRender, nextFallbackChildSet, prevFallbackChildSet
|
|
682
|
+
updateFiberRecursively(onRender, nextFallbackChildSet, prevFallbackChildSet);
|
|
711
683
|
}
|
|
712
684
|
} else if (prevDidTimeout && !nextDidTimeOut) {
|
|
713
685
|
// Fallback -> Primary:
|
|
@@ -746,14 +718,7 @@ const updateFiberRecursively = (
|
|
|
746
718
|
// Schedule updates and mounts depending on whether alternates exist.
|
|
747
719
|
// We don't track deletions here because they are reported separately.
|
|
748
720
|
if (nextChild.alternate) {
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
updateFiberRecursively(
|
|
752
|
-
onRender,
|
|
753
|
-
nextChild,
|
|
754
|
-
prevChild,
|
|
755
|
-
shouldIncludeInTree ? nextFiber : parentFiber,
|
|
756
|
-
);
|
|
721
|
+
updateFiberRecursively(onRender, nextChild, nextChild.alternate);
|
|
757
722
|
} else {
|
|
758
723
|
mountFiberRecursively(onRender, nextChild, false);
|
|
759
724
|
}
|
|
@@ -765,7 +730,7 @@ const updateFiberRecursively = (
|
|
|
765
730
|
};
|
|
766
731
|
|
|
767
732
|
const unmountFiber = (onRender: RenderHandler, fiber: Fiber): void => {
|
|
768
|
-
const isRoot = fiber.tag ===
|
|
733
|
+
const isRoot = fiber.tag === getReactWorkTagsForFiber(fiber).HostRoot;
|
|
769
734
|
|
|
770
735
|
if (isRoot || !shouldFilterFiber(fiber)) {
|
|
771
736
|
onRender(fiber, "unmount");
|
|
@@ -774,7 +739,8 @@ const unmountFiber = (onRender: RenderHandler, fiber: Fiber): void => {
|
|
|
774
739
|
|
|
775
740
|
const unmountFiberChildrenRecursively = (onRender: RenderHandler, fiber: Fiber): void => {
|
|
776
741
|
// We might meet a nested Suspense on our way.
|
|
777
|
-
const isTimedOutSuspense =
|
|
742
|
+
const isTimedOutSuspense =
|
|
743
|
+
fiber.tag === getReactWorkTagsForFiber(fiber).SuspenseComponent && fiber.memoizedState !== null;
|
|
778
744
|
let child = fiber.child;
|
|
779
745
|
|
|
780
746
|
if (isTimedOutSuspense) {
|
|
@@ -800,7 +766,7 @@ const unmountFiberChildrenRecursively = (onRender: RenderHandler, fiber: Fiber):
|
|
|
800
766
|
|
|
801
767
|
let commitId = 0;
|
|
802
768
|
const rootInstanceMap = new WeakMap<
|
|
803
|
-
FiberRoot,
|
|
769
|
+
Fiber | FiberRoot,
|
|
804
770
|
{
|
|
805
771
|
id: number;
|
|
806
772
|
prevFiber: Fiber | null;
|
|
@@ -814,7 +780,7 @@ const rootInstanceMap = new WeakMap<
|
|
|
814
780
|
* console.log(phase)
|
|
815
781
|
* })
|
|
816
782
|
*/
|
|
817
|
-
export const traverseRenderedFibers = (root: FiberRoot, onRender: RenderHandler): void => {
|
|
783
|
+
export const traverseRenderedFibers = (root: Fiber | FiberRoot, onRender: RenderHandler): void => {
|
|
818
784
|
const fiber = "current" in root ? root.current : root;
|
|
819
785
|
|
|
820
786
|
let rootInstance = rootInstanceMap.get(root);
|
|
@@ -831,20 +797,22 @@ export const traverseRenderedFibers = (root: FiberRoot, onRender: RenderHandler)
|
|
|
831
797
|
} else if (prevFiber !== null) {
|
|
832
798
|
const wasMounted =
|
|
833
799
|
prevFiber &&
|
|
834
|
-
prevFiber.memoizedState
|
|
835
|
-
prevFiber.memoizedState.element
|
|
800
|
+
prevFiber.memoizedState !== null &&
|
|
801
|
+
prevFiber.memoizedState.element !== null &&
|
|
802
|
+
prevFiber.memoizedState.element !== undefined &&
|
|
836
803
|
// A dehydrated root is not considered mounted
|
|
837
804
|
prevFiber.memoizedState.isDehydrated !== true;
|
|
838
805
|
const isMounted =
|
|
839
|
-
fiber.memoizedState
|
|
840
|
-
fiber.memoizedState.element
|
|
806
|
+
fiber.memoizedState !== null &&
|
|
807
|
+
fiber.memoizedState.element !== null &&
|
|
808
|
+
fiber.memoizedState.element !== undefined &&
|
|
841
809
|
// A dehydrated root is not considered mounted
|
|
842
810
|
fiber.memoizedState.isDehydrated !== true;
|
|
843
811
|
|
|
844
812
|
if (!wasMounted && isMounted) {
|
|
845
813
|
mountFiberRecursively(onRender, fiber, false);
|
|
846
814
|
} else if (wasMounted && isMounted) {
|
|
847
|
-
updateFiberRecursively(onRender, fiber, fiber.alternate
|
|
815
|
+
updateFiberRecursively(onRender, fiber, fiber.alternate);
|
|
848
816
|
} else if (wasMounted && !isMounted) {
|
|
849
817
|
unmountFiber(onRender, fiber);
|
|
850
818
|
}
|
|
@@ -859,16 +827,15 @@ const overrideRenderers = new Set<ReactRenderer>();
|
|
|
859
827
|
let areOverrideRenderersWired = false;
|
|
860
828
|
|
|
861
829
|
const wireOverrideRenderers = (): void => {
|
|
862
|
-
if (areOverrideRenderersWired) return;
|
|
863
830
|
if (!hasRDTHook()) return;
|
|
864
831
|
const rdtHook = getRDTHook();
|
|
865
|
-
if (!rdtHook?.renderers) return;
|
|
866
|
-
areOverrideRenderersWired = true;
|
|
867
832
|
|
|
868
|
-
|
|
833
|
+
setHookEventDispatchers(rdtHook);
|
|
869
834
|
for (const renderer of rdtHook.renderers.values()) {
|
|
870
835
|
overrideRenderers.add(renderer);
|
|
871
836
|
}
|
|
837
|
+
if (areOverrideRenderersWired) return;
|
|
838
|
+
areOverrideRenderersWired = true;
|
|
872
839
|
onRendererInject((renderer) => {
|
|
873
840
|
overrideRenderers.add(renderer);
|
|
874
841
|
});
|
|
@@ -880,14 +847,13 @@ const getRootRenderer = (fiber: Fiber): ReactRenderer | null => {
|
|
|
880
847
|
while (hostRootFiber.return) {
|
|
881
848
|
hostRootFiber = hostRootFiber.return;
|
|
882
849
|
}
|
|
883
|
-
const
|
|
850
|
+
const fiberRoot = hostRootFiber.stateNode;
|
|
851
|
+
if (!isFiberRoot(fiberRoot)) return null;
|
|
852
|
+
const rendererId = rootRendererIds.get(fiberRoot);
|
|
884
853
|
if (rendererId === undefined) return null;
|
|
885
|
-
return getRDTHook().renderers
|
|
854
|
+
return getRDTHook().renderers.get(rendererId) ?? null;
|
|
886
855
|
};
|
|
887
856
|
|
|
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
857
|
const resolveOverrideRenderers = (fiber: Fiber): ReactRenderer[] => {
|
|
892
858
|
wireOverrideRenderers();
|
|
893
859
|
const rootRenderer = getRootRenderer(fiber);
|
|
@@ -913,15 +879,14 @@ const getHookStateDispatch = (
|
|
|
913
879
|
hookIndex: number,
|
|
914
880
|
): ((value: unknown) => void) | null => {
|
|
915
881
|
let hookState = fiber.memoizedState;
|
|
916
|
-
for (let
|
|
882
|
+
for (let currentHookIndex = 0; currentHookIndex < hookIndex; currentHookIndex++) {
|
|
917
883
|
if (!hookState?.next) return null;
|
|
918
884
|
hookState = hookState.next;
|
|
919
885
|
}
|
|
920
886
|
const queue = hookState?.queue;
|
|
921
|
-
if (isPOJO(queue)
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
return null;
|
|
887
|
+
if (!isPOJO(queue)) return null;
|
|
888
|
+
const dispatch = queue.dispatch;
|
|
889
|
+
return typeof dispatch === "function" ? (value) => dispatch(value) : null;
|
|
925
890
|
};
|
|
926
891
|
|
|
927
892
|
const findContextProviderFiber = (fiber: Fiber, contextType: unknown): Fiber | null => {
|
|
@@ -947,28 +912,30 @@ const isPOJO = (maybePOJO: unknown): maybePOJO is Record<string, unknown> => {
|
|
|
947
912
|
const buildPathsFromValue = (
|
|
948
913
|
maybePOJO: Record<string, unknown>,
|
|
949
914
|
basePath: string[] = [],
|
|
950
|
-
|
|
951
|
-
|
|
915
|
+
ancestors = new WeakSet<object>(),
|
|
916
|
+
): ValueWrite[] => {
|
|
917
|
+
if (ancestors.has(maybePOJO)) {
|
|
952
918
|
return [{ path: basePath, value: maybePOJO }];
|
|
953
919
|
}
|
|
954
920
|
|
|
955
|
-
|
|
921
|
+
ancestors.add(maybePOJO);
|
|
922
|
+
const paths: ValueWrite[] = [];
|
|
956
923
|
|
|
957
|
-
for (const key
|
|
958
|
-
const value = maybePOJO[key];
|
|
924
|
+
for (const [key, value] of Object.entries(maybePOJO)) {
|
|
959
925
|
const path = basePath.concat(key);
|
|
960
926
|
|
|
961
927
|
if (isPOJO(value)) {
|
|
962
|
-
paths.push(...buildPathsFromValue(value, path));
|
|
928
|
+
paths.push(...buildPathsFromValue(value, path, ancestors));
|
|
963
929
|
} else {
|
|
964
930
|
paths.push({ path, value });
|
|
965
931
|
}
|
|
966
932
|
}
|
|
967
933
|
|
|
934
|
+
ancestors.delete(maybePOJO);
|
|
968
935
|
return paths;
|
|
969
936
|
};
|
|
970
937
|
|
|
971
|
-
const buildValueWrites = (partialValue: unknown):
|
|
938
|
+
const buildValueWrites = (partialValue: unknown): ValueWrite[] =>
|
|
972
939
|
isPOJO(partialValue) ? buildPathsFromValue(partialValue) : [{ path: [], value: partialValue }];
|
|
973
940
|
|
|
974
941
|
export const overrideProps = (fiber: Fiber, partialValue: Record<string, unknown>) => {
|
|
@@ -985,11 +952,10 @@ export const overrideHookState = (fiber: Fiber, id: number, partialValue: unknow
|
|
|
985
952
|
const writes = buildValueWrites(partialValue);
|
|
986
953
|
|
|
987
954
|
if (renderers.length > 0) {
|
|
988
|
-
const hookId = String(id);
|
|
989
955
|
for (const renderer of renderers) {
|
|
990
956
|
for (const { path, value } of writes) {
|
|
991
957
|
try {
|
|
992
|
-
renderer.overrideHookState?.(fiber,
|
|
958
|
+
renderer.overrideHookState?.(fiber, id, path, value);
|
|
993
959
|
} catch {}
|
|
994
960
|
}
|
|
995
961
|
}
|
|
@@ -1022,24 +988,22 @@ export const overrideContext = (fiber: Fiber, contextType: unknown, partialValue
|
|
|
1022
988
|
export interface InstrumentationOptions {
|
|
1023
989
|
name?: string;
|
|
1024
990
|
onActive?: () => unknown;
|
|
1025
|
-
onCommitFiberRoot?: (
|
|
991
|
+
onCommitFiberRoot?: (
|
|
992
|
+
rendererID: number,
|
|
993
|
+
root: FiberRoot,
|
|
994
|
+
priority: number | void,
|
|
995
|
+
didError?: boolean,
|
|
996
|
+
) => unknown;
|
|
1026
997
|
onCommitFiberUnmount?: (rendererID: number, fiber: Fiber) => unknown;
|
|
1027
998
|
onPostCommitFiberRoot?: (rendererID: number, root: FiberRoot) => unknown;
|
|
1028
999
|
onScheduleFiberRoot?: (rendererID: number, root: FiberRoot, children: React.ReactNode) => unknown;
|
|
1029
1000
|
}
|
|
1030
1001
|
|
|
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
|
-
>();
|
|
1002
|
+
interface InstrumentationSubscription {
|
|
1003
|
+
options: InstrumentationOptions;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
const instrumentationSubscriptions = new Set<InstrumentationSubscription>();
|
|
1043
1007
|
|
|
1044
1008
|
interface HookDispatchers {
|
|
1045
1009
|
onCommitFiberRoot: ReactDevToolsGlobalHook["onCommitFiberRoot"];
|
|
@@ -1049,6 +1013,7 @@ interface HookDispatchers {
|
|
|
1049
1013
|
}
|
|
1050
1014
|
|
|
1051
1015
|
const hookDispatchers = new WeakMap<ReactDevToolsGlobalHook, Partial<HookDispatchers>>();
|
|
1016
|
+
let didSubscribeToHookReplacements = false;
|
|
1052
1017
|
|
|
1053
1018
|
const rootRendererIds = new WeakMap<FiberRoot, number>();
|
|
1054
1019
|
|
|
@@ -1057,7 +1022,7 @@ const rootRendererIds = new WeakMap<FiberRoot, number>();
|
|
|
1057
1022
|
// next instrument() call installs a fresh wrapper over it; a superseded
|
|
1058
1023
|
// wrapper still forwards the previous chain but skips the listeners so
|
|
1059
1024
|
// they never fire twice.
|
|
1060
|
-
const
|
|
1025
|
+
const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
1061
1026
|
const dispatchers = hookDispatchers.get(rdtHook) ?? {};
|
|
1062
1027
|
hookDispatchers.set(rdtHook, dispatchers);
|
|
1063
1028
|
|
|
@@ -1070,13 +1035,29 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1070
1035
|
rendererID,
|
|
1071
1036
|
root,
|
|
1072
1037
|
priority,
|
|
1038
|
+
didError,
|
|
1073
1039
|
) => {
|
|
1074
|
-
prevOnCommitFiberRoot
|
|
1040
|
+
if (prevOnCommitFiberRoot) {
|
|
1041
|
+
prevOnCommitFiberRoot.call(rdtHook, rendererID, root, priority, didError);
|
|
1042
|
+
}
|
|
1075
1043
|
if (hookDispatchers.get(rdtHook)?.onCommitFiberRoot !== dispatchCommitFiberRoot) return;
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1044
|
+
setReactWorkTagsForFiber(root.current, rdtHook.renderers.get(rendererID));
|
|
1045
|
+
const rootMemoizedState = root.current.memoizedState;
|
|
1046
|
+
const isUnmounting =
|
|
1047
|
+
rootMemoizedState === null ||
|
|
1048
|
+
rootMemoizedState.element === null ||
|
|
1049
|
+
rootMemoizedState.element === undefined;
|
|
1050
|
+
if (isUnmounting) {
|
|
1051
|
+
_fiberRoots.delete(root);
|
|
1052
|
+
rootRendererIds.delete(root);
|
|
1053
|
+
} else {
|
|
1054
|
+
_fiberRoots.add(root);
|
|
1055
|
+
rootRendererIds.set(root, rendererID);
|
|
1056
|
+
}
|
|
1057
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
1058
|
+
if (options.onCommitFiberRoot) {
|
|
1059
|
+
options.onCommitFiberRoot(rendererID, root, priority, didError);
|
|
1060
|
+
}
|
|
1080
1061
|
}
|
|
1081
1062
|
};
|
|
1082
1063
|
dispatchers.onCommitFiberRoot = dispatchCommitFiberRoot;
|
|
@@ -1092,12 +1073,17 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1092
1073
|
rendererID,
|
|
1093
1074
|
fiber,
|
|
1094
1075
|
) => {
|
|
1095
|
-
|
|
1076
|
+
setReactWorkTagsForFiber(fiber, rdtHook.renderers.get(rendererID));
|
|
1077
|
+
if (prevOnCommitFiberUnmount) {
|
|
1078
|
+
prevOnCommitFiberUnmount.call(rdtHook, rendererID, fiber);
|
|
1079
|
+
}
|
|
1096
1080
|
if (hookDispatchers.get(rdtHook)?.onCommitFiberUnmount !== dispatchCommitFiberUnmount) {
|
|
1097
1081
|
return;
|
|
1098
1082
|
}
|
|
1099
|
-
for (const
|
|
1100
|
-
|
|
1083
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
1084
|
+
if (options.onCommitFiberUnmount) {
|
|
1085
|
+
options.onCommitFiberUnmount(rendererID, fiber);
|
|
1086
|
+
}
|
|
1101
1087
|
}
|
|
1102
1088
|
};
|
|
1103
1089
|
dispatchers.onCommitFiberUnmount = dispatchCommitFiberUnmount;
|
|
@@ -1113,12 +1099,16 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1113
1099
|
rendererID,
|
|
1114
1100
|
root,
|
|
1115
1101
|
) => {
|
|
1116
|
-
prevOnPostCommitFiberRoot
|
|
1102
|
+
if (prevOnPostCommitFiberRoot) {
|
|
1103
|
+
prevOnPostCommitFiberRoot.call(rdtHook, rendererID, root);
|
|
1104
|
+
}
|
|
1117
1105
|
if (hookDispatchers.get(rdtHook)?.onPostCommitFiberRoot !== dispatchPostCommitFiberRoot) {
|
|
1118
1106
|
return;
|
|
1119
1107
|
}
|
|
1120
|
-
for (const
|
|
1121
|
-
|
|
1108
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
1109
|
+
if (options.onPostCommitFiberRoot) {
|
|
1110
|
+
options.onPostCommitFiberRoot(rendererID, root);
|
|
1111
|
+
}
|
|
1122
1112
|
}
|
|
1123
1113
|
};
|
|
1124
1114
|
dispatchers.onPostCommitFiberRoot = dispatchPostCommitFiberRoot;
|
|
@@ -1135,10 +1125,14 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1135
1125
|
root,
|
|
1136
1126
|
children,
|
|
1137
1127
|
) => {
|
|
1138
|
-
prevOnScheduleFiberRoot
|
|
1128
|
+
if (prevOnScheduleFiberRoot) {
|
|
1129
|
+
prevOnScheduleFiberRoot.call(rdtHook, rendererID, root, children);
|
|
1130
|
+
}
|
|
1139
1131
|
if (hookDispatchers.get(rdtHook)?.onScheduleFiberRoot !== dispatchScheduleFiberRoot) return;
|
|
1140
|
-
for (const
|
|
1141
|
-
|
|
1132
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
1133
|
+
if (options.onScheduleFiberRoot) {
|
|
1134
|
+
options.onScheduleFiberRoot(rendererID, root, children);
|
|
1135
|
+
}
|
|
1142
1136
|
}
|
|
1143
1137
|
};
|
|
1144
1138
|
dispatchers.onScheduleFiberRoot = dispatchScheduleFiberRoot;
|
|
@@ -1151,8 +1145,7 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1151
1145
|
* dispatches to a set of listeners, so multiple `instrument` calls compose
|
|
1152
1146
|
* without stacking patches. Returns an unsubscribe function that removes
|
|
1153
1147
|
* exactly the handlers this call registered.
|
|
1154
|
-
* The
|
|
1155
|
-
* bippy subscriptions through `using`.
|
|
1148
|
+
* The unsubscribe function can also be disposed through `using`.
|
|
1156
1149
|
* @example
|
|
1157
1150
|
* const unsubscribe = instrument({
|
|
1158
1151
|
* onActive() {
|
|
@@ -1167,34 +1160,24 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1167
1160
|
export const instrument = (options: InstrumentationOptions): Unsubscribe => {
|
|
1168
1161
|
const rdtHook = getRDTHook(options.onActive);
|
|
1169
1162
|
|
|
1163
|
+
if (!didSubscribeToHookReplacements) {
|
|
1164
|
+
onRDTHookReplace(setHookEventDispatchers);
|
|
1165
|
+
didSubscribeToHookReplacements = true;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1170
1168
|
rdtHook._instrumentationSource = options.name ?? BIPPY_INSTRUMENTATION_STRING;
|
|
1171
1169
|
|
|
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);
|
|
1170
|
+
setHookEventDispatchers(rdtHook);
|
|
1171
|
+
const subscription: InstrumentationSubscription = { options };
|
|
1172
|
+
instrumentationSubscriptions.add(subscription);
|
|
1173
|
+
|
|
1174
|
+
return createUnsubscribe(() => {
|
|
1175
|
+
if (options.onActive) _onActiveListeners.delete(options.onActive);
|
|
1176
|
+
instrumentationSubscriptions.delete(subscription);
|
|
1192
1177
|
});
|
|
1193
1178
|
};
|
|
1194
1179
|
|
|
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.
|
|
1180
|
+
// React uses per-renderer suffixes for host-instance Fiber keys, so discovered keys are cached.
|
|
1198
1181
|
const knownFiberPropertyKeys = new Set<string>();
|
|
1199
1182
|
|
|
1200
1183
|
const isFiberPropertyKey = (key: string): boolean =>
|
|
@@ -1202,6 +1185,37 @@ const isFiberPropertyKey = (key: string): boolean =>
|
|
|
1202
1185
|
key.startsWith("__reactInternalInstance$") ||
|
|
1203
1186
|
key.startsWith("__reactFiber");
|
|
1204
1187
|
|
|
1188
|
+
const getLegacyRootFiber = (hostInstance: object): Fiber | null => {
|
|
1189
|
+
const reactRootContainer = Reflect.get(hostInstance, "_reactRootContainer");
|
|
1190
|
+
if (typeof reactRootContainer !== "object" || reactRootContainer === null) return null;
|
|
1191
|
+
const internalRoot = Reflect.get(reactRootContainer, "_internalRoot");
|
|
1192
|
+
if (typeof internalRoot !== "object" || internalRoot === null) return null;
|
|
1193
|
+
const current = Reflect.get(internalRoot, "current");
|
|
1194
|
+
if (typeof current !== "object" || current === null) return null;
|
|
1195
|
+
const child = Reflect.get(current, "child");
|
|
1196
|
+
return isValidFiber(child) ? child : null;
|
|
1197
|
+
};
|
|
1198
|
+
|
|
1199
|
+
const getInternalInstanceHandle = (hostInstance: object): Fiber | null => {
|
|
1200
|
+
const internalInstanceHandle =
|
|
1201
|
+
Reflect.get(hostInstance, "__internalInstanceHandle") ??
|
|
1202
|
+
Reflect.get(hostInstance, "_internalInstanceHandle");
|
|
1203
|
+
return isValidFiber(internalInstanceHandle) ? internalInstanceHandle : null;
|
|
1204
|
+
};
|
|
1205
|
+
|
|
1206
|
+
const getPublicHostInstance = (stateNode: unknown): unknown => {
|
|
1207
|
+
if (typeof stateNode !== "object" || stateNode === null) return stateNode;
|
|
1208
|
+
const canonical = Reflect.get(stateNode, "canonical");
|
|
1209
|
+
if (typeof canonical === "object" && canonical !== null) {
|
|
1210
|
+
const publicInstance = Reflect.get(canonical, "publicInstance");
|
|
1211
|
+
if (typeof publicInstance === "object" && publicInstance !== null) {
|
|
1212
|
+
return publicInstance;
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
const nativeTag = Reflect.get(stateNode, "_nativeTag");
|
|
1216
|
+
return typeof nativeTag === "number" ? nativeTag : stateNode;
|
|
1217
|
+
};
|
|
1218
|
+
|
|
1205
1219
|
export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
|
|
1206
1220
|
const rdtHook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
1207
1221
|
if (rdtHook?.renderers) {
|
|
@@ -1213,42 +1227,38 @@ export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
|
|
|
1213
1227
|
}
|
|
1214
1228
|
}
|
|
1215
1229
|
|
|
1216
|
-
if (typeof hostInstance === "object" && hostInstance
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
return (hostInstance._reactRootContainer as any)?._internalRoot?.current?.child;
|
|
1220
|
-
}
|
|
1230
|
+
if (typeof hostInstance === "object" && hostInstance !== null) {
|
|
1231
|
+
const legacyRootFiber = getLegacyRootFiber(hostInstance);
|
|
1232
|
+
if (legacyRootFiber) return legacyRootFiber;
|
|
1221
1233
|
|
|
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
|
-
}
|
|
1234
|
+
const internalInstanceHandle = getInternalInstanceHandle(hostInstance);
|
|
1235
|
+
if (internalInstanceHandle) return internalInstanceHandle;
|
|
1233
1236
|
|
|
1234
|
-
const hostInstanceRecord = hostInstance as Record<string, unknown>;
|
|
1235
1237
|
for (const knownKey of knownFiberPropertyKeys) {
|
|
1236
|
-
const fiber =
|
|
1237
|
-
if (fiber) return fiber
|
|
1238
|
+
const fiber = Reflect.get(hostInstance, knownKey);
|
|
1239
|
+
if (isValidFiber(fiber)) return fiber;
|
|
1238
1240
|
}
|
|
1239
1241
|
|
|
1240
|
-
for (const key of Object.keys(
|
|
1242
|
+
for (const key of Object.keys(hostInstance)) {
|
|
1241
1243
|
if (isFiberPropertyKey(key)) {
|
|
1242
1244
|
knownFiberPropertyKeys.add(key);
|
|
1243
|
-
|
|
1245
|
+
const fiber = Reflect.get(hostInstance, key);
|
|
1246
|
+
if (isValidFiber(fiber)) return fiber;
|
|
1244
1247
|
}
|
|
1245
1248
|
}
|
|
1249
|
+
}
|
|
1246
1250
|
|
|
1251
|
+
if (
|
|
1252
|
+
hostInstance !== null &&
|
|
1253
|
+
hostInstance !== undefined &&
|
|
1254
|
+
(typeof hostInstance === "object" || typeof hostInstance === "number")
|
|
1255
|
+
) {
|
|
1247
1256
|
for (const fiberRoot of _fiberRoots) {
|
|
1248
|
-
if (getRootRenderer(fiberRoot.current)?.findFiberByHostInstance) continue;
|
|
1249
1257
|
const fiber = traverseFiber(
|
|
1250
1258
|
fiberRoot.current,
|
|
1251
|
-
(candidateFiber) =>
|
|
1259
|
+
(candidateFiber) =>
|
|
1260
|
+
isHostFiber(candidateFiber) &&
|
|
1261
|
+
getPublicHostInstance(candidateFiber.stateNode) === hostInstance,
|
|
1252
1262
|
);
|
|
1253
1263
|
if (fiber) return fiber;
|
|
1254
1264
|
}
|
|
@@ -1256,6 +1266,17 @@ export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
|
|
|
1256
1266
|
return null;
|
|
1257
1267
|
};
|
|
1258
1268
|
|
|
1259
|
-
export
|
|
1260
|
-
|
|
1261
|
-
|
|
1269
|
+
export {
|
|
1270
|
+
BIPPY_INSTRUMENTATION_STRING,
|
|
1271
|
+
_onActiveListeners,
|
|
1272
|
+
_renderers,
|
|
1273
|
+
getRDTHook,
|
|
1274
|
+
hasRDTHook,
|
|
1275
|
+
installRDTHook,
|
|
1276
|
+
isRealReactDevtools,
|
|
1277
|
+
onRendererInject,
|
|
1278
|
+
patchRDTHook,
|
|
1279
|
+
version,
|
|
1280
|
+
} from "./rdt-hook.js";
|
|
1281
|
+
export type { Unsubscribe } from "./rdt-hook.js";
|
|
1282
|
+
export type * from "./react-internals/index.js";
|