@utoo/pack 1.2.13 → 1.3.0-alpha.1
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/cjs/binding.d.ts +5 -31
- package/cjs/commands/build.js +6 -11
- package/cjs/commands/dev-legacy.d.ts +44 -0
- package/cjs/commands/dev-legacy.js +458 -0
- package/cjs/commands/dev.d.ts +6 -31
- package/cjs/commands/dev.js +142 -365
- package/cjs/core/hmr.d.ts +14 -0
- package/cjs/core/hmr.js +72 -7
- package/cjs/utils/common.d.ts +1 -1
- package/cjs/utils/common.js +1 -2
- package/config_schema.json +14 -0
- package/esm/binding.d.ts +5 -31
- package/esm/commands/build.js +7 -12
- package/esm/commands/dev-legacy.d.ts +44 -0
- package/esm/commands/dev-legacy.js +442 -0
- package/esm/commands/dev.d.ts +6 -31
- package/esm/commands/dev.js +143 -356
- package/esm/core/hmr.d.ts +14 -0
- package/esm/core/hmr.js +73 -8
- package/esm/utils/common.d.ts +1 -1
- package/esm/utils/common.js +1 -1
- package/package.json +13 -9
package/esm/commands/dev.js
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev server implementation using Hono + @hono/node-server + @hono/node-ws.
|
|
3
|
+
* Keeps the same public API as dev.ts; do not remove dev.ts until this is verified.
|
|
4
|
+
*/
|
|
5
|
+
import { serve as honoServe } from "@hono/node-server";
|
|
6
|
+
import { serveStatic } from "@hono/node-server/serve-static";
|
|
7
|
+
import { createNodeWebSocket } from "@hono/node-ws";
|
|
1
8
|
import fs from "fs";
|
|
2
|
-
import
|
|
9
|
+
import getPort from "get-port";
|
|
10
|
+
import { Hono } from "hono";
|
|
3
11
|
import https from "https";
|
|
4
|
-
import { isIPv6 } from "net";
|
|
5
12
|
import path from "path";
|
|
6
|
-
import
|
|
7
|
-
import url from "url";
|
|
8
|
-
import { resolveBundleOptions } from "../config/webpackCompat.js";
|
|
13
|
+
import { resolveBundleOptions, } from "../config/webpackCompat.js";
|
|
9
14
|
import { createHotReloader } from "../core/hmr.js";
|
|
10
15
|
import { blockStdout, getPackPath } from "../utils/common.js";
|
|
11
16
|
import { findRootDir } from "../utils/findRoot.js";
|
|
12
17
|
import { createSelfSignedCertificate } from "../utils/mkcert.js";
|
|
13
18
|
import { printServerInfo } from "../utils/printServerInfo.js";
|
|
14
19
|
import { xcodeProfilingReady } from "../utils/xcodeProfile.js";
|
|
20
|
+
// --- Path helpers (same logic as dev.ts, not exported) ---
|
|
15
21
|
function parsePath(pathStr) {
|
|
16
22
|
const hashIndex = pathStr.indexOf("#");
|
|
17
23
|
const queryIndex = pathStr.indexOf("?");
|
|
@@ -35,18 +41,13 @@ function pathHasPrefix(pathStr, prefix) {
|
|
|
35
41
|
return pathname === prefix || pathname.startsWith(prefix + "/");
|
|
36
42
|
}
|
|
37
43
|
function removePathPrefix(pathStr, prefix) {
|
|
38
|
-
// If the path doesn't start with the prefix we can return it as is.
|
|
39
44
|
if (!pathHasPrefix(pathStr, prefix)) {
|
|
40
45
|
return pathStr;
|
|
41
46
|
}
|
|
42
|
-
// Remove the prefix from the path via slicing.
|
|
43
47
|
const withoutPrefix = pathStr.slice(prefix.length);
|
|
44
|
-
// If the path without the prefix starts with a `/` we can return it as is.
|
|
45
48
|
if (withoutPrefix.startsWith("/")) {
|
|
46
49
|
return withoutPrefix;
|
|
47
50
|
}
|
|
48
|
-
// If the path without the prefix doesn't start with a `/` we need to add it
|
|
49
|
-
// back to the path to make sure it's a valid path.
|
|
50
51
|
return `/${withoutPrefix}`;
|
|
51
52
|
}
|
|
52
53
|
function normalizedPublicPath(publicPath) {
|
|
@@ -56,387 +57,173 @@ function normalizedPublicPath(publicPath) {
|
|
|
56
57
|
}
|
|
57
58
|
try {
|
|
58
59
|
if (URL.canParse(escapedPublicPath)) {
|
|
59
|
-
const
|
|
60
|
-
return
|
|
60
|
+
const u = new URL(escapedPublicPath).toString();
|
|
61
|
+
return u.endsWith("/") ? u.slice(0, -1) : u;
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
catch (_a) { }
|
|
64
65
|
return `/${escapedPublicPath}`;
|
|
65
66
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
// FIXME: fix any type
|
|
80
|
-
const cfgDevServer = (((_a = options.config) === null || _a === void 0 ? void 0 : _a.devServer) || {});
|
|
81
|
-
const serverOpts = {
|
|
82
|
-
hostname: (serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.hostname) || cfgDevServer.host || "localhost",
|
|
83
|
-
port: typeof (serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.port) !== "undefined"
|
|
84
|
-
? serverOptions.port
|
|
85
|
-
: cfgDevServer.port || 3000,
|
|
86
|
-
https: typeof (serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.https) !== "undefined"
|
|
87
|
-
? serverOptions.https
|
|
88
|
-
: cfgDevServer.https,
|
|
89
|
-
logServerInfo: serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.logServerInfo,
|
|
90
|
-
selfSignedCertificate: serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.selfSignedCertificate,
|
|
91
|
-
};
|
|
92
|
-
// If HTTPS is requested and no certificate provided, attempt to generate one.
|
|
93
|
-
if (serverOpts.https && !serverOpts.selfSignedCertificate) {
|
|
67
|
+
async function resolveDevConfig(options, projectPath, rootPath, serverOptions) {
|
|
68
|
+
var _a, _b, _d, _e, _f, _g;
|
|
69
|
+
const cfgDevServer = (_b = (_a = options.config) === null || _a === void 0 ? void 0 : _a.devServer) !== null && _b !== void 0 ? _b : {};
|
|
70
|
+
const port = typeof (serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.port) !== "undefined"
|
|
71
|
+
? serverOptions.port
|
|
72
|
+
: ((_d = cfgDevServer.port) !== null && _d !== void 0 ? _d : 3000);
|
|
73
|
+
const hostname = (_f = (_e = serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.hostname) !== null && _e !== void 0 ? _e : cfgDevServer.host) !== null && _f !== void 0 ? _f : "localhost";
|
|
74
|
+
const useHttps = typeof (serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.https) !== "undefined"
|
|
75
|
+
? serverOptions.https
|
|
76
|
+
: cfgDevServer.https;
|
|
77
|
+
let selfSignedCertificate = serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.selfSignedCertificate;
|
|
78
|
+
if (useHttps && !selfSignedCertificate) {
|
|
94
79
|
try {
|
|
95
|
-
|
|
96
|
-
const cert = await createSelfSignedCertificate(serverOpts.hostname);
|
|
80
|
+
const cert = await createSelfSignedCertificate(hostname);
|
|
97
81
|
if (cert)
|
|
98
|
-
|
|
82
|
+
selfSignedCertificate = cert;
|
|
99
83
|
}
|
|
100
|
-
catch (
|
|
101
|
-
//
|
|
84
|
+
catch (_h) {
|
|
85
|
+
// fall back to http
|
|
102
86
|
}
|
|
103
87
|
}
|
|
104
|
-
|
|
88
|
+
const bundleOptions = {
|
|
105
89
|
...options,
|
|
106
90
|
config: {
|
|
107
91
|
...options.config,
|
|
108
92
|
devServer: {
|
|
109
93
|
hot: true,
|
|
110
|
-
...(options.config.devServer || {}),
|
|
94
|
+
...(((_g = options.config) === null || _g === void 0 ? void 0 : _g.devServer) || {}),
|
|
111
95
|
},
|
|
112
96
|
},
|
|
113
97
|
packPath: getPackPath(),
|
|
114
|
-
}, projectPath || process.cwd(), rootPath);
|
|
115
|
-
}
|
|
116
|
-
export async function startServer(serverOptions, bundleOptions, projectPath, rootPath) {
|
|
117
|
-
let { port, hostname, selfSignedCertificate } = serverOptions;
|
|
118
|
-
process.title = "utoopack-dev-server";
|
|
119
|
-
let handlersReady = () => { };
|
|
120
|
-
let handlersError = () => { };
|
|
121
|
-
let handlersPromise = new Promise((resolve, reject) => {
|
|
122
|
-
handlersReady = resolve;
|
|
123
|
-
handlersError = reject;
|
|
124
|
-
});
|
|
125
|
-
let requestHandler = async (req, res) => {
|
|
126
|
-
if (handlersPromise) {
|
|
127
|
-
await handlersPromise;
|
|
128
|
-
return requestHandler(req, res);
|
|
129
|
-
}
|
|
130
|
-
throw new Error("Invariant request handler was not setup");
|
|
131
98
|
};
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
throw new Error("Invariant upgrade handler was not setup");
|
|
138
|
-
};
|
|
139
|
-
async function requestListener(req, res) {
|
|
140
|
-
try {
|
|
141
|
-
if (handlersPromise) {
|
|
142
|
-
await handlersPromise;
|
|
143
|
-
handlersPromise = undefined;
|
|
144
|
-
}
|
|
145
|
-
await requestHandler(req, res);
|
|
146
|
-
}
|
|
147
|
-
catch (err) {
|
|
148
|
-
res.statusCode = 500;
|
|
149
|
-
res.end("Internal Server Error");
|
|
150
|
-
console.error(`Failed to handle request for ${req.url}`);
|
|
151
|
-
console.error(err);
|
|
152
|
-
}
|
|
99
|
+
const projectPathResolved = projectPath || process.cwd();
|
|
100
|
+
const rootPathResolved = rootPath !== null && rootPath !== void 0 ? rootPath : projectPathResolved;
|
|
101
|
+
const actualPort = await getPort({ port, host: hostname });
|
|
102
|
+
if (actualPort !== port) {
|
|
103
|
+
console.warn(`Port ${port} is in use, using available port ${actualPort} instead.`);
|
|
153
104
|
}
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
catch (err) {
|
|
165
|
-
socket.destroy();
|
|
166
|
-
console.error(`Failed to handle request for ${req.url}`);
|
|
167
|
-
console.error(err);
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
let portRetryCount = 0;
|
|
171
|
-
const originalPort = port;
|
|
172
|
-
server.on("error", (err) => {
|
|
173
|
-
if (port && err.code === "EADDRINUSE" && portRetryCount < 10) {
|
|
174
|
-
port += 1;
|
|
175
|
-
portRetryCount += 1;
|
|
176
|
-
server.listen(port, hostname);
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
console.error(`Failed to start server`);
|
|
180
|
-
console.error(err);
|
|
181
|
-
process.exit(1);
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
await new Promise((resolve) => {
|
|
185
|
-
server.on("listening", async () => {
|
|
186
|
-
const addr = server.address();
|
|
187
|
-
const actualHostname = formatHostname(typeof addr === "object"
|
|
188
|
-
? (addr === null || addr === void 0 ? void 0 : addr.address) || hostname || "localhost"
|
|
189
|
-
: addr);
|
|
190
|
-
const formattedHostname = !hostname || actualHostname === "0.0.0.0"
|
|
191
|
-
? "localhost"
|
|
192
|
-
: actualHostname === "[::]"
|
|
193
|
-
? "[::1]"
|
|
194
|
-
: formatHostname(hostname);
|
|
195
|
-
port = typeof addr === "object" ? (addr === null || addr === void 0 ? void 0 : addr.port) || port : port;
|
|
196
|
-
if (portRetryCount) {
|
|
197
|
-
console.warn(`Port ${originalPort} is in use, using available port ${port} instead.`);
|
|
198
|
-
}
|
|
199
|
-
if (serverOptions.logServerInfo !== false) {
|
|
200
|
-
printServerInfo(serverOptions.https ? "https" : "http", formattedHostname, port);
|
|
201
|
-
}
|
|
202
|
-
try {
|
|
203
|
-
let cleanupStarted = false;
|
|
204
|
-
let closeUpgraded = null;
|
|
205
|
-
const cleanup = () => {
|
|
206
|
-
if (cleanupStarted) {
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
cleanupStarted = true;
|
|
210
|
-
(async () => {
|
|
211
|
-
console.debug("start-server process cleanup");
|
|
212
|
-
await new Promise((res) => {
|
|
213
|
-
server.close((err) => {
|
|
214
|
-
if (err)
|
|
215
|
-
console.error(err);
|
|
216
|
-
res();
|
|
217
|
-
});
|
|
218
|
-
server.closeAllConnections();
|
|
219
|
-
closeUpgraded === null || closeUpgraded === void 0 ? void 0 : closeUpgraded();
|
|
220
|
-
});
|
|
221
|
-
console.debug("start-server process cleanup finished");
|
|
222
|
-
process.exit(0);
|
|
223
|
-
})();
|
|
224
|
-
};
|
|
225
|
-
const exception = (err) => {
|
|
226
|
-
console.error(err);
|
|
227
|
-
};
|
|
228
|
-
process.on("SIGINT", cleanup);
|
|
229
|
-
process.on("SIGTERM", cleanup);
|
|
230
|
-
process.on("rejectionHandled", () => { });
|
|
231
|
-
process.on("uncaughtException", exception);
|
|
232
|
-
process.on("unhandledRejection", exception);
|
|
233
|
-
const initResult = await initialize(bundleOptions, projectPath, rootPath);
|
|
234
|
-
requestHandler = initResult.requestHandler;
|
|
235
|
-
upgradeHandler = initResult.upgradeHandler;
|
|
236
|
-
closeUpgraded = initResult.closeUpgraded;
|
|
237
|
-
handlersReady();
|
|
238
|
-
}
|
|
239
|
-
catch (err) {
|
|
240
|
-
handlersError();
|
|
241
|
-
console.error(err);
|
|
242
|
-
process.exit(1);
|
|
105
|
+
const serveOptsBase = {
|
|
106
|
+
port: actualPort,
|
|
107
|
+
hostname,
|
|
108
|
+
...(useHttps && selfSignedCertificate
|
|
109
|
+
? {
|
|
110
|
+
createServer: https.createServer,
|
|
111
|
+
serverOptions: {
|
|
112
|
+
key: fs.readFileSync(selfSignedCertificate.key),
|
|
113
|
+
cert: fs.readFileSync(selfSignedCertificate.cert),
|
|
114
|
+
},
|
|
243
115
|
}
|
|
244
|
-
|
|
245
|
-
});
|
|
246
|
-
server.listen(port, hostname);
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
export async function initialize(bundleOptions, projectPath, rootPath) {
|
|
250
|
-
process.env.NODE_ENV = "development";
|
|
251
|
-
const hotReloader = await createHotReloader(bundleOptions, projectPath, rootPath);
|
|
252
|
-
await hotReloader.start();
|
|
253
|
-
const requestHandlerImpl = async (req, res) => {
|
|
254
|
-
req.on("error", console.error);
|
|
255
|
-
res.on("error", console.error);
|
|
256
|
-
const handleRequest = async () => {
|
|
257
|
-
var _a, _b;
|
|
258
|
-
if (!(req.method === "GET" || req.method === "HEAD")) {
|
|
259
|
-
res.setHeader("Allow", ["GET", "HEAD"]);
|
|
260
|
-
res.statusCode = 405;
|
|
261
|
-
res.end();
|
|
262
|
-
}
|
|
263
|
-
const distRoot = path.resolve(projectPath, ((_a = bundleOptions.config.output) === null || _a === void 0 ? void 0 : _a.path) || "./dist");
|
|
264
|
-
const publicPath = (_b = bundleOptions.config.output) === null || _b === void 0 ? void 0 : _b.publicPath;
|
|
265
|
-
try {
|
|
266
|
-
const reqUrl = req.url || "";
|
|
267
|
-
let requestPath = url.parse(reqUrl).pathname || "";
|
|
268
|
-
if (publicPath && publicPath !== "runtime") {
|
|
269
|
-
const normalizedPrefix = normalizedPublicPath(publicPath);
|
|
270
|
-
const isAbsoluteUrl = normalizedPrefix.startsWith("http://") ||
|
|
271
|
-
normalizedPrefix.startsWith("https://");
|
|
272
|
-
if (!isAbsoluteUrl && normalizedPrefix) {
|
|
273
|
-
if (pathHasPrefix(requestPath, normalizedPrefix)) {
|
|
274
|
-
requestPath = removePathPrefix(requestPath, normalizedPrefix);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
return await serveStatic(req, res, requestPath, { root: distRoot });
|
|
279
|
-
}
|
|
280
|
-
catch (err) {
|
|
281
|
-
res.setHeader("Cache-Control", "private, no-cache, no-store, max-age=0, must-revalidate");
|
|
282
|
-
res.statusCode = 404;
|
|
283
|
-
res.end();
|
|
284
|
-
}
|
|
285
|
-
};
|
|
286
|
-
try {
|
|
287
|
-
await handleRequest();
|
|
288
|
-
}
|
|
289
|
-
catch (err) {
|
|
290
|
-
res.statusCode = 500;
|
|
291
|
-
res.end("Internal Server Error");
|
|
292
|
-
}
|
|
293
|
-
};
|
|
294
|
-
let requestHandler = requestHandlerImpl;
|
|
295
|
-
const logError = async (type, err) => {
|
|
296
|
-
if (type === "unhandledRejection") {
|
|
297
|
-
console.error("unhandledRejection: ", err);
|
|
298
|
-
}
|
|
299
|
-
else if (type === "uncaughtException") {
|
|
300
|
-
console.error("uncaughtException: ", err);
|
|
301
|
-
}
|
|
302
|
-
};
|
|
303
|
-
process.on("uncaughtException", logError.bind(null, "uncaughtException"));
|
|
304
|
-
process.on("unhandledRejection", logError.bind(null, "unhandledRejection"));
|
|
305
|
-
const upgradeHandler = async (req, socket, head) => {
|
|
306
|
-
var _a;
|
|
307
|
-
try {
|
|
308
|
-
const isHMRRequest = (_a = req.url) === null || _a === void 0 ? void 0 : _a.includes("turbopack-hmr");
|
|
309
|
-
if (isHMRRequest) {
|
|
310
|
-
hotReloader.onHMR(req, socket, head);
|
|
311
|
-
}
|
|
312
|
-
else {
|
|
313
|
-
socket.end();
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
catch (err) {
|
|
317
|
-
console.error("Error handling upgrade request", err);
|
|
318
|
-
socket.end();
|
|
319
|
-
}
|
|
116
|
+
: {}),
|
|
320
117
|
};
|
|
321
118
|
return {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
},
|
|
119
|
+
bundleOptions,
|
|
120
|
+
projectPathResolved,
|
|
121
|
+
rootPathResolved,
|
|
122
|
+
serveOptsBase,
|
|
327
123
|
};
|
|
328
124
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
const controller = createAbortController(res);
|
|
335
|
-
const writer = createWriterFromResponse(res, waitUntilForEnd);
|
|
336
|
-
await readable.pipeTo(writer, { signal: controller.signal });
|
|
337
|
-
}
|
|
338
|
-
catch (err) {
|
|
339
|
-
if (isAbortError(err))
|
|
340
|
-
return;
|
|
341
|
-
throw new Error("failed to pipe response", { cause: err });
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
export function createAbortController(response) {
|
|
345
|
-
const controller = new AbortController();
|
|
346
|
-
response.once("close", () => {
|
|
347
|
-
if (response.writableFinished)
|
|
348
|
-
return;
|
|
349
|
-
controller.abort(new ResponseAborted());
|
|
350
|
-
});
|
|
351
|
-
return controller;
|
|
352
|
-
}
|
|
353
|
-
export function isAbortError(e) {
|
|
354
|
-
return (e === null || e === void 0 ? void 0 : e.name) === "AbortError" || (e === null || e === void 0 ? void 0 : e.name) === ResponseAbortedName;
|
|
355
|
-
}
|
|
356
|
-
export const ResponseAbortedName = "ResponseAborted";
|
|
357
|
-
export class ResponseAborted extends Error {
|
|
358
|
-
constructor() {
|
|
359
|
-
super(...arguments);
|
|
360
|
-
this.name = ResponseAbortedName;
|
|
125
|
+
// --- Entry ---
|
|
126
|
+
export function serve(options, projectPath, rootPath, serverOptions) {
|
|
127
|
+
const bundleOptions = resolveBundleOptions(options, projectPath, rootPath);
|
|
128
|
+
if (!rootPath) {
|
|
129
|
+
rootPath = findRootDir(projectPath || process.cwd());
|
|
361
130
|
}
|
|
131
|
+
return runDev(bundleOptions, projectPath, rootPath, serverOptions);
|
|
362
132
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
133
|
+
const HMR_PATH = "/turbopack-hmr";
|
|
134
|
+
async function runDev(options, projectPath, rootPath, serverOptions) {
|
|
135
|
+
var _a, _b, _d, _e;
|
|
136
|
+
blockStdout();
|
|
137
|
+
process.title = "utoopack-dev-server";
|
|
138
|
+
if (process.env.XCODE_PROFILE) {
|
|
139
|
+
await xcodeProfilingReady();
|
|
368
140
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
141
|
+
process.env.NODE_ENV = "development";
|
|
142
|
+
const { bundleOptions, projectPathResolved, rootPathResolved, serveOptsBase, } = await resolveDevConfig(options, projectPath, rootPath, serverOptions);
|
|
143
|
+
const hotReloader = await createHotReloader(bundleOptions, projectPathResolved, rootPathResolved);
|
|
144
|
+
await hotReloader.start();
|
|
145
|
+
const distRoot = path.resolve(projectPathResolved, ((_b = (_a = options.config) === null || _a === void 0 ? void 0 : _a.output) === null || _b === void 0 ? void 0 : _b.path) || "./dist");
|
|
146
|
+
const publicPath = (_e = (_d = options.config) === null || _d === void 0 ? void 0 : _d.output) === null || _e === void 0 ? void 0 : _e.publicPath;
|
|
147
|
+
// Skip prefix stripping for "runtime" and when publicPath is absent (match dev.ts).
|
|
148
|
+
const normalizedPrefix = publicPath && publicPath !== "runtime"
|
|
149
|
+
? normalizedPublicPath(publicPath)
|
|
150
|
+
: "";
|
|
151
|
+
const app = new Hono();
|
|
152
|
+
const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app });
|
|
153
|
+
const rewriteRequestPath = (reqPath) => {
|
|
154
|
+
if (!normalizedPrefix)
|
|
155
|
+
return reqPath;
|
|
156
|
+
// Absolute-URL publicPath: do not rewrite (match dev.ts).
|
|
157
|
+
if (normalizedPrefix.startsWith("http://") ||
|
|
158
|
+
normalizedPrefix.startsWith("https://")) {
|
|
159
|
+
return reqPath;
|
|
160
|
+
}
|
|
161
|
+
if (pathHasPrefix(reqPath, normalizedPrefix)) {
|
|
162
|
+
return removePathPrefix(reqPath, normalizedPrefix);
|
|
163
|
+
}
|
|
164
|
+
return reqPath;
|
|
165
|
+
};
|
|
166
|
+
// HMR WebSocket route must be registered before "/*" so it is not handled by serveStatic
|
|
167
|
+
app.get(HMR_PATH, upgradeWebSocket((_c) => ({
|
|
168
|
+
onOpen(_ev, ws) {
|
|
169
|
+
hotReloader.registerClient(ws);
|
|
170
|
+
},
|
|
171
|
+
onMessage(ev, ws) {
|
|
384
172
|
try {
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
res.flush();
|
|
388
|
-
}
|
|
389
|
-
if (!ok) {
|
|
390
|
-
await drained.promise;
|
|
391
|
-
drained = new DetachedPromise();
|
|
392
|
-
}
|
|
173
|
+
const data = typeof ev.data === "string" ? ev.data : ev.data.toString();
|
|
174
|
+
hotReloader.handleClientMessage(ws, data);
|
|
393
175
|
}
|
|
394
176
|
catch (err) {
|
|
395
|
-
|
|
396
|
-
throw new Error("failed to write chunk to response", { cause: err });
|
|
177
|
+
console.error("HMR message error", err);
|
|
397
178
|
}
|
|
398
179
|
},
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
return;
|
|
402
|
-
res.destroy(err);
|
|
180
|
+
onClose(_ev, ws) {
|
|
181
|
+
hotReloader.unregisterClient(ws);
|
|
403
182
|
},
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
await waitUntilForEnd;
|
|
407
|
-
}
|
|
408
|
-
if (res.writableFinished)
|
|
409
|
-
return;
|
|
410
|
-
res.end();
|
|
411
|
-
return finished.promise;
|
|
183
|
+
onError(err) {
|
|
184
|
+
console.error("HMR WebSocket error", err);
|
|
412
185
|
},
|
|
186
|
+
})));
|
|
187
|
+
// GET handles HEAD automatically in Hono; serveStatic serves both
|
|
188
|
+
app.get("/*", serveStatic({
|
|
189
|
+
root: distRoot,
|
|
190
|
+
rewriteRequestPath,
|
|
191
|
+
}));
|
|
192
|
+
app.all("*", (c) => c.body(null, 405, { Allow: "GET, HEAD" }));
|
|
193
|
+
const server = honoServe({
|
|
194
|
+
...serveOptsBase,
|
|
195
|
+
fetch: app.fetch,
|
|
413
196
|
});
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
reject = rej;
|
|
422
|
-
});
|
|
423
|
-
this.resolve = resolve;
|
|
424
|
-
this.reject = reject;
|
|
197
|
+
injectWebSocket(server);
|
|
198
|
+
if ((serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.logServerInfo) !== false) {
|
|
199
|
+
const scheme = serveOptsBase.serverOptions ? "https" : "http";
|
|
200
|
+
const displayHost = serveOptsBase.hostname === "0.0.0.0"
|
|
201
|
+
? "localhost"
|
|
202
|
+
: serveOptsBase.hostname;
|
|
203
|
+
printServerInfo(scheme, displayHost, serveOptsBase.port);
|
|
425
204
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
205
|
+
const cleanup = () => {
|
|
206
|
+
hotReloader.close();
|
|
207
|
+
// We always create HTTP/1.1 server (http or https), so closeAllConnections exists; Hono's
|
|
208
|
+
// ServerType union includes HTTP/2, so TS does not narrow. Use runtime check to satisfy types.
|
|
209
|
+
if ("closeAllConnections" in server &&
|
|
210
|
+
typeof server.closeAllConnections === "function") {
|
|
211
|
+
server.closeAllConnections();
|
|
212
|
+
}
|
|
213
|
+
server.close((err) => {
|
|
214
|
+
if (err) {
|
|
215
|
+
console.error(err);
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
process.exit(0);
|
|
219
|
+
});
|
|
220
|
+
};
|
|
221
|
+
const exception = (err) => {
|
|
222
|
+
console.error(err);
|
|
223
|
+
};
|
|
224
|
+
process.on("SIGINT", cleanup);
|
|
225
|
+
process.on("SIGTERM", cleanup);
|
|
226
|
+
process.on("rejectionHandled", () => { });
|
|
227
|
+
process.on("uncaughtException", exception);
|
|
228
|
+
process.on("unhandledRejection", exception);
|
|
442
229
|
}
|
package/esm/core/hmr.d.ts
CHANGED
|
@@ -13,6 +13,11 @@ export interface WebpackStats {
|
|
|
13
13
|
toJson(options?: any): any;
|
|
14
14
|
toString(options?: any): string;
|
|
15
15
|
}
|
|
16
|
+
/** Client handle for HMR: any object with send(data) usable as Set/WeakMap key (e.g. ws WebSocket or hono WSContext). */
|
|
17
|
+
export interface WSLike {
|
|
18
|
+
send(data: string): void;
|
|
19
|
+
close(code?: number, reason?: string): void;
|
|
20
|
+
}
|
|
16
21
|
export interface HotReloaderInterface {
|
|
17
22
|
turbopackProject?: Project;
|
|
18
23
|
serverStats: WebpackStats | null;
|
|
@@ -20,9 +25,18 @@ export interface HotReloaderInterface {
|
|
|
20
25
|
clearHmrServerError(): void;
|
|
21
26
|
start(): Promise<void>;
|
|
22
27
|
send(action: HMR_ACTION_TYPES): void;
|
|
28
|
+
/**
|
|
29
|
+
* @deprecated Used by legacy dev server (dev-legacy.ts). Prefer registerClient / unregisterClient / handleClientMessage (e.g. dev.ts).
|
|
30
|
+
*/
|
|
23
31
|
onHMR(req: IncomingMessage, socket: Duplex, head: Buffer, onUpgrade?: (client: {
|
|
24
32
|
send(data: string): void;
|
|
25
33
|
}) => void): void;
|
|
34
|
+
/** Register a WebSocket client (e.g. from @hono/node-ws upgradeWebSocket). Call unregisterClient on close. */
|
|
35
|
+
registerClient(ws: WSLike): void;
|
|
36
|
+
/** Unregister and cleanup subscriptions for a client. */
|
|
37
|
+
unregisterClient(ws: WSLike): void;
|
|
38
|
+
/** Handle a message from a client (JSON string). */
|
|
39
|
+
handleClientMessage(ws: WSLike, data: string): void;
|
|
26
40
|
buildFallbackError(): Promise<void>;
|
|
27
41
|
close(): void;
|
|
28
42
|
}
|