bippy 0.7.3-dev.a6ed02c → 0.7.3-dev.e2b879b
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/dist/core.cjs +1 -1
- package/dist/core.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +6 -3
- package/dist/index.d.ts +6 -3
- package/dist/index.js +1 -1
- package/dist/rdt-hook.cjs +1 -1
- package/dist/rdt-hook.js +1 -1
- package/dist/source.cjs +12 -12
- package/dist/source.js +13 -13
- package/package.json +3 -31
- package/src/core.ts +66 -64
- package/src/rdt-hook.ts +2 -0
- package/src/react-internals/current-fiber.ts +84 -0
- package/src/react-internals/index.ts +6 -1
- package/src/react.ts +37 -16
- package/src/source/inspect-hooks.ts +24 -18
package/src/react.ts
CHANGED
|
@@ -5,6 +5,11 @@ import type { Fiber } from "./react-internals/index.js";
|
|
|
5
5
|
|
|
6
6
|
export type { Fiber } from "./react-internals/index.js";
|
|
7
7
|
|
|
8
|
+
interface FiberHookEffect {
|
|
9
|
+
create: unknown;
|
|
10
|
+
next: unknown;
|
|
11
|
+
}
|
|
12
|
+
|
|
8
13
|
const preserveState = (state: undefined): undefined => state;
|
|
9
14
|
const readEmptySnapshot = (): undefined => undefined;
|
|
10
15
|
const unsubscribeFromEmptyStore = (): void => {};
|
|
@@ -53,24 +58,40 @@ const useReducerCapture = (): void => {
|
|
|
53
58
|
const useFiberWithExternalStore = (): Fiber | undefined =>
|
|
54
59
|
captureFiberFromHook(useExternalStoreCapture) ?? undefined;
|
|
55
60
|
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
const renderedFiberRef = React.useRef<Fiber | null>(null);
|
|
59
|
-
const hookFiber = captureFiberFromHook(useReducerCapture);
|
|
60
|
-
const fiber =
|
|
61
|
-
hookFiber ??
|
|
62
|
-
(renderedFiberRef.current !== committedFiberRef.current
|
|
63
|
-
? renderedFiberRef.current
|
|
64
|
-
: committedFiberRef.current?.alternate) ??
|
|
65
|
-
committedFiberRef.current;
|
|
66
|
-
|
|
67
|
-
renderedFiberRef.current = fiber;
|
|
61
|
+
const isHookEffect = (value: unknown): value is FiberHookEffect =>
|
|
62
|
+
typeof value === "object" && value !== null && "create" in value && "next" in value;
|
|
68
63
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
const hasRenderMarker = (fiber: Fiber, renderMarker: () => void): boolean => {
|
|
65
|
+
const lastEffect = fiber.updateQueue?.lastEffect;
|
|
66
|
+
if (!isHookEffect(lastEffect)) return false;
|
|
67
|
+
let effect = lastEffect;
|
|
68
|
+
do {
|
|
69
|
+
if (effect.create === renderMarker) return true;
|
|
70
|
+
if (!isHookEffect(effect.next)) return false;
|
|
71
|
+
effect = effect.next;
|
|
72
|
+
} while (effect !== lastEffect);
|
|
73
|
+
return false;
|
|
74
|
+
};
|
|
72
75
|
|
|
73
|
-
|
|
76
|
+
// HACK: React 17 only binds the rendering Fiber on mount. Later renders are identified through
|
|
77
|
+
// the effect list instead: renderWithHooks clears `updateQueue` on the work-in-progress Fiber,
|
|
78
|
+
// so only the Fiber that is rendering right now holds this render's marker effect.
|
|
79
|
+
const useFiberWithReducer = (): Fiber | undefined => {
|
|
80
|
+
const fiberRef = React.useRef<Fiber | null>(null);
|
|
81
|
+
const renderMarker = (): void => {};
|
|
82
|
+
React.useEffect(renderMarker, []);
|
|
83
|
+
const mountedFiber = captureFiberFromHook(useReducerCapture);
|
|
84
|
+
if (mountedFiber) {
|
|
85
|
+
fiberRef.current = mountedFiber;
|
|
86
|
+
return mountedFiber;
|
|
87
|
+
}
|
|
88
|
+
const knownFiber = fiberRef.current;
|
|
89
|
+
if (!knownFiber) return undefined;
|
|
90
|
+
return (
|
|
91
|
+
[knownFiber, knownFiber.alternate].find(
|
|
92
|
+
(candidate) => candidate !== null && hasRenderMarker(candidate, renderMarker),
|
|
93
|
+
) ?? undefined
|
|
94
|
+
);
|
|
74
95
|
};
|
|
75
96
|
|
|
76
97
|
export const useFiber =
|
|
@@ -87,6 +87,16 @@ let currentHook: MemoizedState | null = null;
|
|
|
87
87
|
let currentContextDependency: ContextDependency<unknown> | null = null;
|
|
88
88
|
let currentThenableIndex = 0;
|
|
89
89
|
let currentThenableState: unknown[] | null = null;
|
|
90
|
+
let currentMemoCacheIndex = 0;
|
|
91
|
+
let isInspectingHooks = false;
|
|
92
|
+
|
|
93
|
+
const assertNotInspectingHooks = (): void => {
|
|
94
|
+
if (isInspectingHooks) {
|
|
95
|
+
throw new BippyHookInspectionError(
|
|
96
|
+
"Hook inspection cannot be called during another inspection.",
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
90
100
|
|
|
91
101
|
const SuspenseException: unknown = new Error(
|
|
92
102
|
"Suspense interrupted this render. This error is an internal implementation detail of `use`.",
|
|
@@ -323,16 +333,8 @@ const dispatcherUseMemoCache = (size: number): unknown[] => {
|
|
|
323
333
|
const memoCache = fiber.updateQueue?.memoCache;
|
|
324
334
|
if (memoCache === null || memoCache === undefined) return [];
|
|
325
335
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
memoCacheSlots = memoCache.data[memoCache.index] = Array.from(
|
|
329
|
-
{ length: size },
|
|
330
|
-
() => REACT_MEMO_CACHE_SENTINEL,
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
memoCache.index++;
|
|
335
|
-
return memoCacheSlots;
|
|
336
|
+
const memoCacheSlots = memoCache.data[currentMemoCacheIndex++];
|
|
337
|
+
return memoCacheSlots?.slice() ?? Array.from({ length: size }, () => REACT_MEMO_CACHE_SENTINEL);
|
|
336
338
|
};
|
|
337
339
|
|
|
338
340
|
const dispatcherUseOptimistic = (passthrough: unknown): [unknown, () => void] => {
|
|
@@ -834,6 +836,7 @@ const performDispatcherInspection = <TArgs extends unknown[]>(
|
|
|
834
836
|
): HooksTree => {
|
|
835
837
|
const previousDispatcher = readDispatcher(dispatcherRef);
|
|
836
838
|
writeDispatcher(dispatcherRef, dispatcherProxy);
|
|
839
|
+
isInspectingHooks = true;
|
|
837
840
|
|
|
838
841
|
let capturedHookLog: HookLogEntry[] = [];
|
|
839
842
|
let ancestorStackError: Error | undefined;
|
|
@@ -846,6 +849,7 @@ const performDispatcherInspection = <TArgs extends unknown[]>(
|
|
|
846
849
|
} finally {
|
|
847
850
|
capturedHookLog = hookLog;
|
|
848
851
|
hookLog = [];
|
|
852
|
+
isInspectingHooks = false;
|
|
849
853
|
writeDispatcher(dispatcherRef, previousDispatcher);
|
|
850
854
|
}
|
|
851
855
|
|
|
@@ -938,6 +942,7 @@ export const inspectHooks = (
|
|
|
938
942
|
props: Record<string, unknown>,
|
|
939
943
|
target: ReactDevToolsTarget = globalThis,
|
|
940
944
|
): HooksTree => {
|
|
945
|
+
assertNotInspectingHooks();
|
|
941
946
|
const dispatcherRef = requireDispatcherRef(undefined, target);
|
|
942
947
|
getPrimitiveStackCache();
|
|
943
948
|
currentHook = null;
|
|
@@ -961,6 +966,7 @@ export const inspectHooks = (
|
|
|
961
966
|
};
|
|
962
967
|
|
|
963
968
|
export const getFiberHooks = (fiber: Fiber): HooksTree => {
|
|
969
|
+
assertNotInspectingHooks();
|
|
964
970
|
const dispatcherRef = requireDispatcherRef(fiber);
|
|
965
971
|
const workTags = getReactWorkTagsForFiber(fiber);
|
|
966
972
|
|
|
@@ -976,6 +982,7 @@ export const getFiberHooks = (fiber: Fiber): HooksTree => {
|
|
|
976
982
|
|
|
977
983
|
currentHook = fiber.memoizedState;
|
|
978
984
|
currentFiber = fiber;
|
|
985
|
+
currentMemoCacheIndex = 0;
|
|
979
986
|
|
|
980
987
|
const debugThenableState = fiber.dependencies?._debugThenableState;
|
|
981
988
|
const usedThenables = Array.isArray(debugThenableState)
|
|
@@ -984,18 +991,16 @@ export const getFiberHooks = (fiber: Fiber): HooksTree => {
|
|
|
984
991
|
currentThenableState = Array.isArray(usedThenables) ? usedThenables : null;
|
|
985
992
|
currentThenableIndex = 0;
|
|
986
993
|
|
|
987
|
-
resolveContextDependency(fiber);
|
|
988
|
-
|
|
989
|
-
const type = fiber.type;
|
|
990
|
-
let props = fiber.memoizedProps;
|
|
991
|
-
if (type !== fiber.elementType) {
|
|
992
|
-
props = resolveDefaultProps(type, props);
|
|
993
|
-
}
|
|
994
|
-
|
|
995
994
|
const originalConsoleMethods = suppressConsole();
|
|
996
995
|
const contextMap = new Map<ReactContext<unknown>, unknown>();
|
|
997
996
|
|
|
998
997
|
try {
|
|
998
|
+
resolveContextDependency(fiber);
|
|
999
|
+
const type = fiber.type;
|
|
1000
|
+
const props =
|
|
1001
|
+
type !== fiber.elementType
|
|
1002
|
+
? resolveDefaultProps(type, fiber.memoizedProps)
|
|
1003
|
+
: fiber.memoizedProps;
|
|
999
1004
|
if (
|
|
1000
1005
|
currentContextDependency !== null &&
|
|
1001
1006
|
!Object.hasOwn(currentContextDependency, "memoizedValue")
|
|
@@ -1030,6 +1035,7 @@ export const getFiberHooks = (fiber: Fiber): HooksTree => {
|
|
|
1030
1035
|
currentContextDependency = null;
|
|
1031
1036
|
currentThenableState = null;
|
|
1032
1037
|
currentThenableIndex = 0;
|
|
1038
|
+
currentMemoCacheIndex = 0;
|
|
1033
1039
|
restoreContexts(contextMap);
|
|
1034
1040
|
restoreConsole(originalConsoleMethods);
|
|
1035
1041
|
}
|