bippy 0.7.2 → 0.7.3-dev.0c7e504
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 -0
- package/README.md +8 -68
- package/dist/core.cjs +1 -1
- package/dist/core.js +1 -1
- package/dist/errors.d.cts +56 -9
- package/dist/errors.d.ts +56 -9
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +14 -38
- package/dist/index.d.ts +18 -42
- package/dist/index.js +1 -1
- package/dist/install-hook-only.cjs +1 -1
- package/dist/install-hook-only.js +1 -1
- package/dist/rdt-hook.cjs +1 -1
- package/dist/rdt-hook.js +1 -1
- package/dist/source.cjs +12 -11
- package/dist/source.d.cts +36 -9
- package/dist/source.d.ts +36 -9
- package/dist/source.js +12 -11
- package/package.json +5 -33
- package/src/core.ts +200 -120
- package/src/rdt-hook.ts +128 -70
- package/src/react-internals/current-fiber.ts +84 -0
- package/src/react-internals/index.ts +6 -1
- package/src/react-internals/types.ts +33 -6
- package/src/react.ts +131 -55
- package/src/source/constants.ts +1 -3
- package/src/source/get-display-name-from-source.ts +10 -3
- package/src/source/get-source.ts +39 -19
- package/src/source/index.ts +17 -3
- package/src/source/inspect-hooks.ts +193 -46
- package/src/source/owner-stack.ts +43 -35
- package/src/source/parse-debug-stack.ts +1 -1
- package/src/source/parse-hook-names.ts +15 -8
- package/src/source/parse-stack.ts +3 -1
- package/src/source/renderer-dispatchers.ts +9 -4
- package/src/source/symbolication.ts +53 -32
package/src/rdt-hook.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// This module must load before React so renderers can inject into the hook.
|
|
2
2
|
|
|
3
|
-
import type { ReactDevToolsGlobalHook, ReactRenderer } from "./react-internals/index.js";
|
|
3
|
+
import type { FiberRoot, ReactDevToolsGlobalHook, ReactRenderer } from "./react-internals/index.js";
|
|
4
4
|
|
|
5
5
|
export interface Unsubscribe extends Disposable {
|
|
6
6
|
(): void;
|
|
@@ -14,8 +14,17 @@ interface RendererInjectListener {
|
|
|
14
14
|
(renderer: ReactRenderer): void;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
interface RendererInjectSubscription {
|
|
18
|
+
listener: RendererInjectListener;
|
|
19
|
+
target: ReactDevToolsTarget;
|
|
20
|
+
}
|
|
21
|
+
|
|
17
22
|
interface RDTHookReplaceListener {
|
|
18
|
-
(rdtHook: ReactDevToolsGlobalHook): void;
|
|
23
|
+
(rdtHook: ReactDevToolsGlobalHook, target: ReactDevToolsTarget): void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ReactDevToolsTarget {
|
|
27
|
+
__REACT_DEVTOOLS_GLOBAL_HOOK__?: ReactDevToolsGlobalHook;
|
|
19
28
|
}
|
|
20
29
|
|
|
21
30
|
export const version = process.env.VERSION;
|
|
@@ -25,10 +34,23 @@ const objectDefineProperty = Object.defineProperty;
|
|
|
25
34
|
|
|
26
35
|
const noOp = (): void => {};
|
|
27
36
|
|
|
37
|
+
const getTargetHook = (target: ReactDevToolsTarget): ReactDevToolsGlobalHook | undefined =>
|
|
38
|
+
target.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
39
|
+
|
|
28
40
|
export const createUnsubscribe = (unsubscribe: () => void): Unsubscribe =>
|
|
29
41
|
Object.assign(unsubscribe, { [Symbol.dispose]: unsubscribe });
|
|
30
42
|
|
|
43
|
+
export const isFiberRootUnmounted = (fiberRoot: FiberRoot): boolean => {
|
|
44
|
+
const rootState = fiberRoot.current.memoizedState;
|
|
45
|
+
return (
|
|
46
|
+
rootState === null ||
|
|
47
|
+
(rootState !== undefined && (rootState.element === null || rootState.element === undefined))
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
31
51
|
const checkDCE = (functionToCheck: unknown): void => {
|
|
52
|
+
// HACK: Node loads React's unbundled entrypoints, where development guards are intentionally not eliminated.
|
|
53
|
+
if (typeof process !== "undefined" && process.versions?.node) return;
|
|
32
54
|
try {
|
|
33
55
|
const code = Function.prototype.toString.call(functionToCheck);
|
|
34
56
|
if (code.indexOf("^_^") > -1) {
|
|
@@ -47,7 +69,7 @@ const checkDCE = (functionToCheck: unknown): void => {
|
|
|
47
69
|
export const isRealReactDevtools = (
|
|
48
70
|
rdtHook: ReactDevToolsGlobalHook | undefined | null = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__,
|
|
49
71
|
): boolean => {
|
|
50
|
-
return Boolean(rdtHook && "getFiberRoots" in rdtHook);
|
|
72
|
+
return Boolean(rdtHook && "getFiberRoots" in rdtHook && rdtHook._isBippyHook !== true);
|
|
51
73
|
};
|
|
52
74
|
|
|
53
75
|
// HACK: react-refresh's inject wrapper is the only stock hook whose parameter is named
|
|
@@ -69,53 +91,63 @@ export const _onActiveListeners = new Set<ActiveListener>();
|
|
|
69
91
|
|
|
70
92
|
export const _renderers = new Set<ReactRenderer>();
|
|
71
93
|
|
|
72
|
-
const
|
|
94
|
+
const activeListenerTargets = new WeakMap<ActiveListener, Set<ReactDevToolsTarget>>();
|
|
95
|
+
const rendererInjectSubscriptions = new Set<RendererInjectSubscription>();
|
|
73
96
|
const rdtHookReplaceListeners = new Set<RDTHookReplaceListener>();
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
97
|
+
const notifiedRenderersByTarget = new WeakMap<ReactDevToolsTarget, WeakSet<ReactRenderer>>();
|
|
98
|
+
|
|
99
|
+
const addActiveListener = (listener: ActiveListener, target: ReactDevToolsTarget): void => {
|
|
100
|
+
_onActiveListeners.add(listener);
|
|
101
|
+
const targets = activeListenerTargets.get(listener) ?? new Set<ReactDevToolsTarget>();
|
|
102
|
+
targets.add(target);
|
|
103
|
+
activeListenerTargets.set(listener, targets);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export const removeActiveListener = (
|
|
107
|
+
listener: ActiveListener,
|
|
108
|
+
target: ReactDevToolsTarget,
|
|
109
|
+
): void => {
|
|
110
|
+
const targets = activeListenerTargets.get(listener);
|
|
111
|
+
targets?.delete(target);
|
|
112
|
+
if (!targets || targets.size === 0) {
|
|
113
|
+
activeListenerTargets.delete(listener);
|
|
114
|
+
_onActiveListeners.delete(listener);
|
|
84
115
|
}
|
|
85
116
|
};
|
|
86
117
|
|
|
87
|
-
const
|
|
88
|
-
for (const listener of
|
|
89
|
-
listener(
|
|
118
|
+
const notifyActiveListeners = (target: ReactDevToolsTarget): void => {
|
|
119
|
+
for (const listener of _onActiveListeners) {
|
|
120
|
+
if (activeListenerTargets.get(listener)?.has(target)) listener();
|
|
90
121
|
}
|
|
91
122
|
};
|
|
92
123
|
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
124
|
+
const notifyRendererInjectListeners = (
|
|
125
|
+
target: ReactDevToolsTarget,
|
|
126
|
+
renderer: ReactRenderer,
|
|
127
|
+
): void => {
|
|
128
|
+
for (const subscription of rendererInjectSubscriptions) {
|
|
129
|
+
if (subscription.target === target) subscription.listener(renderer);
|
|
96
130
|
}
|
|
97
131
|
};
|
|
98
132
|
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
notifyRendererInjectListeners(renderer);
|
|
107
|
-
}
|
|
108
|
-
return rendererId;
|
|
109
|
-
};
|
|
110
|
-
rdtHook.inject = nextInject;
|
|
111
|
-
notifyingInjectByHook.set(rdtHook, nextInject);
|
|
133
|
+
const notifyRDTHookReplaceListeners = (
|
|
134
|
+
rdtHook: ReactDevToolsGlobalHook,
|
|
135
|
+
target: ReactDevToolsTarget,
|
|
136
|
+
): void => {
|
|
137
|
+
for (const listener of rdtHookReplaceListeners) {
|
|
138
|
+
listener(rdtHook, target);
|
|
139
|
+
}
|
|
112
140
|
};
|
|
113
141
|
|
|
114
|
-
export const onRendererInject = (
|
|
115
|
-
|
|
116
|
-
|
|
142
|
+
export const onRendererInject = (
|
|
143
|
+
listener: RendererInjectListener,
|
|
144
|
+
target: ReactDevToolsTarget = globalThis,
|
|
145
|
+
): Unsubscribe => {
|
|
146
|
+
getRDTHook(undefined, target);
|
|
147
|
+
const subscription = { listener, target };
|
|
148
|
+
rendererInjectSubscriptions.add(subscription);
|
|
117
149
|
return createUnsubscribe(() => {
|
|
118
|
-
|
|
150
|
+
rendererInjectSubscriptions.delete(subscription);
|
|
119
151
|
});
|
|
120
152
|
};
|
|
121
153
|
|
|
@@ -126,8 +158,13 @@ export const onRDTHookReplace = (listener: RDTHookReplaceListener): Unsubscribe
|
|
|
126
158
|
});
|
|
127
159
|
};
|
|
128
160
|
|
|
161
|
+
// A Map built in another realm (an iframe target) fails instanceof, so replacing it
|
|
162
|
+
// would discard that realm's renderer registry. Object.prototype.toString is realm-agnostic.
|
|
163
|
+
export const isRendererMap = (value: unknown): value is Map<number, ReactRenderer> =>
|
|
164
|
+
Object.prototype.toString.call(value) === "[object Map]";
|
|
165
|
+
|
|
129
166
|
const getRendererMap = (rdtHook: ReactDevToolsGlobalHook): Map<number, ReactRenderer> => {
|
|
130
|
-
if (!(rdtHook.renderers
|
|
167
|
+
if (!isRendererMap(rdtHook.renderers)) {
|
|
131
168
|
rdtHook.renderers = new Map();
|
|
132
169
|
}
|
|
133
170
|
return rdtHook.renderers;
|
|
@@ -135,6 +172,7 @@ const getRendererMap = (rdtHook: ReactDevToolsGlobalHook): Map<number, ReactRend
|
|
|
135
172
|
|
|
136
173
|
const trackInjectedRenderer = (
|
|
137
174
|
rdtHook: ReactDevToolsGlobalHook,
|
|
175
|
+
target: ReactDevToolsTarget,
|
|
138
176
|
renderers: Map<number, ReactRenderer>,
|
|
139
177
|
rendererId: number,
|
|
140
178
|
renderer: ReactRenderer,
|
|
@@ -143,28 +181,47 @@ const trackInjectedRenderer = (
|
|
|
143
181
|
_renderers.add(renderer);
|
|
144
182
|
if (!rdtHook._instrumentationIsActive) {
|
|
145
183
|
rdtHook._instrumentationIsActive = true;
|
|
146
|
-
notifyActiveListeners();
|
|
184
|
+
notifyActiveListeners(target);
|
|
147
185
|
}
|
|
186
|
+
const notifiedRenderers = notifiedRenderersByTarget.get(target) ?? new WeakSet<ReactRenderer>();
|
|
187
|
+
notifiedRenderersByTarget.set(target, notifiedRenderers);
|
|
188
|
+
if (notifiedRenderers.has(renderer)) return;
|
|
189
|
+
notifiedRenderers.add(renderer);
|
|
190
|
+
notifyRendererInjectListeners(target, renderer);
|
|
148
191
|
};
|
|
149
192
|
|
|
150
|
-
export const installRDTHook = (
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
193
|
+
export const installRDTHook = (
|
|
194
|
+
onActive?: ActiveListener,
|
|
195
|
+
target: ReactDevToolsTarget = globalThis,
|
|
196
|
+
): ReactDevToolsGlobalHook => {
|
|
197
|
+
if (onActive) addActiveListener(onActive, target);
|
|
154
198
|
const renderers = new Map<number, ReactRenderer>();
|
|
199
|
+
const fiberRoots = new Map<number, Set<FiberRoot>>();
|
|
200
|
+
// Callers register roots by mutating the returned set, so it must be memoized.
|
|
201
|
+
const getRendererFiberRoots = (rendererId: number): Set<FiberRoot> => {
|
|
202
|
+
const rendererRoots = fiberRoots.get(rendererId) ?? new Set<FiberRoot>();
|
|
203
|
+
fiberRoots.set(rendererId, rendererRoots);
|
|
204
|
+
return rendererRoots;
|
|
205
|
+
};
|
|
155
206
|
let rendererIdCounter = 0;
|
|
156
207
|
let rdtHook: ReactDevToolsGlobalHook = {
|
|
157
208
|
_instrumentationIsActive: false,
|
|
158
209
|
_instrumentationSource: BIPPY_INSTRUMENTATION_STRING,
|
|
210
|
+
_isBippyHook: true,
|
|
159
211
|
checkDCE,
|
|
212
|
+
getFiberRoots: getRendererFiberRoots,
|
|
160
213
|
hasUnsupportedRendererAttached: false,
|
|
161
214
|
inject: (renderer) => {
|
|
162
215
|
const nextRendererId = ++rendererIdCounter;
|
|
163
|
-
trackInjectedRenderer(rdtHook, renderers, nextRendererId, renderer);
|
|
216
|
+
trackInjectedRenderer(rdtHook, target, renderers, nextRendererId, renderer);
|
|
164
217
|
return nextRendererId;
|
|
165
218
|
},
|
|
166
219
|
on: noOp,
|
|
167
|
-
onCommitFiberRoot:
|
|
220
|
+
onCommitFiberRoot: (rendererId, fiberRoot) => {
|
|
221
|
+
const rendererRoots = getRendererFiberRoots(rendererId);
|
|
222
|
+
if (isFiberRootUnmounted(fiberRoot)) rendererRoots.delete(fiberRoot);
|
|
223
|
+
else rendererRoots.add(fiberRoot);
|
|
224
|
+
},
|
|
168
225
|
onCommitFiberUnmount: noOp,
|
|
169
226
|
onPostCommitFiberRoot: noOp,
|
|
170
227
|
renderers,
|
|
@@ -172,13 +229,11 @@ export const installRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHo
|
|
|
172
229
|
supportsFlight: true,
|
|
173
230
|
};
|
|
174
231
|
try {
|
|
175
|
-
objectDefineProperty(
|
|
232
|
+
objectDefineProperty(target, "__REACT_DEVTOOLS_GLOBAL_HOOK__", {
|
|
176
233
|
configurable: true,
|
|
177
234
|
enumerable: true,
|
|
178
|
-
get()
|
|
179
|
-
|
|
180
|
-
},
|
|
181
|
-
set(newHook) {
|
|
235
|
+
get: () => rdtHook,
|
|
236
|
+
set: (newHook: ReactDevToolsGlobalHook | undefined) => {
|
|
182
237
|
if (newHook && typeof newHook === "object") {
|
|
183
238
|
const ourRenderers = rdtHook.renderers;
|
|
184
239
|
rdtHook = newHook;
|
|
@@ -188,13 +243,13 @@ export const installRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHo
|
|
|
188
243
|
nextRenderers.set(rendererId, renderer);
|
|
189
244
|
});
|
|
190
245
|
if (ourRenderers.size > 0 || rdtHookReplaceListeners.size > 0) {
|
|
191
|
-
patchRDTHook(onActive);
|
|
246
|
+
patchRDTHook(onActive, target);
|
|
192
247
|
}
|
|
193
|
-
notifyRDTHookReplaceListeners(rdtHook);
|
|
248
|
+
notifyRDTHookReplaceListeners(rdtHook, target);
|
|
194
249
|
}
|
|
195
250
|
},
|
|
196
251
|
});
|
|
197
|
-
if (typeof window !== "undefined") {
|
|
252
|
+
if (target === globalThis && typeof window !== "undefined") {
|
|
198
253
|
// HACK: The DevTools extension uses hasOwnProperty to decide whether it may replace an earlier hook.
|
|
199
254
|
const originalWindowHasOwnProperty = window.hasOwnProperty.bind(window);
|
|
200
255
|
const originalHasOwnPropertyDescriptor = Object.getOwnPropertyDescriptor(
|
|
@@ -224,17 +279,18 @@ export const installRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHo
|
|
|
224
279
|
});
|
|
225
280
|
}
|
|
226
281
|
} catch {
|
|
227
|
-
patchRDTHook(onActive);
|
|
282
|
+
patchRDTHook(onActive, target);
|
|
228
283
|
}
|
|
229
284
|
return rdtHook;
|
|
230
285
|
};
|
|
231
286
|
|
|
232
|
-
export const patchRDTHook = (
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
287
|
+
export const patchRDTHook = (
|
|
288
|
+
onActive?: ActiveListener,
|
|
289
|
+
target: ReactDevToolsTarget = globalThis,
|
|
290
|
+
): void => {
|
|
291
|
+
if (onActive) addActiveListener(onActive, target);
|
|
236
292
|
let didNotifyActiveListeners = false;
|
|
237
|
-
const rdtHook =
|
|
293
|
+
const rdtHook = getTargetHook(target);
|
|
238
294
|
if (!rdtHook) return;
|
|
239
295
|
const renderers = getRendererMap(rdtHook);
|
|
240
296
|
if (!rdtHook._instrumentationSource) {
|
|
@@ -252,20 +308,20 @@ export const patchRDTHook = (onActive?: ActiveListener): void => {
|
|
|
252
308
|
if (renderers.size) {
|
|
253
309
|
renderers.forEach((renderer) => _renderers.add(renderer));
|
|
254
310
|
rdtHook._instrumentationIsActive = true;
|
|
255
|
-
notifyActiveListeners();
|
|
311
|
+
notifyActiveListeners(target);
|
|
256
312
|
didNotifyActiveListeners = true;
|
|
257
313
|
} else if (!isReactDevtools && isReactRefresh(rdtHook)) {
|
|
258
314
|
// HACK: react-refresh's stub inject never records renderers, so a React app
|
|
259
315
|
// that injected before bippy loaded is undetectable through the renderers map.
|
|
260
316
|
// A react-refresh hook implies a dev renderer, so activate immediately.
|
|
261
317
|
rdtHook._instrumentationIsActive = true;
|
|
262
|
-
notifyActiveListeners();
|
|
318
|
+
notifyActiveListeners(target);
|
|
263
319
|
didNotifyActiveListeners = true;
|
|
264
320
|
}
|
|
265
321
|
const previousInject = rdtHook.inject;
|
|
266
322
|
rdtHook.inject = (renderer) => {
|
|
267
323
|
const rendererId = previousInject.call(rdtHook, renderer);
|
|
268
|
-
trackInjectedRenderer(rdtHook, renderers, rendererId, renderer);
|
|
324
|
+
trackInjectedRenderer(rdtHook, target, renderers, rendererId, renderer);
|
|
269
325
|
return rendererId;
|
|
270
326
|
};
|
|
271
327
|
}
|
|
@@ -274,18 +330,20 @@ export const patchRDTHook = (onActive?: ActiveListener): void => {
|
|
|
274
330
|
}
|
|
275
331
|
};
|
|
276
332
|
|
|
277
|
-
export const hasRDTHook = (): boolean =>
|
|
278
|
-
|
|
279
|
-
};
|
|
333
|
+
export const hasRDTHook = (target: ReactDevToolsTarget = globalThis): boolean =>
|
|
334
|
+
Object.hasOwn(target, "__REACT_DEVTOOLS_GLOBAL_HOOK__");
|
|
280
335
|
|
|
281
336
|
/**
|
|
282
337
|
* Returns the current React DevTools global hook.
|
|
283
338
|
*/
|
|
284
|
-
export const getRDTHook = (
|
|
285
|
-
|
|
286
|
-
|
|
339
|
+
export const getRDTHook = (
|
|
340
|
+
onActive?: ActiveListener,
|
|
341
|
+
target: ReactDevToolsTarget = globalThis,
|
|
342
|
+
): ReactDevToolsGlobalHook => {
|
|
343
|
+
if (!hasRDTHook(target)) {
|
|
344
|
+
return installRDTHook(onActive, target);
|
|
287
345
|
}
|
|
288
346
|
|
|
289
|
-
patchRDTHook(onActive);
|
|
290
|
-
return
|
|
347
|
+
patchRDTHook(onActive, target);
|
|
348
|
+
return getTargetHook(target) ?? installRDTHook(onActive, target);
|
|
291
349
|
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Adapted from ReactFiberTreeReflection.js. Copyright (c) Meta Platforms, Inc. and affiliates. MIT licensed.
|
|
2
|
+
import type { Fiber } from "./types.js";
|
|
3
|
+
import { getReactWorkTagsForFiber } from "./index.js";
|
|
4
|
+
|
|
5
|
+
export const getCurrentFiberFromRoot = (fiber: Fiber): Fiber | null => {
|
|
6
|
+
const alternate = fiber.alternate;
|
|
7
|
+
if (!alternate) return null;
|
|
8
|
+
let firstBranch = fiber;
|
|
9
|
+
let secondBranch = alternate;
|
|
10
|
+
|
|
11
|
+
while (firstBranch.return) {
|
|
12
|
+
const firstParent = firstBranch.return;
|
|
13
|
+
const secondParent = firstParent.alternate;
|
|
14
|
+
if (!secondParent) {
|
|
15
|
+
const nextParent = firstParent.return;
|
|
16
|
+
if (!nextParent) return null;
|
|
17
|
+
firstBranch = secondBranch = nextParent;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (firstParent.child === secondParent.child) {
|
|
22
|
+
let child = firstParent.child;
|
|
23
|
+
while (child) {
|
|
24
|
+
if (child === firstBranch || child === secondBranch) {
|
|
25
|
+
let root = firstParent;
|
|
26
|
+
while (root.return) root = root.return;
|
|
27
|
+
if (root.tag !== getReactWorkTagsForFiber(root).HostRoot) return null;
|
|
28
|
+
return child === firstBranch ? fiber : alternate;
|
|
29
|
+
}
|
|
30
|
+
child = child.sibling;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (firstBranch.return !== secondBranch.return) {
|
|
36
|
+
firstBranch = firstParent;
|
|
37
|
+
secondBranch = secondParent;
|
|
38
|
+
} else {
|
|
39
|
+
let didFindChild = false;
|
|
40
|
+
let child = firstParent.child;
|
|
41
|
+
while (child) {
|
|
42
|
+
if (child === firstBranch) {
|
|
43
|
+
firstBranch = firstParent;
|
|
44
|
+
secondBranch = secondParent;
|
|
45
|
+
didFindChild = true;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
if (child === secondBranch) {
|
|
49
|
+
firstBranch = secondParent;
|
|
50
|
+
secondBranch = firstParent;
|
|
51
|
+
didFindChild = true;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
child = child.sibling;
|
|
55
|
+
}
|
|
56
|
+
if (!didFindChild) {
|
|
57
|
+
child = secondParent.child;
|
|
58
|
+
while (child) {
|
|
59
|
+
if (child === firstBranch) {
|
|
60
|
+
firstBranch = secondParent;
|
|
61
|
+
secondBranch = firstParent;
|
|
62
|
+
didFindChild = true;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
if (child === secondBranch) {
|
|
66
|
+
firstBranch = firstParent;
|
|
67
|
+
secondBranch = secondParent;
|
|
68
|
+
didFindChild = true;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
child = child.sibling;
|
|
72
|
+
}
|
|
73
|
+
if (!didFindChild) return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (firstBranch.alternate !== secondBranch) return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (firstBranch.tag !== getReactWorkTagsForFiber(firstBranch).HostRoot) return null;
|
|
80
|
+
const root = firstBranch.stateNode;
|
|
81
|
+
if (typeof root !== "object" || root === null || !("current" in root)) return null;
|
|
82
|
+
if (root.current === firstBranch) return fiber;
|
|
83
|
+
return root.current === secondBranch ? alternate : null;
|
|
84
|
+
};
|
|
@@ -49,11 +49,16 @@ export const getReactWorkTagsForFiber = (fiber: Fiber): Readonly<ReactWorkTagMap
|
|
|
49
49
|
|
|
50
50
|
const traversedFibers: Fiber[] = [fiber];
|
|
51
51
|
let rootFiber = fiber;
|
|
52
|
+
let workTags = defaultReactWorkTags;
|
|
52
53
|
while (rootFiber.return) {
|
|
53
54
|
rootFiber = rootFiber.return;
|
|
55
|
+
const ancestorWorkTags = fiberReactWorkTags.get(rootFiber);
|
|
56
|
+
if (ancestorWorkTags) {
|
|
57
|
+
workTags = ancestorWorkTags;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
54
60
|
traversedFibers.push(rootFiber);
|
|
55
61
|
}
|
|
56
|
-
const workTags = fiberReactWorkTags.get(rootFiber) ?? defaultReactWorkTags;
|
|
57
62
|
for (const traversedFiber of traversedFibers) {
|
|
58
63
|
fiberReactWorkTags.set(traversedFiber, workTags);
|
|
59
64
|
if (traversedFiber.alternate) {
|
|
@@ -31,12 +31,30 @@ export interface ServerComponentInfo {
|
|
|
31
31
|
env?: string;
|
|
32
32
|
owner?: Fiber | ServerComponentInfo | null;
|
|
33
33
|
debugStack?: Error | null;
|
|
34
|
+
debugLocation?: Error | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ReactIOInfo {
|
|
38
|
+
name: string;
|
|
39
|
+
start: number;
|
|
40
|
+
end: number;
|
|
41
|
+
byteSize?: number;
|
|
42
|
+
value?: Promise<unknown> | null;
|
|
43
|
+
env?: string;
|
|
44
|
+
owner?: ServerComponentInfo | null;
|
|
45
|
+
stack?: unknown[] | null;
|
|
46
|
+
debugStack?: Error | null;
|
|
34
47
|
}
|
|
35
48
|
|
|
36
49
|
export interface ReactDebugInfo {
|
|
37
|
-
|
|
50
|
+
awaited?: ReactIOInfo;
|
|
51
|
+
debugLocation?: Error | null;
|
|
38
52
|
env?: string;
|
|
39
53
|
name?: string;
|
|
54
|
+
owner?: ServerComponentInfo | null;
|
|
55
|
+
stack?: unknown[] | null;
|
|
56
|
+
debugStack?: Error | null;
|
|
57
|
+
time?: number;
|
|
40
58
|
}
|
|
41
59
|
|
|
42
60
|
export interface ReactMemoCache {
|
|
@@ -71,10 +89,10 @@ export interface Fiber<T = unknown> extends Omit<
|
|
|
71
89
|
| "tag"
|
|
72
90
|
| "updateQueue"
|
|
73
91
|
> {
|
|
74
|
-
_debugInfo?: ReactDebugInfo[];
|
|
92
|
+
_debugInfo?: ReactDebugInfo[] | null;
|
|
75
93
|
_debugOwner?: Fiber | ServerComponentInfo | null;
|
|
76
94
|
_debugSource?: FiberDebugSource | null;
|
|
77
|
-
_debugStack?: Error
|
|
95
|
+
_debugStack?: Error | null;
|
|
78
96
|
alternate: Fiber | null;
|
|
79
97
|
child: Fiber | null;
|
|
80
98
|
deletions: Fiber[] | null;
|
|
@@ -96,6 +114,8 @@ export interface HostFiber<T = unknown> extends Fiber<T> {
|
|
|
96
114
|
|
|
97
115
|
export interface FiberRoot {
|
|
98
116
|
current: Fiber;
|
|
117
|
+
effectDuration?: number;
|
|
118
|
+
passiveEffectDuration?: number;
|
|
99
119
|
}
|
|
100
120
|
|
|
101
121
|
export interface MemoizedState {
|
|
@@ -115,6 +135,7 @@ export interface ReactDevToolsEventHandler {
|
|
|
115
135
|
export interface ReactDevToolsGlobalHook {
|
|
116
136
|
_instrumentationIsActive?: boolean;
|
|
117
137
|
_instrumentationSource?: string;
|
|
138
|
+
_isBippyHook?: boolean;
|
|
118
139
|
checkDCE: (fn: unknown) => void;
|
|
119
140
|
emit?: (event: string, data?: unknown) => void;
|
|
120
141
|
getFiberRoots?: (rendererID: number) => Set<FiberRoot>;
|
|
@@ -149,14 +170,20 @@ export type RendererDispatcherRef = CurrentDispatcherRef | LegacyDispatcherRef;
|
|
|
149
170
|
|
|
150
171
|
export interface ReactRenderer extends Omit<
|
|
151
172
|
ReactReconciler.DevToolsConfig<unknown, unknown, unknown>,
|
|
152
|
-
"findFiberByHostInstance"
|
|
173
|
+
"bundleType" | "findFiberByHostInstance"
|
|
153
174
|
> {
|
|
175
|
+
bundleType: number;
|
|
154
176
|
currentDispatcherRef?: RendererDispatcherRef | null;
|
|
155
177
|
findFiberByHostInstance?: (hostInstance: unknown) => Fiber | null;
|
|
156
178
|
getCurrentFiber?: () => Fiber | null;
|
|
157
179
|
overrideContext?: (fiber: Fiber, contextType: unknown, path: string[], value: unknown) => void;
|
|
158
180
|
|
|
159
|
-
overrideHookState?: (
|
|
181
|
+
overrideHookState?: (
|
|
182
|
+
fiber: Fiber,
|
|
183
|
+
id: number,
|
|
184
|
+
path: Array<number | string>,
|
|
185
|
+
value: unknown,
|
|
186
|
+
) => void;
|
|
160
187
|
overrideHookStateDeletePath?: (fiber: Fiber, id: number, path: Array<number | string>) => void;
|
|
161
188
|
overrideHookStateRenamePath?: (
|
|
162
189
|
fiber: Fiber,
|
|
@@ -164,7 +191,7 @@ export interface ReactRenderer extends Omit<
|
|
|
164
191
|
oldPath: Array<number | string>,
|
|
165
192
|
newPath: Array<number | string>,
|
|
166
193
|
) => void;
|
|
167
|
-
overrideProps?: (fiber: Fiber, path: string
|
|
194
|
+
overrideProps?: (fiber: Fiber, path: Array<number | string>, value: unknown) => void;
|
|
168
195
|
overridePropsDeletePath?: (fiber: Fiber, path: Array<number | string>) => void;
|
|
169
196
|
overridePropsRenamePath?: (
|
|
170
197
|
fiber: Fiber,
|