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