bippy 0.6.1-dev.61475ed → 0.6.1-dev.93556ef
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 +51 -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 +410 -0
- package/dist/errors.d.ts +410 -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 +86 -69
- package/dist/source.d.ts +86 -69
- package/dist/source.js +14 -5
- package/package.json +22 -16
- package/src/core.ts +348 -344
- 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 +186 -0
- package/src/source/constants.ts +1 -1
- package/src/source/error-stack.ts +11 -0
- package/src/source/get-display-name-from-source.ts +44 -40
- package/src/source/get-source.ts +43 -26
- package/src/source/index.ts +11 -1
- package/src/source/inspect-hooks.ts +220 -207
- package/src/source/owner-stack.ts +119 -174
- package/src/source/parse-debug-stack.ts +4 -4
- package/src/source/parse-hook-names.ts +14 -53
- package/src/source/parse-stack.ts +14 -31
- package/src/source/renderer-dispatchers.ts +30 -0
- package/src/source/symbolication.ts +709 -120
- package/dist/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,7 @@ export const getFiberStack = (fiber: Fiber): Fiber[] => {
|
|
|
297
306
|
let currentFiber = fiber;
|
|
298
307
|
while (currentFiber.return) {
|
|
299
308
|
stack.push(currentFiber);
|
|
300
|
-
currentFiber = currentFiber.return
|
|
309
|
+
currentFiber = currentFiber.return;
|
|
301
310
|
}
|
|
302
311
|
return stack;
|
|
303
312
|
};
|
|
@@ -306,8 +315,9 @@ export const getFiberStack = (fiber: Fiber): Fiber[] => {
|
|
|
306
315
|
* Returns `true` if the {@link Fiber} should be filtered out during reconciliation.
|
|
307
316
|
*/
|
|
308
317
|
const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
318
|
+
const workTags = getReactWorkTagsForFiber(fiber);
|
|
309
319
|
switch (fiber.tag) {
|
|
310
|
-
case
|
|
320
|
+
case workTags.DehydratedSuspenseComponent:
|
|
311
321
|
// TODO: ideally we would show dehydrated Suspense immediately.
|
|
312
322
|
// However, it has some special behavior (like disconnecting
|
|
313
323
|
// an alternate and turning into real Suspense) which breaks DevTools.
|
|
@@ -315,13 +325,13 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
|
315
325
|
// https://github.com/bvaughn/react-devtools-experimental/issues/197
|
|
316
326
|
return true;
|
|
317
327
|
|
|
318
|
-
case
|
|
319
|
-
case
|
|
320
|
-
case
|
|
321
|
-
case
|
|
328
|
+
case workTags.Fragment:
|
|
329
|
+
case workTags.HostText:
|
|
330
|
+
case workTags.LegacyHiddenComponent:
|
|
331
|
+
case workTags.OffscreenComponent:
|
|
322
332
|
return true;
|
|
323
333
|
|
|
324
|
-
case
|
|
334
|
+
case workTags.HostRoot:
|
|
325
335
|
// It is never valid to filter the root element.
|
|
326
336
|
return false;
|
|
327
337
|
|
|
@@ -331,15 +341,15 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
|
331
341
|
|
|
332
342
|
if (typeof symbolOrNumber === "symbol") {
|
|
333
343
|
return (
|
|
334
|
-
symbolOrNumber.description === CONCURRENT_MODE_SYMBOL_DESCRIPTION ||
|
|
335
|
-
symbolOrNumber.description === DEPRECATED_ASYNC_MODE_SYMBOL_DESCRIPTION
|
|
344
|
+
symbolOrNumber.description === ReactSymbols.CONCURRENT_MODE_SYMBOL_DESCRIPTION ||
|
|
345
|
+
symbolOrNumber.description === ReactSymbols.DEPRECATED_ASYNC_MODE_SYMBOL_DESCRIPTION
|
|
336
346
|
);
|
|
337
347
|
}
|
|
338
348
|
|
|
339
349
|
switch (symbolOrNumber) {
|
|
340
|
-
case CONCURRENT_MODE_NUMBER:
|
|
341
|
-
case CONCURRENT_MODE_SYMBOL_STRING:
|
|
342
|
-
case DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
|
|
350
|
+
case ReactSymbols.CONCURRENT_MODE_NUMBER:
|
|
351
|
+
case ReactSymbols.CONCURRENT_MODE_SYMBOL_STRING:
|
|
352
|
+
case ReactSymbols.DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
|
|
343
353
|
return true;
|
|
344
354
|
|
|
345
355
|
default:
|
|
@@ -405,75 +415,57 @@ export function traverseFiber(
|
|
|
405
415
|
): Promise<Fiber | null>;
|
|
406
416
|
export function traverseFiber(
|
|
407
417
|
fiber: Fiber | null,
|
|
408
|
-
selector:
|
|
418
|
+
selector: FiberSelector,
|
|
419
|
+
ascending?: boolean,
|
|
420
|
+
): Fiber | null | Promise<Fiber | null>;
|
|
421
|
+
export function traverseFiber(
|
|
422
|
+
fiber: Fiber | null,
|
|
423
|
+
selector: FiberSelector,
|
|
409
424
|
ascending = false,
|
|
410
425
|
): Fiber | null | Promise<Fiber | null> {
|
|
411
426
|
if (!fiber) return null;
|
|
412
427
|
|
|
413
|
-
const
|
|
414
|
-
if (
|
|
415
|
-
return (
|
|
416
|
-
|
|
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
|
-
})();
|
|
428
|
+
const selection = selector(fiber);
|
|
429
|
+
if (isPromiseLike<boolean | void>(selection)) {
|
|
430
|
+
return Promise.resolve(selection).then((didSelectFiber) =>
|
|
431
|
+
didSelectFiber === true ? fiber : traverseFiberChildren(fiber, selector, ascending),
|
|
432
|
+
);
|
|
430
433
|
}
|
|
434
|
+
if (selection === true) return fiber;
|
|
431
435
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
let child = ascending ? fiber.return : fiber.child;
|
|
435
|
-
while (child) {
|
|
436
|
-
const match = traverseFiberSync(child, selector as (node: Fiber) => boolean | void, ascending);
|
|
437
|
-
if (match) return match;
|
|
438
|
-
child = ascending ? null : child.sibling;
|
|
439
|
-
}
|
|
440
|
-
return null;
|
|
436
|
+
return traverseFiberChildren(fiber, selector, ascending);
|
|
441
437
|
}
|
|
442
438
|
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
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;
|
|
439
|
+
const isPromiseLike = <Result>(value: unknown): value is PromiseLike<Result> =>
|
|
440
|
+
(typeof value === "object" || typeof value === "function") &&
|
|
441
|
+
value !== null &&
|
|
442
|
+
"then" in value &&
|
|
443
|
+
typeof value.then === "function";
|
|
455
444
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
445
|
+
const traverseFiberChildren = (
|
|
446
|
+
fiber: Fiber,
|
|
447
|
+
selector: FiberSelector,
|
|
448
|
+
ascending: boolean,
|
|
449
|
+
): Fiber | null | Promise<Fiber | null> => {
|
|
450
|
+
const firstChild = ascending ? fiber.return : fiber.child;
|
|
451
|
+
return traverseFiberSiblings(firstChild, selector, ascending);
|
|
459
452
|
};
|
|
460
453
|
|
|
461
|
-
const
|
|
454
|
+
const traverseFiberSiblings = (
|
|
462
455
|
fiber: Fiber | null,
|
|
463
|
-
selector:
|
|
464
|
-
ascending
|
|
465
|
-
): Promise<Fiber | null> => {
|
|
456
|
+
selector: FiberSelector,
|
|
457
|
+
ascending: boolean,
|
|
458
|
+
): Fiber | null | Promise<Fiber | null> => {
|
|
466
459
|
if (!fiber) return null;
|
|
467
|
-
if ((await selector(fiber)) === true) return fiber;
|
|
468
460
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
461
|
+
const nextSibling = ascending ? null : fiber.sibling;
|
|
462
|
+
const match = traverseFiber(fiber, selector, ascending);
|
|
463
|
+
if (isPromiseLike<Fiber | null>(match)) {
|
|
464
|
+
return Promise.resolve(match).then(
|
|
465
|
+
(resolvedMatch) => resolvedMatch ?? traverseFiberSiblings(nextSibling, selector, ascending),
|
|
466
|
+
);
|
|
475
467
|
}
|
|
476
|
-
return
|
|
468
|
+
return match ?? traverseFiberSiblings(nextSibling, selector, ascending);
|
|
477
469
|
};
|
|
478
470
|
|
|
479
471
|
/**
|
|
@@ -485,12 +477,12 @@ const traverseFiberAsync = async (
|
|
|
485
477
|
* console.log(selfTime, totalTime);
|
|
486
478
|
* ```
|
|
487
479
|
*/
|
|
488
|
-
export const getTimings = (fiber?: Fiber | null):
|
|
480
|
+
export const getTimings = (fiber?: Fiber | null): FiberTimings => {
|
|
489
481
|
const totalTime = fiber?.actualDuration ?? 0;
|
|
490
482
|
let selfTime = totalTime;
|
|
491
483
|
// TODO: calculate a DOM time, which is just host component summed up
|
|
492
484
|
let child = fiber?.child ?? null;
|
|
493
|
-
while (totalTime > 0 && child
|
|
485
|
+
while (totalTime > 0 && child !== null) {
|
|
494
486
|
selfTime -= child.actualDuration ?? 0;
|
|
495
487
|
child = child.sibling;
|
|
496
488
|
}
|
|
@@ -501,48 +493,29 @@ export const getTimings = (fiber?: Fiber | null): { selfTime: number; totalTime:
|
|
|
501
493
|
* Returns `true` if the {@link Fiber} uses React Compiler's memo cache.
|
|
502
494
|
*/
|
|
503
495
|
export const hasMemoCache = (fiber: Fiber): boolean => {
|
|
504
|
-
return Boolean(
|
|
496
|
+
return Boolean(fiber.updateQueue?.memoCache);
|
|
505
497
|
};
|
|
506
498
|
|
|
507
|
-
type FiberType =
|
|
508
|
-
| React.ComponentType<unknown>
|
|
509
|
-
| React.ForwardRefExoticComponent<unknown>
|
|
510
|
-
| React.MemoExoticComponent<React.ComponentType<unknown>>;
|
|
511
|
-
|
|
512
499
|
/**
|
|
513
500
|
* Returns the type (e.g. component definition) of the {@link Fiber}
|
|
514
501
|
*/
|
|
515
502
|
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;
|
|
503
|
+
if (isComponentType(type)) return type;
|
|
504
|
+
if (typeof type !== "object" || type === null) return null;
|
|
505
|
+
return getType(Reflect.get(type, "type") ?? Reflect.get(type, "render"));
|
|
528
506
|
};
|
|
529
507
|
|
|
530
508
|
/**
|
|
531
509
|
* Returns the display name of the {@link Fiber} type.
|
|
532
510
|
*/
|
|
533
511
|
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;
|
|
512
|
+
if (typeof type === "string") return type;
|
|
513
|
+
if ((typeof type !== "function" && typeof type !== "object") || type === null) return null;
|
|
514
|
+
const name = getTypeName(type);
|
|
542
515
|
if (name) return name;
|
|
543
|
-
const unwrappedType = getType(
|
|
516
|
+
const unwrappedType = getType(type);
|
|
544
517
|
if (!unwrappedType) return null;
|
|
545
|
-
return unwrappedType
|
|
518
|
+
return getTypeName(unwrappedType);
|
|
546
519
|
};
|
|
547
520
|
|
|
548
521
|
/**
|
|
@@ -550,7 +523,10 @@ export const getDisplayName = (type: unknown): null | string => {
|
|
|
550
523
|
*/
|
|
551
524
|
export const detectReactBuildType = (renderer: ReactRenderer): "development" | "production" => {
|
|
552
525
|
try {
|
|
553
|
-
if (
|
|
526
|
+
if (
|
|
527
|
+
typeof renderer.version === "string" &&
|
|
528
|
+
renderer.bundleType === ReactBuildType.Development
|
|
529
|
+
) {
|
|
554
530
|
return "development";
|
|
555
531
|
}
|
|
556
532
|
} catch {}
|
|
@@ -562,11 +538,7 @@ export const detectReactBuildType = (renderer: ReactRenderer): "development" | "
|
|
|
562
538
|
*/
|
|
563
539
|
export const isInstrumentationActive = (): boolean => {
|
|
564
540
|
const rdtHook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
565
|
-
return (
|
|
566
|
-
Boolean(rdtHook?._instrumentationIsActive) ||
|
|
567
|
-
isRealReactDevtools(rdtHook) ||
|
|
568
|
-
isReactRefresh(rdtHook)
|
|
569
|
-
);
|
|
541
|
+
return Boolean(rdtHook?._instrumentationIsActive) || isRealReactDevtools(rdtHook);
|
|
570
542
|
};
|
|
571
543
|
|
|
572
544
|
export const _fiberRoots = new Set<FiberRoot>();
|
|
@@ -582,37 +554,35 @@ export const getLatestFiber = (fiber: Fiber): Fiber => {
|
|
|
582
554
|
}
|
|
583
555
|
for (const root of _fiberRoots) {
|
|
584
556
|
const latestFiber = traverseFiber(root.current, (innerFiber) => {
|
|
585
|
-
if (innerFiber === fiber) return true;
|
|
557
|
+
if (innerFiber === fiber || innerFiber === alternate) return true;
|
|
586
558
|
});
|
|
587
559
|
if (latestFiber) return latestFiber;
|
|
588
560
|
}
|
|
589
561
|
return fiber;
|
|
590
562
|
};
|
|
591
563
|
|
|
592
|
-
export type RenderHandler = <S>(fiber: Fiber, phase: RenderPhase, state?: S) => unknown;
|
|
593
|
-
|
|
594
564
|
export type RenderPhase = "mount" | "unmount" | "update";
|
|
595
565
|
|
|
596
|
-
let
|
|
566
|
+
let nextFiberId = 0;
|
|
597
567
|
const fiberIdMap = new WeakMap<Fiber, number>();
|
|
598
568
|
|
|
599
|
-
export const setFiberId = (fiber: Fiber,
|
|
600
|
-
fiberIdMap.set(fiber,
|
|
569
|
+
export const setFiberId = (fiber: Fiber, fiberId: number = nextFiberId++): void => {
|
|
570
|
+
fiberIdMap.set(fiber, fiberId);
|
|
571
|
+
if (Number.isSafeInteger(fiberId) && fiberId >= nextFiberId) {
|
|
572
|
+
nextFiberId = fiberId + 1;
|
|
573
|
+
}
|
|
601
574
|
};
|
|
602
575
|
|
|
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
576
|
export const getFiberId = (fiber: Fiber): number => {
|
|
607
|
-
let
|
|
608
|
-
if (
|
|
609
|
-
|
|
577
|
+
let currentFiberId = fiberIdMap.get(fiber);
|
|
578
|
+
if (currentFiberId === undefined && fiber.alternate) {
|
|
579
|
+
currentFiberId = fiberIdMap.get(fiber.alternate);
|
|
610
580
|
}
|
|
611
|
-
if (
|
|
612
|
-
|
|
613
|
-
setFiberId(fiber,
|
|
581
|
+
if (currentFiberId === undefined) {
|
|
582
|
+
currentFiberId = nextFiberId++;
|
|
583
|
+
setFiberId(fiber, currentFiberId);
|
|
614
584
|
}
|
|
615
|
-
return
|
|
585
|
+
return currentFiberId;
|
|
616
586
|
};
|
|
617
587
|
|
|
618
588
|
const mountFiberRecursively = (
|
|
@@ -622,16 +592,14 @@ const mountFiberRecursively = (
|
|
|
622
592
|
): void => {
|
|
623
593
|
let fiber: Fiber | null = firstChild;
|
|
624
594
|
|
|
625
|
-
while (fiber
|
|
626
|
-
|
|
627
|
-
getFiberId(fiber);
|
|
628
|
-
}
|
|
595
|
+
while (fiber !== null) {
|
|
596
|
+
getFiberId(fiber);
|
|
629
597
|
const shouldIncludeInTree = !shouldFilterFiber(fiber);
|
|
630
598
|
if (shouldIncludeInTree && didFiberRender(fiber)) {
|
|
631
599
|
onRender(fiber, "mount");
|
|
632
600
|
}
|
|
633
601
|
|
|
634
|
-
if (fiber.tag ===
|
|
602
|
+
if (fiber.tag === getReactWorkTagsForFiber(fiber).SuspenseComponent) {
|
|
635
603
|
const isTimedOut = fiber.memoizedState !== null;
|
|
636
604
|
if (isTimedOut) {
|
|
637
605
|
// Special case: if Suspense mounts in a timed-out state,
|
|
@@ -646,18 +614,12 @@ const mountFiberRecursively = (
|
|
|
646
614
|
}
|
|
647
615
|
}
|
|
648
616
|
} 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
|
-
}
|
|
617
|
+
const primaryChild = fiber.child?.child ?? null;
|
|
656
618
|
if (primaryChild !== null) {
|
|
657
619
|
mountFiberRecursively(onRender, primaryChild, false);
|
|
658
620
|
}
|
|
659
621
|
}
|
|
660
|
-
} else if (fiber.child
|
|
622
|
+
} else if (fiber.child !== null) {
|
|
661
623
|
mountFiberRecursively(onRender, fiber.child, true);
|
|
662
624
|
}
|
|
663
625
|
fiber = traverseSiblings ? fiber.sibling : null;
|
|
@@ -667,18 +629,13 @@ const mountFiberRecursively = (
|
|
|
667
629
|
const updateFiberRecursively = (
|
|
668
630
|
onRender: RenderHandler,
|
|
669
631
|
nextFiber: Fiber,
|
|
670
|
-
prevFiber: Fiber,
|
|
671
|
-
parentFiber: Fiber | null,
|
|
632
|
+
prevFiber: Fiber | null,
|
|
672
633
|
): void => {
|
|
673
|
-
|
|
674
|
-
getFiberId(nextFiber);
|
|
675
|
-
}
|
|
634
|
+
getFiberId(nextFiber);
|
|
676
635
|
if (!prevFiber) return;
|
|
677
|
-
|
|
678
|
-
getFiberId(prevFiber);
|
|
679
|
-
}
|
|
636
|
+
getFiberId(prevFiber);
|
|
680
637
|
|
|
681
|
-
const isSuspense = nextFiber.tag ===
|
|
638
|
+
const isSuspense = nextFiber.tag === getReactWorkTagsForFiber(nextFiber).SuspenseComponent;
|
|
682
639
|
|
|
683
640
|
const shouldIncludeInTree = !shouldFilterFiber(nextFiber);
|
|
684
641
|
if (shouldIncludeInTree && didFiberRender(nextFiber)) {
|
|
@@ -707,7 +664,7 @@ const updateFiberRecursively = (
|
|
|
707
664
|
const prevFallbackChildSet = prevFiber.child?.sibling ?? null;
|
|
708
665
|
|
|
709
666
|
if (nextFallbackChildSet !== null && prevFallbackChildSet !== null) {
|
|
710
|
-
updateFiberRecursively(onRender, nextFallbackChildSet, prevFallbackChildSet
|
|
667
|
+
updateFiberRecursively(onRender, nextFallbackChildSet, prevFallbackChildSet);
|
|
711
668
|
}
|
|
712
669
|
} else if (prevDidTimeout && !nextDidTimeOut) {
|
|
713
670
|
// Fallback -> Primary:
|
|
@@ -746,14 +703,7 @@ const updateFiberRecursively = (
|
|
|
746
703
|
// Schedule updates and mounts depending on whether alternates exist.
|
|
747
704
|
// We don't track deletions here because they are reported separately.
|
|
748
705
|
if (nextChild.alternate) {
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
updateFiberRecursively(
|
|
752
|
-
onRender,
|
|
753
|
-
nextChild,
|
|
754
|
-
prevChild,
|
|
755
|
-
shouldIncludeInTree ? nextFiber : parentFiber,
|
|
756
|
-
);
|
|
706
|
+
updateFiberRecursively(onRender, nextChild, nextChild.alternate);
|
|
757
707
|
} else {
|
|
758
708
|
mountFiberRecursively(onRender, nextChild, false);
|
|
759
709
|
}
|
|
@@ -765,7 +715,7 @@ const updateFiberRecursively = (
|
|
|
765
715
|
};
|
|
766
716
|
|
|
767
717
|
const unmountFiber = (onRender: RenderHandler, fiber: Fiber): void => {
|
|
768
|
-
const isRoot = fiber.tag ===
|
|
718
|
+
const isRoot = fiber.tag === getReactWorkTagsForFiber(fiber).HostRoot;
|
|
769
719
|
|
|
770
720
|
if (isRoot || !shouldFilterFiber(fiber)) {
|
|
771
721
|
onRender(fiber, "unmount");
|
|
@@ -774,7 +724,8 @@ const unmountFiber = (onRender: RenderHandler, fiber: Fiber): void => {
|
|
|
774
724
|
|
|
775
725
|
const unmountFiberChildrenRecursively = (onRender: RenderHandler, fiber: Fiber): void => {
|
|
776
726
|
// We might meet a nested Suspense on our way.
|
|
777
|
-
const isTimedOutSuspense =
|
|
727
|
+
const isTimedOutSuspense =
|
|
728
|
+
fiber.tag === getReactWorkTagsForFiber(fiber).SuspenseComponent && fiber.memoizedState !== null;
|
|
778
729
|
let child = fiber.child;
|
|
779
730
|
|
|
780
731
|
if (isTimedOutSuspense) {
|
|
@@ -798,11 +749,9 @@ const unmountFiberChildrenRecursively = (onRender: RenderHandler, fiber: Fiber):
|
|
|
798
749
|
}
|
|
799
750
|
};
|
|
800
751
|
|
|
801
|
-
let commitId = 0;
|
|
802
752
|
const rootInstanceMap = new WeakMap<
|
|
803
|
-
FiberRoot,
|
|
753
|
+
Fiber | FiberRoot,
|
|
804
754
|
{
|
|
805
|
-
id: number;
|
|
806
755
|
prevFiber: Fiber | null;
|
|
807
756
|
}
|
|
808
757
|
>();
|
|
@@ -814,37 +763,39 @@ const rootInstanceMap = new WeakMap<
|
|
|
814
763
|
* console.log(phase)
|
|
815
764
|
* })
|
|
816
765
|
*/
|
|
817
|
-
export const traverseRenderedFibers = (root: FiberRoot, onRender: RenderHandler): void => {
|
|
766
|
+
export const traverseRenderedFibers = (root: Fiber | FiberRoot, onRender: RenderHandler): void => {
|
|
818
767
|
const fiber = "current" in root ? root.current : root;
|
|
819
768
|
|
|
820
769
|
let rootInstance = rootInstanceMap.get(root);
|
|
821
770
|
|
|
822
771
|
if (!rootInstance) {
|
|
823
|
-
rootInstance = {
|
|
772
|
+
rootInstance = { prevFiber: null };
|
|
824
773
|
rootInstanceMap.set(root, rootInstance);
|
|
825
774
|
}
|
|
826
775
|
|
|
827
776
|
const { prevFiber } = rootInstance;
|
|
828
|
-
// if fiberRoot don't have current instance, means it's been unmounted
|
|
829
777
|
if (!fiber) {
|
|
830
|
-
|
|
778
|
+
if (prevFiber) {
|
|
779
|
+
unmountFiber(onRender, prevFiber);
|
|
780
|
+
}
|
|
831
781
|
} else if (prevFiber !== null) {
|
|
832
782
|
const wasMounted =
|
|
833
|
-
prevFiber &&
|
|
834
|
-
prevFiber.memoizedState
|
|
835
|
-
prevFiber.memoizedState.element
|
|
783
|
+
prevFiber.memoizedState !== null &&
|
|
784
|
+
prevFiber.memoizedState.element !== null &&
|
|
785
|
+
prevFiber.memoizedState.element !== undefined &&
|
|
836
786
|
// A dehydrated root is not considered mounted
|
|
837
787
|
prevFiber.memoizedState.isDehydrated !== true;
|
|
838
788
|
const isMounted =
|
|
839
|
-
fiber.memoizedState
|
|
840
|
-
fiber.memoizedState.element
|
|
789
|
+
fiber.memoizedState !== null &&
|
|
790
|
+
fiber.memoizedState.element !== null &&
|
|
791
|
+
fiber.memoizedState.element !== undefined &&
|
|
841
792
|
// A dehydrated root is not considered mounted
|
|
842
793
|
fiber.memoizedState.isDehydrated !== true;
|
|
843
794
|
|
|
844
795
|
if (!wasMounted && isMounted) {
|
|
845
796
|
mountFiberRecursively(onRender, fiber, false);
|
|
846
797
|
} else if (wasMounted && isMounted) {
|
|
847
|
-
updateFiberRecursively(onRender, fiber, fiber.alternate
|
|
798
|
+
updateFiberRecursively(onRender, fiber, fiber.alternate);
|
|
848
799
|
} else if (wasMounted && !isMounted) {
|
|
849
800
|
unmountFiber(onRender, fiber);
|
|
850
801
|
}
|
|
@@ -859,16 +810,15 @@ const overrideRenderers = new Set<ReactRenderer>();
|
|
|
859
810
|
let areOverrideRenderersWired = false;
|
|
860
811
|
|
|
861
812
|
const wireOverrideRenderers = (): void => {
|
|
862
|
-
if (areOverrideRenderersWired) return;
|
|
863
813
|
if (!hasRDTHook()) return;
|
|
864
814
|
const rdtHook = getRDTHook();
|
|
865
|
-
if (!rdtHook?.renderers) return;
|
|
866
|
-
areOverrideRenderersWired = true;
|
|
867
815
|
|
|
868
|
-
|
|
816
|
+
setHookEventDispatchers(rdtHook);
|
|
869
817
|
for (const renderer of rdtHook.renderers.values()) {
|
|
870
818
|
overrideRenderers.add(renderer);
|
|
871
819
|
}
|
|
820
|
+
if (areOverrideRenderersWired) return;
|
|
821
|
+
areOverrideRenderersWired = true;
|
|
872
822
|
onRendererInject((renderer) => {
|
|
873
823
|
overrideRenderers.add(renderer);
|
|
874
824
|
});
|
|
@@ -880,14 +830,13 @@ const getRootRenderer = (fiber: Fiber): ReactRenderer | null => {
|
|
|
880
830
|
while (hostRootFiber.return) {
|
|
881
831
|
hostRootFiber = hostRootFiber.return;
|
|
882
832
|
}
|
|
883
|
-
const
|
|
833
|
+
const fiberRoot = hostRootFiber.stateNode;
|
|
834
|
+
if (!isFiberRoot(fiberRoot)) return null;
|
|
835
|
+
const rendererId = rootRendererIds.get(fiberRoot);
|
|
884
836
|
if (rendererId === undefined) return null;
|
|
885
|
-
return getRDTHook().renderers
|
|
837
|
+
return getRDTHook().renderers.get(rendererId) ?? null;
|
|
886
838
|
};
|
|
887
839
|
|
|
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
840
|
const resolveOverrideRenderers = (fiber: Fiber): ReactRenderer[] => {
|
|
892
841
|
wireOverrideRenderers();
|
|
893
842
|
const rootRenderer = getRootRenderer(fiber);
|
|
@@ -913,15 +862,14 @@ const getHookStateDispatch = (
|
|
|
913
862
|
hookIndex: number,
|
|
914
863
|
): ((value: unknown) => void) | null => {
|
|
915
864
|
let hookState = fiber.memoizedState;
|
|
916
|
-
for (let
|
|
865
|
+
for (let currentHookIndex = 0; currentHookIndex < hookIndex; currentHookIndex++) {
|
|
917
866
|
if (!hookState?.next) return null;
|
|
918
867
|
hookState = hookState.next;
|
|
919
868
|
}
|
|
920
869
|
const queue = hookState?.queue;
|
|
921
|
-
if (isPOJO(queue)
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
return null;
|
|
870
|
+
if (!isPOJO(queue)) return null;
|
|
871
|
+
const dispatch = queue.dispatch;
|
|
872
|
+
return typeof dispatch === "function" ? (value) => dispatch(value) : null;
|
|
925
873
|
};
|
|
926
874
|
|
|
927
875
|
const findContextProviderFiber = (fiber: Fiber, contextType: unknown): Fiber | null => {
|
|
@@ -947,28 +895,30 @@ const isPOJO = (maybePOJO: unknown): maybePOJO is Record<string, unknown> => {
|
|
|
947
895
|
const buildPathsFromValue = (
|
|
948
896
|
maybePOJO: Record<string, unknown>,
|
|
949
897
|
basePath: string[] = [],
|
|
950
|
-
|
|
951
|
-
|
|
898
|
+
ancestors = new WeakSet<object>(),
|
|
899
|
+
): ValueWrite[] => {
|
|
900
|
+
if (ancestors.has(maybePOJO)) {
|
|
952
901
|
return [{ path: basePath, value: maybePOJO }];
|
|
953
902
|
}
|
|
954
903
|
|
|
955
|
-
|
|
904
|
+
ancestors.add(maybePOJO);
|
|
905
|
+
const paths: ValueWrite[] = [];
|
|
956
906
|
|
|
957
|
-
for (const key
|
|
958
|
-
const value = maybePOJO[key];
|
|
907
|
+
for (const [key, value] of Object.entries(maybePOJO)) {
|
|
959
908
|
const path = basePath.concat(key);
|
|
960
909
|
|
|
961
910
|
if (isPOJO(value)) {
|
|
962
|
-
paths.push(...buildPathsFromValue(value, path));
|
|
911
|
+
paths.push(...buildPathsFromValue(value, path, ancestors));
|
|
963
912
|
} else {
|
|
964
913
|
paths.push({ path, value });
|
|
965
914
|
}
|
|
966
915
|
}
|
|
967
916
|
|
|
917
|
+
ancestors.delete(maybePOJO);
|
|
968
918
|
return paths;
|
|
969
919
|
};
|
|
970
920
|
|
|
971
|
-
const buildValueWrites = (partialValue: unknown):
|
|
921
|
+
const buildValueWrites = (partialValue: unknown): ValueWrite[] =>
|
|
972
922
|
isPOJO(partialValue) ? buildPathsFromValue(partialValue) : [{ path: [], value: partialValue }];
|
|
973
923
|
|
|
974
924
|
export const overrideProps = (fiber: Fiber, partialValue: Record<string, unknown>) => {
|
|
@@ -982,14 +932,13 @@ export const overrideHookState = (fiber: Fiber, id: number, partialValue: unknow
|
|
|
982
932
|
const renderers = resolveOverrideRenderers(fiber).filter((renderer) =>
|
|
983
933
|
Boolean(renderer.overrideHookState),
|
|
984
934
|
);
|
|
985
|
-
const writes = buildValueWrites(partialValue);
|
|
986
935
|
|
|
987
936
|
if (renderers.length > 0) {
|
|
988
|
-
const
|
|
937
|
+
const writes = buildValueWrites(partialValue);
|
|
989
938
|
for (const renderer of renderers) {
|
|
990
939
|
for (const { path, value } of writes) {
|
|
991
940
|
try {
|
|
992
|
-
renderer.overrideHookState?.(fiber,
|
|
941
|
+
renderer.overrideHookState?.(fiber, id, path, value);
|
|
993
942
|
} catch {}
|
|
994
943
|
}
|
|
995
944
|
}
|
|
@@ -1022,24 +971,22 @@ export const overrideContext = (fiber: Fiber, contextType: unknown, partialValue
|
|
|
1022
971
|
export interface InstrumentationOptions {
|
|
1023
972
|
name?: string;
|
|
1024
973
|
onActive?: () => unknown;
|
|
1025
|
-
onCommitFiberRoot?: (
|
|
974
|
+
onCommitFiberRoot?: (
|
|
975
|
+
rendererID: number,
|
|
976
|
+
root: FiberRoot,
|
|
977
|
+
priority: number | void,
|
|
978
|
+
didError?: boolean,
|
|
979
|
+
) => unknown;
|
|
1026
980
|
onCommitFiberUnmount?: (rendererID: number, fiber: Fiber) => unknown;
|
|
1027
981
|
onPostCommitFiberRoot?: (rendererID: number, root: FiberRoot) => unknown;
|
|
1028
982
|
onScheduleFiberRoot?: (rendererID: number, root: FiberRoot, children: React.ReactNode) => unknown;
|
|
1029
983
|
}
|
|
1030
984
|
|
|
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
|
-
>();
|
|
985
|
+
interface InstrumentationSubscription {
|
|
986
|
+
options: InstrumentationOptions;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
const instrumentationSubscriptions = new Set<InstrumentationSubscription>();
|
|
1043
990
|
|
|
1044
991
|
interface HookDispatchers {
|
|
1045
992
|
onCommitFiberRoot: ReactDevToolsGlobalHook["onCommitFiberRoot"];
|
|
@@ -1049,6 +996,7 @@ interface HookDispatchers {
|
|
|
1049
996
|
}
|
|
1050
997
|
|
|
1051
998
|
const hookDispatchers = new WeakMap<ReactDevToolsGlobalHook, Partial<HookDispatchers>>();
|
|
999
|
+
let didSubscribeToHookReplacements = false;
|
|
1052
1000
|
|
|
1053
1001
|
const rootRendererIds = new WeakMap<FiberRoot, number>();
|
|
1054
1002
|
|
|
@@ -1057,7 +1005,7 @@ const rootRendererIds = new WeakMap<FiberRoot, number>();
|
|
|
1057
1005
|
// next instrument() call installs a fresh wrapper over it; a superseded
|
|
1058
1006
|
// wrapper still forwards the previous chain but skips the listeners so
|
|
1059
1007
|
// they never fire twice.
|
|
1060
|
-
const
|
|
1008
|
+
const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
1061
1009
|
const dispatchers = hookDispatchers.get(rdtHook) ?? {};
|
|
1062
1010
|
hookDispatchers.set(rdtHook, dispatchers);
|
|
1063
1011
|
|
|
@@ -1070,13 +1018,29 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1070
1018
|
rendererID,
|
|
1071
1019
|
root,
|
|
1072
1020
|
priority,
|
|
1021
|
+
didError,
|
|
1073
1022
|
) => {
|
|
1074
|
-
prevOnCommitFiberRoot
|
|
1023
|
+
if (prevOnCommitFiberRoot) {
|
|
1024
|
+
prevOnCommitFiberRoot.call(rdtHook, rendererID, root, priority, didError);
|
|
1025
|
+
}
|
|
1075
1026
|
if (hookDispatchers.get(rdtHook)?.onCommitFiberRoot !== dispatchCommitFiberRoot) return;
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1027
|
+
setReactWorkTagsForFiber(root.current, rdtHook.renderers.get(rendererID));
|
|
1028
|
+
const rootMemoizedState = root.current.memoizedState;
|
|
1029
|
+
const isUnmounting =
|
|
1030
|
+
rootMemoizedState === null ||
|
|
1031
|
+
rootMemoizedState.element === null ||
|
|
1032
|
+
rootMemoizedState.element === undefined;
|
|
1033
|
+
if (isUnmounting) {
|
|
1034
|
+
_fiberRoots.delete(root);
|
|
1035
|
+
rootRendererIds.delete(root);
|
|
1036
|
+
} else {
|
|
1037
|
+
_fiberRoots.add(root);
|
|
1038
|
+
rootRendererIds.set(root, rendererID);
|
|
1039
|
+
}
|
|
1040
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
1041
|
+
if (options.onCommitFiberRoot) {
|
|
1042
|
+
options.onCommitFiberRoot(rendererID, root, priority, didError);
|
|
1043
|
+
}
|
|
1080
1044
|
}
|
|
1081
1045
|
};
|
|
1082
1046
|
dispatchers.onCommitFiberRoot = dispatchCommitFiberRoot;
|
|
@@ -1092,12 +1056,17 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1092
1056
|
rendererID,
|
|
1093
1057
|
fiber,
|
|
1094
1058
|
) => {
|
|
1095
|
-
|
|
1059
|
+
setReactWorkTagsForFiber(fiber, rdtHook.renderers.get(rendererID));
|
|
1060
|
+
if (prevOnCommitFiberUnmount) {
|
|
1061
|
+
prevOnCommitFiberUnmount.call(rdtHook, rendererID, fiber);
|
|
1062
|
+
}
|
|
1096
1063
|
if (hookDispatchers.get(rdtHook)?.onCommitFiberUnmount !== dispatchCommitFiberUnmount) {
|
|
1097
1064
|
return;
|
|
1098
1065
|
}
|
|
1099
|
-
for (const
|
|
1100
|
-
|
|
1066
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
1067
|
+
if (options.onCommitFiberUnmount) {
|
|
1068
|
+
options.onCommitFiberUnmount(rendererID, fiber);
|
|
1069
|
+
}
|
|
1101
1070
|
}
|
|
1102
1071
|
};
|
|
1103
1072
|
dispatchers.onCommitFiberUnmount = dispatchCommitFiberUnmount;
|
|
@@ -1113,12 +1082,16 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1113
1082
|
rendererID,
|
|
1114
1083
|
root,
|
|
1115
1084
|
) => {
|
|
1116
|
-
prevOnPostCommitFiberRoot
|
|
1085
|
+
if (prevOnPostCommitFiberRoot) {
|
|
1086
|
+
prevOnPostCommitFiberRoot.call(rdtHook, rendererID, root);
|
|
1087
|
+
}
|
|
1117
1088
|
if (hookDispatchers.get(rdtHook)?.onPostCommitFiberRoot !== dispatchPostCommitFiberRoot) {
|
|
1118
1089
|
return;
|
|
1119
1090
|
}
|
|
1120
|
-
for (const
|
|
1121
|
-
|
|
1091
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
1092
|
+
if (options.onPostCommitFiberRoot) {
|
|
1093
|
+
options.onPostCommitFiberRoot(rendererID, root);
|
|
1094
|
+
}
|
|
1122
1095
|
}
|
|
1123
1096
|
};
|
|
1124
1097
|
dispatchers.onPostCommitFiberRoot = dispatchPostCommitFiberRoot;
|
|
@@ -1135,10 +1108,14 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1135
1108
|
root,
|
|
1136
1109
|
children,
|
|
1137
1110
|
) => {
|
|
1138
|
-
prevOnScheduleFiberRoot
|
|
1111
|
+
if (prevOnScheduleFiberRoot) {
|
|
1112
|
+
prevOnScheduleFiberRoot.call(rdtHook, rendererID, root, children);
|
|
1113
|
+
}
|
|
1139
1114
|
if (hookDispatchers.get(rdtHook)?.onScheduleFiberRoot !== dispatchScheduleFiberRoot) return;
|
|
1140
|
-
for (const
|
|
1141
|
-
|
|
1115
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
1116
|
+
if (options.onScheduleFiberRoot) {
|
|
1117
|
+
options.onScheduleFiberRoot(rendererID, root, children);
|
|
1118
|
+
}
|
|
1142
1119
|
}
|
|
1143
1120
|
};
|
|
1144
1121
|
dispatchers.onScheduleFiberRoot = dispatchScheduleFiberRoot;
|
|
@@ -1151,8 +1128,7 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1151
1128
|
* dispatches to a set of listeners, so multiple `instrument` calls compose
|
|
1152
1129
|
* without stacking patches. Returns an unsubscribe function that removes
|
|
1153
1130
|
* exactly the handlers this call registered.
|
|
1154
|
-
* The
|
|
1155
|
-
* bippy subscriptions through `using`.
|
|
1131
|
+
* The unsubscribe function can also be disposed through `using`.
|
|
1156
1132
|
* @example
|
|
1157
1133
|
* const unsubscribe = instrument({
|
|
1158
1134
|
* onActive() {
|
|
@@ -1167,34 +1143,24 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1167
1143
|
export const instrument = (options: InstrumentationOptions): Unsubscribe => {
|
|
1168
1144
|
const rdtHook = getRDTHook(options.onActive);
|
|
1169
1145
|
|
|
1146
|
+
if (!didSubscribeToHookReplacements) {
|
|
1147
|
+
onRDTHookReplace(setHookEventDispatchers);
|
|
1148
|
+
didSubscribeToHookReplacements = true;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1170
1151
|
rdtHook._instrumentationSource = options.name ?? BIPPY_INSTRUMENTATION_STRING;
|
|
1171
1152
|
|
|
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);
|
|
1153
|
+
setHookEventDispatchers(rdtHook);
|
|
1154
|
+
const subscription: InstrumentationSubscription = { options };
|
|
1155
|
+
instrumentationSubscriptions.add(subscription);
|
|
1156
|
+
|
|
1157
|
+
return createUnsubscribe(() => {
|
|
1158
|
+
if (options.onActive) _onActiveListeners.delete(options.onActive);
|
|
1159
|
+
instrumentationSubscriptions.delete(subscription);
|
|
1192
1160
|
});
|
|
1193
1161
|
};
|
|
1194
1162
|
|
|
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.
|
|
1163
|
+
// React uses per-renderer suffixes for host-instance Fiber keys, so discovered keys are cached.
|
|
1198
1164
|
const knownFiberPropertyKeys = new Set<string>();
|
|
1199
1165
|
|
|
1200
1166
|
const isFiberPropertyKey = (key: string): boolean =>
|
|
@@ -1202,6 +1168,37 @@ const isFiberPropertyKey = (key: string): boolean =>
|
|
|
1202
1168
|
key.startsWith("__reactInternalInstance$") ||
|
|
1203
1169
|
key.startsWith("__reactFiber");
|
|
1204
1170
|
|
|
1171
|
+
const getLegacyRootFiber = (hostInstance: object): Fiber | null => {
|
|
1172
|
+
const reactRootContainer = Reflect.get(hostInstance, "_reactRootContainer");
|
|
1173
|
+
if (typeof reactRootContainer !== "object" || reactRootContainer === null) return null;
|
|
1174
|
+
const internalRoot = Reflect.get(reactRootContainer, "_internalRoot");
|
|
1175
|
+
if (typeof internalRoot !== "object" || internalRoot === null) return null;
|
|
1176
|
+
const current = Reflect.get(internalRoot, "current");
|
|
1177
|
+
if (typeof current !== "object" || current === null) return null;
|
|
1178
|
+
const child = Reflect.get(current, "child");
|
|
1179
|
+
return isValidFiber(child) ? child : null;
|
|
1180
|
+
};
|
|
1181
|
+
|
|
1182
|
+
const getInternalInstanceHandle = (hostInstance: object): Fiber | null => {
|
|
1183
|
+
const internalInstanceHandle =
|
|
1184
|
+
Reflect.get(hostInstance, "__internalInstanceHandle") ??
|
|
1185
|
+
Reflect.get(hostInstance, "_internalInstanceHandle");
|
|
1186
|
+
return isValidFiber(internalInstanceHandle) ? internalInstanceHandle : null;
|
|
1187
|
+
};
|
|
1188
|
+
|
|
1189
|
+
const getPublicHostInstance = (stateNode: unknown): unknown => {
|
|
1190
|
+
if (typeof stateNode !== "object" || stateNode === null) return stateNode;
|
|
1191
|
+
const canonical = Reflect.get(stateNode, "canonical");
|
|
1192
|
+
if (typeof canonical === "object" && canonical !== null) {
|
|
1193
|
+
const publicInstance = Reflect.get(canonical, "publicInstance");
|
|
1194
|
+
if (typeof publicInstance === "object" && publicInstance !== null) {
|
|
1195
|
+
return publicInstance;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
const nativeTag = Reflect.get(stateNode, "_nativeTag");
|
|
1199
|
+
return typeof nativeTag === "number" ? nativeTag : stateNode;
|
|
1200
|
+
};
|
|
1201
|
+
|
|
1205
1202
|
export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
|
|
1206
1203
|
const rdtHook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
1207
1204
|
if (rdtHook?.renderers) {
|
|
@@ -1213,42 +1210,38 @@ export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
|
|
|
1213
1210
|
}
|
|
1214
1211
|
}
|
|
1215
1212
|
|
|
1216
|
-
if (typeof hostInstance === "object" && hostInstance
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
return (hostInstance._reactRootContainer as any)?._internalRoot?.current?.child;
|
|
1220
|
-
}
|
|
1213
|
+
if (typeof hostInstance === "object" && hostInstance !== null) {
|
|
1214
|
+
const legacyRootFiber = getLegacyRootFiber(hostInstance);
|
|
1215
|
+
if (legacyRootFiber) return legacyRootFiber;
|
|
1221
1216
|
|
|
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
|
-
}
|
|
1217
|
+
const internalInstanceHandle = getInternalInstanceHandle(hostInstance);
|
|
1218
|
+
if (internalInstanceHandle) return internalInstanceHandle;
|
|
1233
1219
|
|
|
1234
|
-
const hostInstanceRecord = hostInstance as Record<string, unknown>;
|
|
1235
1220
|
for (const knownKey of knownFiberPropertyKeys) {
|
|
1236
|
-
const fiber =
|
|
1237
|
-
if (fiber) return fiber
|
|
1221
|
+
const fiber = Reflect.get(hostInstance, knownKey);
|
|
1222
|
+
if (isValidFiber(fiber)) return fiber;
|
|
1238
1223
|
}
|
|
1239
1224
|
|
|
1240
|
-
for (const key of Object.keys(
|
|
1225
|
+
for (const key of Object.keys(hostInstance)) {
|
|
1241
1226
|
if (isFiberPropertyKey(key)) {
|
|
1242
1227
|
knownFiberPropertyKeys.add(key);
|
|
1243
|
-
|
|
1228
|
+
const fiber = Reflect.get(hostInstance, key);
|
|
1229
|
+
if (isValidFiber(fiber)) return fiber;
|
|
1244
1230
|
}
|
|
1245
1231
|
}
|
|
1232
|
+
}
|
|
1246
1233
|
|
|
1234
|
+
if (
|
|
1235
|
+
hostInstance !== null &&
|
|
1236
|
+
hostInstance !== undefined &&
|
|
1237
|
+
(typeof hostInstance === "object" || typeof hostInstance === "number")
|
|
1238
|
+
) {
|
|
1247
1239
|
for (const fiberRoot of _fiberRoots) {
|
|
1248
|
-
if (getRootRenderer(fiberRoot.current)?.findFiberByHostInstance) continue;
|
|
1249
1240
|
const fiber = traverseFiber(
|
|
1250
1241
|
fiberRoot.current,
|
|
1251
|
-
(candidateFiber) =>
|
|
1242
|
+
(candidateFiber) =>
|
|
1243
|
+
isHostFiber(candidateFiber) &&
|
|
1244
|
+
getPublicHostInstance(candidateFiber.stateNode) === hostInstance,
|
|
1252
1245
|
);
|
|
1253
1246
|
if (fiber) return fiber;
|
|
1254
1247
|
}
|
|
@@ -1256,6 +1249,17 @@ export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
|
|
|
1256
1249
|
return null;
|
|
1257
1250
|
};
|
|
1258
1251
|
|
|
1259
|
-
export
|
|
1260
|
-
|
|
1261
|
-
|
|
1252
|
+
export {
|
|
1253
|
+
BIPPY_INSTRUMENTATION_STRING,
|
|
1254
|
+
_onActiveListeners,
|
|
1255
|
+
_renderers,
|
|
1256
|
+
getRDTHook,
|
|
1257
|
+
hasRDTHook,
|
|
1258
|
+
installRDTHook,
|
|
1259
|
+
isRealReactDevtools,
|
|
1260
|
+
onRendererInject,
|
|
1261
|
+
patchRDTHook,
|
|
1262
|
+
version,
|
|
1263
|
+
} from "./rdt-hook.js";
|
|
1264
|
+
export type { Unsubscribe } from "./rdt-hook.js";
|
|
1265
|
+
export type * from "./react-internals/index.js";
|