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/core.ts
CHANGED
|
@@ -10,19 +10,20 @@ import type {
|
|
|
10
10
|
} from "./react-internals/index.js";
|
|
11
11
|
|
|
12
12
|
import {
|
|
13
|
-
_onActiveListeners,
|
|
14
13
|
BIPPY_INSTRUMENTATION_STRING,
|
|
15
14
|
createUnsubscribe,
|
|
16
15
|
getRDTHook,
|
|
17
16
|
hasRDTHook,
|
|
17
|
+
isFiberRootUnmounted,
|
|
18
18
|
isReactRefresh,
|
|
19
19
|
isRealReactDevtools,
|
|
20
20
|
onRDTHookReplace,
|
|
21
|
+
removeActiveListener,
|
|
21
22
|
} from "./rdt-hook.js";
|
|
22
|
-
import type { Unsubscribe } from "./rdt-hook.js";
|
|
23
|
+
import type { ReactDevToolsTarget, Unsubscribe } from "./rdt-hook.js";
|
|
24
|
+
import { getCurrentFiberFromRoot } from "./react-internals/current-fiber.js";
|
|
23
25
|
import {
|
|
24
26
|
getReactWorkTagsForFiber,
|
|
25
|
-
MutationMask,
|
|
26
27
|
ReactBuildType,
|
|
27
28
|
ReactFiberFlags,
|
|
28
29
|
ReactSymbols,
|
|
@@ -64,8 +65,8 @@ export const isValidElement = (element: unknown): element is React.ReactElement
|
|
|
64
65
|
typeof element === "object" &&
|
|
65
66
|
element !== null &&
|
|
66
67
|
"$$typeof" in element &&
|
|
67
|
-
(
|
|
68
|
-
|
|
68
|
+
(element.$$typeof === Symbol.for("react.element") ||
|
|
69
|
+
element.$$typeof === Symbol.for("react.transitional.element"));
|
|
69
70
|
|
|
70
71
|
/**
|
|
71
72
|
* Returns `true` if object is a React Fiber.
|
|
@@ -78,7 +79,7 @@ export const isFiber = (fiber: unknown): fiber is Fiber =>
|
|
|
78
79
|
"return" in fiber &&
|
|
79
80
|
"child" in fiber &&
|
|
80
81
|
"sibling" in fiber &&
|
|
81
|
-
"flags" in fiber;
|
|
82
|
+
("flags" in fiber || "effectTag" in fiber);
|
|
82
83
|
|
|
83
84
|
const isFiberRoot = (fiberRoot: unknown): fiberRoot is FiberRoot =>
|
|
84
85
|
typeof fiberRoot === "object" &&
|
|
@@ -128,7 +129,7 @@ export const isCompositeFiber = (fiber: Fiber): boolean => {
|
|
|
128
129
|
*/
|
|
129
130
|
export const didFiberRender = (fiber: Fiber): boolean => {
|
|
130
131
|
const nextProps = fiber.memoizedProps;
|
|
131
|
-
const prevProps = fiber.alternate?.memoizedProps
|
|
132
|
+
const prevProps = fiber.alternate?.memoizedProps;
|
|
132
133
|
const flags = fiber.flags ?? fiber.effectTag ?? 0;
|
|
133
134
|
const workTags = getReactWorkTagsForFiber(fiber);
|
|
134
135
|
|
|
@@ -152,16 +153,6 @@ export const didFiberRender = (fiber: Fiber): boolean => {
|
|
|
152
153
|
}
|
|
153
154
|
};
|
|
154
155
|
|
|
155
|
-
/**
|
|
156
|
-
* Returns `true` if the {@link Fiber} has committed. Note that this does not mean the fiber has committed in the current commit, just that it has committed in the past.
|
|
157
|
-
*/
|
|
158
|
-
export const didFiberCommit = (fiber: Fiber): boolean => {
|
|
159
|
-
return Boolean(
|
|
160
|
-
(fiber.flags & (MutationMask | ReactFiberFlags.Cloned)) !== 0 ||
|
|
161
|
-
(fiber.subtreeFlags & (MutationMask | ReactFiberFlags.Cloned)) !== 0,
|
|
162
|
-
);
|
|
163
|
-
};
|
|
164
|
-
|
|
165
156
|
/**
|
|
166
157
|
* Returns `true` if the {@link Fiber} should be filtered out during reconciliation.
|
|
167
158
|
*/
|
|
@@ -210,41 +201,57 @@ const shouldFilterFiber = (fiber: Fiber): boolean => {
|
|
|
210
201
|
}
|
|
211
202
|
};
|
|
212
203
|
|
|
204
|
+
interface TraverseFiber {
|
|
205
|
+
(
|
|
206
|
+
fiber: Fiber | null,
|
|
207
|
+
selector: (node: Fiber) => boolean | void,
|
|
208
|
+
ascending?: boolean,
|
|
209
|
+
): Fiber | null;
|
|
210
|
+
(
|
|
211
|
+
fiber: Fiber | null,
|
|
212
|
+
selector: (node: Fiber) => Promise<boolean | void>,
|
|
213
|
+
ascending?: boolean,
|
|
214
|
+
): Promise<Fiber | null>;
|
|
215
|
+
(
|
|
216
|
+
fiber: Fiber | null,
|
|
217
|
+
selector: FiberSelector,
|
|
218
|
+
ascending?: boolean,
|
|
219
|
+
): Fiber | null | Promise<Fiber | null>;
|
|
220
|
+
}
|
|
221
|
+
|
|
213
222
|
/**
|
|
214
223
|
* Traverses up or down a {@link Fiber}, return `true` to stop and select a node.
|
|
215
224
|
*/
|
|
216
|
-
export
|
|
217
|
-
fiber: Fiber | null,
|
|
218
|
-
selector: (node: Fiber) => boolean | void,
|
|
219
|
-
ascending?: boolean,
|
|
220
|
-
): Fiber | null;
|
|
221
|
-
export function traverseFiber(
|
|
222
|
-
fiber: Fiber | null,
|
|
223
|
-
selector: (node: Fiber) => Promise<boolean | void>,
|
|
224
|
-
ascending?: boolean,
|
|
225
|
-
): Promise<Fiber | null>;
|
|
226
|
-
export function traverseFiber(
|
|
227
|
-
fiber: Fiber | null,
|
|
228
|
-
selector: FiberSelector,
|
|
229
|
-
ascending?: boolean,
|
|
230
|
-
): Fiber | null | Promise<Fiber | null>;
|
|
231
|
-
export function traverseFiber(
|
|
225
|
+
export const traverseFiber = ((
|
|
232
226
|
fiber: Fiber | null,
|
|
233
227
|
selector: FiberSelector,
|
|
234
228
|
ascending = false,
|
|
235
|
-
): Fiber | null | Promise<Fiber | null> {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
229
|
+
): Fiber | null | Promise<Fiber | null> => {
|
|
230
|
+
const pendingSiblings: Fiber[] = [];
|
|
231
|
+
const getNextFiber = (currentFiber: Fiber): Fiber | null => {
|
|
232
|
+
if (ascending) return currentFiber.return;
|
|
233
|
+
if (currentFiber !== fiber && currentFiber.sibling) {
|
|
234
|
+
pendingSiblings.push(currentFiber.sibling);
|
|
235
|
+
}
|
|
236
|
+
return currentFiber.child ?? pendingSiblings.pop() ?? null;
|
|
237
|
+
};
|
|
238
|
+
const visit = (firstFiber: Fiber | null): Fiber | null | Promise<Fiber | null> => {
|
|
239
|
+
let currentFiber = firstFiber;
|
|
240
|
+
while (currentFiber) {
|
|
241
|
+
const selectedFiber = currentFiber;
|
|
242
|
+
const selection = selector(selectedFiber);
|
|
243
|
+
if (isPromiseLike<boolean | void>(selection)) {
|
|
244
|
+
return Promise.resolve(selection).then((didSelectFiber) =>
|
|
245
|
+
didSelectFiber === true ? selectedFiber : visit(getNextFiber(selectedFiber)),
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
if (selection === true) return selectedFiber;
|
|
249
|
+
currentFiber = getNextFiber(selectedFiber);
|
|
250
|
+
}
|
|
251
|
+
return null;
|
|
252
|
+
};
|
|
253
|
+
return visit(fiber);
|
|
254
|
+
}) as TraverseFiber;
|
|
248
255
|
|
|
249
256
|
const isPromiseLike = <Result>(value: unknown): value is PromiseLike<Result> =>
|
|
250
257
|
(typeof value === "object" || typeof value === "function") &&
|
|
@@ -252,32 +259,6 @@ const isPromiseLike = <Result>(value: unknown): value is PromiseLike<Result> =>
|
|
|
252
259
|
"then" in value &&
|
|
253
260
|
typeof value.then === "function";
|
|
254
261
|
|
|
255
|
-
const traverseFiberChildren = (
|
|
256
|
-
fiber: Fiber,
|
|
257
|
-
selector: FiberSelector,
|
|
258
|
-
ascending: boolean,
|
|
259
|
-
): Fiber | null | Promise<Fiber | null> => {
|
|
260
|
-
const firstChild = ascending ? fiber.return : fiber.child;
|
|
261
|
-
return traverseFiberSiblings(firstChild, selector, ascending);
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
const traverseFiberSiblings = (
|
|
265
|
-
fiber: Fiber | null,
|
|
266
|
-
selector: FiberSelector,
|
|
267
|
-
ascending: boolean,
|
|
268
|
-
): Fiber | null | Promise<Fiber | null> => {
|
|
269
|
-
if (!fiber) return null;
|
|
270
|
-
|
|
271
|
-
const nextSibling = ascending ? null : fiber.sibling;
|
|
272
|
-
const match = traverseFiber(fiber, selector, ascending);
|
|
273
|
-
if (isPromiseLike<Fiber | null>(match)) {
|
|
274
|
-
return Promise.resolve(match).then(
|
|
275
|
-
(resolvedMatch) => resolvedMatch ?? traverseFiberSiblings(nextSibling, selector, ascending),
|
|
276
|
-
);
|
|
277
|
-
}
|
|
278
|
-
return match ?? traverseFiberSiblings(nextSibling, selector, ascending);
|
|
279
|
-
};
|
|
280
|
-
|
|
281
262
|
/**
|
|
282
263
|
* Returns `true` if the {@link Fiber} uses React Compiler's memo cache.
|
|
283
264
|
*/
|
|
@@ -291,7 +272,14 @@ export const hasMemoCache = (fiber: Fiber): boolean => {
|
|
|
291
272
|
export const getType = (type: unknown): null | React.ComponentType<unknown> => {
|
|
292
273
|
if (isComponentType(type)) return type;
|
|
293
274
|
if (typeof type !== "object" || type === null) return null;
|
|
294
|
-
|
|
275
|
+
const visitedTypes = new Set<object>();
|
|
276
|
+
let currentType: unknown = type;
|
|
277
|
+
while (typeof currentType === "object" && currentType !== null) {
|
|
278
|
+
if (visitedTypes.has(currentType)) return null;
|
|
279
|
+
visitedTypes.add(currentType);
|
|
280
|
+
currentType = Reflect.get(currentType, "type") ?? Reflect.get(currentType, "render");
|
|
281
|
+
}
|
|
282
|
+
return isComponentType(currentType) ? currentType : null;
|
|
295
283
|
};
|
|
296
284
|
|
|
297
285
|
/**
|
|
@@ -312,10 +300,7 @@ export const getDisplayName = (type: unknown): null | string => {
|
|
|
312
300
|
*/
|
|
313
301
|
export const detectReactBuildType = (renderer: ReactRenderer): "development" | "production" => {
|
|
314
302
|
try {
|
|
315
|
-
if (
|
|
316
|
-
typeof renderer.version === "string" &&
|
|
317
|
-
renderer.bundleType === ReactBuildType.Development
|
|
318
|
-
) {
|
|
303
|
+
if (typeof renderer.version === "string" && renderer.bundleType > ReactBuildType.Production) {
|
|
319
304
|
return "development";
|
|
320
305
|
}
|
|
321
306
|
} catch {}
|
|
@@ -325,8 +310,8 @@ export const detectReactBuildType = (renderer: ReactRenderer): "development" | "
|
|
|
325
310
|
/**
|
|
326
311
|
* Returns `true` if bippy's instrumentation is active.
|
|
327
312
|
*/
|
|
328
|
-
export const isInstrumentationActive = (): boolean => {
|
|
329
|
-
const rdtHook =
|
|
313
|
+
export const isInstrumentationActive = (target: ReactDevToolsTarget = globalThis): boolean => {
|
|
314
|
+
const rdtHook = target.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
330
315
|
return (
|
|
331
316
|
Boolean(rdtHook?._instrumentationIsActive) ||
|
|
332
317
|
isRealReactDevtools(rdtHook) ||
|
|
@@ -336,6 +321,7 @@ export const isInstrumentationActive = (): boolean => {
|
|
|
336
321
|
|
|
337
322
|
export const _fiberRoots = new Set<FiberRoot>();
|
|
338
323
|
const rootRendererIds = new WeakMap<FiberRoot, number>();
|
|
324
|
+
const rootHooks = new WeakMap<FiberRoot, ReactDevToolsGlobalHook>();
|
|
339
325
|
|
|
340
326
|
/**
|
|
341
327
|
* Returns the latest fiber (since it may be double-buffered).
|
|
@@ -343,6 +329,8 @@ const rootRendererIds = new WeakMap<FiberRoot, number>();
|
|
|
343
329
|
export const getLatestFiber = (fiber: Fiber): Fiber => {
|
|
344
330
|
const alternate = fiber.alternate;
|
|
345
331
|
if (!alternate) return fiber;
|
|
332
|
+
const currentFiber = getCurrentFiberFromRoot(fiber);
|
|
333
|
+
if (currentFiber) return currentFiber;
|
|
346
334
|
|
|
347
335
|
let rootFiber = fiber;
|
|
348
336
|
while (rootFiber.return) {
|
|
@@ -370,29 +358,61 @@ export const getLatestFiber = (fiber: Fiber): Fiber => {
|
|
|
370
358
|
/**
|
|
371
359
|
* Returns the renderer that owns the {@link Fiber}.
|
|
372
360
|
*/
|
|
373
|
-
export const getRenderer = (
|
|
374
|
-
|
|
375
|
-
|
|
361
|
+
export const getRenderer = (
|
|
362
|
+
fiber: Fiber,
|
|
363
|
+
target: ReactDevToolsTarget = globalThis,
|
|
364
|
+
): ReactRenderer | null => {
|
|
376
365
|
let rootFiber = fiber;
|
|
377
|
-
while (rootFiber.return)
|
|
378
|
-
rootFiber = rootFiber.return;
|
|
379
|
-
}
|
|
380
|
-
|
|
366
|
+
while (rootFiber.return) rootFiber = rootFiber.return;
|
|
381
367
|
const fiberRoot = rootFiber.stateNode;
|
|
382
368
|
if (!isFiberRoot(fiberRoot)) return null;
|
|
383
369
|
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
|
|
370
|
+
const trackedRendererId = rootRendererIds.get(fiberRoot);
|
|
371
|
+
const trackedHook = rootHooks.get(fiberRoot);
|
|
372
|
+
if (trackedRendererId !== undefined && trackedHook) {
|
|
373
|
+
return trackedHook.renderers.get(trackedRendererId) ?? null;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const rdtHook = target.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
377
|
+
if (!rdtHook?.getFiberRoots) return null;
|
|
378
|
+
for (const [rendererId, renderer] of rdtHook.renderers) {
|
|
379
|
+
if (!rdtHook.getFiberRoots(rendererId).has(fiberRoot)) continue;
|
|
380
|
+
_fiberRoots.add(fiberRoot);
|
|
381
|
+
rootRendererIds.set(fiberRoot, rendererId);
|
|
382
|
+
rootHooks.set(fiberRoot, rdtHook);
|
|
383
|
+
setReactWorkTagsForFiber(fiberRoot.current, renderer);
|
|
384
|
+
return renderer;
|
|
385
|
+
}
|
|
386
|
+
return null;
|
|
387
387
|
};
|
|
388
388
|
|
|
389
389
|
export type RenderPhase = "mount" | "unmount" | "update";
|
|
390
390
|
|
|
391
|
+
interface FiberReference {
|
|
392
|
+
deref: () => Fiber | undefined;
|
|
393
|
+
}
|
|
394
|
+
|
|
391
395
|
let nextFiberId = 0;
|
|
392
396
|
const fiberIdMap = new WeakMap<Fiber, number>();
|
|
397
|
+
const fiberByIdMap = new Map<number, FiberReference>();
|
|
398
|
+
const fiberIdFinalizationRegistry =
|
|
399
|
+
typeof FinalizationRegistry === "function"
|
|
400
|
+
? new FinalizationRegistry<number>((fiberId) => {
|
|
401
|
+
if (!fiberByIdMap.get(fiberId)?.deref()) fiberByIdMap.delete(fiberId);
|
|
402
|
+
})
|
|
403
|
+
: null;
|
|
404
|
+
|
|
405
|
+
const createFiberReference = (fiber: Fiber): FiberReference =>
|
|
406
|
+
typeof WeakRef === "function" ? new WeakRef(fiber) : { deref: () => fiber };
|
|
393
407
|
|
|
394
408
|
export const setFiberId = (fiber: Fiber, fiberId: number = nextFiberId++): void => {
|
|
409
|
+
const previousFiberId = fiberIdMap.get(fiber);
|
|
410
|
+
if (previousFiberId !== undefined && previousFiberId !== fiberId) {
|
|
411
|
+
fiberByIdMap.delete(previousFiberId);
|
|
412
|
+
}
|
|
395
413
|
fiberIdMap.set(fiber, fiberId);
|
|
414
|
+
fiberByIdMap.set(fiberId, createFiberReference(fiber));
|
|
415
|
+
fiberIdFinalizationRegistry?.register(fiber, fiberId);
|
|
396
416
|
if (Number.isSafeInteger(fiberId) && fiberId >= nextFiberId) {
|
|
397
417
|
nextFiberId = fiberId + 1;
|
|
398
418
|
}
|
|
@@ -402,6 +422,7 @@ export const getFiberId = (fiber: Fiber): number => {
|
|
|
402
422
|
let currentFiberId = fiberIdMap.get(fiber);
|
|
403
423
|
if (currentFiberId === undefined && fiber.alternate) {
|
|
404
424
|
currentFiberId = fiberIdMap.get(fiber.alternate);
|
|
425
|
+
if (currentFiberId !== undefined) setFiberId(fiber, currentFiberId);
|
|
405
426
|
}
|
|
406
427
|
if (currentFiberId === undefined) {
|
|
407
428
|
currentFiberId = nextFiberId++;
|
|
@@ -410,6 +431,33 @@ export const getFiberId = (fiber: Fiber): number => {
|
|
|
410
431
|
return currentFiberId;
|
|
411
432
|
};
|
|
412
433
|
|
|
434
|
+
export const getFiberById = (fiberId: number): Fiber | null => {
|
|
435
|
+
const fiber = fiberByIdMap.get(fiberId)?.deref();
|
|
436
|
+
if (!fiber) {
|
|
437
|
+
fiberByIdMap.delete(fiberId);
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
return getLatestFiber(fiber);
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
const releaseFiberId = (fiber: Fiber): void => {
|
|
444
|
+
const relatedFibers = fiber.alternate ? [fiber, fiber.alternate] : [fiber];
|
|
445
|
+
const fiberIds = new Set<number>();
|
|
446
|
+
|
|
447
|
+
for (const relatedFiber of relatedFibers) {
|
|
448
|
+
const fiberId = fiberIdMap.get(relatedFiber);
|
|
449
|
+
if (fiberId !== undefined) fiberIds.add(fiberId);
|
|
450
|
+
fiberIdMap.delete(relatedFiber);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
for (const fiberId of fiberIds) {
|
|
454
|
+
const assignedFiber = fiberByIdMap.get(fiberId)?.deref();
|
|
455
|
+
if (!assignedFiber || relatedFibers.includes(assignedFiber)) {
|
|
456
|
+
fiberByIdMap.delete(fiberId);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
|
|
413
461
|
const mountFiberRecursively = (
|
|
414
462
|
onRender: RenderHandler,
|
|
415
463
|
firstChild: Fiber,
|
|
@@ -435,13 +483,13 @@ const mountFiberRecursively = (
|
|
|
435
483
|
if (fallbackChildFragment) {
|
|
436
484
|
const fallbackChild = fallbackChildFragment.child;
|
|
437
485
|
if (fallbackChild !== null) {
|
|
438
|
-
mountFiberRecursively(onRender, fallbackChild,
|
|
486
|
+
mountFiberRecursively(onRender, fallbackChild, true);
|
|
439
487
|
}
|
|
440
488
|
}
|
|
441
489
|
} else {
|
|
442
490
|
const primaryChild = fiber.child?.child ?? null;
|
|
443
491
|
if (primaryChild !== null) {
|
|
444
|
-
mountFiberRecursively(onRender, primaryChild,
|
|
492
|
+
mountFiberRecursively(onRender, primaryChild, true);
|
|
445
493
|
}
|
|
446
494
|
}
|
|
447
495
|
} else if (fiber.child !== null) {
|
|
@@ -635,6 +683,7 @@ export const traverseRenderedFibers = (root: Fiber | FiberRoot, onRender: Render
|
|
|
635
683
|
|
|
636
684
|
export interface InstrumentationOptions {
|
|
637
685
|
name?: string;
|
|
686
|
+
target?: ReactDevToolsTarget;
|
|
638
687
|
onActive?: () => unknown;
|
|
639
688
|
onCommitFiberRoot?: (
|
|
640
689
|
rendererID: number,
|
|
@@ -649,6 +698,7 @@ export interface InstrumentationOptions {
|
|
|
649
698
|
|
|
650
699
|
interface InstrumentationSubscription {
|
|
651
700
|
options: InstrumentationOptions;
|
|
701
|
+
target: ReactDevToolsTarget;
|
|
652
702
|
}
|
|
653
703
|
|
|
654
704
|
const instrumentationSubscriptions = new Set<InstrumentationSubscription>();
|
|
@@ -661,6 +711,7 @@ interface HookDispatchers {
|
|
|
661
711
|
}
|
|
662
712
|
|
|
663
713
|
const hookDispatchers = new WeakMap<ReactDevToolsGlobalHook, Partial<HookDispatchers>>();
|
|
714
|
+
const hookTargets = new WeakMap<ReactDevToolsGlobalHook, ReactDevToolsTarget>();
|
|
664
715
|
let didSubscribeToHookReplacements = false;
|
|
665
716
|
|
|
666
717
|
// each hook event is dispatched from a single re-installable wrapper. If
|
|
@@ -690,20 +741,17 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
|
690
741
|
setReactWorkTagsForFiber(root.current, rdtHook.renderers.get(rendererID));
|
|
691
742
|
// Custom renderers and test harnesses commit roots without a memoizedState;
|
|
692
743
|
// those must stay tracked, so only explicit unmount evidence removes a root.
|
|
693
|
-
|
|
694
|
-
const isUnmounting =
|
|
695
|
-
rootMemoizedState === null ||
|
|
696
|
-
(rootMemoizedState !== undefined &&
|
|
697
|
-
(rootMemoizedState.element === null || rootMemoizedState.element === undefined));
|
|
698
|
-
if (isUnmounting) {
|
|
744
|
+
if (isFiberRootUnmounted(root)) {
|
|
699
745
|
_fiberRoots.delete(root);
|
|
700
746
|
rootRendererIds.delete(root);
|
|
747
|
+
rootHooks.delete(root);
|
|
701
748
|
} else {
|
|
702
749
|
_fiberRoots.add(root);
|
|
703
750
|
rootRendererIds.set(root, rendererID);
|
|
751
|
+
rootHooks.set(root, rdtHook);
|
|
704
752
|
}
|
|
705
|
-
for (const { options } of instrumentationSubscriptions) {
|
|
706
|
-
if (options.onCommitFiberRoot) {
|
|
753
|
+
for (const { options, target } of instrumentationSubscriptions) {
|
|
754
|
+
if (target === hookTargets.get(rdtHook) && options.onCommitFiberRoot) {
|
|
707
755
|
options.onCommitFiberRoot(rendererID, root, priority, didError);
|
|
708
756
|
}
|
|
709
757
|
}
|
|
@@ -728,10 +776,14 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
|
728
776
|
if (hookDispatchers.get(rdtHook)?.onCommitFiberUnmount !== dispatchCommitFiberUnmount) {
|
|
729
777
|
return;
|
|
730
778
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
options.onCommitFiberUnmount
|
|
779
|
+
try {
|
|
780
|
+
for (const { options, target } of instrumentationSubscriptions) {
|
|
781
|
+
if (target === hookTargets.get(rdtHook) && options.onCommitFiberUnmount) {
|
|
782
|
+
options.onCommitFiberUnmount(rendererID, fiber);
|
|
783
|
+
}
|
|
734
784
|
}
|
|
785
|
+
} finally {
|
|
786
|
+
releaseFiberId(fiber);
|
|
735
787
|
}
|
|
736
788
|
};
|
|
737
789
|
dispatchers.onCommitFiberUnmount = dispatchCommitFiberUnmount;
|
|
@@ -753,8 +805,8 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
|
753
805
|
if (hookDispatchers.get(rdtHook)?.onPostCommitFiberRoot !== dispatchPostCommitFiberRoot) {
|
|
754
806
|
return;
|
|
755
807
|
}
|
|
756
|
-
for (const { options } of instrumentationSubscriptions) {
|
|
757
|
-
if (options.onPostCommitFiberRoot) {
|
|
808
|
+
for (const { options, target } of instrumentationSubscriptions) {
|
|
809
|
+
if (target === hookTargets.get(rdtHook) && options.onPostCommitFiberRoot) {
|
|
758
810
|
options.onPostCommitFiberRoot(rendererID, root);
|
|
759
811
|
}
|
|
760
812
|
}
|
|
@@ -777,8 +829,8 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
|
777
829
|
prevOnScheduleFiberRoot.call(rdtHook, rendererID, root, children);
|
|
778
830
|
}
|
|
779
831
|
if (hookDispatchers.get(rdtHook)?.onScheduleFiberRoot !== dispatchScheduleFiberRoot) return;
|
|
780
|
-
for (const { options } of instrumentationSubscriptions) {
|
|
781
|
-
if (options.onScheduleFiberRoot) {
|
|
832
|
+
for (const { options, target } of instrumentationSubscriptions) {
|
|
833
|
+
if (target === hookTargets.get(rdtHook) && options.onScheduleFiberRoot) {
|
|
782
834
|
options.onScheduleFiberRoot(rendererID, root, children);
|
|
783
835
|
}
|
|
784
836
|
}
|
|
@@ -788,11 +840,23 @@ const setHookEventDispatchers = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
|
788
840
|
}
|
|
789
841
|
};
|
|
790
842
|
|
|
791
|
-
const
|
|
843
|
+
const handleHookReplacement = (
|
|
844
|
+
rdtHook: ReactDevToolsGlobalHook,
|
|
845
|
+
target: ReactDevToolsTarget,
|
|
846
|
+
): void => {
|
|
847
|
+
hookTargets.set(rdtHook, target);
|
|
848
|
+
setHookEventDispatchers(rdtHook);
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
const wireHookEventDispatchers = (
|
|
852
|
+
rdtHook: ReactDevToolsGlobalHook,
|
|
853
|
+
target: ReactDevToolsTarget = globalThis,
|
|
854
|
+
): void => {
|
|
792
855
|
if (!didSubscribeToHookReplacements) {
|
|
793
|
-
onRDTHookReplace(
|
|
856
|
+
onRDTHookReplace(handleHookReplacement);
|
|
794
857
|
didSubscribeToHookReplacements = true;
|
|
795
858
|
}
|
|
859
|
+
hookTargets.set(rdtHook, target);
|
|
796
860
|
setHookEventDispatchers(rdtHook);
|
|
797
861
|
};
|
|
798
862
|
|
|
@@ -822,15 +886,16 @@ try {
|
|
|
822
886
|
* unsubscribe();
|
|
823
887
|
*/
|
|
824
888
|
export const instrument = (options: InstrumentationOptions): Unsubscribe => {
|
|
825
|
-
const
|
|
889
|
+
const target = options.target ?? globalThis;
|
|
890
|
+
const rdtHook = getRDTHook(options.onActive, target);
|
|
826
891
|
rdtHook._instrumentationSource = options.name ?? BIPPY_INSTRUMENTATION_STRING;
|
|
827
892
|
|
|
828
|
-
wireHookEventDispatchers(rdtHook);
|
|
829
|
-
const subscription: InstrumentationSubscription = { options };
|
|
893
|
+
wireHookEventDispatchers(rdtHook, target);
|
|
894
|
+
const subscription: InstrumentationSubscription = { options, target };
|
|
830
895
|
instrumentationSubscriptions.add(subscription);
|
|
831
896
|
|
|
832
897
|
return createUnsubscribe(() => {
|
|
833
|
-
if (options.onActive)
|
|
898
|
+
if (options.onActive) removeActiveListener(options.onActive, target);
|
|
834
899
|
instrumentationSubscriptions.delete(subscription);
|
|
835
900
|
});
|
|
836
901
|
};
|
|
@@ -874,9 +939,12 @@ const getPublicHostInstance = (stateNode: unknown): unknown => {
|
|
|
874
939
|
return typeof nativeTag === "number" ? nativeTag : stateNode;
|
|
875
940
|
};
|
|
876
941
|
|
|
877
|
-
export const getFiber = <
|
|
878
|
-
|
|
879
|
-
|
|
942
|
+
export const getFiber = <HostInstance>(
|
|
943
|
+
hostInstance: HostInstance,
|
|
944
|
+
target: ReactDevToolsTarget = globalThis,
|
|
945
|
+
): Fiber | null => {
|
|
946
|
+
const rdtHook = target.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
947
|
+
if (rdtHook) {
|
|
880
948
|
for (const renderer of rdtHook.renderers.values()) {
|
|
881
949
|
try {
|
|
882
950
|
const fiber = renderer.findFiberByHostInstance?.(hostInstance);
|
|
@@ -899,19 +967,31 @@ export const getFiber = <T>(hostInstance: T): Fiber | null => {
|
|
|
899
967
|
|
|
900
968
|
for (const key of Object.keys(hostInstance)) {
|
|
901
969
|
if (isFiberPropertyKey(key)) {
|
|
902
|
-
knownFiberPropertyKeys.add(key);
|
|
903
970
|
const fiber = Reflect.get(hostInstance, key);
|
|
904
|
-
if (isFiber(fiber))
|
|
971
|
+
if (isFiber(fiber)) {
|
|
972
|
+
knownFiberPropertyKeys.add(key);
|
|
973
|
+
return fiber;
|
|
974
|
+
}
|
|
905
975
|
}
|
|
906
976
|
}
|
|
907
977
|
}
|
|
908
978
|
|
|
909
979
|
if (
|
|
910
980
|
hostInstance !== null &&
|
|
911
|
-
hostInstance !== undefined &&
|
|
912
981
|
(typeof hostInstance === "object" || typeof hostInstance === "number")
|
|
913
982
|
) {
|
|
983
|
+
const targetFiberRoots = new Set<FiberRoot>();
|
|
984
|
+
if (rdtHook?.getFiberRoots) {
|
|
985
|
+
for (const rendererId of rdtHook.renderers.keys()) {
|
|
986
|
+
for (const fiberRoot of rdtHook.getFiberRoots(rendererId)) {
|
|
987
|
+
targetFiberRoots.add(fiberRoot);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
}
|
|
914
991
|
for (const fiberRoot of _fiberRoots) {
|
|
992
|
+
if (rootHooks.get(fiberRoot) === rdtHook) targetFiberRoots.add(fiberRoot);
|
|
993
|
+
}
|
|
994
|
+
for (const fiberRoot of targetFiberRoots) {
|
|
915
995
|
const fiber = traverseFiber(
|
|
916
996
|
fiberRoot.current,
|
|
917
997
|
(candidateFiber) =>
|
|
@@ -928,16 +1008,16 @@ export const getFiberFromHostInstance = getFiber;
|
|
|
928
1008
|
|
|
929
1009
|
export {
|
|
930
1010
|
BIPPY_INSTRUMENTATION_STRING,
|
|
931
|
-
_onActiveListeners,
|
|
932
1011
|
_renderers,
|
|
933
1012
|
getRDTHook,
|
|
934
1013
|
hasRDTHook,
|
|
935
1014
|
installRDTHook,
|
|
1015
|
+
isFiberRootUnmounted,
|
|
936
1016
|
isReactRefresh,
|
|
937
1017
|
isRealReactDevtools,
|
|
938
1018
|
onRendererInject,
|
|
939
1019
|
patchRDTHook,
|
|
940
1020
|
version,
|
|
941
1021
|
} from "./rdt-hook.js";
|
|
942
|
-
export type { Unsubscribe } from "./rdt-hook.js";
|
|
1022
|
+
export type { ReactDevToolsTarget, Unsubscribe } from "./rdt-hook.js";
|
|
943
1023
|
export * from "./react-internals/index.js";
|