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