bippy 0.6.1-dev.61475ed → 0.6.1-dev.d7876ea
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/README.md +32 -48
- package/dist/core.cjs +1 -1
- package/dist/core.d.cts +42 -56
- package/dist/core.d.ts +42 -56
- package/dist/core.js +1 -1
- package/dist/core2.cjs +9 -0
- package/dist/core2.d.cts +3 -3
- package/dist/core2.d.ts +3 -3
- package/dist/core2.js +9 -0
- package/dist/errors.d.cts +436 -0
- package/dist/errors.d.ts +436 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -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 +1 -1
- package/dist/install-hook-only.d.ts +1 -1
- package/dist/install-hook-only.iife.js +1 -1
- package/dist/install-hook-only.js +1 -1
- package/dist/rdt-hook.cjs +1 -1
- package/dist/rdt-hook.js +1 -1
- package/dist/source.cjs +14 -4
- package/dist/source.d.cts +18 -9
- package/dist/source.d.ts +18 -9
- package/dist/source.js +14 -4
- package/package.json +21 -15
- package/src/core.ts +343 -253
- package/src/errors.ts +99 -0
- package/src/generated/react-work-tags.d.ts +261 -0
- package/src/generated/react-work-tags.js +240 -0
- package/src/rdt-hook.ts +154 -149
- package/src/react-internals.ts +65 -0
- package/src/semver.ts +78 -0
- package/src/source/constants.ts +1 -1
- package/src/source/error-stack.ts +11 -0
- package/src/source/get-source.ts +2 -1
- package/src/source/index.ts +9 -1
- package/src/source/inspect-hooks.ts +199 -164
- package/src/source/owner-stack.ts +84 -114
- package/src/source/parse-debug-stack.ts +4 -4
- package/src/source/parse-stack.ts +1 -1
- package/src/source/symbolication.ts +365 -89
- package/src/types.ts +165 -334
- package/src/unsubscribe.ts +2 -2
- 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/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,91 @@ import type {
|
|
|
7
6
|
ContextDependency,
|
|
8
7
|
Fiber,
|
|
9
8
|
FiberRoot,
|
|
9
|
+
HostFiber,
|
|
10
10
|
MemoizedState,
|
|
11
11
|
ReactDevToolsGlobalHook,
|
|
12
12
|
ReactRenderer,
|
|
13
13
|
} from "./types.js";
|
|
14
14
|
|
|
15
|
+
import { BippyInstrumentationError, BippyReactDevToolsError, runWithBippyError } from "./errors.js";
|
|
16
|
+
|
|
15
17
|
import {
|
|
16
18
|
_onActiveListeners,
|
|
17
19
|
BIPPY_INSTRUMENTATION_STRING,
|
|
18
20
|
getRDTHook,
|
|
19
21
|
hasRDTHook,
|
|
20
|
-
isReactRefresh,
|
|
21
22
|
isRealReactDevtools,
|
|
23
|
+
onRDTHookReplace,
|
|
22
24
|
onRendererInject,
|
|
23
25
|
} from "./rdt-hook.js";
|
|
26
|
+
import {
|
|
27
|
+
getReactWorkTagsForFiber,
|
|
28
|
+
MutationMask,
|
|
29
|
+
ReactBuildType,
|
|
30
|
+
ReactFiberFlags,
|
|
31
|
+
ReactSymbols,
|
|
32
|
+
setReactWorkTagsForFiber,
|
|
33
|
+
} from "./react-internals.js";
|
|
24
34
|
import { toUnsubscribe, type Unsubscribe } from "./unsubscribe.js";
|
|
25
35
|
|
|
26
|
-
|
|
27
|
-
export
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
36
|
+
export { getReactWorkTags, ReactSymbols } from "./react-internals.js";
|
|
37
|
+
export type { ReactWorkTagMap, ReactWorkTagVersion } from "./react-internals.js";
|
|
38
|
+
export {
|
|
39
|
+
BippyError,
|
|
40
|
+
BippyHookInstallationError,
|
|
41
|
+
BippyHookInspectionError,
|
|
42
|
+
BippyHookListenerError,
|
|
43
|
+
BippyHookRenderError,
|
|
44
|
+
BippyInstrumentationError,
|
|
45
|
+
BippyReactBuildError,
|
|
46
|
+
BippyReactDevToolsError,
|
|
47
|
+
BippySourceMapError,
|
|
48
|
+
BippyUnsupportedHookError,
|
|
49
|
+
} from "./errors.js";
|
|
50
|
+
|
|
51
|
+
const isObjectRecord = (value: unknown): value is Record<string, unknown> =>
|
|
52
|
+
typeof value === "object" && value !== null;
|
|
53
|
+
|
|
54
|
+
const isComponentType = (value: unknown): value is React.ComponentType<unknown> =>
|
|
55
|
+
typeof value === "function";
|
|
56
|
+
|
|
57
|
+
const getPropertyValue = (value: object, key: PropertyKey): unknown => {
|
|
58
|
+
let currentValue: object | null = value;
|
|
59
|
+
while (currentValue) {
|
|
60
|
+
const descriptor = Object.getOwnPropertyDescriptor(currentValue, key);
|
|
61
|
+
if (descriptor) {
|
|
62
|
+
if ("value" in descriptor) return descriptor.value;
|
|
63
|
+
return descriptor.get?.call(value);
|
|
64
|
+
}
|
|
65
|
+
currentValue = Object.getPrototypeOf(currentValue);
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const getTypeName = (value: object): string | null => {
|
|
71
|
+
const displayName = getPropertyValue(value, "displayName");
|
|
72
|
+
if (typeof displayName === "string" && displayName) return displayName;
|
|
73
|
+
const name = getPropertyValue(value, "name");
|
|
74
|
+
return typeof name === "string" && name ? name : null;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
interface FiberSelector {
|
|
78
|
+
(node: Fiber): boolean | Promise<boolean | void> | void;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface FiberTimings {
|
|
82
|
+
selfTime: number;
|
|
83
|
+
totalTime: number;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface RenderHandler {
|
|
87
|
+
<State>(fiber: Fiber, phase: RenderPhase, state?: State): unknown;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface ValueWrite {
|
|
91
|
+
path: string[];
|
|
92
|
+
value: unknown;
|
|
93
|
+
}
|
|
69
94
|
|
|
70
95
|
/**
|
|
71
96
|
* Returns `true` if object is a React Element.
|
|
@@ -74,10 +99,9 @@ const MutationMask =
|
|
|
74
99
|
*/
|
|
75
100
|
export const isValidElement = (element: unknown): element is React.ReactElement =>
|
|
76
101
|
typeof element === "object" &&
|
|
77
|
-
element
|
|
102
|
+
element !== null &&
|
|
78
103
|
"$$typeof" in element &&
|
|
79
|
-
|
|
80
|
-
[ELEMENT_TYPE_SYMBOL_STRING, TRANSITIONAL_ELEMENT_TYPE_SYMBOL_STRING].includes(
|
|
104
|
+
[ReactSymbols.LEGACY_ELEMENT_SYMBOL_STRING, ReactSymbols.ELEMENT_SYMBOL_STRING].includes(
|
|
81
105
|
String(element.$$typeof),
|
|
82
106
|
);
|
|
83
107
|
|
|
@@ -86,7 +110,7 @@ export const isValidElement = (element: unknown): element is React.ReactElement
|
|
|
86
110
|
*/
|
|
87
111
|
export const isValidFiber = (fiber: unknown): fiber is Fiber =>
|
|
88
112
|
typeof fiber === "object" &&
|
|
89
|
-
fiber
|
|
113
|
+
fiber !== null &&
|
|
90
114
|
"tag" in fiber &&
|
|
91
115
|
"stateNode" in fiber &&
|
|
92
116
|
"return" in fiber &&
|
|
@@ -99,16 +123,16 @@ export const isValidFiber = (fiber: unknown): fiber is Fiber =>
|
|
|
99
123
|
*
|
|
100
124
|
* @see https://reactnative.dev/architecture/glossary#host-view-tree-and-host-view
|
|
101
125
|
*/
|
|
102
|
-
export const isHostFiber = (fiber: Fiber):
|
|
126
|
+
export const isHostFiber = (fiber: Fiber): fiber is HostFiber => {
|
|
127
|
+
const workTags = getReactWorkTagsForFiber(fiber);
|
|
103
128
|
switch (fiber.tag) {
|
|
104
|
-
case
|
|
105
|
-
|
|
106
|
-
case
|
|
107
|
-
|
|
108
|
-
case HostSingletonTag:
|
|
129
|
+
case workTags.HostComponent:
|
|
130
|
+
case workTags.HostText:
|
|
131
|
+
case workTags.HostHoistable:
|
|
132
|
+
case workTags.HostSingleton:
|
|
109
133
|
return true;
|
|
110
134
|
default:
|
|
111
|
-
return
|
|
135
|
+
return false;
|
|
112
136
|
}
|
|
113
137
|
};
|
|
114
138
|
|
|
@@ -118,12 +142,13 @@ export const isHostFiber = (fiber: Fiber): boolean => {
|
|
|
118
142
|
* @see https://reactnative.dev/architecture/glossary#react-composite-components
|
|
119
143
|
*/
|
|
120
144
|
export const isCompositeFiber = (fiber: Fiber): boolean => {
|
|
145
|
+
const workTags = getReactWorkTagsForFiber(fiber);
|
|
121
146
|
switch (fiber.tag) {
|
|
122
|
-
case
|
|
123
|
-
case
|
|
124
|
-
case
|
|
125
|
-
case
|
|
126
|
-
case
|
|
147
|
+
case workTags.ClassComponent:
|
|
148
|
+
case workTags.ForwardRef:
|
|
149
|
+
case workTags.FunctionComponent:
|
|
150
|
+
case workTags.MemoComponent:
|
|
151
|
+
case workTags.SimpleMemoComponent:
|
|
127
152
|
return true;
|
|
128
153
|
default:
|
|
129
154
|
return false;
|
|
@@ -230,16 +255,17 @@ export const traverseProps = (
|
|
|
230
255
|
export const didFiberRender = (fiber: Fiber): boolean => {
|
|
231
256
|
const nextProps = fiber.memoizedProps;
|
|
232
257
|
const prevProps = fiber.alternate?.memoizedProps || {};
|
|
233
|
-
const flags = fiber.flags ??
|
|
258
|
+
const flags = fiber.flags ?? fiber.effectTag ?? 0;
|
|
259
|
+
const workTags = getReactWorkTagsForFiber(fiber);
|
|
234
260
|
|
|
235
261
|
switch (fiber.tag) {
|
|
236
|
-
case
|
|
237
|
-
case
|
|
238
|
-
case
|
|
239
|
-
case
|
|
240
|
-
case
|
|
241
|
-
case
|
|
242
|
-
return (flags & PerformedWork) === PerformedWork;
|
|
262
|
+
case workTags.ClassComponent:
|
|
263
|
+
case workTags.ContextConsumer:
|
|
264
|
+
case workTags.ForwardRef:
|
|
265
|
+
case workTags.FunctionComponent:
|
|
266
|
+
case workTags.MemoComponent:
|
|
267
|
+
case workTags.SimpleMemoComponent: {
|
|
268
|
+
return (flags & ReactFiberFlags.PerformedWork) === ReactFiberFlags.PerformedWork;
|
|
243
269
|
}
|
|
244
270
|
default:
|
|
245
271
|
// Host nodes (DOM, root, etc.)
|
|
@@ -257,8 +283,8 @@ export const didFiberRender = (fiber: Fiber): boolean => {
|
|
|
257
283
|
*/
|
|
258
284
|
export const didFiberCommit = (fiber: Fiber): boolean => {
|
|
259
285
|
return Boolean(
|
|
260
|
-
(fiber.flags & (MutationMask | Cloned)) !== 0 ||
|
|
261
|
-
(fiber.subtreeFlags & (MutationMask | Cloned)) !== 0,
|
|
286
|
+
(fiber.flags & (MutationMask | ReactFiberFlags.Cloned)) !== 0 ||
|
|
287
|
+
(fiber.subtreeFlags & (MutationMask | ReactFiberFlags.Cloned)) !== 0,
|
|
262
288
|
);
|
|
263
289
|
};
|
|
264
290
|
|
|
@@ -297,7 +323,8 @@ export const getFiberStack = (fiber: Fiber): Fiber[] => {
|
|
|
297
323
|
let currentFiber = fiber;
|
|
298
324
|
while (currentFiber.return) {
|
|
299
325
|
stack.push(currentFiber);
|
|
300
|
-
|
|
326
|
+
const parentFiber = currentFiber.return;
|
|
327
|
+
currentFiber = parentFiber;
|
|
301
328
|
}
|
|
302
329
|
return stack;
|
|
303
330
|
};
|
|
@@ -306,8 +333,9 @@ export const getFiberStack = (fiber: Fiber): Fiber[] => {
|
|
|
306
333
|
* Returns `true` if the {@link Fiber} should be filtered out during reconciliation.
|
|
307
334
|
*/
|
|
308
335
|
const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
336
|
+
const workTags = getReactWorkTagsForFiber(fiber);
|
|
309
337
|
switch (fiber.tag) {
|
|
310
|
-
case
|
|
338
|
+
case workTags.DehydratedSuspenseComponent:
|
|
311
339
|
// TODO: ideally we would show dehydrated Suspense immediately.
|
|
312
340
|
// However, it has some special behavior (like disconnecting
|
|
313
341
|
// an alternate and turning into real Suspense) which breaks DevTools.
|
|
@@ -315,13 +343,13 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
|
315
343
|
// https://github.com/bvaughn/react-devtools-experimental/issues/197
|
|
316
344
|
return true;
|
|
317
345
|
|
|
318
|
-
case
|
|
319
|
-
case
|
|
320
|
-
case
|
|
321
|
-
case
|
|
346
|
+
case workTags.Fragment:
|
|
347
|
+
case workTags.HostText:
|
|
348
|
+
case workTags.LegacyHiddenComponent:
|
|
349
|
+
case workTags.OffscreenComponent:
|
|
322
350
|
return true;
|
|
323
351
|
|
|
324
|
-
case
|
|
352
|
+
case workTags.HostRoot:
|
|
325
353
|
// It is never valid to filter the root element.
|
|
326
354
|
return false;
|
|
327
355
|
|
|
@@ -331,15 +359,15 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
|
331
359
|
|
|
332
360
|
if (typeof symbolOrNumber === "symbol") {
|
|
333
361
|
return (
|
|
334
|
-
symbolOrNumber.description === CONCURRENT_MODE_SYMBOL_DESCRIPTION ||
|
|
335
|
-
symbolOrNumber.description === DEPRECATED_ASYNC_MODE_SYMBOL_DESCRIPTION
|
|
362
|
+
symbolOrNumber.description === ReactSymbols.CONCURRENT_MODE_SYMBOL_DESCRIPTION ||
|
|
363
|
+
symbolOrNumber.description === ReactSymbols.DEPRECATED_ASYNC_MODE_SYMBOL_DESCRIPTION
|
|
336
364
|
);
|
|
337
365
|
}
|
|
338
366
|
|
|
339
367
|
switch (symbolOrNumber) {
|
|
340
|
-
case CONCURRENT_MODE_NUMBER:
|
|
341
|
-
case CONCURRENT_MODE_SYMBOL_STRING:
|
|
342
|
-
case DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
|
|
368
|
+
case ReactSymbols.CONCURRENT_MODE_NUMBER:
|
|
369
|
+
case ReactSymbols.CONCURRENT_MODE_SYMBOL_STRING:
|
|
370
|
+
case ReactSymbols.DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
|
|
343
371
|
return true;
|
|
344
372
|
|
|
345
373
|
default:
|
|
@@ -405,7 +433,7 @@ export function traverseFiber(
|
|
|
405
433
|
): Promise<Fiber | null>;
|
|
406
434
|
export function traverseFiber(
|
|
407
435
|
fiber: Fiber | null,
|
|
408
|
-
selector:
|
|
436
|
+
selector: FiberSelector,
|
|
409
437
|
ascending = false,
|
|
410
438
|
): Fiber | null | Promise<Fiber | null> {
|
|
411
439
|
if (!fiber) return null;
|
|
@@ -417,11 +445,7 @@ export function traverseFiber(
|
|
|
417
445
|
|
|
418
446
|
let child = ascending ? fiber.return : fiber.child;
|
|
419
447
|
while (child) {
|
|
420
|
-
const match = await traverseFiberAsync(
|
|
421
|
-
child,
|
|
422
|
-
selector as (node: Fiber) => Promise<boolean | void>,
|
|
423
|
-
ascending,
|
|
424
|
-
);
|
|
448
|
+
const match = await traverseFiberAsync(child, selector, ascending);
|
|
425
449
|
if (match) return match;
|
|
426
450
|
child = ascending ? null : child.sibling;
|
|
427
451
|
}
|
|
@@ -433,7 +457,7 @@ export function traverseFiber(
|
|
|
433
457
|
|
|
434
458
|
let child = ascending ? fiber.return : fiber.child;
|
|
435
459
|
while (child) {
|
|
436
|
-
const match = traverseFiberSync(child, selector
|
|
460
|
+
const match = traverseFiberSync(child, selector, ascending);
|
|
437
461
|
if (match) return match;
|
|
438
462
|
child = ascending ? null : child.sibling;
|
|
439
463
|
}
|
|
@@ -442,11 +466,13 @@ export function traverseFiber(
|
|
|
442
466
|
|
|
443
467
|
const traverseFiberSync = (
|
|
444
468
|
fiber: Fiber | null,
|
|
445
|
-
selector:
|
|
469
|
+
selector: FiberSelector,
|
|
446
470
|
ascending = false,
|
|
447
471
|
): Fiber | null => {
|
|
448
472
|
if (!fiber) return null;
|
|
449
|
-
|
|
473
|
+
const selection = selector(fiber);
|
|
474
|
+
if (selection instanceof Promise) return null;
|
|
475
|
+
if (selection === true) return fiber;
|
|
450
476
|
|
|
451
477
|
let child = ascending ? fiber.return : fiber.child;
|
|
452
478
|
while (child) {
|
|
@@ -460,7 +486,7 @@ const traverseFiberSync = (
|
|
|
460
486
|
|
|
461
487
|
const traverseFiberAsync = async (
|
|
462
488
|
fiber: Fiber | null,
|
|
463
|
-
selector:
|
|
489
|
+
selector: FiberSelector,
|
|
464
490
|
ascending = false,
|
|
465
491
|
): Promise<Fiber | null> => {
|
|
466
492
|
if (!fiber) return null;
|
|
@@ -485,12 +511,12 @@ const traverseFiberAsync = async (
|
|
|
485
511
|
* console.log(selfTime, totalTime);
|
|
486
512
|
* ```
|
|
487
513
|
*/
|
|
488
|
-
export const getTimings = (fiber?: Fiber | null):
|
|
514
|
+
export const getTimings = (fiber?: Fiber | null): FiberTimings => {
|
|
489
515
|
const totalTime = fiber?.actualDuration ?? 0;
|
|
490
516
|
let selfTime = totalTime;
|
|
491
517
|
// TODO: calculate a DOM time, which is just host component summed up
|
|
492
518
|
let child = fiber?.child ?? null;
|
|
493
|
-
while (totalTime > 0 && child
|
|
519
|
+
while (totalTime > 0 && child !== null) {
|
|
494
520
|
selfTime -= child.actualDuration ?? 0;
|
|
495
521
|
child = child.sibling;
|
|
496
522
|
}
|
|
@@ -501,48 +527,29 @@ export const getTimings = (fiber?: Fiber | null): { selfTime: number; totalTime:
|
|
|
501
527
|
* Returns `true` if the {@link Fiber} uses React Compiler's memo cache.
|
|
502
528
|
*/
|
|
503
529
|
export const hasMemoCache = (fiber: Fiber): boolean => {
|
|
504
|
-
return Boolean(
|
|
530
|
+
return Boolean(fiber.updateQueue?.memoCache);
|
|
505
531
|
};
|
|
506
532
|
|
|
507
|
-
type FiberType =
|
|
508
|
-
| React.ComponentType<unknown>
|
|
509
|
-
| React.ForwardRefExoticComponent<unknown>
|
|
510
|
-
| React.MemoExoticComponent<React.ComponentType<unknown>>;
|
|
511
|
-
|
|
512
533
|
/**
|
|
513
534
|
* Returns the type (e.g. component definition) of the {@link Fiber}
|
|
514
535
|
*/
|
|
515
536
|
export const getType = (type: unknown): null | React.ComponentType<unknown> => {
|
|
516
|
-
|
|
517
|
-
if (
|
|
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;
|
|
537
|
+
if (isComponentType(type)) return type;
|
|
538
|
+
if (!isObjectRecord(type)) return null;
|
|
539
|
+
return getType(type.type ?? type.render);
|
|
528
540
|
};
|
|
529
541
|
|
|
530
542
|
/**
|
|
531
543
|
* Returns the display name of the {@link Fiber} type.
|
|
532
544
|
*/
|
|
533
545
|
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;
|
|
546
|
+
if (typeof type === "string") return type;
|
|
547
|
+
if ((typeof type !== "function" && typeof type !== "object") || type === null) return null;
|
|
548
|
+
const name = getTypeName(type);
|
|
542
549
|
if (name) return name;
|
|
543
|
-
const unwrappedType = getType(
|
|
550
|
+
const unwrappedType = getType(type);
|
|
544
551
|
if (!unwrappedType) return null;
|
|
545
|
-
return unwrappedType
|
|
552
|
+
return getTypeName(unwrappedType);
|
|
546
553
|
};
|
|
547
554
|
|
|
548
555
|
/**
|
|
@@ -550,7 +557,10 @@ export const getDisplayName = (type: unknown): null | string => {
|
|
|
550
557
|
*/
|
|
551
558
|
export const detectReactBuildType = (renderer: ReactRenderer): "development" | "production" => {
|
|
552
559
|
try {
|
|
553
|
-
if (
|
|
560
|
+
if (
|
|
561
|
+
typeof renderer.version === "string" &&
|
|
562
|
+
renderer.bundleType === ReactBuildType.Development
|
|
563
|
+
) {
|
|
554
564
|
return "development";
|
|
555
565
|
}
|
|
556
566
|
} catch {}
|
|
@@ -562,11 +572,7 @@ export const detectReactBuildType = (renderer: ReactRenderer): "development" | "
|
|
|
562
572
|
*/
|
|
563
573
|
export const isInstrumentationActive = (): boolean => {
|
|
564
574
|
const rdtHook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
565
|
-
return (
|
|
566
|
-
Boolean(rdtHook?._instrumentationIsActive) ||
|
|
567
|
-
isRealReactDevtools(rdtHook) ||
|
|
568
|
-
isReactRefresh(rdtHook)
|
|
569
|
-
);
|
|
575
|
+
return Boolean(rdtHook?._instrumentationIsActive) || isRealReactDevtools(rdtHook);
|
|
570
576
|
};
|
|
571
577
|
|
|
572
578
|
export const _fiberRoots = new Set<FiberRoot>();
|
|
@@ -589,30 +595,25 @@ export const getLatestFiber = (fiber: Fiber): Fiber => {
|
|
|
589
595
|
return fiber;
|
|
590
596
|
};
|
|
591
597
|
|
|
592
|
-
export type RenderHandler = <S>(fiber: Fiber, phase: RenderPhase, state?: S) => unknown;
|
|
593
|
-
|
|
594
598
|
export type RenderPhase = "mount" | "unmount" | "update";
|
|
595
599
|
|
|
596
|
-
let
|
|
600
|
+
let nextFiberId = 0;
|
|
597
601
|
const fiberIdMap = new WeakMap<Fiber, number>();
|
|
598
602
|
|
|
599
|
-
export const setFiberId = (fiber: Fiber,
|
|
600
|
-
fiberIdMap.set(fiber,
|
|
603
|
+
export const setFiberId = (fiber: Fiber, fiberId: number = nextFiberId++): void => {
|
|
604
|
+
fiberIdMap.set(fiber, fiberId);
|
|
601
605
|
};
|
|
602
606
|
|
|
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
607
|
export const getFiberId = (fiber: Fiber): number => {
|
|
607
|
-
let
|
|
608
|
-
if (
|
|
609
|
-
|
|
608
|
+
let currentFiberId = fiberIdMap.get(fiber);
|
|
609
|
+
if (currentFiberId === undefined && fiber.alternate) {
|
|
610
|
+
currentFiberId = fiberIdMap.get(fiber.alternate);
|
|
610
611
|
}
|
|
611
|
-
if (
|
|
612
|
-
|
|
613
|
-
setFiberId(fiber,
|
|
612
|
+
if (currentFiberId === undefined) {
|
|
613
|
+
currentFiberId = nextFiberId++;
|
|
614
|
+
setFiberId(fiber, currentFiberId);
|
|
614
615
|
}
|
|
615
|
-
return
|
|
616
|
+
return currentFiberId;
|
|
616
617
|
};
|
|
617
618
|
|
|
618
619
|
const mountFiberRecursively = (
|
|
@@ -622,7 +623,7 @@ const mountFiberRecursively = (
|
|
|
622
623
|
): void => {
|
|
623
624
|
let fiber: Fiber | null = firstChild;
|
|
624
625
|
|
|
625
|
-
while (fiber
|
|
626
|
+
while (fiber !== null) {
|
|
626
627
|
if (!fiberIdMap.has(fiber)) {
|
|
627
628
|
getFiberId(fiber);
|
|
628
629
|
}
|
|
@@ -631,7 +632,7 @@ const mountFiberRecursively = (
|
|
|
631
632
|
onRender(fiber, "mount");
|
|
632
633
|
}
|
|
633
634
|
|
|
634
|
-
if (fiber.tag ===
|
|
635
|
+
if (fiber.tag === getReactWorkTagsForFiber(fiber).SuspenseComponent) {
|
|
635
636
|
const isTimedOut = fiber.memoizedState !== null;
|
|
636
637
|
if (isTimedOut) {
|
|
637
638
|
// Special case: if Suspense mounts in a timed-out state,
|
|
@@ -646,18 +647,12 @@ const mountFiberRecursively = (
|
|
|
646
647
|
}
|
|
647
648
|
}
|
|
648
649
|
} 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
|
-
}
|
|
650
|
+
const primaryChild = fiber.child?.child ?? null;
|
|
656
651
|
if (primaryChild !== null) {
|
|
657
652
|
mountFiberRecursively(onRender, primaryChild, false);
|
|
658
653
|
}
|
|
659
654
|
}
|
|
660
|
-
} else if (fiber.child
|
|
655
|
+
} else if (fiber.child !== null) {
|
|
661
656
|
mountFiberRecursively(onRender, fiber.child, true);
|
|
662
657
|
}
|
|
663
658
|
fiber = traverseSiblings ? fiber.sibling : null;
|
|
@@ -678,7 +673,7 @@ const updateFiberRecursively = (
|
|
|
678
673
|
getFiberId(prevFiber);
|
|
679
674
|
}
|
|
680
675
|
|
|
681
|
-
const isSuspense = nextFiber.tag ===
|
|
676
|
+
const isSuspense = nextFiber.tag === getReactWorkTagsForFiber(nextFiber).SuspenseComponent;
|
|
682
677
|
|
|
683
678
|
const shouldIncludeInTree = !shouldFilterFiber(nextFiber);
|
|
684
679
|
if (shouldIncludeInTree && didFiberRender(nextFiber)) {
|
|
@@ -765,7 +760,7 @@ const updateFiberRecursively = (
|
|
|
765
760
|
};
|
|
766
761
|
|
|
767
762
|
const unmountFiber = (onRender: RenderHandler, fiber: Fiber): void => {
|
|
768
|
-
const isRoot = fiber.tag ===
|
|
763
|
+
const isRoot = fiber.tag === getReactWorkTagsForFiber(fiber).HostRoot;
|
|
769
764
|
|
|
770
765
|
if (isRoot || !shouldFilterFiber(fiber)) {
|
|
771
766
|
onRender(fiber, "unmount");
|
|
@@ -774,7 +769,8 @@ const unmountFiber = (onRender: RenderHandler, fiber: Fiber): void => {
|
|
|
774
769
|
|
|
775
770
|
const unmountFiberChildrenRecursively = (onRender: RenderHandler, fiber: Fiber): void => {
|
|
776
771
|
// We might meet a nested Suspense on our way.
|
|
777
|
-
const isTimedOutSuspense =
|
|
772
|
+
const isTimedOutSuspense =
|
|
773
|
+
fiber.tag === getReactWorkTagsForFiber(fiber).SuspenseComponent && fiber.memoizedState !== null;
|
|
778
774
|
let child = fiber.child;
|
|
779
775
|
|
|
780
776
|
if (isTimedOutSuspense) {
|
|
@@ -831,13 +827,15 @@ export const traverseRenderedFibers = (root: FiberRoot, onRender: RenderHandler)
|
|
|
831
827
|
} else if (prevFiber !== null) {
|
|
832
828
|
const wasMounted =
|
|
833
829
|
prevFiber &&
|
|
834
|
-
prevFiber.memoizedState
|
|
835
|
-
prevFiber.memoizedState.element
|
|
830
|
+
prevFiber.memoizedState !== null &&
|
|
831
|
+
prevFiber.memoizedState.element !== null &&
|
|
832
|
+
prevFiber.memoizedState.element !== undefined &&
|
|
836
833
|
// A dehydrated root is not considered mounted
|
|
837
834
|
prevFiber.memoizedState.isDehydrated !== true;
|
|
838
835
|
const isMounted =
|
|
839
|
-
fiber.memoizedState
|
|
840
|
-
fiber.memoizedState.element
|
|
836
|
+
fiber.memoizedState !== null &&
|
|
837
|
+
fiber.memoizedState.element !== null &&
|
|
838
|
+
fiber.memoizedState.element !== undefined &&
|
|
841
839
|
// A dehydrated root is not considered mounted
|
|
842
840
|
fiber.memoizedState.isDehydrated !== true;
|
|
843
841
|
|
|
@@ -859,16 +857,15 @@ const overrideRenderers = new Set<ReactRenderer>();
|
|
|
859
857
|
let areOverrideRenderersWired = false;
|
|
860
858
|
|
|
861
859
|
const wireOverrideRenderers = (): void => {
|
|
862
|
-
if (areOverrideRenderersWired) return;
|
|
863
860
|
if (!hasRDTHook()) return;
|
|
864
861
|
const rdtHook = getRDTHook();
|
|
865
|
-
if (!rdtHook?.renderers) return;
|
|
866
|
-
areOverrideRenderersWired = true;
|
|
867
862
|
|
|
868
|
-
|
|
863
|
+
setHookEventDispatchers(rdtHook);
|
|
869
864
|
for (const renderer of rdtHook.renderers.values()) {
|
|
870
865
|
overrideRenderers.add(renderer);
|
|
871
866
|
}
|
|
867
|
+
if (areOverrideRenderersWired) return;
|
|
868
|
+
areOverrideRenderersWired = true;
|
|
872
869
|
onRendererInject((renderer) => {
|
|
873
870
|
overrideRenderers.add(renderer);
|
|
874
871
|
});
|
|
@@ -882,12 +879,9 @@ const getRootRenderer = (fiber: Fiber): ReactRenderer | null => {
|
|
|
882
879
|
}
|
|
883
880
|
const rendererId = rootRendererIds.get(hostRootFiber.stateNode);
|
|
884
881
|
if (rendererId === undefined) return null;
|
|
885
|
-
return getRDTHook().renderers
|
|
882
|
+
return getRDTHook().renderers.get(rendererId) ?? null;
|
|
886
883
|
};
|
|
887
884
|
|
|
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
885
|
const resolveOverrideRenderers = (fiber: Fiber): ReactRenderer[] => {
|
|
892
886
|
wireOverrideRenderers();
|
|
893
887
|
const rootRenderer = getRootRenderer(fiber);
|
|
@@ -913,15 +907,14 @@ const getHookStateDispatch = (
|
|
|
913
907
|
hookIndex: number,
|
|
914
908
|
): ((value: unknown) => void) | null => {
|
|
915
909
|
let hookState = fiber.memoizedState;
|
|
916
|
-
for (let
|
|
910
|
+
for (let currentHookIndex = 0; currentHookIndex < hookIndex; currentHookIndex++) {
|
|
917
911
|
if (!hookState?.next) return null;
|
|
918
912
|
hookState = hookState.next;
|
|
919
913
|
}
|
|
920
914
|
const queue = hookState?.queue;
|
|
921
|
-
if (isPOJO(queue)
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
return null;
|
|
915
|
+
if (!isPOJO(queue)) return null;
|
|
916
|
+
const dispatch = queue.dispatch;
|
|
917
|
+
return typeof dispatch === "function" ? (value) => dispatch(value) : null;
|
|
925
918
|
};
|
|
926
919
|
|
|
927
920
|
const findContextProviderFiber = (fiber: Fiber, contextType: unknown): Fiber | null => {
|
|
@@ -947,12 +940,12 @@ const isPOJO = (maybePOJO: unknown): maybePOJO is Record<string, unknown> => {
|
|
|
947
940
|
const buildPathsFromValue = (
|
|
948
941
|
maybePOJO: Record<string, unknown>,
|
|
949
942
|
basePath: string[] = [],
|
|
950
|
-
):
|
|
943
|
+
): ValueWrite[] => {
|
|
951
944
|
if (!isPOJO(maybePOJO)) {
|
|
952
945
|
return [{ path: basePath, value: maybePOJO }];
|
|
953
946
|
}
|
|
954
947
|
|
|
955
|
-
const paths:
|
|
948
|
+
const paths: ValueWrite[] = [];
|
|
956
949
|
|
|
957
950
|
for (const key in maybePOJO) {
|
|
958
951
|
const value = maybePOJO[key];
|
|
@@ -968,7 +961,7 @@ const buildPathsFromValue = (
|
|
|
968
961
|
return paths;
|
|
969
962
|
};
|
|
970
963
|
|
|
971
|
-
const buildValueWrites = (partialValue: unknown):
|
|
964
|
+
const buildValueWrites = (partialValue: unknown): ValueWrite[] =>
|
|
972
965
|
isPOJO(partialValue) ? buildPathsFromValue(partialValue) : [{ path: [], value: partialValue }];
|
|
973
966
|
|
|
974
967
|
export const overrideProps = (fiber: Fiber, partialValue: Record<string, unknown>) => {
|
|
@@ -985,11 +978,10 @@ export const overrideHookState = (fiber: Fiber, id: number, partialValue: unknow
|
|
|
985
978
|
const writes = buildValueWrites(partialValue);
|
|
986
979
|
|
|
987
980
|
if (renderers.length > 0) {
|
|
988
|
-
const hookId = String(id);
|
|
989
981
|
for (const renderer of renderers) {
|
|
990
982
|
for (const { path, value } of writes) {
|
|
991
983
|
try {
|
|
992
|
-
renderer.overrideHookState?.(fiber,
|
|
984
|
+
renderer.overrideHookState?.(fiber, id, path, value);
|
|
993
985
|
} catch {}
|
|
994
986
|
}
|
|
995
987
|
}
|
|
@@ -1022,24 +1014,35 @@ export const overrideContext = (fiber: Fiber, contextType: unknown, partialValue
|
|
|
1022
1014
|
export interface InstrumentationOptions {
|
|
1023
1015
|
name?: string;
|
|
1024
1016
|
onActive?: () => unknown;
|
|
1025
|
-
onCommitFiberRoot?: (
|
|
1017
|
+
onCommitFiberRoot?: (
|
|
1018
|
+
rendererID: number,
|
|
1019
|
+
root: FiberRoot,
|
|
1020
|
+
priority: number | void,
|
|
1021
|
+
didError?: boolean,
|
|
1022
|
+
) => unknown;
|
|
1026
1023
|
onCommitFiberUnmount?: (rendererID: number, fiber: Fiber) => unknown;
|
|
1027
1024
|
onPostCommitFiberRoot?: (rendererID: number, root: FiberRoot) => unknown;
|
|
1028
1025
|
onScheduleFiberRoot?: (rendererID: number, root: FiberRoot, children: React.ReactNode) => unknown;
|
|
1029
1026
|
}
|
|
1030
1027
|
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
const
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1028
|
+
interface InstrumentationSubscription {
|
|
1029
|
+
options: InstrumentationOptions;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
const instrumentationSubscriptions = new Set<InstrumentationSubscription>();
|
|
1033
|
+
|
|
1034
|
+
const runInstrumentationCallback = (
|
|
1035
|
+
instrumentationName: string,
|
|
1036
|
+
callbackName: string,
|
|
1037
|
+
callback: () => unknown,
|
|
1038
|
+
): void =>
|
|
1039
|
+
runWithBippyError(
|
|
1040
|
+
callback,
|
|
1041
|
+
(cause) => new BippyInstrumentationError(instrumentationName, callbackName, cause),
|
|
1042
|
+
);
|
|
1043
|
+
|
|
1044
|
+
const runReactDevToolsCallback = (callbackName: string, callback: () => unknown): void =>
|
|
1045
|
+
runWithBippyError(callback, (cause) => new BippyReactDevToolsError(callbackName, cause));
|
|
1043
1046
|
|
|
1044
1047
|
interface HookDispatchers {
|
|
1045
1048
|
onCommitFiberRoot: ReactDevToolsGlobalHook["onCommitFiberRoot"];
|
|
@@ -1049,6 +1052,7 @@ interface HookDispatchers {
|
|
|
1049
1052
|
}
|
|
1050
1053
|
|
|
1051
1054
|
const hookDispatchers = new WeakMap<ReactDevToolsGlobalHook, Partial<HookDispatchers>>();
|
|
1055
|
+
let didSubscribeToHookReplacements = false;
|
|
1052
1056
|
|
|
1053
1057
|
const rootRendererIds = new WeakMap<FiberRoot, number>();
|
|
1054
1058
|
|
|
@@ -1057,7 +1061,7 @@ const rootRendererIds = new WeakMap<FiberRoot, number>();
|
|
|
1057
1061
|
// next instrument() call installs a fresh wrapper over it; a superseded
|
|
1058
1062
|
// wrapper still forwards the previous chain but skips the listeners so
|
|
1059
1063
|
// they never fire twice.
|
|
1060
|
-
const
|
|
1064
|
+
const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
1061
1065
|
const dispatchers = hookDispatchers.get(rdtHook) ?? {};
|
|
1062
1066
|
hookDispatchers.set(rdtHook, dispatchers);
|
|
1063
1067
|
|
|
@@ -1070,13 +1074,35 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1070
1074
|
rendererID,
|
|
1071
1075
|
root,
|
|
1072
1076
|
priority,
|
|
1077
|
+
didError,
|
|
1073
1078
|
) => {
|
|
1074
|
-
prevOnCommitFiberRoot
|
|
1079
|
+
if (prevOnCommitFiberRoot) {
|
|
1080
|
+
runReactDevToolsCallback("onCommitFiberRoot", () =>
|
|
1081
|
+
prevOnCommitFiberRoot(rendererID, root, priority, didError),
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
1075
1084
|
if (hookDispatchers.get(rdtHook)?.onCommitFiberRoot !== dispatchCommitFiberRoot) return;
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1085
|
+
setReactWorkTagsForFiber(root.current, rdtHook.renderers.get(rendererID));
|
|
1086
|
+
const rootMemoizedState = root.current.memoizedState;
|
|
1087
|
+
const isUnmounting =
|
|
1088
|
+
rootMemoizedState === null ||
|
|
1089
|
+
rootMemoizedState.element === null ||
|
|
1090
|
+
rootMemoizedState.element === undefined;
|
|
1091
|
+
if (isUnmounting) {
|
|
1092
|
+
_fiberRoots.delete(root);
|
|
1093
|
+
rootRendererIds.delete(root);
|
|
1094
|
+
} else {
|
|
1095
|
+
_fiberRoots.add(root);
|
|
1096
|
+
rootRendererIds.set(root, rendererID);
|
|
1097
|
+
}
|
|
1098
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
1099
|
+
if (options.onCommitFiberRoot) {
|
|
1100
|
+
runInstrumentationCallback(
|
|
1101
|
+
options.name ?? BIPPY_INSTRUMENTATION_STRING,
|
|
1102
|
+
"onCommitFiberRoot",
|
|
1103
|
+
() => options.onCommitFiberRoot?.(rendererID, root, priority, didError),
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1080
1106
|
}
|
|
1081
1107
|
};
|
|
1082
1108
|
dispatchers.onCommitFiberRoot = dispatchCommitFiberRoot;
|
|
@@ -1092,12 +1118,23 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1092
1118
|
rendererID,
|
|
1093
1119
|
fiber,
|
|
1094
1120
|
) => {
|
|
1095
|
-
|
|
1121
|
+
setReactWorkTagsForFiber(fiber, rdtHook.renderers.get(rendererID));
|
|
1122
|
+
if (prevOnCommitFiberUnmount) {
|
|
1123
|
+
runReactDevToolsCallback("onCommitFiberUnmount", () =>
|
|
1124
|
+
prevOnCommitFiberUnmount(rendererID, fiber),
|
|
1125
|
+
);
|
|
1126
|
+
}
|
|
1096
1127
|
if (hookDispatchers.get(rdtHook)?.onCommitFiberUnmount !== dispatchCommitFiberUnmount) {
|
|
1097
1128
|
return;
|
|
1098
1129
|
}
|
|
1099
|
-
for (const
|
|
1100
|
-
|
|
1130
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
1131
|
+
if (options.onCommitFiberUnmount) {
|
|
1132
|
+
runInstrumentationCallback(
|
|
1133
|
+
options.name ?? BIPPY_INSTRUMENTATION_STRING,
|
|
1134
|
+
"onCommitFiberUnmount",
|
|
1135
|
+
() => options.onCommitFiberUnmount?.(rendererID, fiber),
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1101
1138
|
}
|
|
1102
1139
|
};
|
|
1103
1140
|
dispatchers.onCommitFiberUnmount = dispatchCommitFiberUnmount;
|
|
@@ -1113,12 +1150,22 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1113
1150
|
rendererID,
|
|
1114
1151
|
root,
|
|
1115
1152
|
) => {
|
|
1116
|
-
prevOnPostCommitFiberRoot
|
|
1153
|
+
if (prevOnPostCommitFiberRoot) {
|
|
1154
|
+
runReactDevToolsCallback("onPostCommitFiberRoot", () =>
|
|
1155
|
+
prevOnPostCommitFiberRoot(rendererID, root),
|
|
1156
|
+
);
|
|
1157
|
+
}
|
|
1117
1158
|
if (hookDispatchers.get(rdtHook)?.onPostCommitFiberRoot !== dispatchPostCommitFiberRoot) {
|
|
1118
1159
|
return;
|
|
1119
1160
|
}
|
|
1120
|
-
for (const
|
|
1121
|
-
|
|
1161
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
1162
|
+
if (options.onPostCommitFiberRoot) {
|
|
1163
|
+
runInstrumentationCallback(
|
|
1164
|
+
options.name ?? BIPPY_INSTRUMENTATION_STRING,
|
|
1165
|
+
"onPostCommitFiberRoot",
|
|
1166
|
+
() => options.onPostCommitFiberRoot?.(rendererID, root),
|
|
1167
|
+
);
|
|
1168
|
+
}
|
|
1122
1169
|
}
|
|
1123
1170
|
};
|
|
1124
1171
|
dispatchers.onPostCommitFiberRoot = dispatchPostCommitFiberRoot;
|
|
@@ -1135,10 +1182,20 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1135
1182
|
root,
|
|
1136
1183
|
children,
|
|
1137
1184
|
) => {
|
|
1138
|
-
prevOnScheduleFiberRoot
|
|
1185
|
+
if (prevOnScheduleFiberRoot) {
|
|
1186
|
+
runReactDevToolsCallback("onScheduleFiberRoot", () =>
|
|
1187
|
+
prevOnScheduleFiberRoot(rendererID, root, children),
|
|
1188
|
+
);
|
|
1189
|
+
}
|
|
1139
1190
|
if (hookDispatchers.get(rdtHook)?.onScheduleFiberRoot !== dispatchScheduleFiberRoot) return;
|
|
1140
|
-
for (const
|
|
1141
|
-
|
|
1191
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
1192
|
+
if (options.onScheduleFiberRoot) {
|
|
1193
|
+
runInstrumentationCallback(
|
|
1194
|
+
options.name ?? BIPPY_INSTRUMENTATION_STRING,
|
|
1195
|
+
"onScheduleFiberRoot",
|
|
1196
|
+
() => options.onScheduleFiberRoot?.(rendererID, root, children),
|
|
1197
|
+
);
|
|
1198
|
+
}
|
|
1142
1199
|
}
|
|
1143
1200
|
};
|
|
1144
1201
|
dispatchers.onScheduleFiberRoot = dispatchScheduleFiberRoot;
|
|
@@ -1165,36 +1222,35 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1165
1222
|
* unsubscribe();
|
|
1166
1223
|
*/
|
|
1167
1224
|
export const instrument = (options: InstrumentationOptions): Unsubscribe => {
|
|
1168
|
-
const
|
|
1225
|
+
const onActiveListener = options.onActive;
|
|
1226
|
+
const onActive = onActiveListener
|
|
1227
|
+
? () =>
|
|
1228
|
+
runInstrumentationCallback(
|
|
1229
|
+
options.name ?? BIPPY_INSTRUMENTATION_STRING,
|
|
1230
|
+
"onActive",
|
|
1231
|
+
onActiveListener,
|
|
1232
|
+
)
|
|
1233
|
+
: undefined;
|
|
1234
|
+
const rdtHook = getRDTHook(onActive);
|
|
1235
|
+
|
|
1236
|
+
if (!didSubscribeToHookReplacements) {
|
|
1237
|
+
onRDTHookReplace(setHookEventDispatchers);
|
|
1238
|
+
didSubscribeToHookReplacements = true;
|
|
1239
|
+
}
|
|
1169
1240
|
|
|
1170
1241
|
rdtHook._instrumentationSource = options.name ?? BIPPY_INSTRUMENTATION_STRING;
|
|
1171
1242
|
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
onActive,
|
|
1176
|
-
onCommitFiberRoot,
|
|
1177
|
-
onCommitFiberUnmount,
|
|
1178
|
-
onPostCommitFiberRoot,
|
|
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);
|
|
1243
|
+
setHookEventDispatchers(rdtHook);
|
|
1244
|
+
const subscription: InstrumentationSubscription = { options };
|
|
1245
|
+
instrumentationSubscriptions.add(subscription);
|
|
1185
1246
|
|
|
1186
1247
|
return toUnsubscribe(() => {
|
|
1187
1248
|
if (onActive) _onActiveListeners.delete(onActive);
|
|
1188
|
-
|
|
1189
|
-
if (onCommitFiberUnmount) commitFiberUnmountListeners.delete(onCommitFiberUnmount);
|
|
1190
|
-
if (onPostCommitFiberRoot) postCommitFiberRootListeners.delete(onPostCommitFiberRoot);
|
|
1191
|
-
if (onScheduleFiberRoot) scheduleFiberRootListeners.delete(onScheduleFiberRoot);
|
|
1249
|
+
instrumentationSubscriptions.delete(subscription);
|
|
1192
1250
|
});
|
|
1193
1251
|
};
|
|
1194
1252
|
|
|
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.
|
|
1253
|
+
// React uses per-renderer suffixes for host-instance Fiber keys, so discovered keys are cached.
|
|
1198
1254
|
const knownFiberPropertyKeys = new Set<string>();
|
|
1199
1255
|
|
|
1200
1256
|
const isFiberPropertyKey = (key: string): boolean =>
|
|
@@ -1202,6 +1258,32 @@ const isFiberPropertyKey = (key: string): boolean =>
|
|
|
1202
1258
|
key.startsWith("__reactInternalInstance$") ||
|
|
1203
1259
|
key.startsWith("__reactFiber");
|
|
1204
1260
|
|
|
1261
|
+
const getLegacyRootFiber = (hostInstance: Record<string, unknown>): Fiber | null => {
|
|
1262
|
+
const reactRootContainer = hostInstance._reactRootContainer;
|
|
1263
|
+
if (!isObjectRecord(reactRootContainer)) return null;
|
|
1264
|
+
const internalRoot = reactRootContainer._internalRoot;
|
|
1265
|
+
if (!isObjectRecord(internalRoot)) return null;
|
|
1266
|
+
const current = internalRoot.current;
|
|
1267
|
+
if (!isObjectRecord(current)) return null;
|
|
1268
|
+
const child = current.child;
|
|
1269
|
+
return isValidFiber(child) ? child : null;
|
|
1270
|
+
};
|
|
1271
|
+
|
|
1272
|
+
const getInternalInstanceHandle = (hostInstance: Record<string, unknown>): Fiber | null => {
|
|
1273
|
+
const internalInstanceHandle =
|
|
1274
|
+
hostInstance.__internalInstanceHandle ?? hostInstance._internalInstanceHandle;
|
|
1275
|
+
return isValidFiber(internalInstanceHandle) ? internalInstanceHandle : null;
|
|
1276
|
+
};
|
|
1277
|
+
|
|
1278
|
+
const getPublicHostInstance = (stateNode: unknown): unknown => {
|
|
1279
|
+
if (!isObjectRecord(stateNode)) return stateNode;
|
|
1280
|
+
const canonical = stateNode.canonical;
|
|
1281
|
+
if (isObjectRecord(canonical) && isObjectRecord(canonical.publicInstance)) {
|
|
1282
|
+
return canonical.publicInstance;
|
|
1283
|
+
}
|
|
1284
|
+
return typeof stateNode._nativeTag === "number" ? stateNode._nativeTag : stateNode;
|
|
1285
|
+
};
|
|
1286
|
+
|
|
1205
1287
|
export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
|
|
1206
1288
|
const rdtHook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
1207
1289
|
if (rdtHook?.renderers) {
|
|
@@ -1213,42 +1295,38 @@ export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
|
|
|
1213
1295
|
}
|
|
1214
1296
|
}
|
|
1215
1297
|
|
|
1216
|
-
if (
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
return (hostInstance._reactRootContainer as any)?._internalRoot?.current?.child;
|
|
1220
|
-
}
|
|
1298
|
+
if (isObjectRecord(hostInstance)) {
|
|
1299
|
+
const legacyRootFiber = getLegacyRootFiber(hostInstance);
|
|
1300
|
+
if (legacyRootFiber) return legacyRootFiber;
|
|
1221
1301
|
|
|
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
|
-
}
|
|
1302
|
+
const internalInstanceHandle = getInternalInstanceHandle(hostInstance);
|
|
1303
|
+
if (internalInstanceHandle) return internalInstanceHandle;
|
|
1233
1304
|
|
|
1234
|
-
const hostInstanceRecord = hostInstance as Record<string, unknown>;
|
|
1235
1305
|
for (const knownKey of knownFiberPropertyKeys) {
|
|
1236
|
-
const fiber =
|
|
1237
|
-
if (fiber) return fiber
|
|
1306
|
+
const fiber = hostInstance[knownKey];
|
|
1307
|
+
if (isValidFiber(fiber)) return fiber;
|
|
1238
1308
|
}
|
|
1239
1309
|
|
|
1240
|
-
for (const key of Object.keys(
|
|
1310
|
+
for (const key of Object.keys(hostInstance)) {
|
|
1241
1311
|
if (isFiberPropertyKey(key)) {
|
|
1242
1312
|
knownFiberPropertyKeys.add(key);
|
|
1243
|
-
|
|
1313
|
+
const fiber = hostInstance[key];
|
|
1314
|
+
if (isValidFiber(fiber)) return fiber;
|
|
1244
1315
|
}
|
|
1245
1316
|
}
|
|
1317
|
+
}
|
|
1246
1318
|
|
|
1319
|
+
if (
|
|
1320
|
+
hostInstance !== null &&
|
|
1321
|
+
hostInstance !== undefined &&
|
|
1322
|
+
(typeof hostInstance === "object" || typeof hostInstance === "number")
|
|
1323
|
+
) {
|
|
1247
1324
|
for (const fiberRoot of _fiberRoots) {
|
|
1248
|
-
if (getRootRenderer(fiberRoot.current)?.findFiberByHostInstance) continue;
|
|
1249
1325
|
const fiber = traverseFiber(
|
|
1250
1326
|
fiberRoot.current,
|
|
1251
|
-
(candidateFiber) =>
|
|
1327
|
+
(candidateFiber) =>
|
|
1328
|
+
isHostFiber(candidateFiber) &&
|
|
1329
|
+
getPublicHostInstance(candidateFiber.stateNode) === hostInstance,
|
|
1252
1330
|
);
|
|
1253
1331
|
if (fiber) return fiber;
|
|
1254
1332
|
}
|
|
@@ -1256,6 +1334,18 @@ export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
|
|
|
1256
1334
|
return null;
|
|
1257
1335
|
};
|
|
1258
1336
|
|
|
1259
|
-
export
|
|
1337
|
+
export {
|
|
1338
|
+
BIPPY_INSTRUMENTATION_STRING,
|
|
1339
|
+
_onActiveListeners,
|
|
1340
|
+
_renderers,
|
|
1341
|
+
getRDTHook,
|
|
1342
|
+
hasRDTHook,
|
|
1343
|
+
installRDTHook,
|
|
1344
|
+
isRealReactDevtools,
|
|
1345
|
+
onRendererInject,
|
|
1346
|
+
patchRDTHook,
|
|
1347
|
+
safelyInstallRDTHook,
|
|
1348
|
+
version,
|
|
1349
|
+
} from "./rdt-hook.js";
|
|
1260
1350
|
export * from "./unsubscribe.js";
|
|
1261
1351
|
export type * from "./types.js";
|