bippy 0.6.1-dev.fe75557 → 0.7.0-dev.697d535
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 +223 -461
- package/dist/core.cjs +1 -1
- package/dist/core.js +1 -1
- package/dist/errors.d.cts +410 -0
- package/dist/errors.d.ts +410 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +153 -3
- package/dist/index.d.ts +153 -3
- 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.js +1 -1
- package/dist/rdt-hook.cjs +1 -1
- package/dist/rdt-hook.js +1 -1
- package/dist/source.cjs +14 -5
- package/dist/source.d.cts +86 -69
- package/dist/source.d.ts +86 -69
- package/dist/source.js +14 -5
- package/package.json +26 -27
- package/src/core.ts +353 -685
- package/src/errors.ts +34 -0
- package/src/index.ts +1 -0
- package/src/install-hook-only.ts +2 -2
- package/src/rdt-hook.ts +160 -161
- 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/react-internals/types.ts +186 -0
- package/src/react.ts +76 -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 +44 -40
- package/src/source/get-source.ts +43 -26
- package/src/source/index.ts +11 -1
- package/src/source/inspect-hooks.ts +220 -207
- package/src/source/owner-stack.ts +119 -174
- 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 +709 -120
- package/dist/core.d.cts +0 -214
- package/dist/core.d.ts +0 -214
- package/dist/core2.d.cts +0 -3
- package/dist/core2.d.ts +0 -3
- package/dist/get-source.cjs +0 -19
- package/dist/get-source.js +0 -19
- package/dist/index.iife.js +0 -9
- package/dist/install-hook-only.iife.js +0 -9
- 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/dist/unsubscribe.d.ts
DELETED
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from "react";
|
|
2
|
-
|
|
3
|
-
//#region src/types.d.ts
|
|
4
|
-
type BundleType = 0 | 1;
|
|
5
|
-
type Flags = number;
|
|
6
|
-
type Lanes = number;
|
|
7
|
-
type TypeOfMode = number;
|
|
8
|
-
type RootTag = 0 | 1 | 2;
|
|
9
|
-
type LanePriority = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17;
|
|
10
|
-
type WorkTag = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31;
|
|
11
|
-
type HookType = "useState" | "useReducer" | "useContext" | "useRef" | "useEffect" | "useLayoutEffect" | "useCallback" | "useMemo" | "useImperativeHandle" | "useDebugValue" | "useDeferredValue" | "useTransition" | "useMutableSource" | "useOpaqueIdentifier" | "useCacheRefresh";
|
|
12
|
-
type FiberRoot = any;
|
|
13
|
-
type MutableSource = any;
|
|
14
|
-
type OpaqueHandle = any;
|
|
15
|
-
type OpaqueRoot = any;
|
|
16
|
-
type React$AbstractComponent<_Config, _Instance = unknown> = any;
|
|
17
|
-
type HostConfig = Record<string, any>;
|
|
18
|
-
interface Source {
|
|
19
|
-
fileName: string;
|
|
20
|
-
lineNumber: number;
|
|
21
|
-
}
|
|
22
|
-
interface RefObject {
|
|
23
|
-
current: any;
|
|
24
|
-
}
|
|
25
|
-
interface Thenable<T> {
|
|
26
|
-
then(resolve: () => T, reject?: () => T): T;
|
|
27
|
-
}
|
|
28
|
-
interface ReactContext<T> {
|
|
29
|
-
$$typeof: symbol | number;
|
|
30
|
-
Consumer: ReactContext<T>;
|
|
31
|
-
Provider: ReactProviderType<T>;
|
|
32
|
-
_calculateChangedBits: ((a: T, b: T) => number) | null;
|
|
33
|
-
_currentValue: T;
|
|
34
|
-
_currentValue2: T;
|
|
35
|
-
_threadCount: number;
|
|
36
|
-
_currentRenderer?: Record<string, any> | null;
|
|
37
|
-
_currentRenderer2?: Record<string, any> | null;
|
|
38
|
-
displayName?: string;
|
|
39
|
-
}
|
|
40
|
-
interface ReactProviderType<T> {
|
|
41
|
-
$$typeof: symbol | number;
|
|
42
|
-
_context: ReactContext<T>;
|
|
43
|
-
}
|
|
44
|
-
interface ReactProvider<T> {
|
|
45
|
-
$$typeof: symbol | number;
|
|
46
|
-
type: ReactProviderType<T>;
|
|
47
|
-
key: null | string;
|
|
48
|
-
ref: null;
|
|
49
|
-
props: {
|
|
50
|
-
value: T;
|
|
51
|
-
children?: ReactNode;
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
interface ReactConsumer<T> {
|
|
55
|
-
$$typeof: symbol | number;
|
|
56
|
-
type: ReactContext<T>;
|
|
57
|
-
key: null | string;
|
|
58
|
-
ref: null;
|
|
59
|
-
props: {
|
|
60
|
-
children: (value: T) => ReactNode;
|
|
61
|
-
unstable_observedBits?: number;
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
interface ReactPortal {
|
|
65
|
-
$$typeof: symbol | number;
|
|
66
|
-
key: null | string;
|
|
67
|
-
containerInfo: any;
|
|
68
|
-
children: ReactNode;
|
|
69
|
-
implementation: any;
|
|
70
|
-
}
|
|
71
|
-
interface ComponentSelector {
|
|
72
|
-
$$typeof: symbol | number;
|
|
73
|
-
value: React$AbstractComponent<never, unknown>;
|
|
74
|
-
}
|
|
75
|
-
interface HasPseudoClassSelector {
|
|
76
|
-
$$typeof: symbol | number;
|
|
77
|
-
value: Selector[];
|
|
78
|
-
}
|
|
79
|
-
interface RoleSelector {
|
|
80
|
-
$$typeof: symbol | number;
|
|
81
|
-
value: string;
|
|
82
|
-
}
|
|
83
|
-
interface TextSelector {
|
|
84
|
-
$$typeof: symbol | number;
|
|
85
|
-
value: string;
|
|
86
|
-
}
|
|
87
|
-
interface TestNameSelector {
|
|
88
|
-
$$typeof: symbol | number;
|
|
89
|
-
value: string;
|
|
90
|
-
}
|
|
91
|
-
type Selector = ComponentSelector | HasPseudoClassSelector | RoleSelector | TextSelector | TestNameSelector;
|
|
92
|
-
interface DevToolsConfig<Instance = any, TextInstance = any, RendererInspectionConfig = any> {
|
|
93
|
-
bundleType: BundleType;
|
|
94
|
-
version: string;
|
|
95
|
-
rendererPackageName: string;
|
|
96
|
-
findFiberByHostInstance?: (instance: Instance | TextInstance) => ReactFiber | null;
|
|
97
|
-
rendererConfig?: RendererInspectionConfig;
|
|
98
|
-
}
|
|
99
|
-
interface SuspenseHydrationCallbacks<SuspenseInstance = unknown> {
|
|
100
|
-
onHydrated?: (suspenseInstance: SuspenseInstance) => void;
|
|
101
|
-
onDeleted?: (suspenseInstance: SuspenseInstance) => void;
|
|
102
|
-
}
|
|
103
|
-
interface TransitionTracingCallbacks {
|
|
104
|
-
onTransitionStart?: (transitionName: string, startTime: number) => void;
|
|
105
|
-
onTransitionProgress?: (transitionName: string, startTime: number, currentTime: number, pending: Array<{
|
|
106
|
-
name: null | string;
|
|
107
|
-
}>) => void;
|
|
108
|
-
onTransitionIncomplete?: (transitionName: string, startTime: number, deletions: Array<{
|
|
109
|
-
type: string;
|
|
110
|
-
name?: string;
|
|
111
|
-
newName?: string;
|
|
112
|
-
endTime: number;
|
|
113
|
-
}>) => void;
|
|
114
|
-
onTransitionComplete?: (transitionName: string, startTime: number, endTime: number) => void;
|
|
115
|
-
onMarkerProgress?: (transitionName: string, marker: string, startTime: number, currentTime: number, pending: Array<{
|
|
116
|
-
name: null | string;
|
|
117
|
-
}>) => void;
|
|
118
|
-
onMarkerIncomplete?: (transitionName: string, marker: string, startTime: number, deletions: Array<{
|
|
119
|
-
type: string;
|
|
120
|
-
name?: string;
|
|
121
|
-
newName?: string;
|
|
122
|
-
endTime: number;
|
|
123
|
-
}>) => void;
|
|
124
|
-
onMarkerComplete?: (transitionName: string, marker: string, startTime: number, endTime: number) => void;
|
|
125
|
-
}
|
|
126
|
-
interface ReactFiber {
|
|
127
|
-
tag: WorkTag;
|
|
128
|
-
key: null | string;
|
|
129
|
-
elementType: any;
|
|
130
|
-
type: any;
|
|
131
|
-
stateNode: any;
|
|
132
|
-
return: ReactFiber | null;
|
|
133
|
-
child: ReactFiber | null;
|
|
134
|
-
sibling: ReactFiber | null;
|
|
135
|
-
index: number;
|
|
136
|
-
ref: null | (((handle: unknown) => void) & {
|
|
137
|
-
_stringRef?: string | null;
|
|
138
|
-
}) | RefObject;
|
|
139
|
-
pendingProps: any;
|
|
140
|
-
memoizedProps: any;
|
|
141
|
-
updateQueue: unknown;
|
|
142
|
-
memoizedState: any;
|
|
143
|
-
dependencies: Dependencies | null;
|
|
144
|
-
mode: TypeOfMode;
|
|
145
|
-
flags: Flags;
|
|
146
|
-
subtreeFlags: Flags;
|
|
147
|
-
deletions: ReactFiber[] | null;
|
|
148
|
-
nextEffect: ReactFiber | null;
|
|
149
|
-
firstEffect: ReactFiber | null;
|
|
150
|
-
lastEffect: ReactFiber | null;
|
|
151
|
-
lanes: Lanes;
|
|
152
|
-
childLanes: Lanes;
|
|
153
|
-
alternate: ReactFiber | null;
|
|
154
|
-
actualDuration?: number;
|
|
155
|
-
actualStartTime?: number;
|
|
156
|
-
selfBaseDuration?: number;
|
|
157
|
-
treeBaseDuration?: number;
|
|
158
|
-
_debugID?: number;
|
|
159
|
-
_debugSource?: Source | null;
|
|
160
|
-
_debugOwner?: ReactFiber | null;
|
|
161
|
-
_debugIsCurrentlyTiming?: boolean;
|
|
162
|
-
_debugNeedsRemount?: boolean;
|
|
163
|
-
_debugHookTypes?: HookType[] | null;
|
|
164
|
-
}
|
|
165
|
-
interface ContextDependency<T> {
|
|
166
|
-
context: ReactContext<T>;
|
|
167
|
-
memoizedValue: T;
|
|
168
|
-
next: ContextDependency<unknown> | null;
|
|
169
|
-
observedBits: number;
|
|
170
|
-
}
|
|
171
|
-
interface Dependencies {
|
|
172
|
-
firstContext: ContextDependency<unknown> | null;
|
|
173
|
-
lanes: Lanes;
|
|
174
|
-
}
|
|
175
|
-
interface Effect {
|
|
176
|
-
[key: string]: unknown;
|
|
177
|
-
create: (...args: unknown[]) => unknown;
|
|
178
|
-
deps: null | unknown[];
|
|
179
|
-
destroy: ((...args: unknown[]) => unknown) | null;
|
|
180
|
-
next: Effect | null;
|
|
181
|
-
tag: number;
|
|
182
|
-
}
|
|
183
|
-
interface Family {
|
|
184
|
-
current: unknown;
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* React 19 flight metadata for a server component owner (ReactComponentInfo).
|
|
188
|
-
* Unlike client owners it has no `tag`; the owner chain continues via `owner`.
|
|
189
|
-
*/
|
|
190
|
-
interface ServerComponentInfo {
|
|
191
|
-
name?: string;
|
|
192
|
-
env?: string;
|
|
193
|
-
owner?: Fiber | ServerComponentInfo | null;
|
|
194
|
-
debugStack?: Error | null;
|
|
195
|
-
}
|
|
196
|
-
interface RendererRefreshUpdate {
|
|
197
|
-
staleFamilies: Set<Family>;
|
|
198
|
-
updatedFamilies: Set<Family>;
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Represents a react-internal Fiber node.
|
|
202
|
-
*/
|
|
203
|
-
type Fiber<T = any> = Omit<ReactFiber, "alternate" | "child" | "dependencies" | "memoizedProps" | "memoizedState" | "pendingProps" | "return" | "sibling" | "stateNode" | "updateQueue"> & {
|
|
204
|
-
_debugInfo?: Array<{
|
|
205
|
-
debugLocation?: unknown;
|
|
206
|
-
env?: string;
|
|
207
|
-
name?: string;
|
|
208
|
-
}>;
|
|
209
|
-
_debugOwner?: Fiber;
|
|
210
|
-
_debugSource?: {
|
|
211
|
-
columnNumber?: number;
|
|
212
|
-
fileName: string;
|
|
213
|
-
lineNumber: number;
|
|
214
|
-
};
|
|
215
|
-
_debugStack?: Error & {
|
|
216
|
-
stack: string;
|
|
217
|
-
};
|
|
218
|
-
alternate: Fiber | null;
|
|
219
|
-
child: Fiber | null;
|
|
220
|
-
dependencies: Dependencies | null;
|
|
221
|
-
memoizedProps: Props;
|
|
222
|
-
memoizedState: MemoizedState;
|
|
223
|
-
pendingProps: Props;
|
|
224
|
-
return: Fiber | null;
|
|
225
|
-
sibling: Fiber | null;
|
|
226
|
-
stateNode: T;
|
|
227
|
-
updateQueue: {
|
|
228
|
-
[key: string]: unknown;
|
|
229
|
-
lastEffect: Effect | null;
|
|
230
|
-
};
|
|
231
|
-
};
|
|
232
|
-
interface MemoizedState {
|
|
233
|
-
[key: string]: unknown;
|
|
234
|
-
memoizedState: unknown;
|
|
235
|
-
next: MemoizedState | null;
|
|
236
|
-
}
|
|
237
|
-
interface Props {
|
|
238
|
-
[key: string]: unknown;
|
|
239
|
-
}
|
|
240
|
-
interface ReactDevToolsGlobalHook {
|
|
241
|
-
_instrumentationIsActive?: boolean;
|
|
242
|
-
_instrumentationSource?: string;
|
|
243
|
-
checkDCE: (fn: unknown) => void;
|
|
244
|
-
hasUnsupportedRendererAttached: boolean;
|
|
245
|
-
inject: (renderer: ReactRenderer) => number;
|
|
246
|
-
on: () => void;
|
|
247
|
-
onCommitFiberRoot: (rendererID: number, root: FiberRoot, priority: number | void) => void;
|
|
248
|
-
onCommitFiberUnmount: (rendererID: number, fiber: Fiber) => void;
|
|
249
|
-
onPostCommitFiberRoot: (rendererID: number, root: FiberRoot) => void;
|
|
250
|
-
onScheduleFiberRoot?: (rendererID: number, root: FiberRoot, children: ReactNode) => void;
|
|
251
|
-
renderers: Map<number, ReactRenderer>;
|
|
252
|
-
supportsFiber: boolean;
|
|
253
|
-
supportsFlight: boolean;
|
|
254
|
-
}
|
|
255
|
-
interface ReactRenderer {
|
|
256
|
-
bundleType: 0 | 1;
|
|
257
|
-
currentDispatcherRef: any;
|
|
258
|
-
findFiberByHostInstance?: (hostInstance: unknown) => Fiber | null;
|
|
259
|
-
getCurrentFiber?: (fiber: Fiber) => Fiber | null;
|
|
260
|
-
overrideContext?: (fiber: Fiber, contextType: unknown, path: string[], value: unknown) => void;
|
|
261
|
-
overrideHookState?: (fiber: Fiber, id: string, path: string[], value: unknown) => void;
|
|
262
|
-
overrideHookStateDeletePath?: (fiber: Fiber, id: number, path: Array<number | string>) => void;
|
|
263
|
-
overrideHookStateRenamePath?: (fiber: Fiber, id: number, oldPath: Array<number | string>, newPath: Array<number | string>) => void;
|
|
264
|
-
overrideProps?: (fiber: Fiber, path: string[], value: unknown) => void;
|
|
265
|
-
overridePropsDeletePath?: (fiber: Fiber, path: Array<number | string>) => void;
|
|
266
|
-
overridePropsRenamePath?: (fiber: Fiber, oldPath: Array<number | string>, newPath: Array<number | string>) => void;
|
|
267
|
-
reconcilerVersion: string;
|
|
268
|
-
rendererPackageName: string;
|
|
269
|
-
scheduleRefresh?: (root: FiberRoot, update: RendererRefreshUpdate) => void;
|
|
270
|
-
scheduleRoot?: (root: FiberRoot, element: React.ReactNode) => void;
|
|
271
|
-
scheduleUpdate?: (fiber: Fiber) => void;
|
|
272
|
-
setErrorHandler?: (newShouldErrorImpl: (fiber: Fiber) => boolean) => void;
|
|
273
|
-
setRefreshHandler?: (handler: ((fiber: Fiber) => Family | null) | null) => void;
|
|
274
|
-
setSuspenseHandler?: (newShouldSuspendImpl: (suspenseInstance: unknown) => void) => void;
|
|
275
|
-
version: string;
|
|
276
|
-
}
|
|
277
|
-
declare global {
|
|
278
|
-
var __REACT_DEVTOOLS_GLOBAL_HOOK__: ReactDevToolsGlobalHook | undefined;
|
|
279
|
-
}
|
|
280
|
-
//#endregion
|
|
281
|
-
//#region src/unsubscribe.d.ts
|
|
282
|
-
interface Unsubscribe extends Disposable {
|
|
283
|
-
(): void;
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Wraps a teardown callback so it is both callable and a `Disposable`,
|
|
287
|
-
* letting subscriptions compose through explicit resource management:
|
|
288
|
-
*
|
|
289
|
-
* @example
|
|
290
|
-
* ```ts
|
|
291
|
-
* using instrumentation = instrument({ onCommitFiberRoot });
|
|
292
|
-
* using refresh = instrumentReactRefresh({ onRefresh: handleRefresh });
|
|
293
|
-
* // both torn down automatically at scope exit
|
|
294
|
-
* ```
|
|
295
|
-
*/
|
|
296
|
-
declare const toUnsubscribe: (dispose: () => void) => Unsubscribe;
|
|
297
|
-
//#endregion
|
|
298
|
-
export { ReactRenderer as A, TextSelector as B, React$AbstractComponent as C, ReactPortal as D, ReactDevToolsGlobalHook as E, Selector as F, TransitionTracingCallbacks as H, ServerComponentInfo as I, Source as L, RendererRefreshUpdate as M, RoleSelector as N, ReactProvider as O, RootTag as P, SuspenseHydrationCallbacks as R, Props as S, ReactContext as T, TypeOfMode as U, Thenable as V, WorkTag as W, Lanes as _, ContextDependency as a, OpaqueHandle as b, Effect as c, FiberRoot as d, Flags as f, LanePriority as g, HostConfig as h, ComponentSelector as i, RefObject as j, ReactProviderType as k, Family as l, HookType as m, toUnsubscribe as n, Dependencies as o, HasPseudoClassSelector as p, BundleType as r, DevToolsConfig as s, Unsubscribe as t, Fiber as u, MemoizedState as v, ReactConsumer as w, OpaqueRoot as x, MutableSource as y, TestNameSelector as z };
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export const HMR_RECONNECT_DELAY_MS = 1000;
|
|
2
|
-
|
|
3
|
-
export const PENDING_HOT_UPDATE_MAX_AGE_MS = 10_000;
|
|
4
|
-
|
|
5
|
-
export const VITE_WS_TOKEN_REGEX = /wsToken = "([^"]+)"/;
|
|
6
|
-
|
|
7
|
-
export const HMR_SOURCE_FILE_EXTENSION_REGEX = /\.(?:tsx|ts|jsx|js|mjs|cjs|css)$/;
|
|
8
|
-
|
|
9
|
-
export const BUNDLER_LAYER_PREFIX_REGEX = /^(?:\.\/)?\/?\([a-z][a-z0-9-]*\)\//;
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { isClientEnvironment } from "../rdt-hook.js";
|
|
2
|
-
|
|
3
|
-
import { createMetroHmrTransport } from "./metro-hmr-transport.js";
|
|
4
|
-
import { createNextWebpackHmrTransport } from "./next-webpack-hmr-transport.js";
|
|
5
|
-
import { HmrTransport, HmrUpdateHandler } from "./types.js";
|
|
6
|
-
import { createViteHmrTransport } from "./vite-hmr-transport.js";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Detects the dev server's HMR transport (Next.js webpack, then Metro for
|
|
10
|
-
* React Native, then Vite) and subscribes `onHmrUpdate` to hot updates.
|
|
11
|
-
* Resolves `null` on the server (SSR) and when no known transport is
|
|
12
|
-
* available (production builds, unsupported bundlers — Turbopack exposes
|
|
13
|
-
* `window.TURBOPACK_CHUNK_UPDATE_LISTENERS` but its update payload shape
|
|
14
|
-
* has not been validated, so it is not wired up yet).
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```ts
|
|
18
|
-
* const transport = await detectHmrTransport((filePaths) => {
|
|
19
|
-
* console.log("hot updated:", filePaths);
|
|
20
|
-
* });
|
|
21
|
-
* transport?.dispose();
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
export const detectHmrTransport = async (
|
|
25
|
-
onHmrUpdate: HmrUpdateHandler,
|
|
26
|
-
): Promise<HmrTransport | null> => {
|
|
27
|
-
if (!isClientEnvironment()) return null;
|
|
28
|
-
const webpackTransport = createNextWebpackHmrTransport(onHmrUpdate);
|
|
29
|
-
if (webpackTransport) return webpackTransport;
|
|
30
|
-
const metroTransport = createMetroHmrTransport(onHmrUpdate);
|
|
31
|
-
if (metroTransport) return metroTransport;
|
|
32
|
-
return createViteHmrTransport(onHmrUpdate);
|
|
33
|
-
};
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
import { getType, traverseFiber } from "../core.js";
|
|
2
|
-
import { getRDTHook, isClientEnvironment, onRendererInject } from "../rdt-hook.js";
|
|
3
|
-
import type { Fiber, FiberRoot, ReactRenderer } from "../types.js";
|
|
4
|
-
import { toUnsubscribe, type Unsubscribe } from "../unsubscribe.js";
|
|
5
|
-
import { PENDING_HOT_UPDATE_MAX_AGE_MS } from "./constants.js";
|
|
6
|
-
import { detectHmrTransport } from "./detect-hmr-transport.js";
|
|
7
|
-
import { HmrTransport } from "./types.js";
|
|
8
|
-
|
|
9
|
-
export interface ReactRefreshUpdate {
|
|
10
|
-
/**
|
|
11
|
-
* hot-updated source file paths reported by the bundler's HMR transport
|
|
12
|
-
* (auto-detected: Next.js webpack, Metro, Vite). Best-effort: empty when
|
|
13
|
-
* the bundler does not expose a transport (e.g. Turbopack) or when the
|
|
14
|
-
* transport message has not arrived yet (e.g. Metro delivers updates on
|
|
15
|
-
* an independent socket).
|
|
16
|
-
*/
|
|
17
|
-
filePaths: string[];
|
|
18
|
-
root: FiberRoot;
|
|
19
|
-
/** new component types that were remounted, losing state */
|
|
20
|
-
staleComponents: unknown[];
|
|
21
|
-
/** mounted fibers whose component types were remounted */
|
|
22
|
-
staleFibers: Fiber[];
|
|
23
|
-
/** new component types that re-rendered preserving state */
|
|
24
|
-
updatedComponents: unknown[];
|
|
25
|
-
/** mounted fibers whose component types re-rendered preserving state */
|
|
26
|
-
updatedFibers: Fiber[];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface ReactRefreshHandler {
|
|
30
|
-
(update: ReactRefreshUpdate): void;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface ReactRefreshInstrumentationOptions {
|
|
34
|
-
onRefresh?: ReactRefreshHandler;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const collectFibersByComponentType = (root: FiberRoot, componentTypes: Set<unknown>): Fiber[] => {
|
|
38
|
-
if (componentTypes.size === 0 || !root.current) return [];
|
|
39
|
-
const matchedFibers: Fiber[] = [];
|
|
40
|
-
traverseFiber(root.current, (fiber) => {
|
|
41
|
-
// memo/forwardRef fibers carry the wrapper as fiber.type, while the
|
|
42
|
-
// refresh families can register either the wrapper or the inner type
|
|
43
|
-
if (componentTypes.has(fiber.type) || componentTypes.has(getType(fiber.type))) {
|
|
44
|
-
matchedFibers.push(fiber);
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
return matchedFibers;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const refreshHandlers = new Set<ReactRefreshHandler>();
|
|
51
|
-
const refreshWrappedRenderers = new WeakSet<ReactRenderer>();
|
|
52
|
-
|
|
53
|
-
let pendingFilePaths: string[] = [];
|
|
54
|
-
let pendingFilePathsReceivedAtMs = 0;
|
|
55
|
-
|
|
56
|
-
const bufferHotUpdateFilePaths = (filePaths: string[]): void => {
|
|
57
|
-
const nowMs = Date.now();
|
|
58
|
-
if (nowMs - pendingFilePathsReceivedAtMs > PENDING_HOT_UPDATE_MAX_AGE_MS) {
|
|
59
|
-
pendingFilePaths = [];
|
|
60
|
-
}
|
|
61
|
-
pendingFilePaths.push(...filePaths);
|
|
62
|
-
pendingFilePathsReceivedAtMs = nowMs;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const takeFreshFilePaths = (): string[] => {
|
|
66
|
-
if (Date.now() - pendingFilePathsReceivedAtMs > PENDING_HOT_UPDATE_MAX_AGE_MS) return [];
|
|
67
|
-
const freshFilePaths = [...pendingFilePaths];
|
|
68
|
-
// performReactRefresh calls scheduleRefresh synchronously once per
|
|
69
|
-
// mounted root, so clear the pending paths only after the whole
|
|
70
|
-
// refresh pass instead of on the first root
|
|
71
|
-
queueMicrotask(() => {
|
|
72
|
-
pendingFilePaths = [];
|
|
73
|
-
});
|
|
74
|
-
return freshFilePaths;
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const wrapRendererScheduleRefresh = (renderer: ReactRenderer): void => {
|
|
78
|
-
if (refreshWrappedRenderers.has(renderer)) return;
|
|
79
|
-
const originalScheduleRefresh = renderer.scheduleRefresh;
|
|
80
|
-
if (typeof originalScheduleRefresh !== "function") return;
|
|
81
|
-
refreshWrappedRenderers.add(renderer);
|
|
82
|
-
renderer.scheduleRefresh = (root, update) => {
|
|
83
|
-
originalScheduleRefresh.call(renderer, root, update);
|
|
84
|
-
if (refreshHandlers.size === 0) return;
|
|
85
|
-
const staleComponents = Array.from(update.staleFamilies, (family) => family.current);
|
|
86
|
-
const updatedComponents = Array.from(update.updatedFamilies, (family) => family.current);
|
|
87
|
-
const refreshUpdate: ReactRefreshUpdate = {
|
|
88
|
-
filePaths: takeFreshFilePaths(),
|
|
89
|
-
root,
|
|
90
|
-
staleComponents,
|
|
91
|
-
staleFibers: collectFibersByComponentType(root, new Set(staleComponents)),
|
|
92
|
-
updatedComponents,
|
|
93
|
-
updatedFibers: collectFibersByComponentType(root, new Set(updatedComponents)),
|
|
94
|
-
};
|
|
95
|
-
for (const handler of refreshHandlers) {
|
|
96
|
-
handler(refreshUpdate);
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
let isRefreshWired = false;
|
|
102
|
-
|
|
103
|
-
const ensureRefreshWired = (): void => {
|
|
104
|
-
if (isRefreshWired) return;
|
|
105
|
-
isRefreshWired = true;
|
|
106
|
-
const rdtHook = getRDTHook();
|
|
107
|
-
for (const renderer of rdtHook.renderers.values()) {
|
|
108
|
-
wrapRendererScheduleRefresh(renderer);
|
|
109
|
-
}
|
|
110
|
-
onRendererInject(wrapRendererScheduleRefresh);
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
let activeTransport: HmrTransport | null = null;
|
|
114
|
-
|
|
115
|
-
// the bundler's HMR global may not exist yet when an early subscriber
|
|
116
|
-
// arrives, so detection is retried whenever the first subscriber (re)appears
|
|
117
|
-
const detectTransportForNewSubscriber = (): void => {
|
|
118
|
-
void detectHmrTransport(bufferHotUpdateFilePaths).then((detectedTransport) => {
|
|
119
|
-
if (!detectedTransport) return;
|
|
120
|
-
activeTransport?.dispose();
|
|
121
|
-
activeTransport = detectedTransport;
|
|
122
|
-
});
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Subscribes to react-refresh (fast refresh) updates by wrapping
|
|
127
|
-
* `scheduleRefresh` on every renderer injected into the React DevTools
|
|
128
|
-
* global hook. The react-refresh runtime calls `scheduleRefresh` after each
|
|
129
|
-
* hot update, so this works with any bundler that uses react-refresh (Vite,
|
|
130
|
-
* Next.js webpack, Next.js Turbopack, Metro) without bundler-specific code.
|
|
131
|
-
* Returns an unsubscribe function (a no-op in non-client environments like
|
|
132
|
-
* SSR, so callers never need an environment check). The returned function
|
|
133
|
-
* is also a `Disposable`, so it composes with other bippy subscriptions
|
|
134
|
-
* through `using`.
|
|
135
|
-
*
|
|
136
|
-
* The bundler's HMR transport is auto-detected and each refresh update is
|
|
137
|
-
* augmented with the hot-updated source file paths it reported.
|
|
138
|
-
*
|
|
139
|
-
* The handler runs after React has re-rendered with the new component
|
|
140
|
-
* types, so the refreshed root's fiber tree already carries them;
|
|
141
|
-
* `updatedFibers`/`staleFibers` are the mounted fibers matching the
|
|
142
|
-
* hot-swapped component types.
|
|
143
|
-
*
|
|
144
|
-
* @example
|
|
145
|
-
* ```ts
|
|
146
|
-
* const unsubscribe = instrumentReactRefresh({
|
|
147
|
-
* onRefresh(update) {
|
|
148
|
-
* for (const fiber of update.updatedFibers) {
|
|
149
|
-
* console.log("hot updated:", getDisplayName(fiber.type));
|
|
150
|
-
* }
|
|
151
|
-
* console.log("changed files:", update.filePaths);
|
|
152
|
-
* },
|
|
153
|
-
* });
|
|
154
|
-
* unsubscribe();
|
|
155
|
-
* ```
|
|
156
|
-
*
|
|
157
|
-
* Pair with `getSource(fiber)` from `bippy/source` to symbolicate the
|
|
158
|
-
* source locations of `updatedFibers` when needed.
|
|
159
|
-
*/
|
|
160
|
-
export const instrumentReactRefresh = (
|
|
161
|
-
options: ReactRefreshInstrumentationOptions,
|
|
162
|
-
): Unsubscribe => {
|
|
163
|
-
const { onRefresh } = options;
|
|
164
|
-
if (!onRefresh || !isClientEnvironment()) return toUnsubscribe(() => {});
|
|
165
|
-
ensureRefreshWired();
|
|
166
|
-
if (refreshHandlers.size === 0) {
|
|
167
|
-
detectTransportForNewSubscriber();
|
|
168
|
-
}
|
|
169
|
-
refreshHandlers.add(onRefresh);
|
|
170
|
-
return toUnsubscribe(() => {
|
|
171
|
-
refreshHandlers.delete(onRefresh);
|
|
172
|
-
});
|
|
173
|
-
};
|