bippy 0.7.2-dev.23cd194 → 0.7.2-dev.e113600
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/README.md +0 -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 +8 -35
- package/dist/index.d.ts +8 -35
- 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 +10 -9
- package/dist/source.d.cts +36 -9
- package/dist/source.d.ts +36 -9
- package/dist/source.js +13 -12
- package/package.json +7 -7
- package/src/core.ts +134 -55
- package/src/rdt-hook.ts +126 -70
- package/src/react-internals/types.ts +33 -6
- package/src/react.ts +1 -0
- 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 +169 -28
- package/src/source/owner-stack.ts +43 -35
- 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,9 +34,20 @@ 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 => {
|
|
32
52
|
try {
|
|
33
53
|
const code = Function.prototype.toString.call(functionToCheck);
|
|
@@ -47,7 +67,7 @@ const checkDCE = (functionToCheck: unknown): void => {
|
|
|
47
67
|
export const isRealReactDevtools = (
|
|
48
68
|
rdtHook: ReactDevToolsGlobalHook | undefined | null = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__,
|
|
49
69
|
): boolean => {
|
|
50
|
-
return Boolean(rdtHook && "getFiberRoots" in rdtHook);
|
|
70
|
+
return Boolean(rdtHook && "getFiberRoots" in rdtHook && rdtHook._isBippyHook !== true);
|
|
51
71
|
};
|
|
52
72
|
|
|
53
73
|
// HACK: react-refresh's inject wrapper is the only stock hook whose parameter is named
|
|
@@ -69,53 +89,63 @@ export const _onActiveListeners = new Set<ActiveListener>();
|
|
|
69
89
|
|
|
70
90
|
export const _renderers = new Set<ReactRenderer>();
|
|
71
91
|
|
|
72
|
-
const
|
|
92
|
+
const activeListenerTargets = new WeakMap<ActiveListener, Set<ReactDevToolsTarget>>();
|
|
93
|
+
const rendererInjectSubscriptions = new Set<RendererInjectSubscription>();
|
|
73
94
|
const rdtHookReplaceListeners = new Set<RDTHookReplaceListener>();
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
95
|
+
const notifiedRenderersByTarget = new WeakMap<ReactDevToolsTarget, WeakSet<ReactRenderer>>();
|
|
96
|
+
|
|
97
|
+
const addActiveListener = (listener: ActiveListener, target: ReactDevToolsTarget): void => {
|
|
98
|
+
_onActiveListeners.add(listener);
|
|
99
|
+
const targets = activeListenerTargets.get(listener) ?? new Set<ReactDevToolsTarget>();
|
|
100
|
+
targets.add(target);
|
|
101
|
+
activeListenerTargets.set(listener, targets);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const removeActiveListener = (
|
|
105
|
+
listener: ActiveListener,
|
|
106
|
+
target: ReactDevToolsTarget,
|
|
107
|
+
): void => {
|
|
108
|
+
const targets = activeListenerTargets.get(listener);
|
|
109
|
+
targets?.delete(target);
|
|
110
|
+
if (!targets || targets.size === 0) {
|
|
111
|
+
activeListenerTargets.delete(listener);
|
|
112
|
+
_onActiveListeners.delete(listener);
|
|
84
113
|
}
|
|
85
114
|
};
|
|
86
115
|
|
|
87
|
-
const
|
|
88
|
-
for (const listener of
|
|
89
|
-
listener(
|
|
116
|
+
const notifyActiveListeners = (target: ReactDevToolsTarget): void => {
|
|
117
|
+
for (const listener of _onActiveListeners) {
|
|
118
|
+
if (activeListenerTargets.get(listener)?.has(target)) listener();
|
|
90
119
|
}
|
|
91
120
|
};
|
|
92
121
|
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
122
|
+
const notifyRendererInjectListeners = (
|
|
123
|
+
target: ReactDevToolsTarget,
|
|
124
|
+
renderer: ReactRenderer,
|
|
125
|
+
): void => {
|
|
126
|
+
for (const subscription of rendererInjectSubscriptions) {
|
|
127
|
+
if (subscription.target === target) subscription.listener(renderer);
|
|
96
128
|
}
|
|
97
129
|
};
|
|
98
130
|
|
|
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);
|
|
131
|
+
const notifyRDTHookReplaceListeners = (
|
|
132
|
+
rdtHook: ReactDevToolsGlobalHook,
|
|
133
|
+
target: ReactDevToolsTarget,
|
|
134
|
+
): void => {
|
|
135
|
+
for (const listener of rdtHookReplaceListeners) {
|
|
136
|
+
listener(rdtHook, target);
|
|
137
|
+
}
|
|
112
138
|
};
|
|
113
139
|
|
|
114
|
-
export const onRendererInject = (
|
|
115
|
-
|
|
116
|
-
|
|
140
|
+
export const onRendererInject = (
|
|
141
|
+
listener: RendererInjectListener,
|
|
142
|
+
target: ReactDevToolsTarget = globalThis,
|
|
143
|
+
): Unsubscribe => {
|
|
144
|
+
getRDTHook(undefined, target);
|
|
145
|
+
const subscription = { listener, target };
|
|
146
|
+
rendererInjectSubscriptions.add(subscription);
|
|
117
147
|
return createUnsubscribe(() => {
|
|
118
|
-
|
|
148
|
+
rendererInjectSubscriptions.delete(subscription);
|
|
119
149
|
});
|
|
120
150
|
};
|
|
121
151
|
|
|
@@ -126,8 +156,13 @@ export const onRDTHookReplace = (listener: RDTHookReplaceListener): Unsubscribe
|
|
|
126
156
|
});
|
|
127
157
|
};
|
|
128
158
|
|
|
159
|
+
// A Map built in another realm (an iframe target) fails instanceof, so replacing it
|
|
160
|
+
// would discard that realm's renderer registry. Object.prototype.toString is realm-agnostic.
|
|
161
|
+
export const isRendererMap = (value: unknown): value is Map<number, ReactRenderer> =>
|
|
162
|
+
Object.prototype.toString.call(value) === "[object Map]";
|
|
163
|
+
|
|
129
164
|
const getRendererMap = (rdtHook: ReactDevToolsGlobalHook): Map<number, ReactRenderer> => {
|
|
130
|
-
if (!(rdtHook.renderers
|
|
165
|
+
if (!isRendererMap(rdtHook.renderers)) {
|
|
131
166
|
rdtHook.renderers = new Map();
|
|
132
167
|
}
|
|
133
168
|
return rdtHook.renderers;
|
|
@@ -135,6 +170,7 @@ const getRendererMap = (rdtHook: ReactDevToolsGlobalHook): Map<number, ReactRend
|
|
|
135
170
|
|
|
136
171
|
const trackInjectedRenderer = (
|
|
137
172
|
rdtHook: ReactDevToolsGlobalHook,
|
|
173
|
+
target: ReactDevToolsTarget,
|
|
138
174
|
renderers: Map<number, ReactRenderer>,
|
|
139
175
|
rendererId: number,
|
|
140
176
|
renderer: ReactRenderer,
|
|
@@ -143,28 +179,47 @@ const trackInjectedRenderer = (
|
|
|
143
179
|
_renderers.add(renderer);
|
|
144
180
|
if (!rdtHook._instrumentationIsActive) {
|
|
145
181
|
rdtHook._instrumentationIsActive = true;
|
|
146
|
-
notifyActiveListeners();
|
|
182
|
+
notifyActiveListeners(target);
|
|
147
183
|
}
|
|
184
|
+
const notifiedRenderers = notifiedRenderersByTarget.get(target) ?? new WeakSet<ReactRenderer>();
|
|
185
|
+
notifiedRenderersByTarget.set(target, notifiedRenderers);
|
|
186
|
+
if (notifiedRenderers.has(renderer)) return;
|
|
187
|
+
notifiedRenderers.add(renderer);
|
|
188
|
+
notifyRendererInjectListeners(target, renderer);
|
|
148
189
|
};
|
|
149
190
|
|
|
150
|
-
export const installRDTHook = (
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
191
|
+
export const installRDTHook = (
|
|
192
|
+
onActive?: ActiveListener,
|
|
193
|
+
target: ReactDevToolsTarget = globalThis,
|
|
194
|
+
): ReactDevToolsGlobalHook => {
|
|
195
|
+
if (onActive) addActiveListener(onActive, target);
|
|
154
196
|
const renderers = new Map<number, ReactRenderer>();
|
|
197
|
+
const fiberRoots = new Map<number, Set<FiberRoot>>();
|
|
198
|
+
// Callers register roots by mutating the returned set, so it must be memoized.
|
|
199
|
+
const getRendererFiberRoots = (rendererId: number): Set<FiberRoot> => {
|
|
200
|
+
const rendererRoots = fiberRoots.get(rendererId) ?? new Set<FiberRoot>();
|
|
201
|
+
fiberRoots.set(rendererId, rendererRoots);
|
|
202
|
+
return rendererRoots;
|
|
203
|
+
};
|
|
155
204
|
let rendererIdCounter = 0;
|
|
156
205
|
let rdtHook: ReactDevToolsGlobalHook = {
|
|
157
206
|
_instrumentationIsActive: false,
|
|
158
207
|
_instrumentationSource: BIPPY_INSTRUMENTATION_STRING,
|
|
208
|
+
_isBippyHook: true,
|
|
159
209
|
checkDCE,
|
|
210
|
+
getFiberRoots: getRendererFiberRoots,
|
|
160
211
|
hasUnsupportedRendererAttached: false,
|
|
161
212
|
inject: (renderer) => {
|
|
162
213
|
const nextRendererId = ++rendererIdCounter;
|
|
163
|
-
trackInjectedRenderer(rdtHook, renderers, nextRendererId, renderer);
|
|
214
|
+
trackInjectedRenderer(rdtHook, target, renderers, nextRendererId, renderer);
|
|
164
215
|
return nextRendererId;
|
|
165
216
|
},
|
|
166
217
|
on: noOp,
|
|
167
|
-
onCommitFiberRoot:
|
|
218
|
+
onCommitFiberRoot: (rendererId, fiberRoot) => {
|
|
219
|
+
const rendererRoots = getRendererFiberRoots(rendererId);
|
|
220
|
+
if (isFiberRootUnmounted(fiberRoot)) rendererRoots.delete(fiberRoot);
|
|
221
|
+
else rendererRoots.add(fiberRoot);
|
|
222
|
+
},
|
|
168
223
|
onCommitFiberUnmount: noOp,
|
|
169
224
|
onPostCommitFiberRoot: noOp,
|
|
170
225
|
renderers,
|
|
@@ -172,13 +227,11 @@ export const installRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHo
|
|
|
172
227
|
supportsFlight: true,
|
|
173
228
|
};
|
|
174
229
|
try {
|
|
175
|
-
objectDefineProperty(
|
|
230
|
+
objectDefineProperty(target, "__REACT_DEVTOOLS_GLOBAL_HOOK__", {
|
|
176
231
|
configurable: true,
|
|
177
232
|
enumerable: true,
|
|
178
|
-
get()
|
|
179
|
-
|
|
180
|
-
},
|
|
181
|
-
set(newHook) {
|
|
233
|
+
get: () => rdtHook,
|
|
234
|
+
set: (newHook: ReactDevToolsGlobalHook | undefined) => {
|
|
182
235
|
if (newHook && typeof newHook === "object") {
|
|
183
236
|
const ourRenderers = rdtHook.renderers;
|
|
184
237
|
rdtHook = newHook;
|
|
@@ -188,13 +241,13 @@ export const installRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHo
|
|
|
188
241
|
nextRenderers.set(rendererId, renderer);
|
|
189
242
|
});
|
|
190
243
|
if (ourRenderers.size > 0 || rdtHookReplaceListeners.size > 0) {
|
|
191
|
-
patchRDTHook(onActive);
|
|
244
|
+
patchRDTHook(onActive, target);
|
|
192
245
|
}
|
|
193
|
-
notifyRDTHookReplaceListeners(rdtHook);
|
|
246
|
+
notifyRDTHookReplaceListeners(rdtHook, target);
|
|
194
247
|
}
|
|
195
248
|
},
|
|
196
249
|
});
|
|
197
|
-
if (typeof window !== "undefined") {
|
|
250
|
+
if (target === globalThis && typeof window !== "undefined") {
|
|
198
251
|
// HACK: The DevTools extension uses hasOwnProperty to decide whether it may replace an earlier hook.
|
|
199
252
|
const originalWindowHasOwnProperty = window.hasOwnProperty.bind(window);
|
|
200
253
|
const originalHasOwnPropertyDescriptor = Object.getOwnPropertyDescriptor(
|
|
@@ -224,17 +277,18 @@ export const installRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHo
|
|
|
224
277
|
});
|
|
225
278
|
}
|
|
226
279
|
} catch {
|
|
227
|
-
patchRDTHook(onActive);
|
|
280
|
+
patchRDTHook(onActive, target);
|
|
228
281
|
}
|
|
229
282
|
return rdtHook;
|
|
230
283
|
};
|
|
231
284
|
|
|
232
|
-
export const patchRDTHook = (
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
285
|
+
export const patchRDTHook = (
|
|
286
|
+
onActive?: ActiveListener,
|
|
287
|
+
target: ReactDevToolsTarget = globalThis,
|
|
288
|
+
): void => {
|
|
289
|
+
if (onActive) addActiveListener(onActive, target);
|
|
236
290
|
let didNotifyActiveListeners = false;
|
|
237
|
-
const rdtHook =
|
|
291
|
+
const rdtHook = getTargetHook(target);
|
|
238
292
|
if (!rdtHook) return;
|
|
239
293
|
const renderers = getRendererMap(rdtHook);
|
|
240
294
|
if (!rdtHook._instrumentationSource) {
|
|
@@ -252,20 +306,20 @@ export const patchRDTHook = (onActive?: ActiveListener): void => {
|
|
|
252
306
|
if (renderers.size) {
|
|
253
307
|
renderers.forEach((renderer) => _renderers.add(renderer));
|
|
254
308
|
rdtHook._instrumentationIsActive = true;
|
|
255
|
-
notifyActiveListeners();
|
|
309
|
+
notifyActiveListeners(target);
|
|
256
310
|
didNotifyActiveListeners = true;
|
|
257
311
|
} else if (!isReactDevtools && isReactRefresh(rdtHook)) {
|
|
258
312
|
// HACK: react-refresh's stub inject never records renderers, so a React app
|
|
259
313
|
// that injected before bippy loaded is undetectable through the renderers map.
|
|
260
314
|
// A react-refresh hook implies a dev renderer, so activate immediately.
|
|
261
315
|
rdtHook._instrumentationIsActive = true;
|
|
262
|
-
notifyActiveListeners();
|
|
316
|
+
notifyActiveListeners(target);
|
|
263
317
|
didNotifyActiveListeners = true;
|
|
264
318
|
}
|
|
265
319
|
const previousInject = rdtHook.inject;
|
|
266
320
|
rdtHook.inject = (renderer) => {
|
|
267
321
|
const rendererId = previousInject.call(rdtHook, renderer);
|
|
268
|
-
trackInjectedRenderer(rdtHook, renderers, rendererId, renderer);
|
|
322
|
+
trackInjectedRenderer(rdtHook, target, renderers, rendererId, renderer);
|
|
269
323
|
return rendererId;
|
|
270
324
|
};
|
|
271
325
|
}
|
|
@@ -274,18 +328,20 @@ export const patchRDTHook = (onActive?: ActiveListener): void => {
|
|
|
274
328
|
}
|
|
275
329
|
};
|
|
276
330
|
|
|
277
|
-
export const hasRDTHook = (): boolean =>
|
|
278
|
-
|
|
279
|
-
};
|
|
331
|
+
export const hasRDTHook = (target: ReactDevToolsTarget = globalThis): boolean =>
|
|
332
|
+
Object.hasOwn(target, "__REACT_DEVTOOLS_GLOBAL_HOOK__");
|
|
280
333
|
|
|
281
334
|
/**
|
|
282
335
|
* Returns the current React DevTools global hook.
|
|
283
336
|
*/
|
|
284
|
-
export const getRDTHook = (
|
|
285
|
-
|
|
286
|
-
|
|
337
|
+
export const getRDTHook = (
|
|
338
|
+
onActive?: ActiveListener,
|
|
339
|
+
target: ReactDevToolsTarget = globalThis,
|
|
340
|
+
): ReactDevToolsGlobalHook => {
|
|
341
|
+
if (!hasRDTHook(target)) {
|
|
342
|
+
return installRDTHook(onActive, target);
|
|
287
343
|
}
|
|
288
344
|
|
|
289
|
-
patchRDTHook(onActive);
|
|
290
|
-
return
|
|
345
|
+
patchRDTHook(onActive, target);
|
|
346
|
+
return getTargetHook(target) ?? installRDTHook(onActive, target);
|
|
291
347
|
};
|
|
@@ -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,
|
package/src/react.ts
CHANGED
|
@@ -38,6 +38,7 @@ const captureFiberFromHook = (useCaptureHook: () => void): Fiber | null => {
|
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
const useExternalStoreCapture = (): void => {
|
|
41
|
+
if (typeof useSyncExternalStore !== "function") return;
|
|
41
42
|
Reflect.apply(useSyncExternalStore, React, [
|
|
42
43
|
subscribeToEmptyStore,
|
|
43
44
|
readEmptySnapshot,
|
package/src/source/constants.ts
CHANGED
|
@@ -25,9 +25,7 @@ export const BUNDLED_FILE_PATTERN_REGEX =
|
|
|
25
25
|
|
|
26
26
|
export const QUERY_PARAMETER_PATTERN_REGEX = /^\?[\w~.-]+(?:=[^&#]*)?(?:&[\w~.-]+(?:=[^&#]*)?)*$/;
|
|
27
27
|
|
|
28
|
-
export const
|
|
29
|
-
|
|
30
|
-
export const SERVER_ENV_PATTERN = /\(at [^)]+\)$/;
|
|
28
|
+
export const SERVER_ENV_PATTERN = /\[[^\]]+\]$/;
|
|
31
29
|
|
|
32
30
|
export const REACT_STACK_BOTTOM_FRAME_PATTERNS = [
|
|
33
31
|
"react_stack_bottom_frame",
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
getSourceFromSourceMap,
|
|
7
7
|
getSourceMap,
|
|
8
8
|
type SourceFetch,
|
|
9
|
+
type SourceMapRequestOptions,
|
|
9
10
|
} from "./symbolication.js";
|
|
10
11
|
import type { StackFrame } from "./parse-stack.js";
|
|
11
12
|
|
|
@@ -53,8 +54,9 @@ const extractComponentNameFromSource = (
|
|
|
53
54
|
|
|
54
55
|
export const getDisplayNameFromSource = async (
|
|
55
56
|
fiber: Fiber,
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
shouldUseCache = true,
|
|
58
|
+
sourceFetch?: SourceFetch,
|
|
59
|
+
requestOptions: SourceMapRequestOptions = {},
|
|
58
60
|
): Promise<string | null> => {
|
|
59
61
|
const stackFrame =
|
|
60
62
|
getDefinitionFrameFromOwnedChild(fiber) ??
|
|
@@ -64,7 +66,12 @@ export const getDisplayNameFromSource = async (
|
|
|
64
66
|
return getDisplayName(fiber.type);
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
const bundleSourceMap = await getSourceMap(
|
|
69
|
+
const bundleSourceMap = await getSourceMap(
|
|
70
|
+
stackFrame.fileName,
|
|
71
|
+
shouldUseCache,
|
|
72
|
+
sourceFetch,
|
|
73
|
+
requestOptions,
|
|
74
|
+
);
|
|
68
75
|
|
|
69
76
|
if (!bundleSourceMap) {
|
|
70
77
|
return getDisplayName(fiber.type);
|
package/src/source/get-source.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
getSourceMap,
|
|
20
20
|
symbolicateStack,
|
|
21
21
|
type SourceFetch,
|
|
22
|
+
type SourceMapRequestOptions,
|
|
22
23
|
} from "./symbolication.js";
|
|
23
24
|
|
|
24
25
|
export const hasDebugSource = (
|
|
@@ -67,10 +68,17 @@ const getUsageFrameFromDebugStack = (fiber: Fiber): StackFrame | null => {
|
|
|
67
68
|
return null;
|
|
68
69
|
};
|
|
69
70
|
|
|
71
|
+
export const getRawSource = (fiber: Fiber): FiberSource | null => {
|
|
72
|
+
if (hasDebugSource(fiber)) return fiber._debugSource;
|
|
73
|
+
const stackFrame = getUsageFrameFromDebugStack(fiber) ?? getDefinitionFrameFromOwnedChild(fiber);
|
|
74
|
+
return stackFrame ? toFiberSource(stackFrame) : null;
|
|
75
|
+
};
|
|
76
|
+
|
|
70
77
|
const getSourceByComponentName = async (
|
|
71
78
|
fiber: Fiber,
|
|
72
|
-
|
|
73
|
-
|
|
79
|
+
shouldUseCache: boolean,
|
|
80
|
+
sourceFetch: SourceFetch | undefined,
|
|
81
|
+
requestOptions: SourceMapRequestOptions,
|
|
74
82
|
): Promise<FiberSource | null> => {
|
|
75
83
|
const functionName = getDisplayName(fiber.type);
|
|
76
84
|
if (!functionName) return null;
|
|
@@ -83,7 +91,7 @@ const getSourceByComponentName = async (
|
|
|
83
91
|
continue;
|
|
84
92
|
}
|
|
85
93
|
visitedFileNames.add(fileName);
|
|
86
|
-
const sourceMap = await getSourceMap(fileName,
|
|
94
|
+
const sourceMap = await getSourceMap(fileName, shouldUseCache, sourceFetch, requestOptions);
|
|
87
95
|
if (!sourceMap) continue;
|
|
88
96
|
const source = getSourceFromSourceMapByFunctionName(sourceMap, functionName);
|
|
89
97
|
if (source && !source.isIgnoreListed) return toFiberSource(source);
|
|
@@ -119,30 +127,42 @@ const getSourceByComponentName = async (
|
|
|
119
127
|
*/
|
|
120
128
|
export const getSource = async (
|
|
121
129
|
fiber: Fiber,
|
|
122
|
-
|
|
123
|
-
|
|
130
|
+
shouldUseCache = true,
|
|
131
|
+
sourceFetch?: SourceFetch,
|
|
132
|
+
requestOptions: SourceMapRequestOptions = {},
|
|
124
133
|
): Promise<FiberSource | null> => {
|
|
125
|
-
if (hasDebugSource(fiber))
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
if (hasDebugSource(fiber)) return fiber._debugSource;
|
|
135
|
+
const rawSource = getRawSource(fiber);
|
|
136
|
+
if (rawSource) {
|
|
137
|
+
const [symbolicatedFrame] = await symbolicateStack(
|
|
138
|
+
[
|
|
139
|
+
{
|
|
140
|
+
columnNumber: rawSource.columnNumber,
|
|
141
|
+
fileName: rawSource.fileName,
|
|
142
|
+
functionName: rawSource.functionName,
|
|
143
|
+
lineNumber: rawSource.lineNumber,
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
shouldUseCache,
|
|
147
|
+
sourceFetch,
|
|
148
|
+
requestOptions,
|
|
149
|
+
);
|
|
150
|
+
const symbolicatedSource = toFiberSource(symbolicatedFrame);
|
|
151
|
+
if (symbolicatedSource) return symbolicatedSource;
|
|
137
152
|
}
|
|
138
153
|
|
|
139
|
-
const parentStackFrames = await getParentStack(
|
|
154
|
+
const parentStackFrames = await getParentStack(
|
|
155
|
+
fiber,
|
|
156
|
+
shouldUseCache,
|
|
157
|
+
sourceFetch,
|
|
158
|
+
requestOptions,
|
|
159
|
+
);
|
|
140
160
|
for (const stackFrame of parentStackFrames) {
|
|
141
161
|
if (stackFrame.fileName) {
|
|
142
162
|
return toFiberSource(stackFrame);
|
|
143
163
|
}
|
|
144
164
|
}
|
|
145
|
-
return getSourceByComponentName(fiber,
|
|
165
|
+
return getSourceByComponentName(fiber, shouldUseCache, sourceFetch, requestOptions);
|
|
146
166
|
};
|
|
147
167
|
|
|
148
168
|
const getPathSegmentCount = (path: string): number => path.split("/").filter(Boolean).length;
|
package/src/source/index.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
export {
|
|
2
|
-
|
|
1
|
+
export {
|
|
2
|
+
formatOwnerStack,
|
|
3
|
+
getDefinitionFrameFromOwnedChild,
|
|
4
|
+
getFallbackParentStack,
|
|
5
|
+
getOwnerStack,
|
|
6
|
+
getParentStack,
|
|
7
|
+
getRawOwnerStack,
|
|
8
|
+
hasDebugStack,
|
|
9
|
+
} from "./owner-stack.js";
|
|
10
|
+
export { getRawSource, getSource, isSourceFile, normalizeFileName } from "./get-source.js";
|
|
3
11
|
export {
|
|
4
12
|
getSourceContentFromSourceMap,
|
|
5
13
|
getSourceFromSourceMap,
|
|
@@ -23,5 +31,11 @@ export {
|
|
|
23
31
|
BippySourceMapError,
|
|
24
32
|
BippyUnsupportedHookError,
|
|
25
33
|
} from "../errors.js";
|
|
26
|
-
export {
|
|
34
|
+
export {
|
|
35
|
+
getFiberHooks,
|
|
36
|
+
inspectHooks,
|
|
37
|
+
type HookSource,
|
|
38
|
+
type HooksNode,
|
|
39
|
+
type HooksTree,
|
|
40
|
+
} from "./inspect-hooks.js";
|
|
27
41
|
export { parseHookNames, type HookNames } from "./parse-hook-names.js";
|