bippy 0.6.1-dev.3a24abf → 0.6.1-dev.93556ef
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 -1
- package/README.md +26 -1
- package/dist/core.cjs +1 -1
- package/dist/core.d.cts +39 -42
- package/dist/core.d.ts +39 -42
- package/dist/core.js +1 -1
- package/dist/core2.cjs +1 -1
- package/dist/core2.d.cts +11 -3
- package/dist/core2.d.ts +11 -3
- package/dist/core2.js +1 -1
- package/dist/{types.d.cts → errors.d.cts} +187 -68
- package/dist/{types.d.ts → errors.d.ts} +187 -68
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +11 -3
- package/dist/index.d.ts +11 -3
- package/dist/index.iife.js +1 -1
- package/dist/index.js +1 -1
- package/dist/install-hook-only.cjs +1 -1
- package/dist/install-hook-only.d.cts +8 -0
- package/dist/install-hook-only.d.ts +8 -0
- package/dist/install-hook-only.iife.js +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 +13 -14
- package/dist/source.d.cts +86 -69
- package/dist/source.d.ts +86 -69
- package/dist/source.js +13 -14
- package/package.json +10 -6
- package/src/core.ts +246 -293
- package/src/errors.ts +34 -0
- package/src/install-hook-only.ts +2 -2
- package/src/rdt-hook.ts +152 -110
- package/src/react-internals/generated/react-work-tags.ts +318 -0
- package/src/react-internals/index.ts +67 -0
- package/src/react-internals/semver.ts +80 -0
- package/src/{types.ts → react-internals/types.ts} +13 -86
- package/src/source/error-stack.ts +11 -0
- package/src/source/get-display-name-from-source.ts +44 -40
- package/src/source/get-source.ts +42 -26
- package/src/source/index.ts +11 -1
- package/src/source/inspect-hooks.ts +220 -207
- package/src/source/owner-stack.ts +86 -128
- package/src/source/parse-debug-stack.ts +4 -4
- 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 +703 -135
- package/src/generated/react-work-tags.ts +0 -169
- package/src/react-internals.ts +0 -67
- package/src/unsubscribe.ts +0 -17
package/src/errors.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export class BippyError extends Error {
|
|
2
|
+
constructor(message: string, cause?: unknown) {
|
|
3
|
+
super(message, { cause });
|
|
4
|
+
this.name = "BippyError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class BippyHookInspectionError extends BippyError {
|
|
9
|
+
constructor(message: string, cause?: unknown) {
|
|
10
|
+
super(message, cause);
|
|
11
|
+
this.name = "BippyHookInspectionError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class BippyUnsupportedHookError extends BippyHookInspectionError {
|
|
16
|
+
constructor(message: string, cause?: unknown) {
|
|
17
|
+
super(message, cause);
|
|
18
|
+
this.name = "BippyUnsupportedHookError";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class BippyHookRenderError extends BippyHookInspectionError {
|
|
23
|
+
constructor(message: string, cause?: unknown) {
|
|
24
|
+
super(message, cause);
|
|
25
|
+
this.name = "BippyHookRenderError";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class BippySourceMapError extends BippyError {
|
|
30
|
+
constructor(message: string, cause?: unknown) {
|
|
31
|
+
super(message, cause);
|
|
32
|
+
this.name = "BippySourceMapError";
|
|
33
|
+
}
|
|
34
|
+
}
|
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,27 +1,43 @@
|
|
|
1
1
|
// This module must load before React so renderers can inject into the hook.
|
|
2
2
|
|
|
3
|
-
import type { ReactDevToolsGlobalHook, ReactRenderer } from "./
|
|
4
|
-
|
|
3
|
+
import type { ReactDevToolsGlobalHook, ReactRenderer } from "./react-internals/index.js";
|
|
4
|
+
|
|
5
|
+
export interface Unsubscribe extends Disposable {
|
|
6
|
+
(): void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface ActiveListener {
|
|
10
|
+
(): unknown;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface RendererInjectListener {
|
|
14
|
+
(renderer: ReactRenderer): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface RDTHookReplaceListener {
|
|
18
|
+
(rdtHook: ReactDevToolsGlobalHook): void;
|
|
19
|
+
}
|
|
5
20
|
|
|
6
21
|
export const version = process.env.VERSION;
|
|
7
22
|
export const BIPPY_INSTRUMENTATION_STRING = `bippy-${version}`;
|
|
8
23
|
|
|
9
24
|
const objectDefineProperty = Object.defineProperty;
|
|
10
|
-
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
11
|
-
const objectHasOwnProperty = Object.prototype.hasOwnProperty;
|
|
12
25
|
|
|
13
|
-
const
|
|
26
|
+
const noOp = (): void => {};
|
|
27
|
+
|
|
28
|
+
export const createUnsubscribe = (unsubscribe: () => void): Unsubscribe =>
|
|
29
|
+
Object.assign(unsubscribe, { [Symbol.dispose]: unsubscribe });
|
|
14
30
|
|
|
15
31
|
const checkDCE = (functionToCheck: unknown): void => {
|
|
16
32
|
try {
|
|
17
33
|
const code = Function.prototype.toString.call(functionToCheck);
|
|
18
|
-
if (code.
|
|
34
|
+
if (code.indexOf("^_^") > -1) {
|
|
19
35
|
setTimeout(() => {
|
|
20
36
|
throw new Error(
|
|
21
37
|
"React is running in production mode, but dead code " +
|
|
22
38
|
"elimination has not been applied. Read how to correctly " +
|
|
23
39
|
"configure React for production: " +
|
|
24
|
-
"https://
|
|
40
|
+
"https://react.dev/link/perf-use-production-build",
|
|
25
41
|
);
|
|
26
42
|
});
|
|
27
43
|
}
|
|
@@ -34,49 +50,89 @@ export const isRealReactDevtools = (
|
|
|
34
50
|
return Boolean(rdtHook && "getFiberRoots" in rdtHook);
|
|
35
51
|
};
|
|
36
52
|
|
|
37
|
-
export const _onActiveListeners = new Set<
|
|
53
|
+
export const _onActiveListeners = new Set<ActiveListener>();
|
|
38
54
|
|
|
39
55
|
export const _renderers = new Set<ReactRenderer>();
|
|
40
56
|
|
|
41
|
-
const rendererInjectListeners = new Set<
|
|
57
|
+
const rendererInjectListeners = new Set<RendererInjectListener>();
|
|
58
|
+
const rdtHookReplaceListeners = new Set<RDTHookReplaceListener>();
|
|
42
59
|
// HACK: Hook replacement can leave an old inject wrapper in the call chain, so notifications must be deduplicated.
|
|
43
60
|
const notifiedRenderers = new WeakSet<ReactRenderer>();
|
|
44
|
-
|
|
61
|
+
const notifyingInjectByHook = new WeakMap<
|
|
62
|
+
ReactDevToolsGlobalHook,
|
|
63
|
+
ReactDevToolsGlobalHook["inject"]
|
|
64
|
+
>();
|
|
65
|
+
|
|
66
|
+
const notifyActiveListeners = (): void => {
|
|
67
|
+
for (const listener of _onActiveListeners) {
|
|
68
|
+
listener();
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const notifyRendererInjectListeners = (renderer: ReactRenderer): void => {
|
|
73
|
+
for (const listener of rendererInjectListeners) {
|
|
74
|
+
listener(renderer);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
45
77
|
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
78
|
+
const notifyRDTHookReplaceListeners = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
79
|
+
for (const listener of rdtHookReplaceListeners) {
|
|
80
|
+
listener(rdtHook);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const setRendererInjectDispatcher = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
85
|
+
if (rdtHook.inject === notifyingInjectByHook.get(rdtHook)) return;
|
|
86
|
+
const previousInject = rdtHook.inject;
|
|
49
87
|
const nextInject = (renderer: ReactRenderer) => {
|
|
50
|
-
const rendererId =
|
|
88
|
+
const rendererId = previousInject.call(rdtHook, renderer);
|
|
51
89
|
if (!notifiedRenderers.has(renderer)) {
|
|
52
90
|
notifiedRenderers.add(renderer);
|
|
53
|
-
|
|
54
|
-
listener(renderer);
|
|
55
|
-
}
|
|
91
|
+
notifyRendererInjectListeners(renderer);
|
|
56
92
|
}
|
|
57
93
|
return rendererId;
|
|
58
94
|
};
|
|
59
95
|
rdtHook.inject = nextInject;
|
|
60
|
-
|
|
96
|
+
notifyingInjectByHook.set(rdtHook, nextInject);
|
|
61
97
|
};
|
|
62
98
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
ensureInjectNotifiesListeners(getRDTHook());
|
|
99
|
+
export const onRendererInject = (listener: RendererInjectListener): Unsubscribe => {
|
|
100
|
+
setRendererInjectDispatcher(getRDTHook());
|
|
66
101
|
rendererInjectListeners.add(listener);
|
|
67
|
-
return
|
|
102
|
+
return createUnsubscribe(() => {
|
|
68
103
|
rendererInjectListeners.delete(listener);
|
|
69
104
|
});
|
|
70
105
|
};
|
|
71
106
|
|
|
72
|
-
const
|
|
107
|
+
export const onRDTHookReplace = (listener: RDTHookReplaceListener): Unsubscribe => {
|
|
108
|
+
rdtHookReplaceListeners.add(listener);
|
|
109
|
+
return createUnsubscribe(() => {
|
|
110
|
+
rdtHookReplaceListeners.delete(listener);
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const getRendererMap = (rdtHook: ReactDevToolsGlobalHook): Map<number, ReactRenderer> => {
|
|
73
115
|
if (!(rdtHook.renderers instanceof Map)) {
|
|
74
116
|
rdtHook.renderers = new Map();
|
|
75
117
|
}
|
|
76
118
|
return rdtHook.renderers;
|
|
77
119
|
};
|
|
78
120
|
|
|
79
|
-
|
|
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
|
+
|
|
135
|
+
export const installRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHook => {
|
|
80
136
|
if (onActive) {
|
|
81
137
|
_onActiveListeners.add(onActive);
|
|
82
138
|
}
|
|
@@ -87,20 +143,15 @@ export const installRDTHook = (onActive?: () => unknown): ReactDevToolsGlobalHoo
|
|
|
87
143
|
_instrumentationSource: BIPPY_INSTRUMENTATION_STRING,
|
|
88
144
|
checkDCE,
|
|
89
145
|
hasUnsupportedRendererAttached: false,
|
|
90
|
-
inject(renderer) {
|
|
146
|
+
inject: (renderer) => {
|
|
91
147
|
const nextRendererId = ++rendererIdCounter;
|
|
92
|
-
renderers
|
|
93
|
-
_renderers.add(renderer);
|
|
94
|
-
if (!rdtHook._instrumentationIsActive) {
|
|
95
|
-
rdtHook._instrumentationIsActive = true;
|
|
96
|
-
_onActiveListeners.forEach((listener) => listener());
|
|
97
|
-
}
|
|
148
|
+
trackInjectedRenderer(rdtHook, renderers, nextRendererId, renderer);
|
|
98
149
|
return nextRendererId;
|
|
99
150
|
},
|
|
100
|
-
on:
|
|
101
|
-
onCommitFiberRoot:
|
|
102
|
-
onCommitFiberUnmount:
|
|
103
|
-
onPostCommitFiberRoot:
|
|
151
|
+
on: noOp,
|
|
152
|
+
onCommitFiberRoot: noOp,
|
|
153
|
+
onCommitFiberUnmount: noOp,
|
|
154
|
+
onPostCommitFiberRoot: noOp,
|
|
104
155
|
renderers,
|
|
105
156
|
supportsFiber: true,
|
|
106
157
|
supportsFlight: true,
|
|
@@ -116,92 +167,99 @@ export const installRDTHook = (onActive?: () => unknown): ReactDevToolsGlobalHoo
|
|
|
116
167
|
if (newHook && typeof newHook === "object") {
|
|
117
168
|
const ourRenderers = rdtHook.renderers;
|
|
118
169
|
rdtHook = newHook;
|
|
119
|
-
const nextRenderers =
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
170
|
+
const nextRenderers = getRendererMap(rdtHook);
|
|
171
|
+
ourRenderers.forEach((renderer, rendererId) => {
|
|
172
|
+
_renderers.add(renderer);
|
|
173
|
+
nextRenderers.set(rendererId, renderer);
|
|
174
|
+
});
|
|
175
|
+
if (ourRenderers.size > 0 || rdtHookReplaceListeners.size > 0) {
|
|
125
176
|
patchRDTHook(onActive);
|
|
126
177
|
}
|
|
178
|
+
notifyRDTHookReplaceListeners(rdtHook);
|
|
127
179
|
}
|
|
128
180
|
},
|
|
129
181
|
});
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
182
|
+
if (typeof window !== "undefined") {
|
|
183
|
+
// HACK: The DevTools extension uses hasOwnProperty to decide whether it may replace an earlier hook.
|
|
184
|
+
const originalWindowHasOwnProperty = window.hasOwnProperty.bind(window);
|
|
185
|
+
const originalHasOwnPropertyDescriptor = Object.getOwnPropertyDescriptor(
|
|
186
|
+
window,
|
|
187
|
+
"hasOwnProperty",
|
|
188
|
+
);
|
|
189
|
+
let didRunHasOwnPropertyHack = false;
|
|
190
|
+
const restoreWindowHasOwnProperty = (): void => {
|
|
191
|
+
if (originalHasOwnPropertyDescriptor) {
|
|
192
|
+
objectDefineProperty(window, "hasOwnProperty", originalHasOwnPropertyDescriptor);
|
|
193
|
+
} else {
|
|
194
|
+
Reflect.deleteProperty(window, "hasOwnProperty");
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
objectDefineProperty(window, "hasOwnProperty", {
|
|
198
|
+
configurable: true,
|
|
199
|
+
value: (propertyKey: PropertyKey) => {
|
|
200
|
+
if (!didRunHasOwnPropertyHack && propertyKey === "__REACT_DEVTOOLS_GLOBAL_HOOK__") {
|
|
201
|
+
didRunHasOwnPropertyHack = true;
|
|
202
|
+
restoreWindowHasOwnProperty();
|
|
139
203
|
globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__ = undefined;
|
|
140
|
-
|
|
141
|
-
return -0;
|
|
204
|
+
return false;
|
|
142
205
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
206
|
+
return originalWindowHasOwnProperty(propertyKey);
|
|
207
|
+
},
|
|
208
|
+
writable: true,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
148
211
|
} catch {
|
|
149
212
|
patchRDTHook(onActive);
|
|
150
213
|
}
|
|
151
214
|
return rdtHook;
|
|
152
215
|
};
|
|
153
216
|
|
|
154
|
-
export const patchRDTHook = (onActive?:
|
|
217
|
+
export const patchRDTHook = (onActive?: ActiveListener): void => {
|
|
155
218
|
if (onActive) {
|
|
156
219
|
_onActiveListeners.add(onActive);
|
|
157
220
|
}
|
|
158
221
|
let didNotifyActiveListeners = false;
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
rdtHook.on = NO_OP;
|
|
174
|
-
}
|
|
175
|
-
if (renderers.size) {
|
|
176
|
-
renderers.forEach((renderer) => _renderers.add(renderer));
|
|
177
|
-
rdtHook._instrumentationIsActive = true;
|
|
178
|
-
_onActiveListeners.forEach((listener) => listener());
|
|
179
|
-
didNotifyActiveListeners = true;
|
|
180
|
-
}
|
|
181
|
-
const prevInject = rdtHook.inject;
|
|
182
|
-
rdtHook.inject = (renderer) => {
|
|
183
|
-
const rendererId = prevInject(renderer);
|
|
184
|
-
_renderers.add(renderer);
|
|
185
|
-
renderers.set(rendererId, renderer);
|
|
186
|
-
rdtHook._instrumentationIsActive = true;
|
|
187
|
-
_onActiveListeners.forEach((listener) => listener());
|
|
188
|
-
return rendererId;
|
|
189
|
-
};
|
|
222
|
+
const rdtHook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
223
|
+
if (!rdtHook) return;
|
|
224
|
+
const renderers = getRendererMap(rdtHook);
|
|
225
|
+
if (!rdtHook._instrumentationSource) {
|
|
226
|
+
rdtHook.checkDCE = checkDCE;
|
|
227
|
+
rdtHook.supportsFiber = true;
|
|
228
|
+
rdtHook.supportsFlight = true;
|
|
229
|
+
rdtHook.hasUnsupportedRendererAttached = false;
|
|
230
|
+
rdtHook._instrumentationSource = BIPPY_INSTRUMENTATION_STRING;
|
|
231
|
+
rdtHook._instrumentationIsActive = false;
|
|
232
|
+
// HACK: DevTools detection must happen after setting the source to avoid recursive patching.
|
|
233
|
+
const isReactDevtools = isRealReactDevtools(rdtHook);
|
|
234
|
+
if (!isReactDevtools) {
|
|
235
|
+
rdtHook.on = noOp;
|
|
190
236
|
}
|
|
191
|
-
if (
|
|
192
|
-
|
|
237
|
+
if (renderers.size) {
|
|
238
|
+
renderers.forEach((renderer) => _renderers.add(renderer));
|
|
239
|
+
rdtHook._instrumentationIsActive = true;
|
|
240
|
+
notifyActiveListeners();
|
|
241
|
+
didNotifyActiveListeners = true;
|
|
193
242
|
}
|
|
194
|
-
|
|
243
|
+
const previousInject = rdtHook.inject;
|
|
244
|
+
rdtHook.inject = (renderer) => {
|
|
245
|
+
const rendererId = previousInject.call(rdtHook, renderer);
|
|
246
|
+
trackInjectedRenderer(rdtHook, renderers, rendererId, renderer);
|
|
247
|
+
return rendererId;
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
if (!didNotifyActiveListeners && (renderers.size || rdtHook._instrumentationIsActive)) {
|
|
251
|
+
onActive?.();
|
|
252
|
+
}
|
|
195
253
|
};
|
|
196
254
|
|
|
197
255
|
export const hasRDTHook = (): boolean => {
|
|
198
|
-
return
|
|
256
|
+
return Object.hasOwn(globalThis, "__REACT_DEVTOOLS_GLOBAL_HOOK__");
|
|
199
257
|
};
|
|
200
258
|
|
|
201
259
|
/**
|
|
202
260
|
* Returns the current React DevTools global hook.
|
|
203
261
|
*/
|
|
204
|
-
export const getRDTHook = (onActive?:
|
|
262
|
+
export const getRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHook => {
|
|
205
263
|
if (!hasRDTHook()) {
|
|
206
264
|
return installRDTHook(onActive);
|
|
207
265
|
}
|
|
@@ -209,19 +267,3 @@ export const getRDTHook = (onActive?: () => unknown): ReactDevToolsGlobalHook =>
|
|
|
209
267
|
patchRDTHook(onActive);
|
|
210
268
|
return globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__ ?? installRDTHook(onActive);
|
|
211
269
|
};
|
|
212
|
-
|
|
213
|
-
export const isClientEnvironment = (): boolean => {
|
|
214
|
-
return Boolean(
|
|
215
|
-
typeof window !== "undefined" &&
|
|
216
|
-
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
217
|
-
(window.document?.createElement || window.navigator?.product === "ReactNative"),
|
|
218
|
-
);
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
export const safelyInstallRDTHook = () => {
|
|
222
|
-
try {
|
|
223
|
-
if (isClientEnvironment()) {
|
|
224
|
-
getRDTHook();
|
|
225
|
-
}
|
|
226
|
-
} catch {}
|
|
227
|
-
};
|