@utoo/pack 0.0.1-alpha.2 → 0.0.1-alpha.20

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,346 @@
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 { compatOptionsFromWebpack } from "./webpackCompat";
12
+ import { xcodeProfilingReady } from "./xcodeProfile";
13
+ export function serve(options, projectPath, rootPath, serverOptions) {
14
+ const bundleOptions = options.compatMode
15
+ ? compatOptionsFromWebpack(options)
16
+ : options;
17
+ return serveInternal(bundleOptions, projectPath, rootPath, serverOptions);
18
+ }
19
+ async function serveInternal(options, projectPath, rootPath, serverOptions) {
20
+ blockStdout();
21
+ if (process.env.XCODE_PROFILE) {
22
+ await xcodeProfilingReady();
23
+ }
24
+ startServer({
25
+ hostname: (serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.hostname) || "localhost",
26
+ port: (serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.port) || 3000,
27
+ https: serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.https,
28
+ selfSignedCertificate: (serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.https)
29
+ ? await createSelfSignedCertificate((serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.hostname) || "localhost")
30
+ : undefined,
31
+ }, options, projectPath || process.cwd(), rootPath);
32
+ }
33
+ export async function startServer(serverOptions, bundleOptions, projectPath, rootPath) {
34
+ let { port, hostname, selfSignedCertificate } = serverOptions;
35
+ process.title = "utoo-pack-dev-server";
36
+ let handlersReady = () => { };
37
+ let handlersError = () => { };
38
+ let handlersPromise = new Promise((resolve, reject) => {
39
+ handlersReady = resolve;
40
+ handlersError = reject;
41
+ });
42
+ let requestHandler = async (req, res) => {
43
+ if (handlersPromise) {
44
+ await handlersPromise;
45
+ return requestHandler(req, res);
46
+ }
47
+ throw new Error("Invariant request handler was not setup");
48
+ };
49
+ let upgradeHandler = async (req, socket, head) => {
50
+ if (handlersPromise) {
51
+ await handlersPromise;
52
+ return upgradeHandler(req, socket, head);
53
+ }
54
+ throw new Error("Invariant upgrade handler was not setup");
55
+ };
56
+ async function requestListener(req, res) {
57
+ try {
58
+ if (handlersPromise) {
59
+ await handlersPromise;
60
+ handlersPromise = undefined;
61
+ }
62
+ await requestHandler(req, res);
63
+ }
64
+ catch (err) {
65
+ res.statusCode = 500;
66
+ res.end("Internal Server Error");
67
+ console.error(`Failed to handle request for ${req.url}`);
68
+ console.error(err);
69
+ }
70
+ }
71
+ const server = selfSignedCertificate
72
+ ? https.createServer({
73
+ key: fs.readFileSync(selfSignedCertificate.key),
74
+ cert: fs.readFileSync(selfSignedCertificate.cert),
75
+ }, requestListener)
76
+ : http.createServer(requestListener);
77
+ server.on("upgrade", async (req, socket, head) => {
78
+ try {
79
+ await upgradeHandler(req, socket, head);
80
+ }
81
+ catch (err) {
82
+ socket.destroy();
83
+ console.error(`Failed to handle request for ${req.url}`);
84
+ console.error(err);
85
+ }
86
+ });
87
+ let portRetryCount = 0;
88
+ const originalPort = port;
89
+ server.on("error", (err) => {
90
+ if (port && err.code === "EADDRINUSE" && portRetryCount < 10) {
91
+ port += 1;
92
+ portRetryCount += 1;
93
+ server.listen(port, hostname);
94
+ }
95
+ else {
96
+ console.error(`Failed to start server`);
97
+ console.error(err);
98
+ process.exit(1);
99
+ }
100
+ });
101
+ await new Promise((resolve) => {
102
+ server.on("listening", async () => {
103
+ const addr = server.address();
104
+ const actualHostname = formatHostname(typeof addr === "object"
105
+ ? (addr === null || addr === void 0 ? void 0 : addr.address) || hostname || "localhost"
106
+ : addr);
107
+ const formattedHostname = !hostname || actualHostname === "0.0.0.0"
108
+ ? "localhost"
109
+ : actualHostname === "[::]"
110
+ ? "[::1]"
111
+ : formatHostname(hostname);
112
+ port = typeof addr === "object" ? (addr === null || addr === void 0 ? void 0 : addr.port) || port : port;
113
+ if (portRetryCount) {
114
+ console.warn(`Port ${originalPort} is in use, using available port ${port} instead.`);
115
+ }
116
+ console.log(`Listening on ${serverOptions.https ? "https" : "http"}://${formattedHostname}:${port} ...`);
117
+ try {
118
+ let cleanupStarted = false;
119
+ let closeUpgraded = null;
120
+ const cleanup = () => {
121
+ if (cleanupStarted) {
122
+ return;
123
+ }
124
+ cleanupStarted = true;
125
+ (async () => {
126
+ console.debug("start-server process cleanup");
127
+ await new Promise((res) => {
128
+ server.close((err) => {
129
+ if (err)
130
+ console.error(err);
131
+ res();
132
+ });
133
+ server.closeAllConnections();
134
+ closeUpgraded === null || closeUpgraded === void 0 ? void 0 : closeUpgraded();
135
+ });
136
+ console.debug("start-server process cleanup finished");
137
+ process.exit(0);
138
+ })();
139
+ };
140
+ const exception = (err) => {
141
+ console.error(err);
142
+ };
143
+ process.on("SIGINT", cleanup);
144
+ process.on("SIGTERM", cleanup);
145
+ process.on("rejectionHandled", () => { });
146
+ process.on("uncaughtException", exception);
147
+ process.on("unhandledRejection", exception);
148
+ const initResult = await initialize(bundleOptions, projectPath, rootPath);
149
+ requestHandler = initResult.requestHandler;
150
+ upgradeHandler = initResult.upgradeHandler;
151
+ closeUpgraded = initResult.closeUpgraded;
152
+ handlersReady();
153
+ }
154
+ catch (err) {
155
+ handlersError();
156
+ console.error(err);
157
+ process.exit(1);
158
+ }
159
+ resolve();
160
+ });
161
+ server.listen(port, hostname);
162
+ });
163
+ }
164
+ export async function initialize(bundleOptions, projectPath, rootPath) {
165
+ process.env.NODE_ENV = "development";
166
+ const hotReloader = await createHotReloader(bundleOptions, projectPath, rootPath);
167
+ await hotReloader.start();
168
+ const requestHandlerImpl = async (req, res) => {
169
+ req.on("error", console.error);
170
+ res.on("error", console.error);
171
+ const handleRequest = async () => {
172
+ var _a;
173
+ if (!(req.method === "GET" || req.method === "HEAD")) {
174
+ res.setHeader("Allow", ["GET", "HEAD"]);
175
+ res.statusCode = 405;
176
+ res.end();
177
+ }
178
+ const distRoot = path.resolve(projectPath, ((_a = bundleOptions.config.output) === null || _a === void 0 ? void 0 : _a.path) || "./dist");
179
+ try {
180
+ const reqUrl = req.url || "";
181
+ const path = url.parse(reqUrl).pathname || "";
182
+ return await serveStatic(req, res, path, { root: distRoot });
183
+ }
184
+ catch (err) {
185
+ res.setHeader("Cache-Control", "private, no-cache, no-store, max-age=0, must-revalidate");
186
+ res.statusCode = 404;
187
+ res.end();
188
+ }
189
+ };
190
+ try {
191
+ await handleRequest();
192
+ }
193
+ catch (err) {
194
+ res.statusCode = 500;
195
+ res.end("Internal Server Error");
196
+ }
197
+ };
198
+ let requestHandler = requestHandlerImpl;
199
+ const logError = async (type, err) => {
200
+ if (type === "unhandledRejection") {
201
+ console.error("unhandledRejection: ", err);
202
+ }
203
+ else if (type === "uncaughtException") {
204
+ console.error("uncaughtException: ", err);
205
+ }
206
+ };
207
+ process.on("uncaughtException", logError.bind(null, "uncaughtException"));
208
+ process.on("unhandledRejection", logError.bind(null, "unhandledRejection"));
209
+ const upgradeHandler = async (req, socket, head) => {
210
+ var _a;
211
+ try {
212
+ const isHMRRequest = (_a = req.url) === null || _a === void 0 ? void 0 : _a.includes("turbopack-hmr");
213
+ if (isHMRRequest) {
214
+ hotReloader.onHMR(req, socket, head);
215
+ }
216
+ else {
217
+ socket.end();
218
+ }
219
+ }
220
+ catch (err) {
221
+ console.error("Error handling upgrade request", err);
222
+ socket.end();
223
+ }
224
+ };
225
+ return {
226
+ requestHandler,
227
+ upgradeHandler,
228
+ closeUpgraded() {
229
+ hotReloader.close();
230
+ },
231
+ };
232
+ }
233
+ export async function pipeToNodeResponse(readable, res, waitUntilForEnd) {
234
+ try {
235
+ const { errored, destroyed } = res;
236
+ if (errored || destroyed)
237
+ return;
238
+ const controller = createAbortController(res);
239
+ const writer = createWriterFromResponse(res, waitUntilForEnd);
240
+ await readable.pipeTo(writer, { signal: controller.signal });
241
+ }
242
+ catch (err) {
243
+ if (isAbortError(err))
244
+ return;
245
+ throw new Error("failed to pipe response", { cause: err });
246
+ }
247
+ }
248
+ export function createAbortController(response) {
249
+ const controller = new AbortController();
250
+ response.once("close", () => {
251
+ if (response.writableFinished)
252
+ return;
253
+ controller.abort(new ResponseAborted());
254
+ });
255
+ return controller;
256
+ }
257
+ export function isAbortError(e) {
258
+ return (e === null || e === void 0 ? void 0 : e.name) === "AbortError" || (e === null || e === void 0 ? void 0 : e.name) === ResponseAbortedName;
259
+ }
260
+ export const ResponseAbortedName = "ResponseAborted";
261
+ export class ResponseAborted extends Error {
262
+ constructor() {
263
+ super(...arguments);
264
+ this.name = ResponseAbortedName;
265
+ }
266
+ }
267
+ function createWriterFromResponse(res, waitUntilForEnd) {
268
+ let started = false;
269
+ let drained = new DetachedPromise();
270
+ function onDrain() {
271
+ drained.resolve();
272
+ }
273
+ res.on("drain", onDrain);
274
+ res.once("close", () => {
275
+ res.off("drain", onDrain);
276
+ drained.resolve();
277
+ });
278
+ const finished = new DetachedPromise();
279
+ res.once("finish", () => {
280
+ finished.resolve();
281
+ });
282
+ return new WritableStream({
283
+ write: async (chunk) => {
284
+ if (!started) {
285
+ started = true;
286
+ res.flushHeaders();
287
+ }
288
+ try {
289
+ const ok = res.write(chunk);
290
+ if ("flush" in res && typeof res.flush === "function") {
291
+ res.flush();
292
+ }
293
+ if (!ok) {
294
+ await drained.promise;
295
+ drained = new DetachedPromise();
296
+ }
297
+ }
298
+ catch (err) {
299
+ res.end();
300
+ throw new Error("failed to write chunk to response", { cause: err });
301
+ }
302
+ },
303
+ abort: (err) => {
304
+ if (res.writableFinished)
305
+ return;
306
+ res.destroy(err);
307
+ },
308
+ close: async () => {
309
+ if (waitUntilForEnd) {
310
+ await waitUntilForEnd;
311
+ }
312
+ if (res.writableFinished)
313
+ return;
314
+ res.end();
315
+ return finished.promise;
316
+ },
317
+ });
318
+ }
319
+ export class DetachedPromise {
320
+ constructor() {
321
+ let resolve;
322
+ let reject;
323
+ this.promise = new Promise((res, rej) => {
324
+ resolve = res;
325
+ reject = rej;
326
+ });
327
+ this.resolve = resolve;
328
+ this.reject = reject;
329
+ }
330
+ }
331
+ export function serveStatic(req, res, path, opts) {
332
+ return new Promise((resolve, reject) => {
333
+ send(req, path, opts)
334
+ .on("directory", () => {
335
+ const err = new Error("No directory access");
336
+ err.code = "ENOENT";
337
+ reject(err);
338
+ })
339
+ .on("error", reject)
340
+ .pipe(res)
341
+ .on("finish", resolve);
342
+ });
343
+ }
344
+ export function formatHostname(hostname) {
345
+ return isIPv6(hostname) ? `[${hostname}]` : hostname;
346
+ }
@@ -1,8 +1,7 @@
1
- import { ModernSourceMapPayload } from "./sourceMap";
2
- import { Project, ProjectOptions, Update as TurbopackUpdate } from "./types";
3
- import type webpack from "webpack";
4
1
  import { IncomingMessage } from "http";
5
2
  import { Duplex } from "stream";
3
+ import type webpack from "webpack";
4
+ import { BundleOptions, Project, Update as TurbopackUpdate } from "./types";
6
5
  export declare const enum HMR_ACTIONS_SENT_TO_BROWSER {
7
6
  RELOAD = "reload",
8
7
  CLIENT_CHANGES = "clientChanges",
@@ -17,10 +16,6 @@ export interface TurbopackMessageAction {
17
16
  action: HMR_ACTIONS_SENT_TO_BROWSER.TURBOPACK_MESSAGE;
18
17
  data: TurbopackUpdate | TurbopackUpdate[];
19
18
  }
20
- export interface TurbopackMessageAction {
21
- action: HMR_ACTIONS_SENT_TO_BROWSER.TURBOPACK_MESSAGE;
22
- data: TurbopackUpdate | TurbopackUpdate[];
23
- }
24
19
  export interface TurbopackConnectedAction {
25
20
  action: HMR_ACTIONS_SENT_TO_BROWSER.TURBOPACK_CONNECTED;
26
21
  data: {
@@ -65,14 +60,12 @@ export interface HotReloaderInterface {
65
60
  clearHmrServerError(): void;
66
61
  start(): Promise<void>;
67
62
  send(action: HMR_ACTION_TYPES): void;
68
- onHMR(req: IncomingMessage, _socket: Duplex, head: Buffer, onUpgrade: (client: {
63
+ onHMR(req: IncomingMessage, socket: Duplex, head: Buffer, onUpgrade?: (client: {
69
64
  send(data: string): void;
70
65
  }) => void): void;
71
66
  buildFallbackError(): Promise<void>;
72
67
  close(): void;
73
68
  }
74
- type FindSourceMapPayload = (sourceURL: string) => ModernSourceMapPayload | undefined;
75
- export declare function setBundlerFindSourceMapImplementation(findSourceMapImplementation: FindSourceMapPayload): void;
76
69
  export type ChangeSubscriptions = Map<string, Promise<AsyncIterableIterator<TurbopackResult>>>;
77
70
  export type ReadyIds = Set<string>;
78
71
  export type StartBuilding = (id: string, forceRebuild: boolean) => () => void;
@@ -83,5 +76,5 @@ export type ClientState = {
83
76
  };
84
77
  export type SendHmr = (id: string, payload: HMR_ACTION_TYPES) => void;
85
78
  export declare const FAST_REFRESH_RUNTIME_RELOAD = "Fast Refresh had to perform a full reload due to a runtime error.";
86
- export declare function createHotReloader(projectOptions: ProjectOptions, projectPath?: string, rootPath?: string): Promise<HotReloaderInterface>;
79
+ export declare function createHotReloader(bundleOptions: BundleOptions, projectPath?: string, rootPath?: string): Promise<HotReloaderInterface>;
87
80
  export {};
@@ -1,71 +1,30 @@
1
+ import { nanoid } from "nanoid";
1
2
  import ws from "ws";
2
- import { join } from "path";
3
- import { pathToFileURL } from "url";
4
3
  import { projectFactory } from "./project";
5
4
  import { createDefineEnv, debounce, processIssues } from "./util";
6
- import { nanoid } from "nanoid";
7
5
  const wsServer = new ws.Server({ noServer: true });
8
6
  const sessionId = Math.floor(Number.MAX_SAFE_INTEGER * Math.random());
9
- /**
10
- * Replaces turbopack:///[project] with the specified project in the `source` field.
11
- */
12
- function rewriteTurbopackSources(projectRoot, sourceMap) {
13
- if ("sections" in sourceMap) {
14
- for (const section of sourceMap.sections) {
15
- rewriteTurbopackSources(projectRoot, section.map);
16
- }
17
- }
18
- else {
19
- for (let i = 0; i < sourceMap.sources.length; i++) {
20
- sourceMap.sources[i] = pathToFileURL(join(projectRoot, sourceMap.sources[i].replace(/turbopack:\/\/\/\[project\]/, ""))).toString();
21
- }
22
- }
23
- }
24
- function getSourceMapFromTurbopack(project, projectRoot, sourceURL) {
25
- let sourceMapJson = null;
26
- try {
27
- sourceMapJson = project.getSourceMapSync(sourceURL);
28
- }
29
- catch (err) { }
30
- if (sourceMapJson === null) {
31
- return undefined;
32
- }
33
- else {
34
- const payload = JSON.parse(sourceMapJson);
35
- // The sourcemap from Turbopack is not yet written to disk so its `sources`
36
- // are not absolute paths yet. We need to rewrite them to be absolute paths.
37
- rewriteTurbopackSources(projectRoot, payload);
38
- return payload;
39
- }
40
- }
41
- // Find a source map using the bundler's API.
42
- // This is only a fallback for when Node.js fails to due to bugs e.g. https://github.com/nodejs/node/issues/52102
43
- // TODO: Remove once all supported Node.js versions are fixed.
44
- // TODO(veil): Set from Webpack as well
45
- let bundlerFindSourceMapPayload = () => undefined;
46
- export function setBundlerFindSourceMapImplementation(findSourceMapImplementation) {
47
- bundlerFindSourceMapPayload = findSourceMapImplementation;
48
- }
49
7
  export const FAST_REFRESH_RUNTIME_RELOAD = "Fast Refresh had to perform a full reload due to a runtime error.";
50
- export async function createHotReloader(projectOptions, projectPath, rootPath) {
51
- var _a, _b;
8
+ export async function createHotReloader(bundleOptions, projectPath, rootPath) {
9
+ var _a;
52
10
  const createProject = projectFactory();
53
11
  const project = await createProject({
54
- processEnv: (_a = projectOptions.processEnv) !== null && _a !== void 0 ? _a : {},
55
- processDefineEnv: createDefineEnv({
56
- config: projectOptions.config,
12
+ processEnv: (_a = bundleOptions.processEnv) !== null && _a !== void 0 ? _a : {},
13
+ defineEnv: createDefineEnv({
14
+ config: bundleOptions.config,
57
15
  dev: true,
58
- optionDefineEnv: projectOptions.processDefineEnv,
16
+ optionDefineEnv: bundleOptions.defineEnv,
59
17
  }),
60
- watch: (_b = projectOptions.watch) !== null && _b !== void 0 ? _b : {
61
- enable: false,
18
+ watch: {
19
+ enable: true,
62
20
  },
63
21
  dev: true,
64
- buildId: nanoid(),
22
+ buildId: bundleOptions.buildId || nanoid(),
65
23
  config: {
66
- ...projectOptions.config,
24
+ ...bundleOptions.config,
25
+ mode: "development",
67
26
  optimization: {
68
- ...projectOptions.config.optimization,
27
+ ...bundleOptions.config.optimization,
69
28
  minify: false,
70
29
  moduleIds: "named",
71
30
  },
@@ -75,7 +34,6 @@ export async function createHotReloader(projectOptions, projectPath, rootPath) {
75
34
  }, {
76
35
  persistentCaching: true,
77
36
  });
78
- setBundlerFindSourceMapImplementation(getSourceMapFromTurbopack.bind(null, project, projectOptions.projectPath));
79
37
  const entrypointsSubscription = project.entrypointsSubscribe();
80
38
  let currentEntriesHandlingResolve;
81
39
  let currentEntriesHandling = new Promise((resolve) => (currentEntriesHandlingResolve = resolve));
@@ -108,9 +66,6 @@ export async function createHotReloader(projectOptions, projectPath, rootPath) {
108
66
  const sendEnqueuedMessagesDebounce = debounce(sendEnqueuedMessages, 2);
109
67
  function sendTurbopackMessage(payload) {
110
68
  var _a;
111
- // TODO(PACK-2049): For some reason we end up emitting hundreds of issues messages on bigger apps,
112
- // a lot of which are duplicates.
113
- // They are currently not handled on the client at all, so might as well not send them for now.
114
69
  payload.diagnostics = [];
115
70
  payload.issues = [];
116
71
  for (const client of clients) {
@@ -174,10 +129,9 @@ export async function createHotReloader(projectOptions, projectPath, rootPath) {
174
129
  const hotReloader = {
175
130
  turbopackProject: project,
176
131
  serverStats: null,
177
- // TODO: Figure out if socket type can match the HotReloaderInterface
178
132
  onHMR(req, socket, head, onUpgrade) {
179
133
  wsServer.handleUpgrade(req, socket, head, (client) => {
180
- onUpgrade(client);
134
+ onUpgrade === null || onUpgrade === void 0 ? void 0 : onUpgrade(client);
181
135
  const subscriptions = new Map();
182
136
  clients.add(client);
183
137
  clientStates.set(client, {
package/esm/index.d.ts CHANGED
@@ -1,15 +1,14 @@
1
- import { NapiDiagnostic, NapiIssue } from "./binding";
2
- export { build } from "./build";
3
- export { watch } from "./watch";
4
- declare global {
5
- export type TurbopackResult<T = {}> = T & {
6
- issues: NapiIssue[];
7
- diagnostics: NapiDiagnostic[];
8
- };
9
- export type RefCell = {
10
- readonly __tag: unique symbol;
11
- };
12
- export type ExternalEndpoint = {
13
- readonly __tag: unique symbol;
14
- };
1
+ import { build } from "./build";
2
+ import { serve } from "./dev";
3
+ import * as webpackCompat from "./webpackCompat";
4
+ export { build };
5
+ export { serve };
6
+ declare const utoopack: {
7
+ build: typeof build;
8
+ serve: typeof serve;
9
+ };
10
+ export default utoopack;
11
+ export type WebpackConfig = webpackCompat.WebpackConfig;
12
+ declare namespace utoopack {
13
+ type WebpackConfig = webpackCompat.WebpackConfig;
15
14
  }
package/esm/index.js CHANGED
@@ -1,2 +1,6 @@
1
- export { build } from "./build";
2
- export { watch } from "./watch";
1
+ import { build } from "./build";
2
+ import { serve } from "./dev";
3
+ export { build };
4
+ export { serve };
5
+ const utoopack = { build, serve };
6
+ export default utoopack;
@@ -0,0 +1,7 @@
1
+ export interface SelfSignedCertificate {
2
+ key: string;
3
+ cert: string;
4
+ rootCA?: string;
5
+ }
6
+ export declare function createSelfSignedCertificate(host?: string, certDir?: string): Promise<SelfSignedCertificate | undefined>;
7
+ export declare function getCacheDirectory(fileDirectory: string, envPath?: string): string;