@utoo/pack 0.0.1-alpha.1 → 0.0.1-alpha.12

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/esm/dev.js ADDED
@@ -0,0 +1,339 @@
1
+ import fs from "fs";
2
+ import http from "http";
3
+ import https from "https";
4
+ import { isIPv6 } from "net";
5
+ import path from "path";
6
+ import send from "send";
7
+ import url from "url";
8
+ import { createHotReloader } from "./hmr";
9
+ import { createSelfSignedCertificate } from "./mkcert";
10
+ import { blockStdout } from "./util";
11
+ import { xcodeProfilingReady } from "./xcodeProfile";
12
+ export async function serve(options, projectPath, rootPath, serverOptions) {
13
+ blockStdout();
14
+ if (process.env.XCODE_PROFILE) {
15
+ await xcodeProfilingReady();
16
+ }
17
+ startServer({
18
+ hostname: (serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.hostname) || "localhost",
19
+ port: (serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.port) || 3000,
20
+ https: serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.https,
21
+ selfSignedCertificate: (serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.https)
22
+ ? await createSelfSignedCertificate((serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.hostname) || "localhost")
23
+ : undefined,
24
+ }, options, projectPath, rootPath);
25
+ }
26
+ export async function startServer(serverOptions, bundleOptions, projectPath, rootPath) {
27
+ let { port, hostname, selfSignedCertificate } = serverOptions;
28
+ process.title = "utoo-pack-dev-server";
29
+ let handlersReady = () => { };
30
+ let handlersError = () => { };
31
+ let handlersPromise = new Promise((resolve, reject) => {
32
+ handlersReady = resolve;
33
+ handlersError = reject;
34
+ });
35
+ let requestHandler = async (req, res) => {
36
+ if (handlersPromise) {
37
+ await handlersPromise;
38
+ return requestHandler(req, res);
39
+ }
40
+ throw new Error("Invariant request handler was not setup");
41
+ };
42
+ let upgradeHandler = async (req, socket, head) => {
43
+ if (handlersPromise) {
44
+ await handlersPromise;
45
+ return upgradeHandler(req, socket, head);
46
+ }
47
+ throw new Error("Invariant upgrade handler was not setup");
48
+ };
49
+ async function requestListener(req, res) {
50
+ try {
51
+ if (handlersPromise) {
52
+ await handlersPromise;
53
+ handlersPromise = undefined;
54
+ }
55
+ await requestHandler(req, res);
56
+ }
57
+ catch (err) {
58
+ res.statusCode = 500;
59
+ res.end("Internal Server Error");
60
+ console.error(`Failed to handle request for ${req.url}`);
61
+ console.error(err);
62
+ }
63
+ }
64
+ const server = selfSignedCertificate
65
+ ? https.createServer({
66
+ key: fs.readFileSync(selfSignedCertificate.key),
67
+ cert: fs.readFileSync(selfSignedCertificate.cert),
68
+ }, requestListener)
69
+ : http.createServer(requestListener);
70
+ server.on("upgrade", async (req, socket, head) => {
71
+ try {
72
+ await upgradeHandler(req, socket, head);
73
+ }
74
+ catch (err) {
75
+ socket.destroy();
76
+ console.error(`Failed to handle request for ${req.url}`);
77
+ console.error(err);
78
+ }
79
+ });
80
+ let portRetryCount = 0;
81
+ const originalPort = port;
82
+ server.on("error", (err) => {
83
+ if (port && err.code === "EADDRINUSE" && portRetryCount < 10) {
84
+ port += 1;
85
+ portRetryCount += 1;
86
+ server.listen(port, hostname);
87
+ }
88
+ else {
89
+ console.error(`Failed to start server`);
90
+ console.error(err);
91
+ process.exit(1);
92
+ }
93
+ });
94
+ await new Promise((resolve) => {
95
+ server.on("listening", async () => {
96
+ const addr = server.address();
97
+ const actualHostname = formatHostname(typeof addr === "object"
98
+ ? (addr === null || addr === void 0 ? void 0 : addr.address) || hostname || "localhost"
99
+ : addr);
100
+ const formattedHostname = !hostname || actualHostname === "0.0.0.0"
101
+ ? "localhost"
102
+ : actualHostname === "[::]"
103
+ ? "[::1]"
104
+ : formatHostname(hostname);
105
+ port = typeof addr === "object" ? (addr === null || addr === void 0 ? void 0 : addr.port) || port : port;
106
+ if (portRetryCount) {
107
+ console.warn(`Port ${originalPort} is in use, using available port ${port} instead.`);
108
+ }
109
+ console.log(`Listening on ${serverOptions.https ? "https" : "http"}://${formattedHostname}:${port} ...`);
110
+ try {
111
+ let cleanupStarted = false;
112
+ let closeUpgraded = null;
113
+ const cleanup = () => {
114
+ if (cleanupStarted) {
115
+ return;
116
+ }
117
+ cleanupStarted = true;
118
+ (async () => {
119
+ console.debug("start-server process cleanup");
120
+ await new Promise((res) => {
121
+ server.close((err) => {
122
+ if (err)
123
+ console.error(err);
124
+ res();
125
+ });
126
+ server.closeAllConnections();
127
+ closeUpgraded === null || closeUpgraded === void 0 ? void 0 : closeUpgraded();
128
+ });
129
+ console.debug("start-server process cleanup finished");
130
+ process.exit(0);
131
+ })();
132
+ };
133
+ const exception = (err) => {
134
+ console.error(err);
135
+ };
136
+ process.on("SIGINT", cleanup);
137
+ process.on("SIGTERM", cleanup);
138
+ process.on("rejectionHandled", () => { });
139
+ process.on("uncaughtException", exception);
140
+ process.on("unhandledRejection", exception);
141
+ const initResult = await initialize(bundleOptions, projectPath, rootPath);
142
+ requestHandler = initResult.requestHandler;
143
+ upgradeHandler = initResult.upgradeHandler;
144
+ closeUpgraded = initResult.closeUpgraded;
145
+ handlersReady();
146
+ }
147
+ catch (err) {
148
+ handlersError();
149
+ console.error(err);
150
+ process.exit(1);
151
+ }
152
+ resolve();
153
+ });
154
+ server.listen(port, hostname);
155
+ });
156
+ }
157
+ export async function initialize(bundleOptions, projectPath, rootPath) {
158
+ process.env.NODE_ENV = "development";
159
+ const hotReloader = await createHotReloader(bundleOptions, projectPath, rootPath);
160
+ await hotReloader.start();
161
+ const requestHandlerImpl = async (req, res) => {
162
+ req.on("error", console.error);
163
+ res.on("error", console.error);
164
+ const handleRequest = async () => {
165
+ var _a;
166
+ if (!(req.method === "GET" || req.method === "HEAD")) {
167
+ res.setHeader("Allow", ["GET", "HEAD"]);
168
+ res.statusCode = 405;
169
+ res.end();
170
+ }
171
+ const distRoot = path.resolve(projectPath, ((_a = bundleOptions.config.output) === null || _a === void 0 ? void 0 : _a.path) || "./dist");
172
+ try {
173
+ const reqUrl = req.url || "";
174
+ const path = url.parse(reqUrl).pathname || "";
175
+ return await serveStatic(req, res, path, { root: distRoot });
176
+ }
177
+ catch (err) {
178
+ res.setHeader("Cache-Control", "private, no-cache, no-store, max-age=0, must-revalidate");
179
+ res.statusCode = 404;
180
+ res.end();
181
+ }
182
+ };
183
+ try {
184
+ await handleRequest();
185
+ }
186
+ catch (err) {
187
+ res.statusCode = 500;
188
+ res.end("Internal Server Error");
189
+ }
190
+ };
191
+ let requestHandler = requestHandlerImpl;
192
+ const logError = async (type, err) => {
193
+ if (type === "unhandledRejection") {
194
+ console.error("unhandledRejection: ", err);
195
+ }
196
+ else if (type === "uncaughtException") {
197
+ console.error("uncaughtException: ", err);
198
+ }
199
+ };
200
+ process.on("uncaughtException", logError.bind(null, "uncaughtException"));
201
+ process.on("unhandledRejection", logError.bind(null, "unhandledRejection"));
202
+ const upgradeHandler = async (req, socket, head) => {
203
+ var _a;
204
+ try {
205
+ const isHMRRequest = (_a = req.url) === null || _a === void 0 ? void 0 : _a.includes("turbopack-hmr");
206
+ if (isHMRRequest) {
207
+ hotReloader.onHMR(req, socket, head);
208
+ }
209
+ else {
210
+ socket.end();
211
+ }
212
+ }
213
+ catch (err) {
214
+ console.error("Error handling upgrade request", err);
215
+ socket.end();
216
+ }
217
+ };
218
+ return {
219
+ requestHandler,
220
+ upgradeHandler,
221
+ closeUpgraded() {
222
+ hotReloader.close();
223
+ },
224
+ };
225
+ }
226
+ export async function pipeToNodeResponse(readable, res, waitUntilForEnd) {
227
+ try {
228
+ const { errored, destroyed } = res;
229
+ if (errored || destroyed)
230
+ return;
231
+ const controller = createAbortController(res);
232
+ const writer = createWriterFromResponse(res, waitUntilForEnd);
233
+ await readable.pipeTo(writer, { signal: controller.signal });
234
+ }
235
+ catch (err) {
236
+ if (isAbortError(err))
237
+ return;
238
+ throw new Error("failed to pipe response", { cause: err });
239
+ }
240
+ }
241
+ export function createAbortController(response) {
242
+ const controller = new AbortController();
243
+ response.once("close", () => {
244
+ if (response.writableFinished)
245
+ return;
246
+ controller.abort(new ResponseAborted());
247
+ });
248
+ return controller;
249
+ }
250
+ export function isAbortError(e) {
251
+ return (e === null || e === void 0 ? void 0 : e.name) === "AbortError" || (e === null || e === void 0 ? void 0 : e.name) === ResponseAbortedName;
252
+ }
253
+ export const ResponseAbortedName = "ResponseAborted";
254
+ export class ResponseAborted extends Error {
255
+ constructor() {
256
+ super(...arguments);
257
+ this.name = ResponseAbortedName;
258
+ }
259
+ }
260
+ function createWriterFromResponse(res, waitUntilForEnd) {
261
+ let started = false;
262
+ let drained = new DetachedPromise();
263
+ function onDrain() {
264
+ drained.resolve();
265
+ }
266
+ res.on("drain", onDrain);
267
+ res.once("close", () => {
268
+ res.off("drain", onDrain);
269
+ drained.resolve();
270
+ });
271
+ const finished = new DetachedPromise();
272
+ res.once("finish", () => {
273
+ finished.resolve();
274
+ });
275
+ return new WritableStream({
276
+ write: async (chunk) => {
277
+ if (!started) {
278
+ started = true;
279
+ res.flushHeaders();
280
+ }
281
+ try {
282
+ const ok = res.write(chunk);
283
+ if ("flush" in res && typeof res.flush === "function") {
284
+ res.flush();
285
+ }
286
+ if (!ok) {
287
+ await drained.promise;
288
+ drained = new DetachedPromise();
289
+ }
290
+ }
291
+ catch (err) {
292
+ res.end();
293
+ throw new Error("failed to write chunk to response", { cause: err });
294
+ }
295
+ },
296
+ abort: (err) => {
297
+ if (res.writableFinished)
298
+ return;
299
+ res.destroy(err);
300
+ },
301
+ close: async () => {
302
+ if (waitUntilForEnd) {
303
+ await waitUntilForEnd;
304
+ }
305
+ if (res.writableFinished)
306
+ return;
307
+ res.end();
308
+ return finished.promise;
309
+ },
310
+ });
311
+ }
312
+ export class DetachedPromise {
313
+ constructor() {
314
+ let resolve;
315
+ let reject;
316
+ this.promise = new Promise((res, rej) => {
317
+ resolve = res;
318
+ reject = rej;
319
+ });
320
+ this.resolve = resolve;
321
+ this.reject = reject;
322
+ }
323
+ }
324
+ export function serveStatic(req, res, path, opts) {
325
+ return new Promise((resolve, reject) => {
326
+ send(req, path, opts)
327
+ .on("directory", () => {
328
+ const err = new Error("No directory access");
329
+ err.code = "ENOENT";
330
+ reject(err);
331
+ })
332
+ .on("error", reject)
333
+ .pipe(res)
334
+ .on("finish", resolve);
335
+ });
336
+ }
337
+ export function formatHostname(hostname) {
338
+ return isIPv6(hostname) ? `[${hostname}]` : hostname;
339
+ }
package/esm/hmr.d.ts ADDED
@@ -0,0 +1,80 @@
1
+ import { IncomingMessage } from "http";
2
+ import { Duplex } from "stream";
3
+ import type webpack from "webpack";
4
+ import { BundleOptions, Project, Update as TurbopackUpdate } from "./types";
5
+ export declare const enum HMR_ACTIONS_SENT_TO_BROWSER {
6
+ RELOAD = "reload",
7
+ CLIENT_CHANGES = "clientChanges",
8
+ SERVER_ONLY_CHANGES = "serverOnlyChanges",
9
+ SYNC = "sync",
10
+ BUILT = "built",
11
+ BUILDING = "building",
12
+ TURBOPACK_MESSAGE = "turbopack-message",
13
+ TURBOPACK_CONNECTED = "turbopack-connected"
14
+ }
15
+ export interface TurbopackMessageAction {
16
+ action: HMR_ACTIONS_SENT_TO_BROWSER.TURBOPACK_MESSAGE;
17
+ data: TurbopackUpdate | TurbopackUpdate[];
18
+ }
19
+ export interface TurbopackConnectedAction {
20
+ action: HMR_ACTIONS_SENT_TO_BROWSER.TURBOPACK_CONNECTED;
21
+ data: {
22
+ sessionId: number;
23
+ };
24
+ }
25
+ interface BuildingAction {
26
+ action: HMR_ACTIONS_SENT_TO_BROWSER.BUILDING;
27
+ }
28
+ export interface CompilationError {
29
+ moduleName?: string;
30
+ message: string;
31
+ details?: string;
32
+ moduleTrace?: Array<{
33
+ moduleName?: string;
34
+ }>;
35
+ stack?: string;
36
+ }
37
+ export interface SyncAction {
38
+ action: HMR_ACTIONS_SENT_TO_BROWSER.SYNC;
39
+ hash: string;
40
+ errors: ReadonlyArray<CompilationError>;
41
+ warnings: ReadonlyArray<CompilationError>;
42
+ updatedModules?: ReadonlyArray<string>;
43
+ }
44
+ export interface BuiltAction {
45
+ action: HMR_ACTIONS_SENT_TO_BROWSER.BUILT;
46
+ hash: string;
47
+ errors: ReadonlyArray<CompilationError>;
48
+ warnings: ReadonlyArray<CompilationError>;
49
+ updatedModules?: ReadonlyArray<string>;
50
+ }
51
+ export interface ReloadAction {
52
+ action: HMR_ACTIONS_SENT_TO_BROWSER.RELOAD;
53
+ data: string;
54
+ }
55
+ export type HMR_ACTION_TYPES = TurbopackMessageAction | TurbopackConnectedAction | BuildingAction | SyncAction | BuiltAction | ReloadAction;
56
+ export interface HotReloaderInterface {
57
+ turbopackProject?: Project;
58
+ serverStats: webpack.Stats | null;
59
+ setHmrServerError(error: Error | null): void;
60
+ clearHmrServerError(): void;
61
+ start(): Promise<void>;
62
+ send(action: HMR_ACTION_TYPES): void;
63
+ onHMR(req: IncomingMessage, socket: Duplex, head: Buffer, onUpgrade?: (client: {
64
+ send(data: string): void;
65
+ }) => void): void;
66
+ buildFallbackError(): Promise<void>;
67
+ close(): void;
68
+ }
69
+ export type ChangeSubscriptions = Map<string, Promise<AsyncIterableIterator<TurbopackResult>>>;
70
+ export type ReadyIds = Set<string>;
71
+ export type StartBuilding = (id: string, forceRebuild: boolean) => () => void;
72
+ export type ClientState = {
73
+ hmrPayloads: Map<string, HMR_ACTION_TYPES>;
74
+ turbopackUpdates: TurbopackUpdate[];
75
+ subscriptions: Map<string, AsyncIterator<any>>;
76
+ };
77
+ export type SendHmr = (id: string, payload: HMR_ACTION_TYPES) => void;
78
+ export declare const FAST_REFRESH_RUNTIME_RELOAD = "Fast Refresh had to perform a full reload due to a runtime error.";
79
+ export declare function createHotReloader(bundleOptions: BundleOptions, projectPath?: string, rootPath?: string): Promise<HotReloaderInterface>;
80
+ export {};
package/esm/hmr.js ADDED
@@ -0,0 +1,278 @@
1
+ import { nanoid } from "nanoid";
2
+ import ws from "ws";
3
+ import { projectFactory } from "./project";
4
+ import { createDefineEnv, debounce, processIssues } from "./util";
5
+ const wsServer = new ws.Server({ noServer: true });
6
+ const sessionId = Math.floor(Number.MAX_SAFE_INTEGER * Math.random());
7
+ export const FAST_REFRESH_RUNTIME_RELOAD = "Fast Refresh had to perform a full reload due to a runtime error.";
8
+ export async function createHotReloader(bundleOptions, projectPath, rootPath) {
9
+ var _a;
10
+ const createProject = projectFactory();
11
+ const project = await createProject({
12
+ processEnv: (_a = bundleOptions.processEnv) !== null && _a !== void 0 ? _a : {},
13
+ processDefineEnv: createDefineEnv({
14
+ config: bundleOptions.config,
15
+ dev: true,
16
+ optionDefineEnv: bundleOptions.processDefineEnv,
17
+ }),
18
+ watch: {
19
+ enable: true,
20
+ },
21
+ dev: true,
22
+ buildId: nanoid(),
23
+ config: {
24
+ ...bundleOptions.config,
25
+ mode: "development",
26
+ optimization: {
27
+ ...bundleOptions.config.optimization,
28
+ minify: false,
29
+ moduleIds: "named",
30
+ },
31
+ },
32
+ projectPath: projectPath || process.cwd(),
33
+ rootPath: rootPath || projectPath || process.cwd(),
34
+ }, {
35
+ persistentCaching: true,
36
+ });
37
+ const entrypointsSubscription = project.entrypointsSubscribe();
38
+ let currentEntriesHandlingResolve;
39
+ let currentEntriesHandling = new Promise((resolve) => (currentEntriesHandlingResolve = resolve));
40
+ let hmrEventHappened = false;
41
+ let hmrHash = 0;
42
+ const clients = new Set();
43
+ const clientStates = new WeakMap();
44
+ function sendToClient(client, payload) {
45
+ client.send(JSON.stringify(payload));
46
+ }
47
+ function sendEnqueuedMessages() {
48
+ for (const client of clients) {
49
+ const state = clientStates.get(client);
50
+ if (!state) {
51
+ continue;
52
+ }
53
+ for (const payload of state.hmrPayloads.values()) {
54
+ sendToClient(client, payload);
55
+ }
56
+ state.hmrPayloads.clear();
57
+ if (state.turbopackUpdates.length > 0) {
58
+ sendToClient(client, {
59
+ action: "turbopack-message" /* HMR_ACTIONS_SENT_TO_BROWSER.TURBOPACK_MESSAGE */,
60
+ data: state.turbopackUpdates,
61
+ });
62
+ state.turbopackUpdates.length = 0;
63
+ }
64
+ }
65
+ }
66
+ const sendEnqueuedMessagesDebounce = debounce(sendEnqueuedMessages, 2);
67
+ function sendTurbopackMessage(payload) {
68
+ var _a;
69
+ payload.diagnostics = [];
70
+ payload.issues = [];
71
+ for (const client of clients) {
72
+ (_a = clientStates.get(client)) === null || _a === void 0 ? void 0 : _a.turbopackUpdates.push(payload);
73
+ }
74
+ hmrEventHappened = true;
75
+ sendEnqueuedMessagesDebounce();
76
+ }
77
+ async function subscribeToHmrEvents(client, id) {
78
+ const state = clientStates.get(client);
79
+ if (!state || state.subscriptions.has(id)) {
80
+ return;
81
+ }
82
+ const subscription = project.hmrEvents(id);
83
+ state.subscriptions.set(id, subscription);
84
+ // The subscription will always emit once, which is the initial
85
+ // computation. This is not a change, so swallow it.
86
+ try {
87
+ await subscription.next();
88
+ for await (const data of subscription) {
89
+ processIssues(data, true, true);
90
+ if (data.type !== "issues") {
91
+ sendTurbopackMessage(data);
92
+ }
93
+ }
94
+ }
95
+ catch (e) {
96
+ // The client might be using an HMR session from a previous server, tell them
97
+ // to fully reload the page to resolve the issue. We can't use
98
+ // `hotReloader.send` since that would force every connected client to
99
+ // reload, only this client is out of date.
100
+ const reloadAction = {
101
+ action: "reload" /* HMR_ACTIONS_SENT_TO_BROWSER.RELOAD */,
102
+ data: `error in HMR event subscription for ${id}: ${e}`,
103
+ };
104
+ sendToClient(client, reloadAction);
105
+ client.close();
106
+ return;
107
+ }
108
+ }
109
+ function unsubscribeFromHmrEvents(client, id) {
110
+ const state = clientStates.get(client);
111
+ if (!state) {
112
+ return;
113
+ }
114
+ const subscription = state.subscriptions.get(id);
115
+ subscription === null || subscription === void 0 ? void 0 : subscription.return();
116
+ }
117
+ async function handleEntrypointsSubscription() {
118
+ for await (const entrypoints of entrypointsSubscription) {
119
+ if (!currentEntriesHandlingResolve) {
120
+ currentEntriesHandling = new Promise(
121
+ // eslint-disable-next-line no-loop-func
122
+ (resolve) => (currentEntriesHandlingResolve = resolve));
123
+ }
124
+ await Promise.all(entrypoints.apps.map((l) => l.writeToDisk().then((res) => processIssues(res, true, true))));
125
+ currentEntriesHandlingResolve();
126
+ currentEntriesHandlingResolve = undefined;
127
+ }
128
+ }
129
+ const hotReloader = {
130
+ turbopackProject: project,
131
+ serverStats: null,
132
+ onHMR(req, socket, head, onUpgrade) {
133
+ wsServer.handleUpgrade(req, socket, head, (client) => {
134
+ onUpgrade === null || onUpgrade === void 0 ? void 0 : onUpgrade(client);
135
+ const subscriptions = new Map();
136
+ clients.add(client);
137
+ clientStates.set(client, {
138
+ hmrPayloads: new Map(),
139
+ turbopackUpdates: [],
140
+ subscriptions,
141
+ });
142
+ client.on("close", () => {
143
+ var _a;
144
+ // Remove active subscriptions
145
+ for (const subscription of subscriptions.values()) {
146
+ (_a = subscription.return) === null || _a === void 0 ? void 0 : _a.call(subscription);
147
+ }
148
+ clientStates.delete(client);
149
+ clients.delete(client);
150
+ });
151
+ client.addEventListener("message", ({ data }) => {
152
+ const parsedData = JSON.parse(typeof data !== "string" ? data.toString() : data);
153
+ // messages
154
+ switch (parsedData.event) {
155
+ case "client-error": // { errorCount, clientId }
156
+ case "client-warning": // { warningCount, clientId }
157
+ case "client-success": // { clientId }
158
+ case "client-full-reload": // { stackTrace, hadRuntimeError }
159
+ const { hadRuntimeError, dependencyChain } = parsedData;
160
+ if (hadRuntimeError) {
161
+ console.warn(FAST_REFRESH_RUNTIME_RELOAD);
162
+ }
163
+ if (Array.isArray(dependencyChain) &&
164
+ typeof dependencyChain[0] === "string") {
165
+ const cleanedModulePath = dependencyChain[0]
166
+ .replace(/^\[project\]/, ".")
167
+ .replace(/ \[.*\] \(.*\)$/, "");
168
+ console.warn(`Fast Refresh had to perform a full reload when ${cleanedModulePath} changed.`);
169
+ }
170
+ break;
171
+ default:
172
+ // Might be a Turbopack message...
173
+ if (!parsedData.type) {
174
+ throw new Error(`unrecognized HMR message "${data}"`);
175
+ }
176
+ }
177
+ // Turbopack messages
178
+ switch (parsedData.type) {
179
+ case "turbopack-subscribe":
180
+ subscribeToHmrEvents(client, parsedData.path);
181
+ break;
182
+ case "turbopack-unsubscribe":
183
+ unsubscribeFromHmrEvents(client, parsedData.path);
184
+ break;
185
+ default:
186
+ if (!parsedData.event) {
187
+ throw new Error(`unrecognized Turbopack HMR message "${data}"`);
188
+ }
189
+ }
190
+ });
191
+ const turbopackConnected = {
192
+ action: "turbopack-connected" /* HMR_ACTIONS_SENT_TO_BROWSER.TURBOPACK_CONNECTED */,
193
+ data: { sessionId },
194
+ };
195
+ sendToClient(client, turbopackConnected);
196
+ const errors = [];
197
+ (async function () {
198
+ const sync = {
199
+ action: "sync" /* HMR_ACTIONS_SENT_TO_BROWSER.SYNC */,
200
+ errors,
201
+ warnings: [],
202
+ hash: "",
203
+ };
204
+ sendToClient(client, sync);
205
+ })();
206
+ });
207
+ },
208
+ send(action) {
209
+ const payload = JSON.stringify(action);
210
+ for (const client of clients) {
211
+ client.send(payload);
212
+ }
213
+ },
214
+ setHmrServerError(_error) {
215
+ // Not implemented yet.
216
+ },
217
+ clearHmrServerError() {
218
+ // Not implemented yet.
219
+ },
220
+ async start() { },
221
+ async buildFallbackError() {
222
+ // Not implemented yet.
223
+ },
224
+ close() {
225
+ for (const wsClient of clients) {
226
+ // it's okay to not cleanly close these websocket connections, this is dev
227
+ wsClient.terminate();
228
+ }
229
+ clients.clear();
230
+ },
231
+ };
232
+ handleEntrypointsSubscription().catch((err) => {
233
+ console.error(err);
234
+ process.exit(1);
235
+ });
236
+ // Write empty manifests
237
+ await currentEntriesHandling;
238
+ async function handleProjectUpdates() {
239
+ for await (const updateMessage of project.updateInfoSubscribe(30)) {
240
+ switch (updateMessage.updateType) {
241
+ case "start": {
242
+ hotReloader.send({ action: "building" /* HMR_ACTIONS_SENT_TO_BROWSER.BUILDING */ });
243
+ break;
244
+ }
245
+ case "end": {
246
+ sendEnqueuedMessages();
247
+ const errors = new Map();
248
+ for (const client of clients) {
249
+ const state = clientStates.get(client);
250
+ if (!state) {
251
+ continue;
252
+ }
253
+ const clientErrors = new Map(errors);
254
+ sendToClient(client, {
255
+ action: "built" /* HMR_ACTIONS_SENT_TO_BROWSER.BUILT */,
256
+ hash: String(++hmrHash),
257
+ errors: [...clientErrors.values()],
258
+ warnings: [],
259
+ });
260
+ }
261
+ if (hmrEventHappened) {
262
+ const time = updateMessage.value.duration;
263
+ const timeMessage = time > 2000 ? `${Math.round(time / 100) / 10}s` : `${time}ms`;
264
+ console.log(`Compiled in ${timeMessage}`);
265
+ hmrEventHappened = false;
266
+ }
267
+ break;
268
+ }
269
+ default:
270
+ }
271
+ }
272
+ }
273
+ handleProjectUpdates().catch((err) => {
274
+ console.error(err);
275
+ process.exit(1);
276
+ });
277
+ return hotReloader;
278
+ }