bippy 0.6.1-dev.61475ed → 0.6.1-dev.d7876ea
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 +32 -48
- package/dist/core.cjs +1 -1
- package/dist/core.d.cts +42 -56
- package/dist/core.d.ts +42 -56
- package/dist/core.js +1 -1
- package/dist/core2.cjs +9 -0
- package/dist/core2.d.cts +3 -3
- package/dist/core2.d.ts +3 -3
- package/dist/core2.js +9 -0
- package/dist/errors.d.cts +436 -0
- package/dist/errors.d.ts +436 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -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 +1 -1
- package/dist/install-hook-only.d.ts +1 -1
- 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 +14 -4
- package/dist/source.d.cts +18 -9
- package/dist/source.d.ts +18 -9
- package/dist/source.js +14 -4
- package/package.json +21 -15
- package/src/core.ts +343 -253
- package/src/errors.ts +99 -0
- package/src/generated/react-work-tags.d.ts +261 -0
- package/src/generated/react-work-tags.js +240 -0
- package/src/rdt-hook.ts +154 -149
- package/src/react-internals.ts +65 -0
- package/src/semver.ts +78 -0
- package/src/source/constants.ts +1 -1
- package/src/source/error-stack.ts +11 -0
- package/src/source/get-source.ts +2 -1
- package/src/source/index.ts +9 -1
- package/src/source/inspect-hooks.ts +199 -164
- package/src/source/owner-stack.ts +84 -114
- package/src/source/parse-debug-stack.ts +4 -4
- package/src/source/parse-stack.ts +1 -1
- package/src/source/symbolication.ts +365 -89
- package/src/types.ts +165 -334
- package/src/unsubscribe.ts +2 -2
- package/dist/get-source.cjs +0 -19
- package/dist/get-source.js +0 -19
- package/dist/react-refresh.cjs +0 -9
- package/dist/react-refresh.d.cts +0 -66
- package/dist/react-refresh.d.ts +0 -66
- package/dist/react-refresh.js +0 -9
- package/dist/unsubscribe.d.cts +0 -298
- package/dist/unsubscribe.d.ts +0 -298
- package/src/react-refresh/constants.ts +0 -9
- package/src/react-refresh/detect-hmr-transport.ts +0 -33
- package/src/react-refresh/index.ts +0 -173
- package/src/react-refresh/metro-hmr-transport.ts +0 -188
- package/src/react-refresh/next-webpack-hmr-transport.ts +0 -72
- package/src/react-refresh/normalize-hmr-file-path.ts +0 -24
- package/src/react-refresh/types.ts +0 -7
- package/src/react-refresh/vite-hmr-transport.ts +0 -116
package/src/rdt-hook.ts
CHANGED
|
@@ -1,28 +1,43 @@
|
|
|
1
|
-
//
|
|
2
|
-
// this file is super important to load the __REACT_DEVTOOLS_GLOBAL_HOOK__ object
|
|
3
|
-
// without this, we can't stub the React DevTools global hook, we don't have a way to instrument the application
|
|
4
|
-
// make sure you import this file first before anything else (particularly React)
|
|
1
|
+
// This module must load before React so renderers can inject into the hook.
|
|
5
2
|
|
|
3
|
+
import {
|
|
4
|
+
BippyHookInstallationError,
|
|
5
|
+
BippyHookListenerError,
|
|
6
|
+
BippyReactBuildError,
|
|
7
|
+
runWithBippyError,
|
|
8
|
+
} from "./errors.js";
|
|
6
9
|
import type { ReactDevToolsGlobalHook, ReactRenderer } from "./types.js";
|
|
7
10
|
import { toUnsubscribe, type Unsubscribe } from "./unsubscribe.js";
|
|
8
11
|
|
|
12
|
+
interface ActiveListener {
|
|
13
|
+
(): unknown;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface RendererInjectListener {
|
|
17
|
+
(renderer: ReactRenderer): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface RDTHookReplaceListener {
|
|
21
|
+
(rdtHook: ReactDevToolsGlobalHook): void;
|
|
22
|
+
}
|
|
23
|
+
|
|
9
24
|
export const version = process.env.VERSION;
|
|
10
25
|
export const BIPPY_INSTRUMENTATION_STRING = `bippy-${version}`;
|
|
11
26
|
|
|
12
27
|
const objectDefineProperty = Object.defineProperty;
|
|
13
|
-
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
14
|
-
const objectHasOwnProperty = Object.prototype.hasOwnProperty;
|
|
15
28
|
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
const noOp = (): void => {};
|
|
30
|
+
|
|
31
|
+
const runHookListener = (listenerName: string, callback: () => unknown): void =>
|
|
32
|
+
runWithBippyError(callback, (cause) => new BippyHookListenerError(listenerName, cause));
|
|
19
33
|
|
|
20
34
|
const checkDCE = (functionToCheck: unknown): void => {
|
|
21
35
|
try {
|
|
22
36
|
const code = Function.prototype.toString.call(functionToCheck);
|
|
23
|
-
if (code.
|
|
37
|
+
if (code.includes("^_^")) {
|
|
38
|
+
// HACK: React DevTools reports failed dead-code elimination asynchronously.
|
|
24
39
|
setTimeout(() => {
|
|
25
|
-
throw new
|
|
40
|
+
throw new BippyReactBuildError(
|
|
26
41
|
"React is running in production mode, but dead code " +
|
|
27
42
|
"elimination has not been applied. Read how to correctly " +
|
|
28
43
|
"configure React for production: " +
|
|
@@ -39,62 +54,75 @@ export const isRealReactDevtools = (
|
|
|
39
54
|
return Boolean(rdtHook && "getFiberRoots" in rdtHook);
|
|
40
55
|
};
|
|
41
56
|
|
|
42
|
-
|
|
43
|
-
let injectFnStr: string | undefined = undefined;
|
|
57
|
+
export const _onActiveListeners = new Set<ActiveListener>();
|
|
44
58
|
|
|
45
|
-
export const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
59
|
+
export const _renderers = new Set<ReactRenderer>();
|
|
60
|
+
|
|
61
|
+
const rendererInjectListeners = new Set<RendererInjectListener>();
|
|
62
|
+
const rdtHookReplaceListeners = new Set<RDTHookReplaceListener>();
|
|
63
|
+
// HACK: Hook replacement can leave an old inject wrapper in the call chain, so notifications must be deduplicated.
|
|
64
|
+
const notifiedRenderers = new WeakSet<ReactRenderer>();
|
|
65
|
+
const notifyingInjectByHook = new WeakMap<
|
|
66
|
+
ReactDevToolsGlobalHook,
|
|
67
|
+
ReactDevToolsGlobalHook["inject"]
|
|
68
|
+
>();
|
|
69
|
+
|
|
70
|
+
const notifyActiveListeners = (): void => {
|
|
71
|
+
for (const listener of _onActiveListeners) {
|
|
72
|
+
runHookListener("onActive", listener);
|
|
51
73
|
}
|
|
52
|
-
// https://github.com/facebook/react/blob/8f8b336734d7c807f5aa11b0f31540e63302d789/packages/react-refresh/src/ReactFreshRuntime.js#L459
|
|
53
|
-
return Boolean(injectFnStr?.includes("(injected)"));
|
|
54
74
|
};
|
|
55
75
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
76
|
+
const notifyRendererInjectListeners = (renderer: ReactRenderer): void => {
|
|
77
|
+
for (const listener of rendererInjectListeners) {
|
|
78
|
+
runHookListener("onRendererInject", () => listener(renderer));
|
|
79
|
+
}
|
|
80
|
+
};
|
|
59
81
|
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
82
|
+
const notifyRDTHookReplaceListeners = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
83
|
+
for (const listener of rdtHookReplaceListeners) {
|
|
84
|
+
runHookListener("onRDTHookReplace", () => listener(rdtHook));
|
|
85
|
+
}
|
|
86
|
+
};
|
|
65
87
|
|
|
66
|
-
const
|
|
67
|
-
if (rdtHook.inject ===
|
|
68
|
-
const
|
|
88
|
+
const setRendererInjectDispatcher = (rdtHook: ReactDevToolsGlobalHook): void => {
|
|
89
|
+
if (rdtHook.inject === notifyingInjectByHook.get(rdtHook)) return;
|
|
90
|
+
const previousInject = rdtHook.inject;
|
|
69
91
|
const nextInject = (renderer: ReactRenderer) => {
|
|
70
|
-
const rendererId =
|
|
92
|
+
const rendererId = previousInject.call(rdtHook, renderer);
|
|
71
93
|
if (!notifiedRenderers.has(renderer)) {
|
|
72
94
|
notifiedRenderers.add(renderer);
|
|
73
|
-
|
|
74
|
-
listener(renderer);
|
|
75
|
-
}
|
|
95
|
+
notifyRendererInjectListeners(renderer);
|
|
76
96
|
}
|
|
77
97
|
return rendererId;
|
|
78
98
|
};
|
|
79
99
|
rdtHook.inject = nextInject;
|
|
80
|
-
|
|
100
|
+
notifyingInjectByHook.set(rdtHook, nextInject);
|
|
81
101
|
};
|
|
82
102
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
* single shared inject wrapper lets multiple consumers (override methods,
|
|
86
|
-
* react-refresh, user code) observe renderers without stacking patches
|
|
87
|
-
* whose restore order matters. Returns an unsubscribe function.
|
|
88
|
-
*/
|
|
89
|
-
export const onRendererInject = (listener: (renderer: ReactRenderer) => void): Unsubscribe => {
|
|
90
|
-
ensureInjectNotifiesListeners(getRDTHook());
|
|
103
|
+
export const onRendererInject = (listener: RendererInjectListener): Unsubscribe => {
|
|
104
|
+
setRendererInjectDispatcher(getRDTHook());
|
|
91
105
|
rendererInjectListeners.add(listener);
|
|
92
106
|
return toUnsubscribe(() => {
|
|
93
107
|
rendererInjectListeners.delete(listener);
|
|
94
108
|
});
|
|
95
109
|
};
|
|
96
110
|
|
|
97
|
-
export const
|
|
111
|
+
export const onRDTHookReplace = (listener: RDTHookReplaceListener): Unsubscribe => {
|
|
112
|
+
rdtHookReplaceListeners.add(listener);
|
|
113
|
+
return toUnsubscribe(() => {
|
|
114
|
+
rdtHookReplaceListeners.delete(listener);
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const getRendererMap = (rdtHook: ReactDevToolsGlobalHook): Map<number, ReactRenderer> => {
|
|
119
|
+
if (!(rdtHook.renderers instanceof Map)) {
|
|
120
|
+
rdtHook.renderers = new Map();
|
|
121
|
+
}
|
|
122
|
+
return rdtHook.renderers;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const installRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHook => {
|
|
98
126
|
if (onActive) {
|
|
99
127
|
_onActiveListeners.add(onActive);
|
|
100
128
|
}
|
|
@@ -105,20 +133,20 @@ export const installRDTHook = (onActive?: () => unknown): ReactDevToolsGlobalHoo
|
|
|
105
133
|
_instrumentationSource: BIPPY_INSTRUMENTATION_STRING,
|
|
106
134
|
checkDCE,
|
|
107
135
|
hasUnsupportedRendererAttached: false,
|
|
108
|
-
inject(renderer) {
|
|
136
|
+
inject: (renderer) => {
|
|
109
137
|
const nextRendererId = ++rendererIdCounter;
|
|
110
138
|
renderers.set(nextRendererId, renderer);
|
|
111
139
|
_renderers.add(renderer);
|
|
112
140
|
if (!rdtHook._instrumentationIsActive) {
|
|
113
141
|
rdtHook._instrumentationIsActive = true;
|
|
114
|
-
|
|
142
|
+
notifyActiveListeners();
|
|
115
143
|
}
|
|
116
144
|
return nextRendererId;
|
|
117
145
|
},
|
|
118
|
-
on:
|
|
119
|
-
onCommitFiberRoot:
|
|
120
|
-
onCommitFiberUnmount:
|
|
121
|
-
onPostCommitFiberRoot:
|
|
146
|
+
on: noOp,
|
|
147
|
+
onCommitFiberRoot: noOp,
|
|
148
|
+
onCommitFiberUnmount: noOp,
|
|
149
|
+
onPostCommitFiberRoot: noOp,
|
|
122
150
|
renderers,
|
|
123
151
|
supportsFiber: true,
|
|
124
152
|
supportsFlight: true,
|
|
@@ -134,137 +162,114 @@ export const installRDTHook = (onActive?: () => unknown): ReactDevToolsGlobalHoo
|
|
|
134
162
|
if (newHook && typeof newHook === "object") {
|
|
135
163
|
const ourRenderers = rdtHook.renderers;
|
|
136
164
|
rdtHook = newHook;
|
|
165
|
+
const nextRenderers = getRendererMap(rdtHook);
|
|
137
166
|
if (ourRenderers.size > 0) {
|
|
138
|
-
ourRenderers.forEach((renderer,
|
|
167
|
+
ourRenderers.forEach((renderer, rendererId) => {
|
|
139
168
|
_renderers.add(renderer);
|
|
140
|
-
|
|
141
|
-
newHook.renderers.set(id, renderer);
|
|
169
|
+
nextRenderers.set(rendererId, renderer);
|
|
142
170
|
});
|
|
171
|
+
}
|
|
172
|
+
if (ourRenderers.size > 0 || rdtHookReplaceListeners.size > 0) {
|
|
143
173
|
patchRDTHook(onActive);
|
|
144
174
|
}
|
|
175
|
+
notifyRDTHookReplaceListeners(rdtHook);
|
|
145
176
|
}
|
|
146
177
|
},
|
|
147
178
|
});
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
179
|
+
if (typeof window !== "undefined") {
|
|
180
|
+
// HACK: The DevTools extension uses hasOwnProperty to decide whether it may replace an earlier hook.
|
|
181
|
+
const originalWindowHasOwnProperty = window.hasOwnProperty.bind(window);
|
|
182
|
+
const originalHasOwnPropertyDescriptor = Object.getOwnPropertyDescriptor(
|
|
183
|
+
window,
|
|
184
|
+
"hasOwnProperty",
|
|
185
|
+
);
|
|
186
|
+
let didRunHasOwnPropertyHack = false;
|
|
187
|
+
const restoreWindowHasOwnProperty = (): void => {
|
|
188
|
+
if (originalHasOwnPropertyDescriptor) {
|
|
189
|
+
objectDefineProperty(window, "hasOwnProperty", originalHasOwnPropertyDescriptor);
|
|
190
|
+
} else {
|
|
191
|
+
Reflect.deleteProperty(window, "hasOwnProperty");
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
objectDefineProperty(window, "hasOwnProperty", {
|
|
195
|
+
configurable: true,
|
|
196
|
+
value: (...propertyKeys: [PropertyKey]) => {
|
|
197
|
+
if (!didRunHasOwnPropertyHack && propertyKeys[0] === "__REACT_DEVTOOLS_GLOBAL_HOOK__") {
|
|
198
|
+
didRunHasOwnPropertyHack = true;
|
|
199
|
+
restoreWindowHasOwnProperty();
|
|
158
200
|
globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__ = undefined;
|
|
159
|
-
|
|
160
|
-
hasRanHack = true;
|
|
161
|
-
return -0;
|
|
201
|
+
return false;
|
|
162
202
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
203
|
+
return originalWindowHasOwnProperty(...propertyKeys);
|
|
204
|
+
},
|
|
205
|
+
writable: true,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
168
208
|
} catch {
|
|
169
209
|
patchRDTHook(onActive);
|
|
170
210
|
}
|
|
171
211
|
return rdtHook;
|
|
172
212
|
};
|
|
173
213
|
|
|
174
|
-
export const patchRDTHook = (onActive?:
|
|
214
|
+
export const patchRDTHook = (onActive?: ActiveListener): void => {
|
|
175
215
|
if (onActive) {
|
|
176
216
|
_onActiveListeners.add(onActive);
|
|
177
217
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if (rdtHook.renderers.size) {
|
|
194
|
-
rdtHook._instrumentationIsActive = true;
|
|
195
|
-
_onActiveListeners.forEach((listener) => listener());
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
const prevInject = rdtHook.inject;
|
|
199
|
-
const isRefresh = isReactRefresh(rdtHook);
|
|
200
|
-
if (isRefresh && !isReactDevtools) {
|
|
201
|
-
isReactRefreshOverride = true;
|
|
202
|
-
// but since the underlying implementation doens't care,
|
|
203
|
-
// it's ok: https://github.com/facebook/react/blob/18eaf51bd51fed8dfed661d64c306759101d0bfd/packages/react-refresh/src/ReactFreshRuntime.js#L430
|
|
204
|
-
const injectedRendererId = rdtHook.inject({
|
|
205
|
-
scheduleRefresh() {},
|
|
206
|
-
} as unknown as ReactRenderer);
|
|
207
|
-
if (injectedRendererId) {
|
|
208
|
-
rdtHook._instrumentationIsActive = true;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
rdtHook.inject = (renderer) => {
|
|
212
|
-
const rendererId = prevInject(renderer);
|
|
213
|
-
_renderers.add(renderer);
|
|
214
|
-
if (isRefresh) {
|
|
215
|
-
// react refresh doesn't inject this properly
|
|
216
|
-
// https://github.com/facebook/react/blob/18eaf51bd51fed8dfed661d64c306759101d0bfd/packages/react-refresh/src/ReactFreshRuntime.js#L430
|
|
217
|
-
rdtHook.renderers.set(rendererId, renderer);
|
|
218
|
-
}
|
|
219
|
-
rdtHook._instrumentationIsActive = true;
|
|
220
|
-
_onActiveListeners.forEach((listener) => listener());
|
|
221
|
-
return rendererId;
|
|
222
|
-
};
|
|
218
|
+
let didNotifyActiveListeners = false;
|
|
219
|
+
const rdtHook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
220
|
+
if (!rdtHook) return;
|
|
221
|
+
const renderers = getRendererMap(rdtHook);
|
|
222
|
+
if (!rdtHook._instrumentationSource) {
|
|
223
|
+
rdtHook.checkDCE = checkDCE;
|
|
224
|
+
rdtHook.supportsFiber = true;
|
|
225
|
+
rdtHook.supportsFlight = true;
|
|
226
|
+
rdtHook.hasUnsupportedRendererAttached = false;
|
|
227
|
+
rdtHook._instrumentationSource = BIPPY_INSTRUMENTATION_STRING;
|
|
228
|
+
rdtHook._instrumentationIsActive = false;
|
|
229
|
+
// HACK: DevTools detection must happen after setting the source to avoid recursive patching.
|
|
230
|
+
const isReactDevtools = isRealReactDevtools(rdtHook);
|
|
231
|
+
if (!isReactDevtools) {
|
|
232
|
+
rdtHook.on = noOp;
|
|
223
233
|
}
|
|
224
|
-
if (
|
|
225
|
-
|
|
226
|
-
rdtHook._instrumentationIsActive
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
) {
|
|
230
|
-
onActive?.();
|
|
234
|
+
if (renderers.size) {
|
|
235
|
+
renderers.forEach((renderer) => _renderers.add(renderer));
|
|
236
|
+
rdtHook._instrumentationIsActive = true;
|
|
237
|
+
notifyActiveListeners();
|
|
238
|
+
didNotifyActiveListeners = true;
|
|
231
239
|
}
|
|
232
|
-
|
|
240
|
+
const previousInject = rdtHook.inject;
|
|
241
|
+
rdtHook.inject = (renderer) => {
|
|
242
|
+
const rendererId = previousInject.call(rdtHook, renderer);
|
|
243
|
+
_renderers.add(renderer);
|
|
244
|
+
renderers.set(rendererId, renderer);
|
|
245
|
+
if (!rdtHook._instrumentationIsActive) {
|
|
246
|
+
rdtHook._instrumentationIsActive = true;
|
|
247
|
+
notifyActiveListeners();
|
|
248
|
+
}
|
|
249
|
+
return rendererId;
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
if (!didNotifyActiveListeners && (renderers.size || rdtHook._instrumentationIsActive)) {
|
|
253
|
+
if (onActive) runHookListener("onActive", onActive);
|
|
254
|
+
}
|
|
233
255
|
};
|
|
234
256
|
|
|
235
257
|
export const hasRDTHook = (): boolean => {
|
|
236
|
-
return
|
|
258
|
+
return Object.hasOwn(globalThis, "__REACT_DEVTOOLS_GLOBAL_HOOK__");
|
|
237
259
|
};
|
|
238
260
|
|
|
239
261
|
/**
|
|
240
262
|
* Returns the current React DevTools global hook.
|
|
241
263
|
*/
|
|
242
|
-
export const getRDTHook = (onActive?:
|
|
264
|
+
export const getRDTHook = (onActive?: ActiveListener): ReactDevToolsGlobalHook => {
|
|
243
265
|
if (!hasRDTHook()) {
|
|
244
266
|
return installRDTHook(onActive);
|
|
245
267
|
}
|
|
246
268
|
|
|
247
269
|
patchRDTHook(onActive);
|
|
248
|
-
|
|
249
|
-
return globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__ as ReactDevToolsGlobalHook;
|
|
270
|
+
return globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__ ?? installRDTHook(onActive);
|
|
250
271
|
};
|
|
251
272
|
|
|
252
|
-
export const
|
|
253
|
-
|
|
254
|
-
typeof window !== "undefined" &&
|
|
255
|
-
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
256
|
-
(window.document?.createElement || window.navigator?.product === "ReactNative"),
|
|
257
|
-
);
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Usually used purely for side effect
|
|
262
|
-
*/
|
|
263
|
-
export const safelyInstallRDTHook = () => {
|
|
264
|
-
try {
|
|
265
|
-
// __REACT_DEVTOOLS_GLOBAL_HOOK__ must exist before React is ever executed
|
|
266
|
-
if (isClientEnvironment()) {
|
|
267
|
-
getRDTHook();
|
|
268
|
-
}
|
|
269
|
-
} catch {}
|
|
273
|
+
export const safelyInstallRDTHook = (): void => {
|
|
274
|
+
runWithBippyError(getRDTHook, (cause) => new BippyHookInstallationError(cause));
|
|
270
275
|
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { getReactWorkTags, ReactFiberFlags } from "./generated/react-work-tags.js";
|
|
2
|
+
import type { ReactWorkTagMap } from "./generated/react-work-tags.js";
|
|
3
|
+
import { compareSemver } from "./semver.js";
|
|
4
|
+
import type { Fiber, ReactRenderer } from "./types.js";
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
getReactWorkTags,
|
|
8
|
+
ReactBuildType,
|
|
9
|
+
ReactFiberFlags,
|
|
10
|
+
ReactSymbols,
|
|
11
|
+
} from "./generated/react-work-tags.js";
|
|
12
|
+
export type {
|
|
13
|
+
HostWorkTag,
|
|
14
|
+
ReactWorkTag,
|
|
15
|
+
ReactWorkTagMap,
|
|
16
|
+
ReactWorkTagVersion,
|
|
17
|
+
} from "./generated/react-work-tags.js";
|
|
18
|
+
|
|
19
|
+
const defaultReactWorkTags = getReactWorkTags("");
|
|
20
|
+
const fiberReactWorkTags = new WeakMap<Fiber, Readonly<ReactWorkTagMap>>();
|
|
21
|
+
|
|
22
|
+
export const getReactWorkTagsForRenderer = (
|
|
23
|
+
renderer?: ReactRenderer | null,
|
|
24
|
+
): Readonly<ReactWorkTagMap> => {
|
|
25
|
+
const reconcilerVersion = renderer?.reconcilerVersion;
|
|
26
|
+
if (reconcilerVersion && compareSemver(reconcilerVersion, reconcilerVersion) === 0) {
|
|
27
|
+
return getReactWorkTags(reconcilerVersion);
|
|
28
|
+
}
|
|
29
|
+
return getReactWorkTags(renderer?.version ?? "");
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const setReactWorkTagsForFiber = (fiber: Fiber, renderer?: ReactRenderer): void => {
|
|
33
|
+
const workTags = getReactWorkTagsForRenderer(renderer);
|
|
34
|
+
fiberReactWorkTags.set(fiber, workTags);
|
|
35
|
+
if (fiber.alternate) fiberReactWorkTags.set(fiber.alternate, workTags);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const getReactWorkTagsForFiber = (fiber: Fiber): Readonly<ReactWorkTagMap> => {
|
|
39
|
+
const cachedWorkTags = fiberReactWorkTags.get(fiber);
|
|
40
|
+
if (cachedWorkTags) return cachedWorkTags;
|
|
41
|
+
|
|
42
|
+
const traversedFibers: Fiber[] = [fiber];
|
|
43
|
+
let rootFiber = fiber;
|
|
44
|
+
while (rootFiber.return) {
|
|
45
|
+
rootFiber = rootFiber.return;
|
|
46
|
+
traversedFibers.push(rootFiber);
|
|
47
|
+
}
|
|
48
|
+
const workTags = fiberReactWorkTags.get(rootFiber) ?? defaultReactWorkTags;
|
|
49
|
+
for (const traversedFiber of traversedFibers) {
|
|
50
|
+
fiberReactWorkTags.set(traversedFiber, workTags);
|
|
51
|
+
if (traversedFiber.alternate) {
|
|
52
|
+
fiberReactWorkTags.set(traversedFiber.alternate, workTags);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return workTags;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const MutationMask =
|
|
59
|
+
ReactFiberFlags.Placement |
|
|
60
|
+
ReactFiberFlags.Update |
|
|
61
|
+
ReactFiberFlags.ChildDeletion |
|
|
62
|
+
ReactFiberFlags.ContentReset |
|
|
63
|
+
ReactFiberFlags.Hydrating |
|
|
64
|
+
ReactFiberFlags.Visibility |
|
|
65
|
+
ReactFiberFlags.Snapshot;
|
package/src/semver.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
interface SemanticVersion {
|
|
2
|
+
major: string;
|
|
3
|
+
minor: string;
|
|
4
|
+
patch: string;
|
|
5
|
+
prerelease: string[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const SEMVER_PATTERN =
|
|
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
|
+
const NUMERIC_IDENTIFIER_PATTERN = /^\d+$/;
|
|
11
|
+
|
|
12
|
+
const parseSemver = (version: string): SemanticVersion | null => {
|
|
13
|
+
const match = SEMVER_PATTERN.exec(version);
|
|
14
|
+
if (!match) return null;
|
|
15
|
+
const prerelease = match[4]?.split(".") ?? [];
|
|
16
|
+
if (
|
|
17
|
+
prerelease.some(
|
|
18
|
+
(identifier) =>
|
|
19
|
+
NUMERIC_IDENTIFIER_PATTERN.test(identifier) &&
|
|
20
|
+
identifier.length > 1 &&
|
|
21
|
+
identifier.startsWith("0"),
|
|
22
|
+
)
|
|
23
|
+
) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
major: match[1],
|
|
28
|
+
minor: match[2],
|
|
29
|
+
patch: match[3],
|
|
30
|
+
prerelease,
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const compareNumericIdentifiers = (left: string, right: string): -1 | 0 | 1 => {
|
|
35
|
+
if (left.length !== right.length) return left.length < right.length ? -1 : 1;
|
|
36
|
+
if (left === right) return 0;
|
|
37
|
+
return left < right ? -1 : 1;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const comparePrereleaseIdentifiers = (left: string, right: string): -1 | 0 | 1 => {
|
|
41
|
+
const isLeftNumeric = NUMERIC_IDENTIFIER_PATTERN.test(left);
|
|
42
|
+
const isRightNumeric = NUMERIC_IDENTIFIER_PATTERN.test(right);
|
|
43
|
+
if (isLeftNumeric && isRightNumeric) return compareNumericIdentifiers(left, right);
|
|
44
|
+
if (isLeftNumeric !== isRightNumeric) return isLeftNumeric ? -1 : 1;
|
|
45
|
+
if (left === right) return 0;
|
|
46
|
+
return left < right ? -1 : 1;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const comparePrerelease = (left: string[], right: string[]): -1 | 0 | 1 => {
|
|
50
|
+
if (left.length === 0 || right.length === 0) {
|
|
51
|
+
if (left.length === right.length) return 0;
|
|
52
|
+
return left.length === 0 ? 1 : -1;
|
|
53
|
+
}
|
|
54
|
+
const identifierCount = Math.max(left.length, right.length);
|
|
55
|
+
for (let identifierIndex = 0; identifierIndex < identifierCount; identifierIndex++) {
|
|
56
|
+
const leftIdentifier = left[identifierIndex];
|
|
57
|
+
const rightIdentifier = right[identifierIndex];
|
|
58
|
+
if (leftIdentifier === undefined || rightIdentifier === undefined) {
|
|
59
|
+
return leftIdentifier === undefined ? -1 : 1;
|
|
60
|
+
}
|
|
61
|
+
const comparison = comparePrereleaseIdentifiers(leftIdentifier, rightIdentifier);
|
|
62
|
+
if (comparison !== 0) return comparison;
|
|
63
|
+
}
|
|
64
|
+
return 0;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const compareSemver = (leftVersion: string, rightVersion: string): -1 | 0 | 1 | null => {
|
|
68
|
+
const left = parseSemver(leftVersion);
|
|
69
|
+
const right = parseSemver(rightVersion);
|
|
70
|
+
if (!left || !right) return null;
|
|
71
|
+
const majorComparison = compareNumericIdentifiers(left.major, right.major);
|
|
72
|
+
if (majorComparison !== 0) return majorComparison;
|
|
73
|
+
const minorComparison = compareNumericIdentifiers(left.minor, right.minor);
|
|
74
|
+
if (minorComparison !== 0) return minorComparison;
|
|
75
|
+
const patchComparison = compareNumericIdentifiers(left.patch, right.patch);
|
|
76
|
+
if (patchComparison !== 0) return patchComparison;
|
|
77
|
+
return comparePrerelease(left.prerelease, right.prerelease);
|
|
78
|
+
};
|
package/src/source/constants.ts
CHANGED
|
@@ -18,7 +18,7 @@ export const SERVER_COMPONENT_URL_PREFIXES = ["rsc://", ABOUT_REACT_PREFIX] as c
|
|
|
18
18
|
|
|
19
19
|
export const ANONYMOUS_FILE_PATTERNS = ["<anonymous>", "eval", ""] as const;
|
|
20
20
|
|
|
21
|
-
export const SOURCE_FILE_EXTENSION_REGEX = /\.(jsx|
|
|
21
|
+
export const SOURCE_FILE_EXTENSION_REGEX = /\.(cjs|cts|js|jsx|mdx|mjs|mts|ts|tsx)$/;
|
|
22
22
|
|
|
23
23
|
export const BUNDLED_FILE_PATTERN_REGEX =
|
|
24
24
|
/(\.min|bundle|chunk|vendor|vendors|runtime|polyfill|polyfills)\.(js|mjs|cjs)$|(chunk|bundle|vendor|vendors|runtime|polyfill|polyfills|framework|app|main|index)[-_.][A-Za-z0-9_-]{4,}\.(js|mjs|cjs)$|[\da-f]{8,}\.(js|mjs|cjs)$|[-_.][\da-f]{20,}\.(js|mjs|cjs)$|\/dist\/|\/build\/|\/.next\/|\/out\/|\/node_modules\/|\.webpack\.|\.vite\.|\.turbopack\./i;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface ErrorConstructorWithPrepareStackTrace {
|
|
2
|
+
prepareStackTrace?: unknown;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
const errorConstructor: ErrorConstructorWithPrepareStackTrace = Error;
|
|
6
|
+
|
|
7
|
+
export const getPrepareStackTrace = (): unknown => errorConstructor.prepareStackTrace;
|
|
8
|
+
|
|
9
|
+
export const setPrepareStackTrace = (prepareStackTrace: unknown): void => {
|
|
10
|
+
errorConstructor.prepareStackTrace = prepareStackTrace;
|
|
11
|
+
};
|
package/src/source/get-source.ts
CHANGED
|
@@ -171,6 +171,7 @@ export const normalizeFileName = (fileName: string): string => {
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
let normalizedFileName = fileName;
|
|
174
|
+
const isWindowsDrivePath = /^[a-zA-Z]:[\\/]/.test(normalizedFileName);
|
|
174
175
|
|
|
175
176
|
const isHttpUrl =
|
|
176
177
|
normalizedFileName.startsWith("http://") || normalizedFileName.startsWith("https://");
|
|
@@ -214,7 +215,7 @@ export const normalizeFileName = (fileName: string): string => {
|
|
|
214
215
|
}
|
|
215
216
|
}
|
|
216
217
|
|
|
217
|
-
if (SCHEME_REGEX.test(normalizedFileName)) {
|
|
218
|
+
if (!isWindowsDrivePath && SCHEME_REGEX.test(normalizedFileName)) {
|
|
218
219
|
const schemeMatch = normalizedFileName.match(SCHEME_REGEX);
|
|
219
220
|
if (schemeMatch) {
|
|
220
221
|
normalizedFileName = normalizedFileName.slice(schemeMatch[0].length);
|
package/src/source/index.ts
CHANGED
|
@@ -6,12 +6,20 @@ export {
|
|
|
6
6
|
symbolicateStack,
|
|
7
7
|
type DecodedSourceMapSection,
|
|
8
8
|
type IndexSourceMap,
|
|
9
|
-
type
|
|
9
|
+
type SourceFetch,
|
|
10
10
|
type SourceMap,
|
|
11
|
+
type SourceMapRequestOptions,
|
|
11
12
|
type StandardSourceMap,
|
|
12
13
|
} from "./symbolication.js";
|
|
13
14
|
export type { FiberSource } from "./types.js";
|
|
14
15
|
export { parseStack, type ParseOptions, type StackFrame } from "./parse-stack.js";
|
|
15
16
|
export { getDisplayNameFromSource } from "./get-display-name-from-source.js";
|
|
17
|
+
export {
|
|
18
|
+
BippyError,
|
|
19
|
+
BippyHookInspectionError,
|
|
20
|
+
BippyHookRenderError,
|
|
21
|
+
BippySourceMapError,
|
|
22
|
+
BippyUnsupportedHookError,
|
|
23
|
+
} from "../errors.js";
|
|
16
24
|
export { getFiberHooks, type HookSource, type HooksNode, type HooksTree } from "./inspect-hooks.js";
|
|
17
25
|
export { parseHookNames, type HookNames } from "./parse-hook-names.js";
|