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
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
import { HMR_RECONNECT_DELAY_MS } from "./constants.js";
|
|
2
|
-
import { HmrTransport, HmrUpdateHandler } from "./types.js";
|
|
3
|
-
|
|
4
|
-
declare global {
|
|
5
|
-
var __turboModuleProxy: ((moduleName: string) => unknown) | undefined;
|
|
6
|
-
var nativeModuleProxy: Record<string, unknown> | undefined;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const getScriptUrlFromSourceCodeModule = (sourceCodeModule: unknown): string | null => {
|
|
10
|
-
if (typeof sourceCodeModule !== "object" || sourceCodeModule === null) return null;
|
|
11
|
-
if (!("getConstants" in sourceCodeModule)) return null;
|
|
12
|
-
const getConstants = sourceCodeModule.getConstants;
|
|
13
|
-
if (typeof getConstants !== "function") return null;
|
|
14
|
-
let constants: unknown;
|
|
15
|
-
try {
|
|
16
|
-
constants = getConstants.call(sourceCodeModule);
|
|
17
|
-
} catch {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
if (typeof constants !== "object" || constants === null) return null;
|
|
21
|
-
if (!("scriptURL" in constants)) return null;
|
|
22
|
-
const scriptUrl = constants.scriptURL;
|
|
23
|
-
return typeof scriptUrl === "string" ? scriptUrl : null;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Resolves the URL the running React Native bundle was loaded from, using
|
|
28
|
-
* the same `SourceCode` native module React Native's own dev tooling reads
|
|
29
|
-
* (via the TurboModule proxy globals, so react-native is not imported).
|
|
30
|
-
* Returns `null` outside a Metro-served React Native runtime.
|
|
31
|
-
*
|
|
32
|
-
* @example
|
|
33
|
-
* ```ts
|
|
34
|
-
* getMetroBundleUrl();
|
|
35
|
-
* // "http://localhost:8081/index.bundle?platform=ios&dev=true"
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
export const getMetroBundleUrl = (): string | null => {
|
|
39
|
-
if (typeof globalThis.__turboModuleProxy === "function") {
|
|
40
|
-
let sourceCodeModule: unknown;
|
|
41
|
-
try {
|
|
42
|
-
sourceCodeModule = globalThis.__turboModuleProxy("SourceCode");
|
|
43
|
-
} catch {
|
|
44
|
-
sourceCodeModule = null;
|
|
45
|
-
}
|
|
46
|
-
const scriptUrl = getScriptUrlFromSourceCodeModule(sourceCodeModule);
|
|
47
|
-
if (scriptUrl) return scriptUrl;
|
|
48
|
-
}
|
|
49
|
-
const legacySourceCodeModule = globalThis.nativeModuleProxy?.SourceCode;
|
|
50
|
-
return getScriptUrlFromSourceCodeModule(legacySourceCodeModule);
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
// HACK: module sourceURLs may be JSC-safe URLs where "//&" stands in for
|
|
54
|
-
// "?" (iOS 16.4 stack traces strip query strings), so the query separator
|
|
55
|
-
// must be normalized before URL parsing.
|
|
56
|
-
const normalizeJscSafeUrl = (jscSafeUrl: string): string => jscSafeUrl.replace("//&", "?");
|
|
57
|
-
|
|
58
|
-
const getSourcePathFromSourceUrl = (sourceUrl: string): string | null => {
|
|
59
|
-
let parsedUrl: URL;
|
|
60
|
-
try {
|
|
61
|
-
parsedUrl = new URL(normalizeJscSafeUrl(sourceUrl));
|
|
62
|
-
} catch {
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
let sourcePath = decodeURIComponent(parsedUrl.pathname);
|
|
66
|
-
if (sourcePath.startsWith("/")) sourcePath = sourcePath.slice(1);
|
|
67
|
-
// Metro rewrites each module's real extension to ".bundle" when building
|
|
68
|
-
// hot-update sourceURLs, so the original extension is unrecoverable.
|
|
69
|
-
if (sourcePath.endsWith(".bundle")) sourcePath = sourcePath.slice(0, -".bundle".length);
|
|
70
|
-
return sourcePath.length > 0 ? sourcePath : null;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const collectModuleSourcePaths = (hmrModules: unknown, filePaths: string[]) => {
|
|
74
|
-
if (!Array.isArray(hmrModules)) return;
|
|
75
|
-
for (const hmrModule of hmrModules) {
|
|
76
|
-
if (typeof hmrModule !== "object" || hmrModule === null) continue;
|
|
77
|
-
if (!("sourceURL" in hmrModule) || typeof hmrModule.sourceURL !== "string") continue;
|
|
78
|
-
const sourcePath = getSourcePathFromSourceUrl(hmrModule.sourceURL);
|
|
79
|
-
if (!sourcePath || sourcePath.includes("node_modules")) continue;
|
|
80
|
-
filePaths.push(sourcePath);
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Extracts the updated source file paths from a raw Metro HMR WebSocket
|
|
86
|
-
* message. Paths are project-relative but extension-less (`src/app`, not
|
|
87
|
-
* `src/app.tsx`) because Metro rewrites module extensions to `.bundle`.
|
|
88
|
-
* The initial update replayed on connect is skipped. Returns an empty
|
|
89
|
-
* array for any other message shape.
|
|
90
|
-
*
|
|
91
|
-
* @example
|
|
92
|
-
* ```ts
|
|
93
|
-
* parseMetroUpdatePaths(rawMessageData);
|
|
94
|
-
* // ["src/app"]
|
|
95
|
-
* ```
|
|
96
|
-
*/
|
|
97
|
-
export const parseMetroUpdatePaths = (rawMessageData: string): string[] => {
|
|
98
|
-
let message: unknown;
|
|
99
|
-
try {
|
|
100
|
-
message = JSON.parse(rawMessageData);
|
|
101
|
-
} catch {
|
|
102
|
-
return [];
|
|
103
|
-
}
|
|
104
|
-
if (typeof message !== "object" || message === null) return [];
|
|
105
|
-
if (!("type" in message) || message.type !== "update") return [];
|
|
106
|
-
if (!("body" in message) || typeof message.body !== "object" || message.body === null) return [];
|
|
107
|
-
const updateBody = message.body;
|
|
108
|
-
if ("isInitialUpdate" in updateBody && updateBody.isInitialUpdate === true) return [];
|
|
109
|
-
const filePaths: string[] = [];
|
|
110
|
-
if ("added" in updateBody) collectModuleSourcePaths(updateBody.added, filePaths);
|
|
111
|
-
if ("modified" in updateBody) collectModuleSourcePaths(updateBody.modified, filePaths);
|
|
112
|
-
return filePaths;
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
export interface MetroHmrTransportOptions {
|
|
116
|
-
bundleUrl?: string;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Subscribes to the Metro dev server's `/hot` HMR WebSocket (as a second
|
|
121
|
-
* client alongside React Native's own) and invokes `onHmrUpdate` with the
|
|
122
|
-
* updated file paths on every hot update. Reconnects automatically when
|
|
123
|
-
* the dev server restarts. Returns `null` when no Metro bundle URL can be
|
|
124
|
-
* resolved (production builds, non-Metro runtimes).
|
|
125
|
-
*
|
|
126
|
-
* @example
|
|
127
|
-
* ```ts
|
|
128
|
-
* const transport = createMetroHmrTransport((filePaths) => {
|
|
129
|
-
* console.log("hot updated:", filePaths);
|
|
130
|
-
* });
|
|
131
|
-
* transport?.dispose();
|
|
132
|
-
* ```
|
|
133
|
-
*/
|
|
134
|
-
export const createMetroHmrTransport = (
|
|
135
|
-
onHmrUpdate: HmrUpdateHandler,
|
|
136
|
-
options: MetroHmrTransportOptions = {},
|
|
137
|
-
): HmrTransport | null => {
|
|
138
|
-
if (typeof WebSocket === "undefined") return null;
|
|
139
|
-
const bundleUrl = options.bundleUrl ?? getMetroBundleUrl();
|
|
140
|
-
if (!bundleUrl) return null;
|
|
141
|
-
|
|
142
|
-
let hotSocketUrl: string;
|
|
143
|
-
try {
|
|
144
|
-
const parsedBundleUrl = new URL(bundleUrl);
|
|
145
|
-
const socketProtocol = parsedBundleUrl.protocol === "https:" ? "wss" : "ws";
|
|
146
|
-
hotSocketUrl = `${socketProtocol}://${parsedBundleUrl.host}/hot`;
|
|
147
|
-
} catch {
|
|
148
|
-
return null;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
let isDisposed = false;
|
|
152
|
-
let socket: WebSocket | null = null;
|
|
153
|
-
let reconnectTimerId: ReturnType<typeof setTimeout> | undefined;
|
|
154
|
-
|
|
155
|
-
const scheduleReconnect = () => {
|
|
156
|
-
if (isDisposed) return;
|
|
157
|
-
reconnectTimerId = setTimeout(connect, HMR_RECONNECT_DELAY_MS);
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
const connect = () => {
|
|
161
|
-
if (isDisposed) return;
|
|
162
|
-
const connectedSocket = new WebSocket(hotSocketUrl);
|
|
163
|
-
socket = connectedSocket;
|
|
164
|
-
connectedSocket.onopen = () => {
|
|
165
|
-
connectedSocket.send(
|
|
166
|
-
JSON.stringify({ type: "register-entrypoints", entryPoints: [bundleUrl] }),
|
|
167
|
-
);
|
|
168
|
-
};
|
|
169
|
-
connectedSocket.onmessage = (event) => {
|
|
170
|
-
const filePaths = parseMetroUpdatePaths(String(event.data));
|
|
171
|
-
if (filePaths.length > 0) onHmrUpdate(filePaths);
|
|
172
|
-
};
|
|
173
|
-
connectedSocket.onclose = scheduleReconnect;
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
connect();
|
|
177
|
-
|
|
178
|
-
return {
|
|
179
|
-
dispose: () => {
|
|
180
|
-
isDisposed = true;
|
|
181
|
-
clearTimeout(reconnectTimerId);
|
|
182
|
-
if (socket) {
|
|
183
|
-
socket.onclose = null;
|
|
184
|
-
socket.close();
|
|
185
|
-
}
|
|
186
|
-
},
|
|
187
|
-
};
|
|
188
|
-
};
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { HMR_SOURCE_FILE_EXTENSION_REGEX } from "./constants.js";
|
|
2
|
-
import { normalizeHmrFilePath } from "./normalize-hmr-file-path.js";
|
|
3
|
-
import { HmrTransport, HmrUpdateHandler } from "./types.js";
|
|
4
|
-
|
|
5
|
-
interface WebpackHotUpdateGlobal {
|
|
6
|
-
(chunkId: unknown, updatedModules: Record<string, unknown> | undefined, runtime: unknown): void;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
declare global {
|
|
10
|
-
interface Window {
|
|
11
|
-
webpackHotUpdate_N_E?: WebpackHotUpdateGlobal;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Normalizes webpack hot-update module keys into project-relative source
|
|
17
|
-
* file paths, dropping node_modules entries and non-source keys (e.g.
|
|
18
|
-
* webpack runtime helpers).
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* ```ts
|
|
22
|
-
* normalizeWebpackModulePaths(["(app-pages-browser)/./app/page.tsx"]);
|
|
23
|
-
* // ["app/page.tsx"]
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
export const normalizeWebpackModulePaths = (moduleKeys: string[]): string[] => {
|
|
27
|
-
const filePaths: string[] = [];
|
|
28
|
-
for (const moduleKey of moduleKeys) {
|
|
29
|
-
if (moduleKey.includes("node_modules")) continue;
|
|
30
|
-
const filePath = normalizeHmrFilePath(moduleKey);
|
|
31
|
-
if (!HMR_SOURCE_FILE_EXTENSION_REGEX.test(filePath)) continue;
|
|
32
|
-
filePaths.push(filePath);
|
|
33
|
-
}
|
|
34
|
-
return filePaths;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Subscribes to Next.js webpack hot updates by wrapping the
|
|
39
|
-
* `webpackHotUpdate_N_E` global and invokes `onHmrUpdate` with the updated
|
|
40
|
-
* file paths. Returns `null` when the page is not a Next.js webpack dev
|
|
41
|
-
* build.
|
|
42
|
-
*
|
|
43
|
-
* @example
|
|
44
|
-
* ```ts
|
|
45
|
-
* const transport = createNextWebpackHmrTransport((filePaths) => {
|
|
46
|
-
* console.log("hot updated:", filePaths);
|
|
47
|
-
* });
|
|
48
|
-
* transport?.dispose();
|
|
49
|
-
* ```
|
|
50
|
-
*/
|
|
51
|
-
export const createNextWebpackHmrTransport = (
|
|
52
|
-
onHmrUpdate: HmrUpdateHandler,
|
|
53
|
-
): HmrTransport | null => {
|
|
54
|
-
if (typeof window === "undefined") return null;
|
|
55
|
-
const originalHotUpdate = window.webpackHotUpdate_N_E;
|
|
56
|
-
if (typeof originalHotUpdate !== "function") return null;
|
|
57
|
-
|
|
58
|
-
const wrappedHotUpdate: WebpackHotUpdateGlobal = (chunkId, updatedModules, runtime) => {
|
|
59
|
-
const filePaths = normalizeWebpackModulePaths(Object.keys(updatedModules ?? {}));
|
|
60
|
-
if (filePaths.length > 0) onHmrUpdate(filePaths);
|
|
61
|
-
originalHotUpdate(chunkId, updatedModules, runtime);
|
|
62
|
-
};
|
|
63
|
-
window.webpackHotUpdate_N_E = wrappedHotUpdate;
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
dispose: () => {
|
|
67
|
-
if (window.webpackHotUpdate_N_E === wrappedHotUpdate) {
|
|
68
|
-
window.webpackHotUpdate_N_E = originalHotUpdate;
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { normalizeFileName } from "../source/get-source.js";
|
|
2
|
-
|
|
3
|
-
import { BUNDLER_LAYER_PREFIX_REGEX } from "./constants.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Normalizes a bundler module key or HMR update path into a plain,
|
|
7
|
-
* project-relative file path. Strips URL schemes (via
|
|
8
|
-
* {@link normalizeFileName}), bundler layer prefixes like
|
|
9
|
-
* `(app-pages-browser)/`, and leading `./` segments.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```ts
|
|
13
|
-
* normalizeHmrFilePath("(app-pages-browser)/./app/page.tsx");
|
|
14
|
-
* // "app/page.tsx"
|
|
15
|
-
* ```
|
|
16
|
-
*/
|
|
17
|
-
export const normalizeHmrFilePath = (filePath: string): string => {
|
|
18
|
-
let normalizedFilePath = normalizeFileName(filePath);
|
|
19
|
-
normalizedFilePath = normalizedFilePath.replace(BUNDLER_LAYER_PREFIX_REGEX, "");
|
|
20
|
-
if (normalizedFilePath.startsWith("./")) {
|
|
21
|
-
normalizedFilePath = normalizedFilePath.slice(2);
|
|
22
|
-
}
|
|
23
|
-
return normalizedFilePath;
|
|
24
|
-
};
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { HMR_RECONNECT_DELAY_MS, VITE_WS_TOKEN_REGEX } from "./constants.js";
|
|
2
|
-
import { HmrTransport, HmrUpdateHandler } from "./types.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Extracts the accepted file paths from a raw Vite HMR WebSocket message.
|
|
6
|
-
* Only `js-update` entries are kept (a `css-update` swaps a stylesheet link
|
|
7
|
-
* without re-running modules). Returns an empty array for any other message
|
|
8
|
-
* shape.
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```ts
|
|
12
|
-
* parseViteUpdatePaths(rawMessageData);
|
|
13
|
-
* // ["/src/app.tsx"]
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
|
-
export const parseViteUpdatePaths = (rawMessageData: string): string[] => {
|
|
17
|
-
let message: unknown;
|
|
18
|
-
try {
|
|
19
|
-
message = JSON.parse(rawMessageData);
|
|
20
|
-
} catch {
|
|
21
|
-
return [];
|
|
22
|
-
}
|
|
23
|
-
if (typeof message !== "object" || message === null) return [];
|
|
24
|
-
if (!("type" in message) || message.type !== "update") return [];
|
|
25
|
-
if (!("updates" in message) || !Array.isArray(message.updates)) return [];
|
|
26
|
-
const filePaths: string[] = [];
|
|
27
|
-
for (const update of message.updates) {
|
|
28
|
-
if (typeof update !== "object" || update === null) continue;
|
|
29
|
-
if (!("type" in update) || update.type !== "js-update") continue;
|
|
30
|
-
if (!("acceptedPath" in update) || typeof update.acceptedPath !== "string") continue;
|
|
31
|
-
filePaths.push(update.acceptedPath);
|
|
32
|
-
}
|
|
33
|
-
return filePaths;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
// HACK: a standalone script is not a Vite module, so import.meta.hot is
|
|
37
|
-
// unavailable; open a second HMR WebSocket using the wsToken scraped from
|
|
38
|
-
// the dev server's own /@vite/client source.
|
|
39
|
-
const fetchViteWsToken = async (): Promise<string | null> => {
|
|
40
|
-
try {
|
|
41
|
-
const response = await fetch("/@vite/client");
|
|
42
|
-
if (!response.ok) return null;
|
|
43
|
-
const clientSource = await response.text();
|
|
44
|
-
return VITE_WS_TOKEN_REGEX.exec(clientSource)?.[1] ?? null;
|
|
45
|
-
} catch {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Subscribes to the current page's Vite dev server HMR WebSocket and invokes
|
|
52
|
-
* `onHmrUpdate` with the updated file paths on every hot update. Reconnects
|
|
53
|
-
* automatically when the dev server restarts. Resolves `null` when the page
|
|
54
|
-
* is not served by Vite.
|
|
55
|
-
*
|
|
56
|
-
* @example
|
|
57
|
-
* ```ts
|
|
58
|
-
* const transport = await createViteHmrTransport((filePaths) => {
|
|
59
|
-
* console.log("hot updated:", filePaths);
|
|
60
|
-
* });
|
|
61
|
-
* transport?.dispose();
|
|
62
|
-
* ```
|
|
63
|
-
*/
|
|
64
|
-
export const createViteHmrTransport = async (
|
|
65
|
-
onHmrUpdate: HmrUpdateHandler,
|
|
66
|
-
): Promise<HmrTransport | null> => {
|
|
67
|
-
if (typeof window === "undefined" || typeof WebSocket === "undefined") return null;
|
|
68
|
-
const initialWsToken = await fetchViteWsToken();
|
|
69
|
-
if (!initialWsToken) return null;
|
|
70
|
-
|
|
71
|
-
let isDisposed = false;
|
|
72
|
-
let socket: WebSocket | null = null;
|
|
73
|
-
let reconnectTimerId: number | undefined;
|
|
74
|
-
|
|
75
|
-
const scheduleReconnect = () => {
|
|
76
|
-
if (isDisposed) return;
|
|
77
|
-
reconnectTimerId = window.setTimeout(() => {
|
|
78
|
-
void fetchViteWsToken().then((freshWsToken) => {
|
|
79
|
-
if (isDisposed) return;
|
|
80
|
-
if (freshWsToken) {
|
|
81
|
-
connect(freshWsToken);
|
|
82
|
-
} else {
|
|
83
|
-
scheduleReconnect();
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
}, HMR_RECONNECT_DELAY_MS);
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const connect = (wsToken: string) => {
|
|
90
|
-
if (isDisposed) return;
|
|
91
|
-
const socketProtocol = location.protocol === "https:" ? "wss" : "ws";
|
|
92
|
-
const connectedSocket = new WebSocket(
|
|
93
|
-
`${socketProtocol}://${location.host}/?token=${wsToken}`,
|
|
94
|
-
"vite-hmr",
|
|
95
|
-
);
|
|
96
|
-
socket = connectedSocket;
|
|
97
|
-
connectedSocket.onmessage = (event) => {
|
|
98
|
-
const filePaths = parseViteUpdatePaths(String(event.data));
|
|
99
|
-
if (filePaths.length > 0) onHmrUpdate(filePaths);
|
|
100
|
-
};
|
|
101
|
-
connectedSocket.onclose = scheduleReconnect;
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
connect(initialWsToken);
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
dispose: () => {
|
|
108
|
-
isDisposed = true;
|
|
109
|
-
window.clearTimeout(reconnectTimerId);
|
|
110
|
-
if (socket) {
|
|
111
|
-
socket.onclose = null;
|
|
112
|
-
socket.close();
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
};
|
|
116
|
-
};
|