bippy 0.6.1-dev.b88fcb4 → 0.6.1-dev.f9e6c65
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 +159 -487
- package/dist/core.cjs +1 -1
- package/dist/core.d.cts +8 -54
- package/dist/core.d.ts +8 -54
- package/dist/core.js +1 -1
- package/dist/core2.cjs +1 -1
- package/dist/core2.d.cts +3 -3
- package/dist/core2.d.ts +3 -3
- package/dist/core2.js +1 -1
- package/dist/errors.d.cts +2 -57
- package/dist/errors.d.ts +2 -57
- 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/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 -13
- package/dist/source.d.cts +77 -78
- package/dist/source.d.ts +77 -78
- package/dist/source.js +12 -13
- package/package.json +6 -3
- package/src/core.ts +101 -530
- package/src/errors.ts +0 -65
- package/src/index.ts +1 -0
- package/src/install-hook-only.ts +2 -2
- package/src/rdt-hook.ts +30 -43
- package/src/react-internals/generated/react-work-tags.ts +6 -2
- package/src/react-internals/index.ts +4 -4
- package/src/react-internals/semver.ts +2 -0
- package/src/react-internals/types.ts +0 -83
- package/src/react.ts +76 -0
- package/src/source/get-display-name-from-source.ts +44 -40
- package/src/source/get-source.ts +40 -24
- package/src/source/index.ts +2 -0
- package/src/source/inspect-hooks.ts +70 -91
- package/src/source/owner-stack.ts +60 -89
- 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 +312 -111
- package/dist/index.iife.js +0 -9
- package/dist/install-hook-only.iife.js +0 -9
package/src/errors.ts
CHANGED
|
@@ -5,55 +5,6 @@ export class BippyError extends Error {
|
|
|
5
5
|
}
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
export class BippyInstrumentationError extends BippyError {
|
|
9
|
-
readonly callbackName: string;
|
|
10
|
-
readonly instrumentationName: string;
|
|
11
|
-
|
|
12
|
-
constructor(instrumentationName: string, callbackName: string, cause: unknown) {
|
|
13
|
-
super(
|
|
14
|
-
`Bippy instrumentation "${instrumentationName}" callback "${callbackName}" failed`,
|
|
15
|
-
cause,
|
|
16
|
-
);
|
|
17
|
-
this.name = "BippyInstrumentationError";
|
|
18
|
-
this.callbackName = callbackName;
|
|
19
|
-
this.instrumentationName = instrumentationName;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export class BippyReactDevToolsError extends BippyError {
|
|
24
|
-
readonly callbackName: string;
|
|
25
|
-
|
|
26
|
-
constructor(callbackName: string, cause: unknown) {
|
|
27
|
-
super(`React DevTools callback "${callbackName}" failed`, cause);
|
|
28
|
-
this.name = "BippyReactDevToolsError";
|
|
29
|
-
this.callbackName = callbackName;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export class BippyHookListenerError extends BippyError {
|
|
34
|
-
readonly listenerName: string;
|
|
35
|
-
|
|
36
|
-
constructor(listenerName: string, cause: unknown) {
|
|
37
|
-
super(`Bippy hook listener "${listenerName}" failed`, cause);
|
|
38
|
-
this.name = "BippyHookListenerError";
|
|
39
|
-
this.listenerName = listenerName;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export class BippyHookInstallationError extends BippyError {
|
|
44
|
-
constructor(cause: unknown) {
|
|
45
|
-
super("Bippy could not install the React DevTools hook", cause);
|
|
46
|
-
this.name = "BippyHookInstallationError";
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export class BippyReactBuildError extends BippyError {
|
|
51
|
-
constructor(message: string, cause?: unknown) {
|
|
52
|
-
super(message, cause);
|
|
53
|
-
this.name = "BippyReactBuildError";
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
8
|
export class BippyHookInspectionError extends BippyError {
|
|
58
9
|
constructor(message: string, cause?: unknown) {
|
|
59
10
|
super(message, cause);
|
|
@@ -81,19 +32,3 @@ export class BippySourceMapError extends BippyError {
|
|
|
81
32
|
this.name = "BippySourceMapError";
|
|
82
33
|
}
|
|
83
34
|
}
|
|
84
|
-
|
|
85
|
-
interface BippyErrorFactory {
|
|
86
|
-
(cause: unknown): BippyError;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export const runWithBippyError = <Result>(
|
|
90
|
-
callback: () => Result,
|
|
91
|
-
createError: BippyErrorFactory,
|
|
92
|
-
): Result => {
|
|
93
|
-
try {
|
|
94
|
-
return callback();
|
|
95
|
-
} catch (cause) {
|
|
96
|
-
if (cause instanceof BippyError) throw cause;
|
|
97
|
-
throw createError(cause);
|
|
98
|
-
}
|
|
99
|
-
};
|
package/src/index.ts
CHANGED
package/src/install-hook-only.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getRDTHook } from "./rdt-hook.js";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
getRDTHook();
|
package/src/rdt-hook.ts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
// This module must load before React so renderers can inject into the hook.
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
BippyHookInstallationError,
|
|
5
|
-
BippyHookListenerError,
|
|
6
|
-
BippyReactBuildError,
|
|
7
|
-
runWithBippyError,
|
|
8
|
-
} from "./errors.js";
|
|
9
3
|
import type { ReactDevToolsGlobalHook, ReactRenderer } from "./react-internals/index.js";
|
|
10
4
|
|
|
11
5
|
export interface Unsubscribe extends Disposable {
|
|
@@ -34,21 +28,16 @@ const noOp = (): void => {};
|
|
|
34
28
|
export const createUnsubscribe = (unsubscribe: () => void): Unsubscribe =>
|
|
35
29
|
Object.assign(unsubscribe, { [Symbol.dispose]: unsubscribe });
|
|
36
30
|
|
|
37
|
-
const runHookListener = (listenerName: string, callback: () => unknown): void => {
|
|
38
|
-
runWithBippyError(callback, (cause) => new BippyHookListenerError(listenerName, cause));
|
|
39
|
-
};
|
|
40
|
-
|
|
41
31
|
const checkDCE = (functionToCheck: unknown): void => {
|
|
42
32
|
try {
|
|
43
33
|
const code = Function.prototype.toString.call(functionToCheck);
|
|
44
|
-
if (code.
|
|
45
|
-
// HACK: React DevTools reports failed dead-code elimination asynchronously.
|
|
34
|
+
if (code.indexOf("^_^") > -1) {
|
|
46
35
|
setTimeout(() => {
|
|
47
|
-
throw new
|
|
36
|
+
throw new Error(
|
|
48
37
|
"React is running in production mode, but dead code " +
|
|
49
38
|
"elimination has not been applied. Read how to correctly " +
|
|
50
39
|
"configure React for production: " +
|
|
51
|
-
"https://
|
|
40
|
+
"https://react.dev/link/perf-use-production-build",
|
|
52
41
|
);
|
|
53
42
|
});
|
|
54
43
|
}
|
|
@@ -76,19 +65,19 @@ const notifyingInjectByHook = new WeakMap<
|
|
|
76
65
|
|
|
77
66
|
const notifyActiveListeners = (): void => {
|
|
78
67
|
for (const listener of _onActiveListeners) {
|
|
79
|
-
|
|
68
|
+
listener();
|
|
80
69
|
}
|
|
81
70
|
};
|
|
82
71
|
|
|
83
72
|
const notifyRendererInjectListeners = (renderer: ReactRenderer): void => {
|
|
84
73
|
for (const listener of rendererInjectListeners) {
|
|
85
|
-
|
|
74
|
+
listener(renderer);
|
|
86
75
|
}
|
|
87
76
|
};
|
|
88
77
|
|
|
89
78
|
const notifyRDTHookReplaceListeners = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
90
79
|
for (const listener of rdtHookReplaceListeners) {
|
|
91
|
-
|
|
80
|
+
listener(rdtHook);
|
|
92
81
|
}
|
|
93
82
|
};
|
|
94
83
|
|
|
@@ -129,6 +118,20 @@ const getRendererMap = (rdtHook: ReactDevToolsGlobalHook): Map<number, ReactRend
|
|
|
129
118
|
return rdtHook.renderers;
|
|
130
119
|
};
|
|
131
120
|
|
|
121
|
+
const trackInjectedRenderer = (
|
|
122
|
+
rdtHook: ReactDevToolsGlobalHook,
|
|
123
|
+
renderers: Map<number, ReactRenderer>,
|
|
124
|
+
rendererId: number,
|
|
125
|
+
renderer: ReactRenderer,
|
|
126
|
+
): void => {
|
|
127
|
+
renderers.set(rendererId, renderer);
|
|
128
|
+
_renderers.add(renderer);
|
|
129
|
+
if (!rdtHook._instrumentationIsActive) {
|
|
130
|
+
rdtHook._instrumentationIsActive = true;
|
|
131
|
+
notifyActiveListeners();
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
132
135
|
export const installRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHook => {
|
|
133
136
|
if (onActive) {
|
|
134
137
|
_onActiveListeners.add(onActive);
|
|
@@ -142,12 +145,7 @@ export const installRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHo
|
|
|
142
145
|
hasUnsupportedRendererAttached: false,
|
|
143
146
|
inject: (renderer) => {
|
|
144
147
|
const nextRendererId = ++rendererIdCounter;
|
|
145
|
-
renderers
|
|
146
|
-
_renderers.add(renderer);
|
|
147
|
-
if (!rdtHook._instrumentationIsActive) {
|
|
148
|
-
rdtHook._instrumentationIsActive = true;
|
|
149
|
-
notifyActiveListeners();
|
|
150
|
-
}
|
|
148
|
+
trackInjectedRenderer(rdtHook, renderers, nextRendererId, renderer);
|
|
151
149
|
return nextRendererId;
|
|
152
150
|
},
|
|
153
151
|
on: noOp,
|
|
@@ -170,12 +168,10 @@ export const installRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHo
|
|
|
170
168
|
const ourRenderers = rdtHook.renderers;
|
|
171
169
|
rdtHook = newHook;
|
|
172
170
|
const nextRenderers = getRendererMap(rdtHook);
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
});
|
|
178
|
-
}
|
|
171
|
+
ourRenderers.forEach((renderer, rendererId) => {
|
|
172
|
+
_renderers.add(renderer);
|
|
173
|
+
nextRenderers.set(rendererId, renderer);
|
|
174
|
+
});
|
|
179
175
|
if (ourRenderers.size > 0 || rdtHookReplaceListeners.size > 0) {
|
|
180
176
|
patchRDTHook(onActive);
|
|
181
177
|
}
|
|
@@ -200,14 +196,14 @@ export const installRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHo
|
|
|
200
196
|
};
|
|
201
197
|
objectDefineProperty(window, "hasOwnProperty", {
|
|
202
198
|
configurable: true,
|
|
203
|
-
value: (
|
|
204
|
-
if (!didRunHasOwnPropertyHack &&
|
|
199
|
+
value: (propertyKey: PropertyKey) => {
|
|
200
|
+
if (!didRunHasOwnPropertyHack && propertyKey === "__REACT_DEVTOOLS_GLOBAL_HOOK__") {
|
|
205
201
|
didRunHasOwnPropertyHack = true;
|
|
206
202
|
restoreWindowHasOwnProperty();
|
|
207
203
|
globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__ = undefined;
|
|
208
204
|
return false;
|
|
209
205
|
}
|
|
210
|
-
return originalWindowHasOwnProperty(
|
|
206
|
+
return originalWindowHasOwnProperty(propertyKey);
|
|
211
207
|
},
|
|
212
208
|
writable: true,
|
|
213
209
|
});
|
|
@@ -247,17 +243,12 @@ export const patchRDTHook = (onActive?: ActiveListener): void => {
|
|
|
247
243
|
const previousInject = rdtHook.inject;
|
|
248
244
|
rdtHook.inject = (renderer) => {
|
|
249
245
|
const rendererId = previousInject.call(rdtHook, renderer);
|
|
250
|
-
|
|
251
|
-
renderers.set(rendererId, renderer);
|
|
252
|
-
if (!rdtHook._instrumentationIsActive) {
|
|
253
|
-
rdtHook._instrumentationIsActive = true;
|
|
254
|
-
notifyActiveListeners();
|
|
255
|
-
}
|
|
246
|
+
trackInjectedRenderer(rdtHook, renderers, rendererId, renderer);
|
|
256
247
|
return rendererId;
|
|
257
248
|
};
|
|
258
249
|
}
|
|
259
250
|
if (!didNotifyActiveListeners && (renderers.size || rdtHook._instrumentationIsActive)) {
|
|
260
|
-
|
|
251
|
+
onActive?.();
|
|
261
252
|
}
|
|
262
253
|
};
|
|
263
254
|
|
|
@@ -276,7 +267,3 @@ export const getRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHook =
|
|
|
276
267
|
patchRDTHook(onActive);
|
|
277
268
|
return globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__ ?? installRDTHook(onActive);
|
|
278
269
|
};
|
|
279
|
-
|
|
280
|
-
export const safelyInstallRDTHook = (): void => {
|
|
281
|
-
runWithBippyError(getRDTHook, (cause) => new BippyHookInstallationError(cause));
|
|
282
|
-
};
|
|
@@ -254,6 +254,7 @@ const reactWorkTagsByVersion = defineReactWorkTags({
|
|
|
254
254
|
export type ReactWorkTagVersion = keyof typeof reactWorkTagsByVersion;
|
|
255
255
|
|
|
256
256
|
export interface GetReactWorkTags {
|
|
257
|
+
(): Readonly<ReactWorkTagMap>;
|
|
257
258
|
<Version extends ReactWorkTagVersion>(
|
|
258
259
|
reactVersion: Version,
|
|
259
260
|
): Readonly<(typeof reactWorkTagsByVersion)[Version]>;
|
|
@@ -302,10 +303,13 @@ const reactWorkTagRanges: ReactWorkTagRange[] = [
|
|
|
302
303
|
},
|
|
303
304
|
];
|
|
304
305
|
|
|
305
|
-
|
|
306
|
+
const defaultReactWorkTags = reactWorkTagsByVersion["17.0.2"];
|
|
307
|
+
|
|
308
|
+
export const getReactWorkTags: GetReactWorkTags = (reactVersion?: string) => {
|
|
309
|
+
if (reactVersion === undefined) return defaultReactWorkTags;
|
|
306
310
|
for (const range of reactWorkTagRanges) {
|
|
307
311
|
const comparison = compareSemver(reactVersion, range.minimumVersion);
|
|
308
|
-
if (comparison === null) return
|
|
312
|
+
if (comparison === null) return defaultReactWorkTags;
|
|
309
313
|
if (comparison === 1 || (comparison === 0 && !range.isMinimumExcluded)) {
|
|
310
314
|
return range.workTags;
|
|
311
315
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getReactWorkTags, ReactFiberFlags } from "./generated/react-work-tags.js";
|
|
2
2
|
import type { ReactWorkTagMap } from "./generated/react-work-tags.js";
|
|
3
|
-
import {
|
|
3
|
+
import { isSemver } from "./semver.js";
|
|
4
4
|
import type { Fiber, ReactRenderer } from "./types.js";
|
|
5
5
|
|
|
6
6
|
export { compareSemver } from "./semver.js";
|
|
@@ -18,17 +18,17 @@ export type {
|
|
|
18
18
|
} from "./generated/react-work-tags.js";
|
|
19
19
|
export * from "./types.js";
|
|
20
20
|
|
|
21
|
-
const defaultReactWorkTags = getReactWorkTags(
|
|
21
|
+
const defaultReactWorkTags = getReactWorkTags();
|
|
22
22
|
const fiberReactWorkTags = new WeakMap<Fiber, Readonly<ReactWorkTagMap>>();
|
|
23
23
|
|
|
24
24
|
export const getReactWorkTagsForRenderer = (
|
|
25
25
|
renderer?: ReactRenderer | null,
|
|
26
26
|
): Readonly<ReactWorkTagMap> => {
|
|
27
27
|
const reconcilerVersion = renderer?.reconcilerVersion;
|
|
28
|
-
if (reconcilerVersion &&
|
|
28
|
+
if (reconcilerVersion && isSemver(reconcilerVersion)) {
|
|
29
29
|
return getReactWorkTags(reconcilerVersion);
|
|
30
30
|
}
|
|
31
|
-
return getReactWorkTags(renderer
|
|
31
|
+
return renderer?.version ? getReactWorkTags(renderer.version) : defaultReactWorkTags;
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
export const setReactWorkTagsForFiber = (fiber: Fiber, renderer?: ReactRenderer): void => {
|
|
@@ -9,6 +9,8 @@ const SEMVER_PATTERN =
|
|
|
9
9
|
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
10
10
|
const NUMERIC_IDENTIFIER_PATTERN = /^\d+$/;
|
|
11
11
|
|
|
12
|
+
export const isSemver = (version: string): boolean => parseSemver(version) !== null;
|
|
13
|
+
|
|
12
14
|
const parseSemver = (version: string): SemanticVersion | null => {
|
|
13
15
|
const match = SEMVER_PATTERN.exec(version);
|
|
14
16
|
if (!match) return null;
|
|
@@ -2,84 +2,12 @@ import type { ReactNode } from "react";
|
|
|
2
2
|
import type ReactReconciler from "react-reconciler";
|
|
3
3
|
import type { HostWorkTag, ReactWorkTag } from "./generated/react-work-tags.js";
|
|
4
4
|
|
|
5
|
-
export type BundleType = ReactReconciler.BundleType;
|
|
6
|
-
export type Flags = ReactReconciler.Flags;
|
|
7
|
-
export type HookType = ReactReconciler.HookType;
|
|
8
|
-
export type LanePriority = ReactReconciler.LanePriority;
|
|
9
|
-
export type Lanes = ReactReconciler.Lanes;
|
|
10
|
-
export type MutableSource = ReactReconciler.MutableSource;
|
|
11
|
-
export type OpaqueHandle = ReactReconciler.OpaqueHandle;
|
|
12
|
-
export type OpaqueRoot = ReactReconciler.OpaqueRoot;
|
|
13
|
-
export type React$AbstractComponent<
|
|
14
|
-
Config,
|
|
15
|
-
Instance = unknown,
|
|
16
|
-
> = ReactReconciler.React$AbstractComponent<Config, Instance>;
|
|
17
|
-
export type RootTag = ReactReconciler.RootTag;
|
|
18
|
-
export type TypeOfMode = ReactReconciler.TypeOfMode;
|
|
19
5
|
export type WorkTag = ReactReconciler.WorkTag | ReactWorkTag;
|
|
20
6
|
|
|
21
|
-
export interface HostConfig<
|
|
22
|
-
Type = unknown,
|
|
23
|
-
HostProps = unknown,
|
|
24
|
-
Container = unknown,
|
|
25
|
-
Instance = unknown,
|
|
26
|
-
TextInstance = unknown,
|
|
27
|
-
SuspenseInstance = unknown,
|
|
28
|
-
HydratableInstance = unknown,
|
|
29
|
-
FormInstance = unknown,
|
|
30
|
-
PublicInstance = unknown,
|
|
31
|
-
HostContext = unknown,
|
|
32
|
-
ChildSet = unknown,
|
|
33
|
-
TimeoutHandle = unknown,
|
|
34
|
-
NoTimeout = unknown,
|
|
35
|
-
TransitionStatus = unknown,
|
|
36
|
-
> extends ReactReconciler.HostConfig<
|
|
37
|
-
Type,
|
|
38
|
-
HostProps,
|
|
39
|
-
Container,
|
|
40
|
-
Instance,
|
|
41
|
-
TextInstance,
|
|
42
|
-
SuspenseInstance,
|
|
43
|
-
HydratableInstance,
|
|
44
|
-
FormInstance,
|
|
45
|
-
PublicInstance,
|
|
46
|
-
HostContext,
|
|
47
|
-
ChildSet,
|
|
48
|
-
TimeoutHandle,
|
|
49
|
-
NoTimeout,
|
|
50
|
-
TransitionStatus
|
|
51
|
-
> {}
|
|
52
|
-
|
|
53
7
|
export interface Source extends ReactReconciler.Source {}
|
|
54
|
-
export interface RefObject extends ReactReconciler.RefObject {}
|
|
55
|
-
export interface Thenable<T> extends ReactReconciler.Thenable<T> {}
|
|
56
8
|
|
|
57
9
|
export interface ReactContext<T> extends ReactReconciler.ReactContext<T> {}
|
|
58
10
|
|
|
59
|
-
export interface ReactProviderType<T> extends ReactReconciler.ReactProviderType<T> {}
|
|
60
|
-
export interface ReactProvider<T> extends ReactReconciler.ReactProvider<T> {}
|
|
61
|
-
export interface ReactConsumer<T> extends ReactReconciler.ReactConsumer<T> {}
|
|
62
|
-
export interface ReactPortal extends ReactReconciler.ReactPortal {}
|
|
63
|
-
|
|
64
|
-
export interface ComponentSelector extends ReactReconciler.ComponentSelector {}
|
|
65
|
-
export interface HasPseudoClassSelector extends ReactReconciler.HasPseudoClassSelector {}
|
|
66
|
-
export interface RoleSelector extends ReactReconciler.RoleSelector {}
|
|
67
|
-
export interface TextSelector extends ReactReconciler.TextSelector {}
|
|
68
|
-
export interface TestNameSelector extends ReactReconciler.TestNameSelector {}
|
|
69
|
-
export type Selector = ReactReconciler.Selector;
|
|
70
|
-
|
|
71
|
-
export interface DevToolsConfig<
|
|
72
|
-
Instance = unknown,
|
|
73
|
-
TextInstance = unknown,
|
|
74
|
-
RendererInspectionConfig = unknown,
|
|
75
|
-
> extends ReactReconciler.DevToolsConfig<Instance, TextInstance, RendererInspectionConfig> {}
|
|
76
|
-
|
|
77
|
-
export interface SuspenseHydrationCallbacks<
|
|
78
|
-
SuspenseInstance = unknown,
|
|
79
|
-
> extends ReactReconciler.SuspenseHydrationCallbacks<SuspenseInstance> {}
|
|
80
|
-
|
|
81
|
-
export interface TransitionTracingCallbacks extends ReactReconciler.TransitionTracingCallbacks {}
|
|
82
|
-
|
|
83
11
|
// HACK: React 17 exposes observedBits while React 18+ exposes memoizedValue on context dependencies.
|
|
84
12
|
export interface ContextDependency<T> extends Omit<
|
|
85
13
|
ReactReconciler.ContextDependency<T>,
|
|
@@ -98,15 +26,6 @@ export interface Dependencies extends Omit<ReactReconciler.Dependencies, "firstC
|
|
|
98
26
|
firstContext: ContextDependency<unknown> | null;
|
|
99
27
|
}
|
|
100
28
|
|
|
101
|
-
export interface Effect {
|
|
102
|
-
[key: string]: unknown;
|
|
103
|
-
create: (...args: unknown[]) => unknown;
|
|
104
|
-
deps: null | unknown[];
|
|
105
|
-
destroy: ((...args: unknown[]) => unknown) | null;
|
|
106
|
-
next: Effect | null;
|
|
107
|
-
tag: number;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
29
|
export interface ServerComponentInfo {
|
|
111
30
|
name?: string;
|
|
112
31
|
env?: string;
|
|
@@ -131,8 +50,6 @@ export interface FiberDebugSource extends Source {
|
|
|
131
50
|
|
|
132
51
|
export interface FiberUpdateQueue {
|
|
133
52
|
[key: string]: unknown;
|
|
134
|
-
dispatch?: unknown;
|
|
135
|
-
lastEffect: Effect | null;
|
|
136
53
|
memoCache?: ReactMemoCache;
|
|
137
54
|
}
|
|
138
55
|
|
package/src/react.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import "./install-hook-only.js";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { isValidFiber } from "./core.js";
|
|
4
|
+
import type { Fiber } from "./react-internals/index.js";
|
|
5
|
+
|
|
6
|
+
export type { Fiber } from "./react-internals/index.js";
|
|
7
|
+
|
|
8
|
+
const preserveState = (state: undefined): undefined => state;
|
|
9
|
+
const readEmptySnapshot = (): undefined => undefined;
|
|
10
|
+
const unsubscribeFromEmptyStore = (): void => {};
|
|
11
|
+
const subscribeToEmptyStore = (): (() => void) => unsubscribeFromEmptyStore;
|
|
12
|
+
const useSyncExternalStore: unknown = Reflect.get(React, "useSyncExternalStore");
|
|
13
|
+
|
|
14
|
+
const captureFiberFromHook = (useCaptureHook: () => void): Fiber | null => {
|
|
15
|
+
const originalBind = Function.prototype.bind;
|
|
16
|
+
let capturedFiber: Fiber | null = null;
|
|
17
|
+
// HACK: React binds hook callbacks to the rendering Fiber in production but exposes no public API for it.
|
|
18
|
+
const bindProxy = new Proxy(originalBind, {
|
|
19
|
+
apply: (bind, functionToBind, boundArguments) => {
|
|
20
|
+
const fiber = boundArguments[1];
|
|
21
|
+
if (!capturedFiber && isValidFiber(fiber)) {
|
|
22
|
+
capturedFiber = fiber;
|
|
23
|
+
}
|
|
24
|
+
return Reflect.apply(bind, functionToBind, boundArguments);
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
Reflect.set(Function.prototype, "bind", bindProxy);
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
useCaptureHook();
|
|
31
|
+
} finally {
|
|
32
|
+
if (Function.prototype.bind === bindProxy) {
|
|
33
|
+
Reflect.set(Function.prototype, "bind", originalBind);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return capturedFiber;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const useExternalStoreCapture = (): void => {
|
|
41
|
+
Reflect.apply(useSyncExternalStore, React, [
|
|
42
|
+
subscribeToEmptyStore,
|
|
43
|
+
readEmptySnapshot,
|
|
44
|
+
readEmptySnapshot,
|
|
45
|
+
]);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const useReducerCapture = (): void => {
|
|
49
|
+
React.useReducer(preserveState, undefined);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const useFiberWithExternalStore = (): Fiber | undefined =>
|
|
53
|
+
captureFiberFromHook(useExternalStoreCapture) ?? undefined;
|
|
54
|
+
|
|
55
|
+
const useFiberWithReducer = (): Fiber | undefined => {
|
|
56
|
+
const committedFiberRef = React.useRef<Fiber | null>(null);
|
|
57
|
+
const renderedFiberRef = React.useRef<Fiber | null>(null);
|
|
58
|
+
const hookFiber = captureFiberFromHook(useReducerCapture);
|
|
59
|
+
const fiber =
|
|
60
|
+
hookFiber ??
|
|
61
|
+
(renderedFiberRef.current !== committedFiberRef.current
|
|
62
|
+
? renderedFiberRef.current
|
|
63
|
+
: committedFiberRef.current?.alternate) ??
|
|
64
|
+
committedFiberRef.current;
|
|
65
|
+
|
|
66
|
+
renderedFiberRef.current = fiber;
|
|
67
|
+
|
|
68
|
+
React.useEffect(() => {
|
|
69
|
+
committedFiberRef.current = fiber;
|
|
70
|
+
}, [fiber]);
|
|
71
|
+
|
|
72
|
+
return fiber ?? undefined;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const useFiber =
|
|
76
|
+
typeof useSyncExternalStore === "function" ? useFiberWithExternalStore : useFiberWithReducer;
|
|
@@ -1,8 +1,29 @@
|
|
|
1
|
-
import { Fiber } from "../react-internals/index.js";
|
|
1
|
+
import type { Fiber } from "../react-internals/index.js";
|
|
2
2
|
import { getDisplayName } from "../core.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
3
|
+
import { getDefinitionFrameFromOwnedChild, getRawParentStack } from "./owner-stack.js";
|
|
4
|
+
import {
|
|
5
|
+
getSourceContentFromSourceMap,
|
|
6
|
+
getSourceFromSourceMap,
|
|
7
|
+
getSourceMap,
|
|
8
|
+
type SourceFetch,
|
|
9
|
+
} from "./symbolication.js";
|
|
10
|
+
import type { StackFrame } from "./parse-stack.js";
|
|
11
|
+
|
|
12
|
+
const COMPONENT_DECLARATION_PATTERNS = [
|
|
13
|
+
/(?:^|export\s+)(?:const|let|var)\s+(\w+)\s*=/,
|
|
14
|
+
/(?:^|export\s+)function\s+(\w+)/,
|
|
15
|
+
/(?:^|export\s+)class\s+(\w+)/,
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
const findComponentDeclarationOnLine = (line: string): string | null => {
|
|
19
|
+
for (const pattern of COMPONENT_DECLARATION_PATTERNS) {
|
|
20
|
+
const match = line.match(pattern);
|
|
21
|
+
if (match?.[1]) return match[1];
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const MAX_DECLARATION_LINE_DISTANCE = 5;
|
|
6
27
|
|
|
7
28
|
const extractComponentNameFromSource = (
|
|
8
29
|
sourceContent: string,
|
|
@@ -15,27 +36,16 @@ const extractComponentNameFromSource = (
|
|
|
15
36
|
return null;
|
|
16
37
|
}
|
|
17
38
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return arrowMatch[1];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const functionMatch = contextLines.match(functionPattern);
|
|
32
|
-
if (functionMatch?.[1]) {
|
|
33
|
-
return functionMatch[1];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const classMatch = contextLines.match(classPattern);
|
|
37
|
-
if (classMatch?.[1]) {
|
|
38
|
-
return classMatch[1];
|
|
39
|
+
for (let lineDistance = 0; lineDistance <= MAX_DECLARATION_LINE_DISTANCE; lineDistance++) {
|
|
40
|
+
const lineIndexes =
|
|
41
|
+
lineDistance === 0
|
|
42
|
+
? [targetLineIndex]
|
|
43
|
+
: [targetLineIndex - lineDistance, targetLineIndex + lineDistance];
|
|
44
|
+
for (const lineIndex of lineIndexes) {
|
|
45
|
+
if (lineIndex < 0 || lineIndex >= lines.length) continue;
|
|
46
|
+
const declarationName = findComponentDeclarationOnLine(lines[lineIndex]);
|
|
47
|
+
if (declarationName) return declarationName;
|
|
48
|
+
}
|
|
39
49
|
}
|
|
40
50
|
|
|
41
51
|
return null;
|
|
@@ -44,10 +54,11 @@ const extractComponentNameFromSource = (
|
|
|
44
54
|
export const getDisplayNameFromSource = async (
|
|
45
55
|
fiber: Fiber,
|
|
46
56
|
cache = true,
|
|
47
|
-
fetchFn?:
|
|
57
|
+
fetchFn?: SourceFetch,
|
|
48
58
|
): Promise<string | null> => {
|
|
49
|
-
const
|
|
50
|
-
|
|
59
|
+
const stackFrame =
|
|
60
|
+
getDefinitionFrameFromOwnedChild(fiber) ??
|
|
61
|
+
getRawParentStack(fiber).find((innerFrame) => innerFrame.fileName);
|
|
51
62
|
|
|
52
63
|
if (!stackFrame?.fileName) {
|
|
53
64
|
return getDisplayName(fiber.type);
|
|
@@ -73,21 +84,14 @@ export const getDisplayNameFromSource = async (
|
|
|
73
84
|
return getDisplayName(fiber.type);
|
|
74
85
|
}
|
|
75
86
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const sourceIndex = bundleSourceMap.sources.indexOf(source.fileName);
|
|
81
|
-
if (sourceIndex === -1 || !bundleSourceMap.sourcesContent[sourceIndex]) {
|
|
82
|
-
return getDisplayName(fiber.type);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const sourceContent = bundleSourceMap.sourcesContent[sourceIndex];
|
|
86
|
-
const extractedName = extractComponentNameFromSource(sourceContent, source.lineNumber);
|
|
87
|
+
const sourceContent = getSourceContentFromSourceMap(bundleSourceMap, source.fileName);
|
|
88
|
+
const extractedName = sourceContent
|
|
89
|
+
? extractComponentNameFromSource(sourceContent, source.lineNumber)
|
|
90
|
+
: null;
|
|
87
91
|
|
|
88
92
|
if (extractedName) {
|
|
89
93
|
return extractedName;
|
|
90
94
|
}
|
|
91
95
|
|
|
92
|
-
return getDisplayName(fiber.type);
|
|
96
|
+
return source.functionName ?? getDisplayName(fiber.type);
|
|
93
97
|
};
|