bippy 0.6.1 → 0.7.0-dev.697d535
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 +223 -463
- package/dist/core.cjs +1 -1
- package/dist/core.js +1 -1
- package/dist/errors.d.cts +410 -0
- package/dist/errors.d.ts +410 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +153 -3
- package/dist/index.d.ts +153 -3
- 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.js +1 -1
- package/dist/rdt-hook.cjs +1 -1
- package/dist/rdt-hook.js +1 -1
- package/dist/source.cjs +14 -5
- package/dist/source.d.cts +86 -69
- package/dist/source.d.ts +86 -69
- package/dist/source.js +14 -5
- package/package.json +26 -27
- package/src/core.ts +353 -685
- package/src/errors.ts +34 -0
- package/src/index.ts +1 -0
- package/src/install-hook-only.ts +2 -2
- package/src/rdt-hook.ts +160 -161
- package/src/react-internals/generated/react-work-tags.ts +318 -0
- package/src/react-internals/index.ts +67 -0
- package/src/react-internals/semver.ts +80 -0
- package/src/react-internals/types.ts +186 -0
- package/src/react.ts +76 -0
- package/src/source/constants.ts +1 -1
- package/src/source/error-stack.ts +11 -0
- package/src/source/get-display-name-from-source.ts +44 -40
- package/src/source/get-source.ts +43 -26
- package/src/source/index.ts +11 -1
- package/src/source/inspect-hooks.ts +220 -207
- package/src/source/owner-stack.ts +119 -174
- package/src/source/parse-debug-stack.ts +4 -4
- package/src/source/parse-hook-names.ts +14 -53
- package/src/source/parse-stack.ts +14 -31
- package/src/source/renderer-dispatchers.ts +30 -0
- package/src/source/symbolication.ts +709 -120
- package/dist/core.d.cts +0 -214
- package/dist/core.d.ts +0 -214
- package/dist/core2.d.cts +0 -3
- package/dist/core2.d.ts +0 -3
- package/dist/get-source.cjs +0 -19
- package/dist/get-source.js +0 -19
- package/dist/index.iife.js +0 -9
- package/dist/install-hook-only.iife.js +0 -9
- 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,71 +1,58 @@
|
|
|
1
|
-
//
|
|
2
|
-
// since it will be executed before the react devtools hook is created
|
|
3
|
-
|
|
1
|
+
// React must remain a type-only import because this module loads immediately after the DevTools hook.
|
|
4
2
|
import type * as React from "react";
|
|
5
3
|
|
|
6
4
|
import type {
|
|
7
|
-
ContextDependency,
|
|
8
5
|
Fiber,
|
|
9
6
|
FiberRoot,
|
|
10
|
-
|
|
7
|
+
HostFiber,
|
|
11
8
|
ReactDevToolsGlobalHook,
|
|
12
9
|
ReactRenderer,
|
|
13
|
-
} from "./
|
|
10
|
+
} from "./react-internals/index.js";
|
|
14
11
|
|
|
15
12
|
import {
|
|
16
13
|
_onActiveListeners,
|
|
17
14
|
BIPPY_INSTRUMENTATION_STRING,
|
|
15
|
+
createUnsubscribe,
|
|
18
16
|
getRDTHook,
|
|
19
17
|
hasRDTHook,
|
|
20
|
-
isReactRefresh,
|
|
21
18
|
isRealReactDevtools,
|
|
22
|
-
|
|
19
|
+
onRDTHookReplace,
|
|
23
20
|
} from "./rdt-hook.js";
|
|
24
|
-
import {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
export
|
|
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
|
-
const Placement = 0b10;
|
|
60
|
-
const Hydrating = 0b1000000000000;
|
|
61
|
-
const Update = 0b100;
|
|
62
|
-
const Cloned = 0b1000;
|
|
63
|
-
const ChildDeletion = 0b10000;
|
|
64
|
-
const ContentReset = 0b100000;
|
|
65
|
-
const Snapshot = 0b10000000000;
|
|
66
|
-
const Visibility = 0b10000000000000;
|
|
67
|
-
const MutationMask =
|
|
68
|
-
Placement | Update | ChildDeletion | ContentReset | Hydrating | Visibility | Snapshot;
|
|
21
|
+
import type { Unsubscribe } from "./rdt-hook.js";
|
|
22
|
+
import {
|
|
23
|
+
getReactWorkTagsForFiber,
|
|
24
|
+
MutationMask,
|
|
25
|
+
ReactBuildType,
|
|
26
|
+
ReactFiberFlags,
|
|
27
|
+
ReactSymbols,
|
|
28
|
+
setReactWorkTagsForFiber,
|
|
29
|
+
} from "./react-internals/index.js";
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
BippyError,
|
|
33
|
+
BippyHookInspectionError,
|
|
34
|
+
BippyHookRenderError,
|
|
35
|
+
BippySourceMapError,
|
|
36
|
+
BippyUnsupportedHookError,
|
|
37
|
+
} from "./errors.js";
|
|
38
|
+
|
|
39
|
+
const isComponentType = (value: unknown): value is React.ComponentType<unknown> =>
|
|
40
|
+
typeof value === "function";
|
|
41
|
+
|
|
42
|
+
const getTypeName = (value: object): string | null => {
|
|
43
|
+
const displayName = "displayName" in value ? value.displayName : null;
|
|
44
|
+
if (typeof displayName === "string" && displayName) return displayName;
|
|
45
|
+
const name = "name" in value ? value.name : null;
|
|
46
|
+
return typeof name === "string" && name ? name : null;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
interface FiberSelector {
|
|
50
|
+
(node: Fiber): boolean | Promise<boolean | void> | void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface RenderHandler {
|
|
54
|
+
(fiber: Fiber, phase: RenderPhase): unknown;
|
|
55
|
+
}
|
|
69
56
|
|
|
70
57
|
/**
|
|
71
58
|
* Returns `true` if object is a React Element.
|
|
@@ -74,19 +61,17 @@ const MutationMask =
|
|
|
74
61
|
*/
|
|
75
62
|
export const isValidElement = (element: unknown): element is React.ReactElement =>
|
|
76
63
|
typeof element === "object" &&
|
|
77
|
-
element
|
|
64
|
+
element !== null &&
|
|
78
65
|
"$$typeof" in element &&
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
String(element.$$typeof),
|
|
82
|
-
);
|
|
66
|
+
(String(element.$$typeof) === ReactSymbols.LEGACY_ELEMENT_SYMBOL_STRING ||
|
|
67
|
+
String(element.$$typeof) === ReactSymbols.ELEMENT_SYMBOL_STRING);
|
|
83
68
|
|
|
84
69
|
/**
|
|
85
70
|
* Returns `true` if object is a React Fiber.
|
|
86
71
|
*/
|
|
87
|
-
export const
|
|
72
|
+
export const isFiber = (fiber: unknown): fiber is Fiber =>
|
|
88
73
|
typeof fiber === "object" &&
|
|
89
|
-
fiber
|
|
74
|
+
fiber !== null &&
|
|
90
75
|
"tag" in fiber &&
|
|
91
76
|
"stateNode" in fiber &&
|
|
92
77
|
"return" in fiber &&
|
|
@@ -94,21 +79,27 @@ export const isValidFiber = (fiber: unknown): fiber is Fiber =>
|
|
|
94
79
|
"sibling" in fiber &&
|
|
95
80
|
"flags" in fiber;
|
|
96
81
|
|
|
82
|
+
const isFiberRoot = (fiberRoot: unknown): fiberRoot is FiberRoot =>
|
|
83
|
+
typeof fiberRoot === "object" &&
|
|
84
|
+
fiberRoot !== null &&
|
|
85
|
+
"current" in fiberRoot &&
|
|
86
|
+
isFiber(fiberRoot.current);
|
|
87
|
+
|
|
97
88
|
/**
|
|
98
89
|
* Returns `true` if fiber is a host fiber. Host fibers are DOM nodes in react-dom, `View` in react-native, etc.
|
|
99
90
|
*
|
|
100
91
|
* @see https://reactnative.dev/architecture/glossary#host-view-tree-and-host-view
|
|
101
92
|
*/
|
|
102
|
-
export const isHostFiber = (fiber: Fiber):
|
|
93
|
+
export const isHostFiber = (fiber: Fiber): fiber is HostFiber => {
|
|
94
|
+
const workTags = getReactWorkTagsForFiber(fiber);
|
|
103
95
|
switch (fiber.tag) {
|
|
104
|
-
case
|
|
105
|
-
|
|
106
|
-
case
|
|
107
|
-
|
|
108
|
-
case HostSingletonTag:
|
|
96
|
+
case workTags.HostComponent:
|
|
97
|
+
case workTags.HostText:
|
|
98
|
+
case workTags.HostHoistable:
|
|
99
|
+
case workTags.HostSingleton:
|
|
109
100
|
return true;
|
|
110
101
|
default:
|
|
111
|
-
return
|
|
102
|
+
return false;
|
|
112
103
|
}
|
|
113
104
|
};
|
|
114
105
|
|
|
@@ -118,128 +109,36 @@ export const isHostFiber = (fiber: Fiber): boolean => {
|
|
|
118
109
|
* @see https://reactnative.dev/architecture/glossary#react-composite-components
|
|
119
110
|
*/
|
|
120
111
|
export const isCompositeFiber = (fiber: Fiber): boolean => {
|
|
112
|
+
const workTags = getReactWorkTagsForFiber(fiber);
|
|
121
113
|
switch (fiber.tag) {
|
|
122
|
-
case
|
|
123
|
-
case
|
|
124
|
-
case
|
|
125
|
-
case
|
|
126
|
-
case
|
|
114
|
+
case workTags.ClassComponent:
|
|
115
|
+
case workTags.ForwardRef:
|
|
116
|
+
case workTags.FunctionComponent:
|
|
117
|
+
case workTags.MemoComponent:
|
|
118
|
+
case workTags.SimpleMemoComponent:
|
|
127
119
|
return true;
|
|
128
120
|
default:
|
|
129
121
|
return false;
|
|
130
122
|
}
|
|
131
123
|
};
|
|
132
124
|
|
|
133
|
-
/**
|
|
134
|
-
* Returns `true` if the object is a {@link Fiber}
|
|
135
|
-
*/
|
|
136
|
-
export const isFiber = (maybeFiber: unknown): maybeFiber is Fiber => {
|
|
137
|
-
if (!maybeFiber || typeof maybeFiber !== "object") return false;
|
|
138
|
-
// this is a fast check. pendingProps will ALWAYS exist in fiber
|
|
139
|
-
// `containerInfo` is in FiberRootNode, not FiberNode
|
|
140
|
-
return "pendingProps" in maybeFiber && !("containerInfo" in maybeFiber);
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Traverses up or down a {@link Fiber}'s contexts, return `true` to stop and select the current and previous context value.
|
|
145
|
-
*/
|
|
146
|
-
export const traverseContexts = (
|
|
147
|
-
fiber: Fiber,
|
|
148
|
-
selector: (
|
|
149
|
-
nextValue: ContextDependency<unknown> | null | undefined,
|
|
150
|
-
prevValue: ContextDependency<unknown> | null | undefined,
|
|
151
|
-
) => boolean | void,
|
|
152
|
-
): boolean => {
|
|
153
|
-
try {
|
|
154
|
-
const nextDependencies = fiber.dependencies;
|
|
155
|
-
const prevDependencies = fiber.alternate?.dependencies;
|
|
156
|
-
|
|
157
|
-
if (!nextDependencies || !prevDependencies) return false;
|
|
158
|
-
if (
|
|
159
|
-
typeof nextDependencies !== "object" ||
|
|
160
|
-
!("firstContext" in nextDependencies) ||
|
|
161
|
-
typeof prevDependencies !== "object" ||
|
|
162
|
-
!("firstContext" in prevDependencies)
|
|
163
|
-
) {
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
let nextContext: ContextDependency<unknown> | null | undefined = nextDependencies.firstContext;
|
|
167
|
-
let prevContext: ContextDependency<unknown> | null | undefined = prevDependencies.firstContext;
|
|
168
|
-
while (
|
|
169
|
-
(nextContext && typeof nextContext === "object" && "memoizedValue" in nextContext) ||
|
|
170
|
-
(prevContext && typeof prevContext === "object" && "memoizedValue" in prevContext)
|
|
171
|
-
) {
|
|
172
|
-
if (selector(nextContext, prevContext) === true) return true;
|
|
173
|
-
|
|
174
|
-
nextContext = nextContext?.next;
|
|
175
|
-
prevContext = prevContext?.next;
|
|
176
|
-
}
|
|
177
|
-
} catch {}
|
|
178
|
-
return false;
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Traverses up or down a {@link Fiber}'s states, return `true` to stop and select the current and previous state value. This stores both state values and effects.
|
|
183
|
-
*/
|
|
184
|
-
export const traverseState = (
|
|
185
|
-
fiber: Fiber,
|
|
186
|
-
selector: (
|
|
187
|
-
nextValue: MemoizedState | null | undefined,
|
|
188
|
-
prevValue: MemoizedState | null | undefined,
|
|
189
|
-
) => boolean | void,
|
|
190
|
-
): boolean => {
|
|
191
|
-
try {
|
|
192
|
-
let nextState: MemoizedState | null | undefined = fiber.memoizedState;
|
|
193
|
-
let prevState: MemoizedState | null | undefined = fiber.alternate?.memoizedState;
|
|
194
|
-
|
|
195
|
-
while (nextState || prevState) {
|
|
196
|
-
if (selector(nextState, prevState) === true) return true;
|
|
197
|
-
|
|
198
|
-
nextState = nextState?.next;
|
|
199
|
-
prevState = prevState?.next;
|
|
200
|
-
}
|
|
201
|
-
} catch {}
|
|
202
|
-
return false;
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Traverses up or down a {@link Fiber}'s props, return `true` to stop and select the current and previous props value.
|
|
207
|
-
*/
|
|
208
|
-
export const traverseProps = (
|
|
209
|
-
fiber: Fiber,
|
|
210
|
-
selector: (propName: string, nextValue: unknown, prevValue: unknown) => boolean | void,
|
|
211
|
-
): boolean => {
|
|
212
|
-
try {
|
|
213
|
-
const nextProps = fiber.memoizedProps;
|
|
214
|
-
const prevProps = fiber.alternate?.memoizedProps || {};
|
|
215
|
-
|
|
216
|
-
for (const propName of Object.keys(nextProps)) {
|
|
217
|
-
if (selector(propName, nextProps[propName], prevProps[propName]) === true) return true;
|
|
218
|
-
}
|
|
219
|
-
for (const propName of Object.keys(prevProps)) {
|
|
220
|
-
if (propName in nextProps) continue;
|
|
221
|
-
if (selector(propName, nextProps[propName], prevProps[propName]) === true) return true;
|
|
222
|
-
}
|
|
223
|
-
} catch {}
|
|
224
|
-
return false;
|
|
225
|
-
};
|
|
226
|
-
|
|
227
125
|
/**
|
|
228
126
|
* Returns `true` if the {@link Fiber} has rendered. Note that this does not mean the fiber has rendered in the current commit, just that it has rendered in the past.
|
|
229
127
|
*/
|
|
230
128
|
export const didFiberRender = (fiber: Fiber): boolean => {
|
|
231
129
|
const nextProps = fiber.memoizedProps;
|
|
232
130
|
const prevProps = fiber.alternate?.memoizedProps || {};
|
|
233
|
-
const flags = fiber.flags ??
|
|
131
|
+
const flags = fiber.flags ?? fiber.effectTag ?? 0;
|
|
132
|
+
const workTags = getReactWorkTagsForFiber(fiber);
|
|
234
133
|
|
|
235
134
|
switch (fiber.tag) {
|
|
236
|
-
case
|
|
237
|
-
case
|
|
238
|
-
case
|
|
239
|
-
case
|
|
240
|
-
case
|
|
241
|
-
case
|
|
242
|
-
return (flags & PerformedWork) === PerformedWork;
|
|
135
|
+
case workTags.ClassComponent:
|
|
136
|
+
case workTags.ContextConsumer:
|
|
137
|
+
case workTags.ForwardRef:
|
|
138
|
+
case workTags.FunctionComponent:
|
|
139
|
+
case workTags.MemoComponent:
|
|
140
|
+
case workTags.SimpleMemoComponent: {
|
|
141
|
+
return (flags & ReactFiberFlags.PerformedWork) === ReactFiberFlags.PerformedWork;
|
|
243
142
|
}
|
|
244
143
|
default:
|
|
245
144
|
// Host nodes (DOM, root, etc.)
|
|
@@ -257,57 +156,18 @@ export const didFiberRender = (fiber: Fiber): boolean => {
|
|
|
257
156
|
*/
|
|
258
157
|
export const didFiberCommit = (fiber: Fiber): boolean => {
|
|
259
158
|
return Boolean(
|
|
260
|
-
(fiber.flags & (MutationMask | Cloned)) !== 0 ||
|
|
261
|
-
(fiber.subtreeFlags & (MutationMask | Cloned)) !== 0,
|
|
159
|
+
(fiber.flags & (MutationMask | ReactFiberFlags.Cloned)) !== 0 ||
|
|
160
|
+
(fiber.subtreeFlags & (MutationMask | ReactFiberFlags.Cloned)) !== 0,
|
|
262
161
|
);
|
|
263
162
|
};
|
|
264
163
|
|
|
265
|
-
/**
|
|
266
|
-
* Returns all host {@link Fiber}s that have committed and rendered.
|
|
267
|
-
*/
|
|
268
|
-
export const getMutatedHostFibers = (fiber: Fiber): Fiber[] => {
|
|
269
|
-
const mutations: Fiber[] = [];
|
|
270
|
-
const stack: Fiber[] = [fiber];
|
|
271
|
-
|
|
272
|
-
while (stack.length) {
|
|
273
|
-
const node = stack.pop();
|
|
274
|
-
if (!node) continue;
|
|
275
|
-
|
|
276
|
-
if (isHostFiber(node) && didFiberCommit(node) && didFiberRender(node)) {
|
|
277
|
-
mutations.push(node);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (node.child) stack.push(node.child);
|
|
281
|
-
if (node.sibling) stack.push(node.sibling);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
return mutations;
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Returns the stack of {@link Fiber}s from the current fiber to the root fiber.
|
|
289
|
-
*
|
|
290
|
-
* @example
|
|
291
|
-
* ```ts
|
|
292
|
-
* [fiber, fiber.return, fiber.return.return, ...]
|
|
293
|
-
* ```
|
|
294
|
-
*/
|
|
295
|
-
export const getFiberStack = (fiber: Fiber): Fiber[] => {
|
|
296
|
-
const stack: Fiber[] = [];
|
|
297
|
-
let currentFiber = fiber;
|
|
298
|
-
while (currentFiber.return) {
|
|
299
|
-
stack.push(currentFiber);
|
|
300
|
-
currentFiber = currentFiber.return as Fiber;
|
|
301
|
-
}
|
|
302
|
-
return stack;
|
|
303
|
-
};
|
|
304
|
-
|
|
305
164
|
/**
|
|
306
165
|
* Returns `true` if the {@link Fiber} should be filtered out during reconciliation.
|
|
307
166
|
*/
|
|
308
167
|
const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
168
|
+
const workTags = getReactWorkTagsForFiber(fiber);
|
|
309
169
|
switch (fiber.tag) {
|
|
310
|
-
case
|
|
170
|
+
case workTags.DehydratedSuspenseComponent:
|
|
311
171
|
// TODO: ideally we would show dehydrated Suspense immediately.
|
|
312
172
|
// However, it has some special behavior (like disconnecting
|
|
313
173
|
// an alternate and turning into real Suspense) which breaks DevTools.
|
|
@@ -315,13 +175,13 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
|
315
175
|
// https://github.com/bvaughn/react-devtools-experimental/issues/197
|
|
316
176
|
return true;
|
|
317
177
|
|
|
318
|
-
case
|
|
319
|
-
case
|
|
320
|
-
case
|
|
321
|
-
case
|
|
178
|
+
case workTags.Fragment:
|
|
179
|
+
case workTags.HostText:
|
|
180
|
+
case workTags.LegacyHiddenComponent:
|
|
181
|
+
case workTags.OffscreenComponent:
|
|
322
182
|
return true;
|
|
323
183
|
|
|
324
|
-
case
|
|
184
|
+
case workTags.HostRoot:
|
|
325
185
|
// It is never valid to filter the root element.
|
|
326
186
|
return false;
|
|
327
187
|
|
|
@@ -331,15 +191,15 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
|
331
191
|
|
|
332
192
|
if (typeof symbolOrNumber === "symbol") {
|
|
333
193
|
return (
|
|
334
|
-
symbolOrNumber.description === CONCURRENT_MODE_SYMBOL_DESCRIPTION ||
|
|
335
|
-
symbolOrNumber.description === DEPRECATED_ASYNC_MODE_SYMBOL_DESCRIPTION
|
|
194
|
+
symbolOrNumber.description === ReactSymbols.CONCURRENT_MODE_SYMBOL_DESCRIPTION ||
|
|
195
|
+
symbolOrNumber.description === ReactSymbols.DEPRECATED_ASYNC_MODE_SYMBOL_DESCRIPTION
|
|
336
196
|
);
|
|
337
197
|
}
|
|
338
198
|
|
|
339
199
|
switch (symbolOrNumber) {
|
|
340
|
-
case CONCURRENT_MODE_NUMBER:
|
|
341
|
-
case CONCURRENT_MODE_SYMBOL_STRING:
|
|
342
|
-
case DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
|
|
200
|
+
case ReactSymbols.CONCURRENT_MODE_NUMBER:
|
|
201
|
+
case ReactSymbols.CONCURRENT_MODE_SYMBOL_STRING:
|
|
202
|
+
case ReactSymbols.DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
|
|
343
203
|
return true;
|
|
344
204
|
|
|
345
205
|
default:
|
|
@@ -349,47 +209,6 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
|
349
209
|
}
|
|
350
210
|
};
|
|
351
211
|
|
|
352
|
-
/**
|
|
353
|
-
* Returns the nearest host {@link Fiber} to the current {@link Fiber}.
|
|
354
|
-
*/
|
|
355
|
-
export const getNearestHostFiber = (fiber: Fiber, ascending = false): Fiber | null => {
|
|
356
|
-
let hostFiber = traverseFiber(fiber, isHostFiber, ascending);
|
|
357
|
-
if (!hostFiber) {
|
|
358
|
-
hostFiber = traverseFiber(fiber, isHostFiber, !ascending);
|
|
359
|
-
}
|
|
360
|
-
return hostFiber;
|
|
361
|
-
};
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Returns all host {@link Fiber}s in the tree that are associated with the current {@link Fiber}.
|
|
365
|
-
*/
|
|
366
|
-
export const getNearestHostFibers = (fiber: Fiber): Fiber[] => {
|
|
367
|
-
const hostFibers: Fiber[] = [];
|
|
368
|
-
const stack: Fiber[] = [];
|
|
369
|
-
|
|
370
|
-
if (isHostFiber(fiber)) {
|
|
371
|
-
hostFibers.push(fiber);
|
|
372
|
-
} else if (fiber.child) {
|
|
373
|
-
stack.push(fiber.child);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
while (stack.length) {
|
|
377
|
-
const currentNode = stack.pop();
|
|
378
|
-
if (!currentNode) break;
|
|
379
|
-
if (isHostFiber(currentNode)) {
|
|
380
|
-
hostFibers.push(currentNode);
|
|
381
|
-
} else if (currentNode.child) {
|
|
382
|
-
stack.push(currentNode.child);
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
if (currentNode.sibling) {
|
|
386
|
-
stack.push(currentNode.sibling);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
return hostFibers;
|
|
391
|
-
};
|
|
392
|
-
|
|
393
212
|
/**
|
|
394
213
|
* Traverses up or down a {@link Fiber}, return `true` to stop and select a node.
|
|
395
214
|
*/
|
|
@@ -405,144 +224,86 @@ export function traverseFiber(
|
|
|
405
224
|
): Promise<Fiber | null>;
|
|
406
225
|
export function traverseFiber(
|
|
407
226
|
fiber: Fiber | null,
|
|
408
|
-
selector:
|
|
227
|
+
selector: FiberSelector,
|
|
228
|
+
ascending?: boolean,
|
|
229
|
+
): Fiber | null | Promise<Fiber | null>;
|
|
230
|
+
export function traverseFiber(
|
|
231
|
+
fiber: Fiber | null,
|
|
232
|
+
selector: FiberSelector,
|
|
409
233
|
ascending = false,
|
|
410
234
|
): Fiber | null | Promise<Fiber | null> {
|
|
411
235
|
if (!fiber) return null;
|
|
412
236
|
|
|
413
|
-
const
|
|
414
|
-
if (
|
|
415
|
-
return (
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
let child = ascending ? fiber.return : fiber.child;
|
|
419
|
-
while (child) {
|
|
420
|
-
const match = await traverseFiberAsync(
|
|
421
|
-
child,
|
|
422
|
-
selector as (node: Fiber) => Promise<boolean | void>,
|
|
423
|
-
ascending,
|
|
424
|
-
);
|
|
425
|
-
if (match) return match;
|
|
426
|
-
child = ascending ? null : child.sibling;
|
|
427
|
-
}
|
|
428
|
-
return null;
|
|
429
|
-
})();
|
|
237
|
+
const selection = selector(fiber);
|
|
238
|
+
if (isPromiseLike<boolean | void>(selection)) {
|
|
239
|
+
return Promise.resolve(selection).then((didSelectFiber) =>
|
|
240
|
+
didSelectFiber === true ? fiber : traverseFiberChildren(fiber, selector, ascending),
|
|
241
|
+
);
|
|
430
242
|
}
|
|
243
|
+
if (selection === true) return fiber;
|
|
431
244
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
let child = ascending ? fiber.return : fiber.child;
|
|
435
|
-
while (child) {
|
|
436
|
-
const match = traverseFiberSync(child, selector as (node: Fiber) => boolean | void, ascending);
|
|
437
|
-
if (match) return match;
|
|
438
|
-
child = ascending ? null : child.sibling;
|
|
439
|
-
}
|
|
440
|
-
return null;
|
|
245
|
+
return traverseFiberChildren(fiber, selector, ascending);
|
|
441
246
|
}
|
|
442
247
|
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
if (!fiber) return null;
|
|
449
|
-
if (selector(fiber) === true) return fiber;
|
|
248
|
+
const isPromiseLike = <Result>(value: unknown): value is PromiseLike<Result> =>
|
|
249
|
+
(typeof value === "object" || typeof value === "function") &&
|
|
250
|
+
value !== null &&
|
|
251
|
+
"then" in value &&
|
|
252
|
+
typeof value.then === "function";
|
|
450
253
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
return null;
|
|
254
|
+
const traverseFiberChildren = (
|
|
255
|
+
fiber: Fiber,
|
|
256
|
+
selector: FiberSelector,
|
|
257
|
+
ascending: boolean,
|
|
258
|
+
): Fiber | null | Promise<Fiber | null> => {
|
|
259
|
+
const firstChild = ascending ? fiber.return : fiber.child;
|
|
260
|
+
return traverseFiberSiblings(firstChild, selector, ascending);
|
|
459
261
|
};
|
|
460
262
|
|
|
461
|
-
const
|
|
263
|
+
const traverseFiberSiblings = (
|
|
462
264
|
fiber: Fiber | null,
|
|
463
|
-
selector:
|
|
464
|
-
ascending
|
|
465
|
-
): Promise<Fiber | null> => {
|
|
265
|
+
selector: FiberSelector,
|
|
266
|
+
ascending: boolean,
|
|
267
|
+
): Fiber | null | Promise<Fiber | null> => {
|
|
466
268
|
if (!fiber) return null;
|
|
467
|
-
if ((await selector(fiber)) === true) return fiber;
|
|
468
269
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
}
|
|
476
|
-
return null;
|
|
477
|
-
};
|
|
478
|
-
|
|
479
|
-
/**
|
|
480
|
-
* Returns the timings of the {@link Fiber}.
|
|
481
|
-
*
|
|
482
|
-
* @example
|
|
483
|
-
* ```ts
|
|
484
|
-
* const { selfTime, totalTime } = getTimings(fiber);
|
|
485
|
-
* console.log(selfTime, totalTime);
|
|
486
|
-
* ```
|
|
487
|
-
*/
|
|
488
|
-
export const getTimings = (fiber?: Fiber | null): { selfTime: number; totalTime: number } => {
|
|
489
|
-
const totalTime = fiber?.actualDuration ?? 0;
|
|
490
|
-
let selfTime = totalTime;
|
|
491
|
-
// TODO: calculate a DOM time, which is just host component summed up
|
|
492
|
-
let child = fiber?.child ?? null;
|
|
493
|
-
while (totalTime > 0 && child != null) {
|
|
494
|
-
selfTime -= child.actualDuration ?? 0;
|
|
495
|
-
child = child.sibling;
|
|
270
|
+
const nextSibling = ascending ? null : fiber.sibling;
|
|
271
|
+
const match = traverseFiber(fiber, selector, ascending);
|
|
272
|
+
if (isPromiseLike<Fiber | null>(match)) {
|
|
273
|
+
return Promise.resolve(match).then(
|
|
274
|
+
(resolvedMatch) => resolvedMatch ?? traverseFiberSiblings(nextSibling, selector, ascending),
|
|
275
|
+
);
|
|
496
276
|
}
|
|
497
|
-
return
|
|
277
|
+
return match ?? traverseFiberSiblings(nextSibling, selector, ascending);
|
|
498
278
|
};
|
|
499
279
|
|
|
500
280
|
/**
|
|
501
281
|
* Returns `true` if the {@link Fiber} uses React Compiler's memo cache.
|
|
502
282
|
*/
|
|
503
283
|
export const hasMemoCache = (fiber: Fiber): boolean => {
|
|
504
|
-
return Boolean(
|
|
284
|
+
return Boolean(fiber.updateQueue?.memoCache);
|
|
505
285
|
};
|
|
506
286
|
|
|
507
|
-
type FiberType =
|
|
508
|
-
| React.ComponentType<unknown>
|
|
509
|
-
| React.ForwardRefExoticComponent<unknown>
|
|
510
|
-
| React.MemoExoticComponent<React.ComponentType<unknown>>;
|
|
511
|
-
|
|
512
287
|
/**
|
|
513
288
|
* Returns the type (e.g. component definition) of the {@link Fiber}
|
|
514
289
|
*/
|
|
515
290
|
export const getType = (type: unknown): null | React.ComponentType<unknown> => {
|
|
516
|
-
|
|
517
|
-
if (typeof
|
|
518
|
-
|
|
519
|
-
}
|
|
520
|
-
if (typeof currentType === "object" && currentType) {
|
|
521
|
-
// memo / forwardRef case
|
|
522
|
-
return getType(
|
|
523
|
-
(currentType as React.MemoExoticComponent<React.ComponentType<unknown>>).type ||
|
|
524
|
-
(currentType as { render: React.ComponentType<unknown> }).render,
|
|
525
|
-
);
|
|
526
|
-
}
|
|
527
|
-
return null;
|
|
291
|
+
if (isComponentType(type)) return type;
|
|
292
|
+
if (typeof type !== "object" || type === null) return null;
|
|
293
|
+
return getType(Reflect.get(type, "type") ?? Reflect.get(type, "render"));
|
|
528
294
|
};
|
|
529
295
|
|
|
530
296
|
/**
|
|
531
297
|
* Returns the display name of the {@link Fiber} type.
|
|
532
298
|
*/
|
|
533
299
|
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;
|
|
300
|
+
if (typeof type === "string") return type;
|
|
301
|
+
if ((typeof type !== "function" && typeof type !== "object") || type === null) return null;
|
|
302
|
+
const name = getTypeName(type);
|
|
542
303
|
if (name) return name;
|
|
543
|
-
const unwrappedType = getType(
|
|
304
|
+
const unwrappedType = getType(type);
|
|
544
305
|
if (!unwrappedType) return null;
|
|
545
|
-
return unwrappedType
|
|
306
|
+
return getTypeName(unwrappedType);
|
|
546
307
|
};
|
|
547
308
|
|
|
548
309
|
/**
|
|
@@ -550,7 +311,10 @@ export const getDisplayName = (type: unknown): null | string => {
|
|
|
550
311
|
*/
|
|
551
312
|
export const detectReactBuildType = (renderer: ReactRenderer): "development" | "production" => {
|
|
552
313
|
try {
|
|
553
|
-
if (
|
|
314
|
+
if (
|
|
315
|
+
typeof renderer.version === "string" &&
|
|
316
|
+
renderer.bundleType === ReactBuildType.Development
|
|
317
|
+
) {
|
|
554
318
|
return "development";
|
|
555
319
|
}
|
|
556
320
|
} catch {}
|
|
@@ -562,14 +326,11 @@ export const detectReactBuildType = (renderer: ReactRenderer): "development" | "
|
|
|
562
326
|
*/
|
|
563
327
|
export const isInstrumentationActive = (): boolean => {
|
|
564
328
|
const rdtHook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
565
|
-
return (
|
|
566
|
-
Boolean(rdtHook?._instrumentationIsActive) ||
|
|
567
|
-
isRealReactDevtools(rdtHook) ||
|
|
568
|
-
isReactRefresh(rdtHook)
|
|
569
|
-
);
|
|
329
|
+
return Boolean(rdtHook?._instrumentationIsActive) || isRealReactDevtools(rdtHook);
|
|
570
330
|
};
|
|
571
331
|
|
|
572
332
|
export const _fiberRoots = new Set<FiberRoot>();
|
|
333
|
+
const rootRendererIds = new WeakMap<FiberRoot, number>();
|
|
573
334
|
|
|
574
335
|
/**
|
|
575
336
|
* Returns the latest fiber (since it may be double-buffered).
|
|
@@ -577,42 +338,71 @@ export const _fiberRoots = new Set<FiberRoot>();
|
|
|
577
338
|
export const getLatestFiber = (fiber: Fiber): Fiber => {
|
|
578
339
|
const alternate = fiber.alternate;
|
|
579
340
|
if (!alternate) return fiber;
|
|
341
|
+
|
|
342
|
+
let rootFiber = fiber;
|
|
343
|
+
while (rootFiber.return) {
|
|
344
|
+
rootFiber = rootFiber.return;
|
|
345
|
+
}
|
|
346
|
+
if (isFiberRoot(rootFiber.stateNode)) {
|
|
347
|
+
const latestFiber = traverseFiber(rootFiber.stateNode.current, (innerFiber) => {
|
|
348
|
+
if (innerFiber === fiber || innerFiber === alternate) return true;
|
|
349
|
+
});
|
|
350
|
+
if (latestFiber) return latestFiber;
|
|
351
|
+
}
|
|
352
|
+
|
|
580
353
|
if (alternate.actualStartTime && fiber.actualStartTime) {
|
|
581
354
|
return alternate.actualStartTime > fiber.actualStartTime ? alternate : fiber;
|
|
582
355
|
}
|
|
583
356
|
for (const root of _fiberRoots) {
|
|
584
357
|
const latestFiber = traverseFiber(root.current, (innerFiber) => {
|
|
585
|
-
if (innerFiber === fiber) return true;
|
|
358
|
+
if (innerFiber === fiber || innerFiber === alternate) return true;
|
|
586
359
|
});
|
|
587
360
|
if (latestFiber) return latestFiber;
|
|
588
361
|
}
|
|
589
362
|
return fiber;
|
|
590
363
|
};
|
|
591
364
|
|
|
592
|
-
|
|
365
|
+
/**
|
|
366
|
+
* Returns the renderer that owns the {@link Fiber}.
|
|
367
|
+
*/
|
|
368
|
+
export const getRenderer = (fiber: Fiber): ReactRenderer | null => {
|
|
369
|
+
if (!hasRDTHook()) return null;
|
|
370
|
+
|
|
371
|
+
let rootFiber = fiber;
|
|
372
|
+
while (rootFiber.return) {
|
|
373
|
+
rootFiber = rootFiber.return;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const fiberRoot = rootFiber.stateNode;
|
|
377
|
+
if (!isFiberRoot(fiberRoot)) return null;
|
|
378
|
+
|
|
379
|
+
const rendererID = rootRendererIds.get(fiberRoot);
|
|
380
|
+
if (rendererID === undefined) return null;
|
|
381
|
+
return getRDTHook().renderers.get(rendererID) ?? null;
|
|
382
|
+
};
|
|
593
383
|
|
|
594
384
|
export type RenderPhase = "mount" | "unmount" | "update";
|
|
595
385
|
|
|
596
|
-
let
|
|
386
|
+
let nextFiberId = 0;
|
|
597
387
|
const fiberIdMap = new WeakMap<Fiber, number>();
|
|
598
388
|
|
|
599
|
-
export const setFiberId = (fiber: Fiber,
|
|
600
|
-
fiberIdMap.set(fiber,
|
|
389
|
+
export const setFiberId = (fiber: Fiber, fiberId: number = nextFiberId++): void => {
|
|
390
|
+
fiberIdMap.set(fiber, fiberId);
|
|
391
|
+
if (Number.isSafeInteger(fiberId) && fiberId >= nextFiberId) {
|
|
392
|
+
nextFiberId = fiberId + 1;
|
|
393
|
+
}
|
|
601
394
|
};
|
|
602
395
|
|
|
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
396
|
export const getFiberId = (fiber: Fiber): number => {
|
|
607
|
-
let
|
|
608
|
-
if (
|
|
609
|
-
|
|
397
|
+
let currentFiberId = fiberIdMap.get(fiber);
|
|
398
|
+
if (currentFiberId === undefined && fiber.alternate) {
|
|
399
|
+
currentFiberId = fiberIdMap.get(fiber.alternate);
|
|
610
400
|
}
|
|
611
|
-
if (
|
|
612
|
-
|
|
613
|
-
setFiberId(fiber,
|
|
401
|
+
if (currentFiberId === undefined) {
|
|
402
|
+
currentFiberId = nextFiberId++;
|
|
403
|
+
setFiberId(fiber, currentFiberId);
|
|
614
404
|
}
|
|
615
|
-
return
|
|
405
|
+
return currentFiberId;
|
|
616
406
|
};
|
|
617
407
|
|
|
618
408
|
const mountFiberRecursively = (
|
|
@@ -622,16 +412,14 @@ const mountFiberRecursively = (
|
|
|
622
412
|
): void => {
|
|
623
413
|
let fiber: Fiber | null = firstChild;
|
|
624
414
|
|
|
625
|
-
while (fiber
|
|
626
|
-
|
|
627
|
-
getFiberId(fiber);
|
|
628
|
-
}
|
|
415
|
+
while (fiber !== null) {
|
|
416
|
+
getFiberId(fiber);
|
|
629
417
|
const shouldIncludeInTree = !shouldFilterFiber(fiber);
|
|
630
418
|
if (shouldIncludeInTree && didFiberRender(fiber)) {
|
|
631
419
|
onRender(fiber, "mount");
|
|
632
420
|
}
|
|
633
421
|
|
|
634
|
-
if (fiber.tag ===
|
|
422
|
+
if (fiber.tag === getReactWorkTagsForFiber(fiber).SuspenseComponent) {
|
|
635
423
|
const isTimedOut = fiber.memoizedState !== null;
|
|
636
424
|
if (isTimedOut) {
|
|
637
425
|
// Special case: if Suspense mounts in a timed-out state,
|
|
@@ -646,18 +434,12 @@ const mountFiberRecursively = (
|
|
|
646
434
|
}
|
|
647
435
|
}
|
|
648
436
|
} 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
|
-
}
|
|
437
|
+
const primaryChild = fiber.child?.child ?? null;
|
|
656
438
|
if (primaryChild !== null) {
|
|
657
439
|
mountFiberRecursively(onRender, primaryChild, false);
|
|
658
440
|
}
|
|
659
441
|
}
|
|
660
|
-
} else if (fiber.child
|
|
442
|
+
} else if (fiber.child !== null) {
|
|
661
443
|
mountFiberRecursively(onRender, fiber.child, true);
|
|
662
444
|
}
|
|
663
445
|
fiber = traverseSiblings ? fiber.sibling : null;
|
|
@@ -667,18 +449,13 @@ const mountFiberRecursively = (
|
|
|
667
449
|
const updateFiberRecursively = (
|
|
668
450
|
onRender: RenderHandler,
|
|
669
451
|
nextFiber: Fiber,
|
|
670
|
-
prevFiber: Fiber,
|
|
671
|
-
parentFiber: Fiber | null,
|
|
452
|
+
prevFiber: Fiber | null,
|
|
672
453
|
): void => {
|
|
673
|
-
|
|
674
|
-
getFiberId(nextFiber);
|
|
675
|
-
}
|
|
454
|
+
getFiberId(nextFiber);
|
|
676
455
|
if (!prevFiber) return;
|
|
677
|
-
|
|
678
|
-
getFiberId(prevFiber);
|
|
679
|
-
}
|
|
456
|
+
getFiberId(prevFiber);
|
|
680
457
|
|
|
681
|
-
const isSuspense = nextFiber.tag ===
|
|
458
|
+
const isSuspense = nextFiber.tag === getReactWorkTagsForFiber(nextFiber).SuspenseComponent;
|
|
682
459
|
|
|
683
460
|
const shouldIncludeInTree = !shouldFilterFiber(nextFiber);
|
|
684
461
|
if (shouldIncludeInTree && didFiberRender(nextFiber)) {
|
|
@@ -707,7 +484,7 @@ const updateFiberRecursively = (
|
|
|
707
484
|
const prevFallbackChildSet = prevFiber.child?.sibling ?? null;
|
|
708
485
|
|
|
709
486
|
if (nextFallbackChildSet !== null && prevFallbackChildSet !== null) {
|
|
710
|
-
updateFiberRecursively(onRender, nextFallbackChildSet, prevFallbackChildSet
|
|
487
|
+
updateFiberRecursively(onRender, nextFallbackChildSet, prevFallbackChildSet);
|
|
711
488
|
}
|
|
712
489
|
} else if (prevDidTimeout && !nextDidTimeOut) {
|
|
713
490
|
// Fallback -> Primary:
|
|
@@ -746,14 +523,7 @@ const updateFiberRecursively = (
|
|
|
746
523
|
// Schedule updates and mounts depending on whether alternates exist.
|
|
747
524
|
// We don't track deletions here because they are reported separately.
|
|
748
525
|
if (nextChild.alternate) {
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
updateFiberRecursively(
|
|
752
|
-
onRender,
|
|
753
|
-
nextChild,
|
|
754
|
-
prevChild,
|
|
755
|
-
shouldIncludeInTree ? nextFiber : parentFiber,
|
|
756
|
-
);
|
|
526
|
+
updateFiberRecursively(onRender, nextChild, nextChild.alternate);
|
|
757
527
|
} else {
|
|
758
528
|
mountFiberRecursively(onRender, nextChild, false);
|
|
759
529
|
}
|
|
@@ -765,7 +535,7 @@ const updateFiberRecursively = (
|
|
|
765
535
|
};
|
|
766
536
|
|
|
767
537
|
const unmountFiber = (onRender: RenderHandler, fiber: Fiber): void => {
|
|
768
|
-
const isRoot = fiber.tag ===
|
|
538
|
+
const isRoot = fiber.tag === getReactWorkTagsForFiber(fiber).HostRoot;
|
|
769
539
|
|
|
770
540
|
if (isRoot || !shouldFilterFiber(fiber)) {
|
|
771
541
|
onRender(fiber, "unmount");
|
|
@@ -774,7 +544,8 @@ const unmountFiber = (onRender: RenderHandler, fiber: Fiber): void => {
|
|
|
774
544
|
|
|
775
545
|
const unmountFiberChildrenRecursively = (onRender: RenderHandler, fiber: Fiber): void => {
|
|
776
546
|
// We might meet a nested Suspense on our way.
|
|
777
|
-
const isTimedOutSuspense =
|
|
547
|
+
const isTimedOutSuspense =
|
|
548
|
+
fiber.tag === getReactWorkTagsForFiber(fiber).SuspenseComponent && fiber.memoizedState !== null;
|
|
778
549
|
let child = fiber.child;
|
|
779
550
|
|
|
780
551
|
if (isTimedOutSuspense) {
|
|
@@ -798,11 +569,9 @@ const unmountFiberChildrenRecursively = (onRender: RenderHandler, fiber: Fiber):
|
|
|
798
569
|
}
|
|
799
570
|
};
|
|
800
571
|
|
|
801
|
-
let commitId = 0;
|
|
802
572
|
const rootInstanceMap = new WeakMap<
|
|
803
|
-
FiberRoot,
|
|
573
|
+
Fiber | FiberRoot,
|
|
804
574
|
{
|
|
805
|
-
id: number;
|
|
806
575
|
prevFiber: Fiber | null;
|
|
807
576
|
}
|
|
808
577
|
>();
|
|
@@ -814,37 +583,39 @@ const rootInstanceMap = new WeakMap<
|
|
|
814
583
|
* console.log(phase)
|
|
815
584
|
* })
|
|
816
585
|
*/
|
|
817
|
-
export const traverseRenderedFibers = (root: FiberRoot, onRender: RenderHandler): void => {
|
|
586
|
+
export const traverseRenderedFibers = (root: Fiber | FiberRoot, onRender: RenderHandler): void => {
|
|
818
587
|
const fiber = "current" in root ? root.current : root;
|
|
819
588
|
|
|
820
589
|
let rootInstance = rootInstanceMap.get(root);
|
|
821
590
|
|
|
822
591
|
if (!rootInstance) {
|
|
823
|
-
rootInstance = {
|
|
592
|
+
rootInstance = { prevFiber: null };
|
|
824
593
|
rootInstanceMap.set(root, rootInstance);
|
|
825
594
|
}
|
|
826
595
|
|
|
827
596
|
const { prevFiber } = rootInstance;
|
|
828
|
-
// if fiberRoot don't have current instance, means it's been unmounted
|
|
829
597
|
if (!fiber) {
|
|
830
|
-
|
|
598
|
+
if (prevFiber) {
|
|
599
|
+
unmountFiber(onRender, prevFiber);
|
|
600
|
+
}
|
|
831
601
|
} else if (prevFiber !== null) {
|
|
832
602
|
const wasMounted =
|
|
833
|
-
prevFiber &&
|
|
834
|
-
prevFiber.memoizedState
|
|
835
|
-
prevFiber.memoizedState.element
|
|
603
|
+
prevFiber.memoizedState !== null &&
|
|
604
|
+
prevFiber.memoizedState.element !== null &&
|
|
605
|
+
prevFiber.memoizedState.element !== undefined &&
|
|
836
606
|
// A dehydrated root is not considered mounted
|
|
837
607
|
prevFiber.memoizedState.isDehydrated !== true;
|
|
838
608
|
const isMounted =
|
|
839
|
-
fiber.memoizedState
|
|
840
|
-
fiber.memoizedState.element
|
|
609
|
+
fiber.memoizedState !== null &&
|
|
610
|
+
fiber.memoizedState.element !== null &&
|
|
611
|
+
fiber.memoizedState.element !== undefined &&
|
|
841
612
|
// A dehydrated root is not considered mounted
|
|
842
613
|
fiber.memoizedState.isDehydrated !== true;
|
|
843
614
|
|
|
844
615
|
if (!wasMounted && isMounted) {
|
|
845
616
|
mountFiberRecursively(onRender, fiber, false);
|
|
846
617
|
} else if (wasMounted && isMounted) {
|
|
847
|
-
updateFiberRecursively(onRender, fiber, fiber.alternate
|
|
618
|
+
updateFiberRecursively(onRender, fiber, fiber.alternate);
|
|
848
619
|
} else if (wasMounted && !isMounted) {
|
|
849
620
|
unmountFiber(onRender, fiber);
|
|
850
621
|
}
|
|
@@ -855,191 +626,25 @@ export const traverseRenderedFibers = (root: FiberRoot, onRender: RenderHandler)
|
|
|
855
626
|
rootInstance.prevFiber = fiber;
|
|
856
627
|
};
|
|
857
628
|
|
|
858
|
-
const overrideRenderers = new Set<ReactRenderer>();
|
|
859
|
-
let areOverrideRenderersWired = false;
|
|
860
|
-
|
|
861
|
-
const wireOverrideRenderers = (): void => {
|
|
862
|
-
if (areOverrideRenderersWired) return;
|
|
863
|
-
if (!hasRDTHook()) return;
|
|
864
|
-
const rdtHook = getRDTHook();
|
|
865
|
-
if (!rdtHook?.renderers) return;
|
|
866
|
-
areOverrideRenderersWired = true;
|
|
867
|
-
|
|
868
|
-
ensureHookDispatchesToListeners(rdtHook);
|
|
869
|
-
for (const renderer of rdtHook.renderers.values()) {
|
|
870
|
-
overrideRenderers.add(renderer);
|
|
871
|
-
}
|
|
872
|
-
onRendererInject((renderer) => {
|
|
873
|
-
overrideRenderers.add(renderer);
|
|
874
|
-
});
|
|
875
|
-
};
|
|
876
|
-
|
|
877
|
-
const getRootRenderer = (fiber: Fiber): ReactRenderer | null => {
|
|
878
|
-
if (!hasRDTHook()) return null;
|
|
879
|
-
let hostRootFiber = fiber;
|
|
880
|
-
while (hostRootFiber.return) {
|
|
881
|
-
hostRootFiber = hostRootFiber.return;
|
|
882
|
-
}
|
|
883
|
-
const rendererId = rootRendererIds.get(hostRootFiber.stateNode);
|
|
884
|
-
if (rendererId === undefined) return null;
|
|
885
|
-
return getRDTHook().renderers?.get(rendererId) ?? null;
|
|
886
|
-
};
|
|
887
|
-
|
|
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
|
-
const resolveOverrideRenderers = (fiber: Fiber): ReactRenderer[] => {
|
|
892
|
-
wireOverrideRenderers();
|
|
893
|
-
const rootRenderer = getRootRenderer(fiber);
|
|
894
|
-
if (rootRenderer) return [rootRenderer];
|
|
895
|
-
return Array.from(overrideRenderers);
|
|
896
|
-
};
|
|
897
|
-
|
|
898
|
-
const applyPropsOverride = (
|
|
899
|
-
renderers: ReactRenderer[],
|
|
900
|
-
fiber: Fiber,
|
|
901
|
-
path: string[],
|
|
902
|
-
value: unknown,
|
|
903
|
-
): void => {
|
|
904
|
-
for (const renderer of renderers) {
|
|
905
|
-
try {
|
|
906
|
-
renderer.overrideProps?.(fiber, path, value);
|
|
907
|
-
} catch {}
|
|
908
|
-
}
|
|
909
|
-
};
|
|
910
|
-
|
|
911
|
-
const getHookStateDispatch = (
|
|
912
|
-
fiber: Fiber,
|
|
913
|
-
hookIndex: number,
|
|
914
|
-
): ((value: unknown) => void) | null => {
|
|
915
|
-
let hookState = fiber.memoizedState;
|
|
916
|
-
for (let i = 0; i < hookIndex; i++) {
|
|
917
|
-
if (!hookState?.next) return null;
|
|
918
|
-
hookState = hookState.next;
|
|
919
|
-
}
|
|
920
|
-
const queue = hookState?.queue;
|
|
921
|
-
if (isPOJO(queue) && typeof queue.dispatch === "function") {
|
|
922
|
-
return queue.dispatch as (value: unknown) => void;
|
|
923
|
-
}
|
|
924
|
-
return null;
|
|
925
|
-
};
|
|
926
|
-
|
|
927
|
-
const findContextProviderFiber = (fiber: Fiber, contextType: unknown): Fiber | null => {
|
|
928
|
-
let currentFiber: Fiber | null = fiber;
|
|
929
|
-
while (currentFiber) {
|
|
930
|
-
const fiberType = currentFiber.type;
|
|
931
|
-
if (fiberType === contextType || fiberType?.Provider === contextType) {
|
|
932
|
-
return currentFiber;
|
|
933
|
-
}
|
|
934
|
-
currentFiber = currentFiber.return;
|
|
935
|
-
}
|
|
936
|
-
return null;
|
|
937
|
-
};
|
|
938
|
-
|
|
939
|
-
const isPOJO = (maybePOJO: unknown): maybePOJO is Record<string, unknown> => {
|
|
940
|
-
return (
|
|
941
|
-
Object.prototype.toString.call(maybePOJO) === "[object Object]" &&
|
|
942
|
-
(Object.getPrototypeOf(maybePOJO) === Object.prototype ||
|
|
943
|
-
Object.getPrototypeOf(maybePOJO) === null)
|
|
944
|
-
);
|
|
945
|
-
};
|
|
946
|
-
|
|
947
|
-
const buildPathsFromValue = (
|
|
948
|
-
maybePOJO: Record<string, unknown>,
|
|
949
|
-
basePath: string[] = [],
|
|
950
|
-
): Array<{ path: string[]; value: unknown }> => {
|
|
951
|
-
if (!isPOJO(maybePOJO)) {
|
|
952
|
-
return [{ path: basePath, value: maybePOJO }];
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
const paths: Array<{ path: string[]; value: unknown }> = [];
|
|
956
|
-
|
|
957
|
-
for (const key in maybePOJO) {
|
|
958
|
-
const value = maybePOJO[key];
|
|
959
|
-
const path = basePath.concat(key);
|
|
960
|
-
|
|
961
|
-
if (isPOJO(value)) {
|
|
962
|
-
paths.push(...buildPathsFromValue(value, path));
|
|
963
|
-
} else {
|
|
964
|
-
paths.push({ path, value });
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
return paths;
|
|
969
|
-
};
|
|
970
|
-
|
|
971
|
-
const buildValueWrites = (partialValue: unknown): Array<{ path: string[]; value: unknown }> =>
|
|
972
|
-
isPOJO(partialValue) ? buildPathsFromValue(partialValue) : [{ path: [], value: partialValue }];
|
|
973
|
-
|
|
974
|
-
export const overrideProps = (fiber: Fiber, partialValue: Record<string, unknown>) => {
|
|
975
|
-
const renderers = resolveOverrideRenderers(fiber);
|
|
976
|
-
for (const { path, value } of buildValueWrites(partialValue)) {
|
|
977
|
-
applyPropsOverride(renderers, fiber, path, value);
|
|
978
|
-
}
|
|
979
|
-
};
|
|
980
|
-
|
|
981
|
-
export const overrideHookState = (fiber: Fiber, id: number, partialValue: unknown) => {
|
|
982
|
-
const renderers = resolveOverrideRenderers(fiber).filter((renderer) =>
|
|
983
|
-
Boolean(renderer.overrideHookState),
|
|
984
|
-
);
|
|
985
|
-
const writes = buildValueWrites(partialValue);
|
|
986
|
-
|
|
987
|
-
if (renderers.length > 0) {
|
|
988
|
-
const hookId = String(id);
|
|
989
|
-
for (const renderer of renderers) {
|
|
990
|
-
for (const { path, value } of writes) {
|
|
991
|
-
try {
|
|
992
|
-
renderer.overrideHookState?.(fiber, hookId, path, value);
|
|
993
|
-
} catch {}
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
return;
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
// production renderers don't expose overrideHookState; dispatching through
|
|
1000
|
-
// the hook's own queue still works there, but only for whole-value writes
|
|
1001
|
-
// (a path write through dispatch would replace the entire hook state)
|
|
1002
|
-
if (isPOJO(partialValue)) return;
|
|
1003
|
-
const dispatch = getHookStateDispatch(fiber, id);
|
|
1004
|
-
if (!dispatch) return;
|
|
1005
|
-
try {
|
|
1006
|
-
dispatch(partialValue);
|
|
1007
|
-
} catch {}
|
|
1008
|
-
};
|
|
1009
|
-
|
|
1010
|
-
export const overrideContext = (fiber: Fiber, contextType: unknown, partialValue: unknown) => {
|
|
1011
|
-
const providerFiber = findContextProviderFiber(fiber, contextType);
|
|
1012
|
-
if (!providerFiber) return;
|
|
1013
|
-
const renderers = resolveOverrideRenderers(providerFiber);
|
|
1014
|
-
for (const { path, value } of buildValueWrites(partialValue)) {
|
|
1015
|
-
applyPropsOverride(renderers, providerFiber, ["value", ...path], value);
|
|
1016
|
-
if (providerFiber.alternate) {
|
|
1017
|
-
applyPropsOverride(renderers, providerFiber.alternate, ["value", ...path], value);
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
};
|
|
1021
|
-
|
|
1022
629
|
export interface InstrumentationOptions {
|
|
1023
630
|
name?: string;
|
|
1024
631
|
onActive?: () => unknown;
|
|
1025
|
-
onCommitFiberRoot?: (
|
|
632
|
+
onCommitFiberRoot?: (
|
|
633
|
+
rendererID: number,
|
|
634
|
+
root: FiberRoot,
|
|
635
|
+
priority: number | void,
|
|
636
|
+
didError?: boolean,
|
|
637
|
+
) => unknown;
|
|
1026
638
|
onCommitFiberUnmount?: (rendererID: number, fiber: Fiber) => unknown;
|
|
1027
639
|
onPostCommitFiberRoot?: (rendererID: number, root: FiberRoot) => unknown;
|
|
1028
640
|
onScheduleFiberRoot?: (rendererID: number, root: FiberRoot, children: React.ReactNode) => unknown;
|
|
1029
641
|
}
|
|
1030
642
|
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
>();
|
|
1037
|
-
const postCommitFiberRootListeners = new Set<
|
|
1038
|
-
NonNullable<InstrumentationOptions["onPostCommitFiberRoot"]>
|
|
1039
|
-
>();
|
|
1040
|
-
const scheduleFiberRootListeners = new Set<
|
|
1041
|
-
NonNullable<InstrumentationOptions["onScheduleFiberRoot"]>
|
|
1042
|
-
>();
|
|
643
|
+
interface InstrumentationSubscription {
|
|
644
|
+
options: InstrumentationOptions;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const instrumentationSubscriptions = new Set<InstrumentationSubscription>();
|
|
1043
648
|
|
|
1044
649
|
interface HookDispatchers {
|
|
1045
650
|
onCommitFiberRoot: ReactDevToolsGlobalHook["onCommitFiberRoot"];
|
|
@@ -1049,15 +654,14 @@ interface HookDispatchers {
|
|
|
1049
654
|
}
|
|
1050
655
|
|
|
1051
656
|
const hookDispatchers = new WeakMap<ReactDevToolsGlobalHook, Partial<HookDispatchers>>();
|
|
1052
|
-
|
|
1053
|
-
const rootRendererIds = new WeakMap<FiberRoot, number>();
|
|
657
|
+
let didSubscribeToHookReplacements = false;
|
|
1054
658
|
|
|
1055
659
|
// each hook event is dispatched from a single re-installable wrapper. If
|
|
1056
660
|
// something overwrites the hook method (devtools, direct assignment), the
|
|
1057
661
|
// next instrument() call installs a fresh wrapper over it; a superseded
|
|
1058
662
|
// wrapper still forwards the previous chain but skips the listeners so
|
|
1059
663
|
// they never fire twice.
|
|
1060
|
-
const
|
|
664
|
+
const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
1061
665
|
const dispatchers = hookDispatchers.get(rdtHook) ?? {};
|
|
1062
666
|
hookDispatchers.set(rdtHook, dispatchers);
|
|
1063
667
|
|
|
@@ -1070,13 +674,29 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1070
674
|
rendererID,
|
|
1071
675
|
root,
|
|
1072
676
|
priority,
|
|
677
|
+
didError,
|
|
1073
678
|
) => {
|
|
1074
|
-
prevOnCommitFiberRoot
|
|
679
|
+
if (prevOnCommitFiberRoot) {
|
|
680
|
+
prevOnCommitFiberRoot.call(rdtHook, rendererID, root, priority, didError);
|
|
681
|
+
}
|
|
1075
682
|
if (hookDispatchers.get(rdtHook)?.onCommitFiberRoot !== dispatchCommitFiberRoot) return;
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
683
|
+
setReactWorkTagsForFiber(root.current, rdtHook.renderers.get(rendererID));
|
|
684
|
+
const rootMemoizedState = root.current.memoizedState;
|
|
685
|
+
const isUnmounting =
|
|
686
|
+
rootMemoizedState === null ||
|
|
687
|
+
rootMemoizedState.element === null ||
|
|
688
|
+
rootMemoizedState.element === undefined;
|
|
689
|
+
if (isUnmounting) {
|
|
690
|
+
_fiberRoots.delete(root);
|
|
691
|
+
rootRendererIds.delete(root);
|
|
692
|
+
} else {
|
|
693
|
+
_fiberRoots.add(root);
|
|
694
|
+
rootRendererIds.set(root, rendererID);
|
|
695
|
+
}
|
|
696
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
697
|
+
if (options.onCommitFiberRoot) {
|
|
698
|
+
options.onCommitFiberRoot(rendererID, root, priority, didError);
|
|
699
|
+
}
|
|
1080
700
|
}
|
|
1081
701
|
};
|
|
1082
702
|
dispatchers.onCommitFiberRoot = dispatchCommitFiberRoot;
|
|
@@ -1092,12 +712,17 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1092
712
|
rendererID,
|
|
1093
713
|
fiber,
|
|
1094
714
|
) => {
|
|
1095
|
-
|
|
715
|
+
setReactWorkTagsForFiber(fiber, rdtHook.renderers.get(rendererID));
|
|
716
|
+
if (prevOnCommitFiberUnmount) {
|
|
717
|
+
prevOnCommitFiberUnmount.call(rdtHook, rendererID, fiber);
|
|
718
|
+
}
|
|
1096
719
|
if (hookDispatchers.get(rdtHook)?.onCommitFiberUnmount !== dispatchCommitFiberUnmount) {
|
|
1097
720
|
return;
|
|
1098
721
|
}
|
|
1099
|
-
for (const
|
|
1100
|
-
|
|
722
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
723
|
+
if (options.onCommitFiberUnmount) {
|
|
724
|
+
options.onCommitFiberUnmount(rendererID, fiber);
|
|
725
|
+
}
|
|
1101
726
|
}
|
|
1102
727
|
};
|
|
1103
728
|
dispatchers.onCommitFiberUnmount = dispatchCommitFiberUnmount;
|
|
@@ -1113,12 +738,16 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1113
738
|
rendererID,
|
|
1114
739
|
root,
|
|
1115
740
|
) => {
|
|
1116
|
-
prevOnPostCommitFiberRoot
|
|
741
|
+
if (prevOnPostCommitFiberRoot) {
|
|
742
|
+
prevOnPostCommitFiberRoot.call(rdtHook, rendererID, root);
|
|
743
|
+
}
|
|
1117
744
|
if (hookDispatchers.get(rdtHook)?.onPostCommitFiberRoot !== dispatchPostCommitFiberRoot) {
|
|
1118
745
|
return;
|
|
1119
746
|
}
|
|
1120
|
-
for (const
|
|
1121
|
-
|
|
747
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
748
|
+
if (options.onPostCommitFiberRoot) {
|
|
749
|
+
options.onPostCommitFiberRoot(rendererID, root);
|
|
750
|
+
}
|
|
1122
751
|
}
|
|
1123
752
|
};
|
|
1124
753
|
dispatchers.onPostCommitFiberRoot = dispatchPostCommitFiberRoot;
|
|
@@ -1135,10 +764,14 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1135
764
|
root,
|
|
1136
765
|
children,
|
|
1137
766
|
) => {
|
|
1138
|
-
prevOnScheduleFiberRoot
|
|
767
|
+
if (prevOnScheduleFiberRoot) {
|
|
768
|
+
prevOnScheduleFiberRoot.call(rdtHook, rendererID, root, children);
|
|
769
|
+
}
|
|
1139
770
|
if (hookDispatchers.get(rdtHook)?.onScheduleFiberRoot !== dispatchScheduleFiberRoot) return;
|
|
1140
|
-
for (const
|
|
1141
|
-
|
|
771
|
+
for (const { options } of instrumentationSubscriptions) {
|
|
772
|
+
if (options.onScheduleFiberRoot) {
|
|
773
|
+
options.onScheduleFiberRoot(rendererID, root, children);
|
|
774
|
+
}
|
|
1142
775
|
}
|
|
1143
776
|
};
|
|
1144
777
|
dispatchers.onScheduleFiberRoot = dispatchScheduleFiberRoot;
|
|
@@ -1146,13 +779,24 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1146
779
|
}
|
|
1147
780
|
};
|
|
1148
781
|
|
|
782
|
+
const wireHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
783
|
+
if (!didSubscribeToHookReplacements) {
|
|
784
|
+
onRDTHookReplace(setHookEventDispatchers);
|
|
785
|
+
didSubscribeToHookReplacements = true;
|
|
786
|
+
}
|
|
787
|
+
setHookEventDispatchers(rdtHook);
|
|
788
|
+
};
|
|
789
|
+
|
|
790
|
+
if (hasRDTHook()) {
|
|
791
|
+
wireHookEventDispatchers(getRDTHook());
|
|
792
|
+
}
|
|
793
|
+
|
|
1149
794
|
/**
|
|
1150
795
|
* Instruments the DevTools hook. Each hook event is patched once and
|
|
1151
796
|
* dispatches to a set of listeners, so multiple `instrument` calls compose
|
|
1152
797
|
* without stacking patches. Returns an unsubscribe function that removes
|
|
1153
798
|
* exactly the handlers this call registered.
|
|
1154
|
-
* The
|
|
1155
|
-
* bippy subscriptions through `using`.
|
|
799
|
+
* The unsubscribe function can also be disposed through `using`.
|
|
1156
800
|
* @example
|
|
1157
801
|
* const unsubscribe = instrument({
|
|
1158
802
|
* onActive() {
|
|
@@ -1166,35 +810,19 @@ const ensureHookDispatchesToListeners = (rdtHook: ReactDevToolsGlobalHook): void
|
|
|
1166
810
|
*/
|
|
1167
811
|
export const instrument = (options: InstrumentationOptions): Unsubscribe => {
|
|
1168
812
|
const rdtHook = getRDTHook(options.onActive);
|
|
1169
|
-
|
|
1170
813
|
rdtHook._instrumentationSource = options.name ?? BIPPY_INSTRUMENTATION_STRING;
|
|
1171
814
|
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
onScheduleFiberRoot,
|
|
1180
|
-
} = options;
|
|
1181
|
-
if (onCommitFiberRoot) commitFiberRootListeners.add(onCommitFiberRoot);
|
|
1182
|
-
if (onCommitFiberUnmount) commitFiberUnmountListeners.add(onCommitFiberUnmount);
|
|
1183
|
-
if (onPostCommitFiberRoot) postCommitFiberRootListeners.add(onPostCommitFiberRoot);
|
|
1184
|
-
if (onScheduleFiberRoot) scheduleFiberRootListeners.add(onScheduleFiberRoot);
|
|
1185
|
-
|
|
1186
|
-
return toUnsubscribe(() => {
|
|
1187
|
-
if (onActive) _onActiveListeners.delete(onActive);
|
|
1188
|
-
if (onCommitFiberRoot) commitFiberRootListeners.delete(onCommitFiberRoot);
|
|
1189
|
-
if (onCommitFiberUnmount) commitFiberUnmountListeners.delete(onCommitFiberUnmount);
|
|
1190
|
-
if (onPostCommitFiberRoot) postCommitFiberRootListeners.delete(onPostCommitFiberRoot);
|
|
1191
|
-
if (onScheduleFiberRoot) scheduleFiberRootListeners.delete(onScheduleFiberRoot);
|
|
815
|
+
wireHookEventDispatchers(rdtHook);
|
|
816
|
+
const subscription: InstrumentationSubscription = { options };
|
|
817
|
+
instrumentationSubscriptions.add(subscription);
|
|
818
|
+
|
|
819
|
+
return createUnsubscribe(() => {
|
|
820
|
+
if (options.onActive) _onActiveListeners.delete(options.onActive);
|
|
821
|
+
instrumentationSubscriptions.delete(subscription);
|
|
1192
822
|
});
|
|
1193
823
|
};
|
|
1194
824
|
|
|
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.
|
|
825
|
+
// React uses per-renderer suffixes for host-instance Fiber keys, so discovered keys are cached.
|
|
1198
826
|
const knownFiberPropertyKeys = new Set<string>();
|
|
1199
827
|
|
|
1200
828
|
const isFiberPropertyKey = (key: string): boolean =>
|
|
@@ -1202,7 +830,38 @@ const isFiberPropertyKey = (key: string): boolean =>
|
|
|
1202
830
|
key.startsWith("__reactInternalInstance$") ||
|
|
1203
831
|
key.startsWith("__reactFiber");
|
|
1204
832
|
|
|
1205
|
-
|
|
833
|
+
const getLegacyRootFiber = (hostInstance: object): Fiber | null => {
|
|
834
|
+
const reactRootContainer = Reflect.get(hostInstance, "_reactRootContainer");
|
|
835
|
+
if (typeof reactRootContainer !== "object" || reactRootContainer === null) return null;
|
|
836
|
+
const internalRoot = Reflect.get(reactRootContainer, "_internalRoot");
|
|
837
|
+
if (typeof internalRoot !== "object" || internalRoot === null) return null;
|
|
838
|
+
const current = Reflect.get(internalRoot, "current");
|
|
839
|
+
if (typeof current !== "object" || current === null) return null;
|
|
840
|
+
const child = Reflect.get(current, "child");
|
|
841
|
+
return isFiber(child) ? child : null;
|
|
842
|
+
};
|
|
843
|
+
|
|
844
|
+
const getInternalInstanceHandle = (hostInstance: object): Fiber | null => {
|
|
845
|
+
const internalInstanceHandle =
|
|
846
|
+
Reflect.get(hostInstance, "__internalInstanceHandle") ??
|
|
847
|
+
Reflect.get(hostInstance, "_internalInstanceHandle");
|
|
848
|
+
return isFiber(internalInstanceHandle) ? internalInstanceHandle : null;
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
const getPublicHostInstance = (stateNode: unknown): unknown => {
|
|
852
|
+
if (typeof stateNode !== "object" || stateNode === null) return stateNode;
|
|
853
|
+
const canonical = Reflect.get(stateNode, "canonical");
|
|
854
|
+
if (typeof canonical === "object" && canonical !== null) {
|
|
855
|
+
const publicInstance = Reflect.get(canonical, "publicInstance");
|
|
856
|
+
if (typeof publicInstance === "object" && publicInstance !== null) {
|
|
857
|
+
return publicInstance;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
const nativeTag = Reflect.get(stateNode, "_nativeTag");
|
|
861
|
+
return typeof nativeTag === "number" ? nativeTag : stateNode;
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
export const getFiber = <T>(hostInstance: T): Fiber | null => {
|
|
1206
865
|
const rdtHook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
1207
866
|
if (rdtHook?.renderers) {
|
|
1208
867
|
for (const renderer of rdtHook.renderers.values()) {
|
|
@@ -1213,42 +872,38 @@ export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
|
|
|
1213
872
|
}
|
|
1214
873
|
}
|
|
1215
874
|
|
|
1216
|
-
if (typeof hostInstance === "object" && hostInstance
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
return (hostInstance._reactRootContainer as any)?._internalRoot?.current?.child;
|
|
1220
|
-
}
|
|
875
|
+
if (typeof hostInstance === "object" && hostInstance !== null) {
|
|
876
|
+
const legacyRootFiber = getLegacyRootFiber(hostInstance);
|
|
877
|
+
if (legacyRootFiber) return legacyRootFiber;
|
|
1221
878
|
|
|
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
|
-
}
|
|
879
|
+
const internalInstanceHandle = getInternalInstanceHandle(hostInstance);
|
|
880
|
+
if (internalInstanceHandle) return internalInstanceHandle;
|
|
1233
881
|
|
|
1234
|
-
const hostInstanceRecord = hostInstance as Record<string, unknown>;
|
|
1235
882
|
for (const knownKey of knownFiberPropertyKeys) {
|
|
1236
|
-
const fiber =
|
|
1237
|
-
if (fiber) return fiber
|
|
883
|
+
const fiber = Reflect.get(hostInstance, knownKey);
|
|
884
|
+
if (isFiber(fiber)) return fiber;
|
|
1238
885
|
}
|
|
1239
886
|
|
|
1240
|
-
for (const key of Object.keys(
|
|
887
|
+
for (const key of Object.keys(hostInstance)) {
|
|
1241
888
|
if (isFiberPropertyKey(key)) {
|
|
1242
889
|
knownFiberPropertyKeys.add(key);
|
|
1243
|
-
|
|
890
|
+
const fiber = Reflect.get(hostInstance, key);
|
|
891
|
+
if (isFiber(fiber)) return fiber;
|
|
1244
892
|
}
|
|
1245
893
|
}
|
|
894
|
+
}
|
|
1246
895
|
|
|
896
|
+
if (
|
|
897
|
+
hostInstance !== null &&
|
|
898
|
+
hostInstance !== undefined &&
|
|
899
|
+
(typeof hostInstance === "object" || typeof hostInstance === "number")
|
|
900
|
+
) {
|
|
1247
901
|
for (const fiberRoot of _fiberRoots) {
|
|
1248
|
-
if (getRootRenderer(fiberRoot.current)?.findFiberByHostInstance) continue;
|
|
1249
902
|
const fiber = traverseFiber(
|
|
1250
903
|
fiberRoot.current,
|
|
1251
|
-
(candidateFiber) =>
|
|
904
|
+
(candidateFiber) =>
|
|
905
|
+
isHostFiber(candidateFiber) &&
|
|
906
|
+
getPublicHostInstance(candidateFiber.stateNode) === hostInstance,
|
|
1252
907
|
);
|
|
1253
908
|
if (fiber) return fiber;
|
|
1254
909
|
}
|
|
@@ -1256,6 +911,19 @@ export const getFiberFromHostInstance = <T>(hostInstance: T): Fiber | null => {
|
|
|
1256
911
|
return null;
|
|
1257
912
|
};
|
|
1258
913
|
|
|
1259
|
-
export
|
|
1260
|
-
|
|
1261
|
-
export
|
|
914
|
+
export const getFiberFromHostInstance = getFiber;
|
|
915
|
+
|
|
916
|
+
export {
|
|
917
|
+
BIPPY_INSTRUMENTATION_STRING,
|
|
918
|
+
_onActiveListeners,
|
|
919
|
+
_renderers,
|
|
920
|
+
getRDTHook,
|
|
921
|
+
hasRDTHook,
|
|
922
|
+
installRDTHook,
|
|
923
|
+
isRealReactDevtools,
|
|
924
|
+
onRendererInject,
|
|
925
|
+
patchRDTHook,
|
|
926
|
+
version,
|
|
927
|
+
} from "./rdt-hook.js";
|
|
928
|
+
export type { Unsubscribe } from "./rdt-hook.js";
|
|
929
|
+
export * from "./react-internals/index.js";
|