@utoo/pack 1.4.3 → 1.4.4-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 CHANGED
@@ -9,6 +9,11 @@ export declare class ExternalObject<T> {
9
9
  [K: symbol]: T
10
10
  }
11
11
  }
12
+ export type Lockfile = { __napiType: "Lockfile" }
13
+ export declare function lockfileTryAcquireSync(path: string, content?: string | undefined | null): Lockfile | null
14
+ export declare function lockfileTryAcquire(path: string, content?: string | undefined | null): Promise<Lockfile | null>
15
+ export declare function lockfileUnlockSync(lockfile: Lockfile): void
16
+ export declare function lockfileUnlock(lockfile: Lockfile): Promise<void>
12
17
  export declare function registerWorkerScheduler(creator: (arg: NapiWorkerCreation) => any, terminator: (arg: NapiWorkerTermination) => any): void
13
18
  export declare function workerCreated(workerId: number): void
14
19
  export interface NapiWorkerCreation {
package/cjs/binding.js CHANGED
@@ -319,7 +319,11 @@ if (!nativeBinding) {
319
319
  }
320
320
  throw new Error(`Failed to load native binding`);
321
321
  }
322
- const { registerWorkerScheduler, workerCreated, recvTaskMessageInWorker, sendTaskMessage, endpointWriteToDisk, endpointServerChangedSubscribe, endpointClientChangedSubscribe, projectNew, projectUpdate, projectOnExit, projectShutdown, projectWriteAllEntrypointsToDisk, projectEntrypointsSubscribe, projectHmrEvents, projectHmrIdentifiersSubscribe, projectUpdateInfoSubscribe, projectTraceSource, projectGetSourceForAsset, projectGetSourceMap, projectGetSourceMapSync, rootTaskDispose, initCustomTraceSubscriber, teardownTraceSubscriber } = nativeBinding;
322
+ const { lockfileTryAcquireSync, lockfileTryAcquire, lockfileUnlockSync, lockfileUnlock, registerWorkerScheduler, workerCreated, recvTaskMessageInWorker, sendTaskMessage, endpointWriteToDisk, endpointServerChangedSubscribe, endpointClientChangedSubscribe, projectNew, projectUpdate, projectOnExit, projectShutdown, projectWriteAllEntrypointsToDisk, projectEntrypointsSubscribe, projectHmrEvents, projectHmrIdentifiersSubscribe, projectUpdateInfoSubscribe, projectTraceSource, projectGetSourceForAsset, projectGetSourceMap, projectGetSourceMapSync, rootTaskDispose, initCustomTraceSubscriber, teardownTraceSubscriber } = nativeBinding;
323
+ module.exports.lockfileTryAcquireSync = lockfileTryAcquireSync;
324
+ module.exports.lockfileTryAcquire = lockfileTryAcquire;
325
+ module.exports.lockfileUnlockSync = lockfileUnlockSync;
326
+ module.exports.lockfileUnlock = lockfileUnlock;
323
327
  module.exports.registerWorkerScheduler = registerWorkerScheduler;
324
328
  module.exports.workerCreated = workerCreated;
325
329
  module.exports.recvTaskMessageInWorker = recvTaskMessageInWorker;
@@ -12,11 +12,13 @@ const path_1 = __importDefault(require("path"));
12
12
  const webpackCompat_1 = require("../config/webpackCompat");
13
13
  const project_1 = require("../core/project");
14
14
  const HtmlPlugin_1 = require("../plugins/HtmlPlugin");
15
+ const cleanOutput_1 = require("../utils/cleanOutput");
15
16
  const common_1 = require("../utils/common");
16
17
  const findRoot_1 = require("../utils/findRoot");
17
18
  const getInitialAssets_1 = require("../utils/getInitialAssets");
18
19
  const htmlEntry_1 = require("../utils/htmlEntry");
19
- const normalize_path_1 = require("../utils/normalize-path");
20
+ const lockfile_1 = require("../utils/lockfile");
21
+ const normalizePath_1 = require("../utils/normalizePath");
20
22
  const runtimePluginStratety_1 = require("../utils/runtimePluginStratety");
21
23
  const validateEntry_1 = require("../utils/validateEntry");
22
24
  const xcodeProfile_1 = require("../utils/xcodeProfile");
@@ -29,7 +31,7 @@ function build(options, projectPath, rootPath) {
29
31
  return buildInternal(bundleOptions, projectPath, rootPath);
30
32
  }
31
33
  async function buildInternal(bundleOptions, projectPath, rootPath) {
32
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
34
+ var _a, _b, _c, _d, _e, _f, _g, _h;
33
35
  (0, common_1.blockStdout)();
34
36
  if (process.env.XCODE_PROFILE) {
35
37
  await (0, xcodeProfile_1.xcodeProfilingReady)();
@@ -39,62 +41,70 @@ async function buildInternal(bundleOptions, projectPath, rootPath) {
39
41
  const persistentCaching = (_a = bundleOptions.config.persistentCaching) !== null && _a !== void 0 ? _a : false;
40
42
  (0, htmlEntry_1.processHtmlEntry)(bundleOptions.config, resolvedProjectPath);
41
43
  (0, validateEntry_1.validateEntryPaths)(bundleOptions.config, resolvedProjectPath);
44
+ await (0, cleanOutput_1.cleanOutput)(bundleOptions.config, resolvedProjectPath);
42
45
  const createProject = (0, project_1.projectFactory)();
43
- const project = await createProject({
44
- processEnv: (_b = bundleOptions.processEnv) !== null && _b !== void 0 ? _b : {},
45
- watch: {
46
- enable: false,
47
- },
48
- dev: (_c = bundleOptions.dev) !== null && _c !== void 0 ? _c : false,
49
- buildId: bundleOptions.buildId || (0, nanoid_1.nanoid)(),
50
- tracing: (_d = bundleOptions.tracing) !== null && _d !== void 0 ? _d : true,
51
- config: {
52
- ...bundleOptions.config,
53
- stats: Boolean(process.env.ANALYZE) ||
54
- bundleOptions.config.stats ||
55
- bundleOptions.config.entry.some((e) => !!e.html),
56
- pluginRuntimeStrategy: (_f = (_e = bundleOptions === null || bundleOptions === void 0 ? void 0 : bundleOptions.config) === null || _e === void 0 ? void 0 : _e.pluginRuntimeStrategy) !== null && _f !== void 0 ? _f : ((0, runtimePluginStratety_1.useWorkerThreads)() ? "workerThreads" : "childProcesses"),
57
- },
58
- projectPath: (0, normalize_path_1.normalizePath)(resolvedProjectPath),
59
- rootPath: resolvedRootPath,
60
- packPath: (0, common_1.getPackPath)(),
61
- }, {
62
- persistentCaching,
63
- // Build mode is a short-lived, one-shot compilation, so avoid paying
64
- // dependency graph bookkeeping cost unless the persistent cache needs it.
65
- dependencyTracking: persistentCaching,
66
- isShortSession: true,
67
- });
68
- const entrypoints = await project.writeAllEntrypointsToDisk();
69
- (0, pack_shared_1.handleIssues)(entrypoints.issues);
70
- const htmlConfigs = [
71
- ...(Array.isArray(bundleOptions.config.html)
72
- ? bundleOptions.config.html
73
- : bundleOptions.config.html
74
- ? [bundleOptions.config.html]
75
- : []),
76
- ...bundleOptions.config.entry
77
- .filter((e) => !!e.html)
78
- .map((e) => e.html),
79
- ];
80
- if (htmlConfigs.length > 0) {
81
- const assets = { js: [], css: [] };
82
- const outputDir = ((_g = bundleOptions.config.output) === null || _g === void 0 ? void 0 : _g.path) || path_1.default.join(process.cwd(), "dist");
83
- if (assets.js.length === 0 && assets.css.length === 0) {
84
- const discovered = (0, getInitialAssets_1.getInitialAssetsFromStats)(outputDir);
85
- assets.js.push(...discovered.js);
86
- assets.css.push(...discovered.css);
46
+ const persistentCacheLock = await (0, lockfile_1.acquirePersistentCacheLock)(resolvedProjectPath, "utoo pack build", persistentCaching);
47
+ let project;
48
+ try {
49
+ project = await createProject({
50
+ processEnv: (_b = bundleOptions.processEnv) !== null && _b !== void 0 ? _b : {},
51
+ watch: {
52
+ enable: false,
53
+ },
54
+ dev: (_c = bundleOptions.dev) !== null && _c !== void 0 ? _c : false,
55
+ buildId: bundleOptions.buildId || (0, nanoid_1.nanoid)(),
56
+ tracing: (_d = bundleOptions.tracing) !== null && _d !== void 0 ? _d : true,
57
+ config: {
58
+ ...bundleOptions.config,
59
+ stats: Boolean(process.env.ANALYZE) ||
60
+ bundleOptions.config.stats ||
61
+ bundleOptions.config.entry.some((e) => !!e.html),
62
+ pluginRuntimeStrategy: (_f = (_e = bundleOptions === null || bundleOptions === void 0 ? void 0 : bundleOptions.config) === null || _e === void 0 ? void 0 : _e.pluginRuntimeStrategy) !== null && _f !== void 0 ? _f : ((0, runtimePluginStratety_1.useWorkerThreads)() ? "workerThreads" : "childProcesses"),
63
+ },
64
+ projectPath: (0, normalizePath_1.normalizePath)(resolvedProjectPath),
65
+ rootPath: resolvedRootPath,
66
+ packPath: (0, common_1.getPackPath)(),
67
+ }, {
68
+ persistentCaching,
69
+ // Build mode is a short-lived, one-shot compilation, so avoid paying
70
+ // dependency graph bookkeeping cost unless the persistent cache needs it.
71
+ dependencyTracking: persistentCaching,
72
+ isShortSession: true,
73
+ });
74
+ const entrypoints = await project.writeAllEntrypointsToDisk();
75
+ (0, pack_shared_1.handleIssues)(entrypoints.issues);
76
+ const htmlConfigs = [
77
+ ...(Array.isArray(bundleOptions.config.html)
78
+ ? bundleOptions.config.html
79
+ : bundleOptions.config.html
80
+ ? [bundleOptions.config.html]
81
+ : []),
82
+ ...bundleOptions.config.entry
83
+ .filter((e) => !!e.html)
84
+ .map((e) => e.html),
85
+ ];
86
+ if (htmlConfigs.length > 0) {
87
+ const assets = { js: [], css: [] };
88
+ const outputDir = (0, cleanOutput_1.getOutputPath)(bundleOptions.config, resolvedProjectPath);
89
+ if (assets.js.length === 0 && assets.css.length === 0) {
90
+ const discovered = (0, getInitialAssets_1.getInitialAssetsFromStats)(outputDir);
91
+ assets.js.push(...discovered.js);
92
+ assets.css.push(...discovered.css);
93
+ }
94
+ const publicPath = (_g = bundleOptions.config.output) === null || _g === void 0 ? void 0 : _g.publicPath;
95
+ for (const config of htmlConfigs) {
96
+ const plugin = new HtmlPlugin_1.HtmlPlugin(config);
97
+ await plugin.generate(outputDir, assets, publicPath);
98
+ }
87
99
  }
88
- const publicPath = (_h = bundleOptions.config.output) === null || _h === void 0 ? void 0 : _h.publicPath;
89
- for (const config of htmlConfigs) {
90
- const plugin = new HtmlPlugin_1.HtmlPlugin(config);
91
- await plugin.generate(outputDir, assets, publicPath);
100
+ if (process.env.ANALYZE) {
101
+ await analyzeBundle(((_h = bundleOptions.config.output) === null || _h === void 0 ? void 0 : _h.path) || "dist");
92
102
  }
93
103
  }
94
- if (process.env.ANALYZE) {
95
- await analyzeBundle(((_j = bundleOptions.config.output) === null || _j === void 0 ? void 0 : _j.path) || "dist");
104
+ finally {
105
+ await (project === null || project === void 0 ? void 0 : project.shutdown());
106
+ persistentCacheLock === null || persistentCacheLock === void 0 ? void 0 : persistentCacheLock.unlockSync();
96
107
  }
97
- await project.shutdown();
98
108
  // TODO: Maybe run tasks in worker is a better way, see
99
109
  // https://github.com/vercel/next.js/blob/512d8283054407ab92b2583ecce3b253c3be7b85/packages/next/src/lib/worker.ts
100
110
  }
@@ -15,10 +15,10 @@ const fs_1 = __importDefault(require("fs"));
15
15
  const get_port_1 = __importDefault(require("get-port"));
16
16
  const hono_1 = require("hono");
17
17
  const https_1 = __importDefault(require("https"));
18
- const path_1 = __importDefault(require("path"));
19
18
  const webpackCompat_1 = require("../config/webpackCompat");
20
19
  const hmr_1 = require("../core/hmr");
21
- const proxy_hono_1 = require("../core/proxy-hono");
20
+ const proxyHono_1 = require("../core/proxyHono");
21
+ const cleanOutput_1 = require("../utils/cleanOutput");
22
22
  const common_1 = require("../utils/common");
23
23
  const findRoot_1 = require("../utils/findRoot");
24
24
  const mkcert_1 = require("../utils/mkcert");
@@ -139,7 +139,7 @@ function serve(options, projectPath, rootPath, serverOptions) {
139
139
  }
140
140
  const HMR_PATH = "/turbopack-hmr";
141
141
  async function runDev(options, projectPath, rootPath, serverOptions) {
142
- var _a, _b, _d, _e, _f, _g, _h;
142
+ var _a, _b, _d, _e, _f;
143
143
  (0, common_1.blockStdout)();
144
144
  process.title = "utoopack-dev-server";
145
145
  if (process.env.XCODE_PROFILE) {
@@ -149,8 +149,8 @@ async function runDev(options, projectPath, rootPath, serverOptions) {
149
149
  const { bundleOptions, projectPathResolved, rootPathResolved, serveOptsBase, } = await resolveDevConfig(options, projectPath, rootPath, serverOptions);
150
150
  const hotReloader = await (0, hmr_1.createHotReloader)(bundleOptions, projectPathResolved, rootPathResolved);
151
151
  await hotReloader.start();
152
- const distRoot = path_1.default.resolve(projectPathResolved, ((_b = (_a = options.config) === null || _a === void 0 ? void 0 : _a.output) === null || _b === void 0 ? void 0 : _b.path) || "./dist");
153
- const publicPath = (_e = (_d = options.config) === null || _d === void 0 ? void 0 : _d.output) === null || _e === void 0 ? void 0 : _e.publicPath;
152
+ const distRoot = (0, cleanOutput_1.getOutputPath)(options.config, projectPathResolved);
153
+ const publicPath = (_b = (_a = options.config) === null || _a === void 0 ? void 0 : _a.output) === null || _b === void 0 ? void 0 : _b.publicPath;
154
154
  // Skip prefix stripping for "runtime" and when publicPath is absent (match dev.ts).
155
155
  const normalizedPrefix = publicPath && publicPath !== "runtime"
156
156
  ? normalizedPublicPath(publicPath)
@@ -191,9 +191,9 @@ async function runDev(options, projectPath, rootPath, serverOptions) {
191
191
  console.error("HMR WebSocket error", err);
192
192
  },
193
193
  })));
194
- const proxyRules = (_g = (_f = bundleOptions.config) === null || _f === void 0 ? void 0 : _f.devServer) === null || _g === void 0 ? void 0 : _g.proxy;
194
+ const proxyRules = (_e = (_d = bundleOptions.config) === null || _d === void 0 ? void 0 : _d.devServer) === null || _e === void 0 ? void 0 : _e.proxy;
195
195
  if (proxyRules && proxyRules.length > 0) {
196
- app.use("*", (0, proxy_hono_1.createHttpProxyMiddleware)(proxyRules));
196
+ app.use("*", (0, proxyHono_1.createHttpProxyMiddleware)(proxyRules));
197
197
  }
198
198
  // GET handles HEAD automatically in Hono; serveStatic serves both
199
199
  app.get("/*", (0, serve_static_1.serveStatic)({
@@ -220,7 +220,7 @@ async function runDev(options, projectPath, rootPath, serverOptions) {
220
220
  server.once("error", onError);
221
221
  });
222
222
  }
223
- await ((_h = serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.onReady) === null || _h === void 0 ? void 0 : _h.call(serverOptions, {
223
+ await ((_f = serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.onReady) === null || _f === void 0 ? void 0 : _f.call(serverOptions, {
224
224
  port: serveOptsBase.port,
225
225
  hostname: serveOptsBase.hostname,
226
226
  }));
package/cjs/core/hmr.js CHANGED
@@ -1,19 +1,17 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.FAST_REFRESH_RUNTIME_RELOAD = exports.HMR_ACTIONS_SENT_TO_BROWSER = void 0;
7
4
  exports.createHotReloader = createHotReloader;
8
5
  const pack_shared_1 = require("@utoo/pack-shared");
9
6
  const nanoid_1 = require("nanoid");
10
- const path_1 = __importDefault(require("path"));
11
7
  const ws_1 = require("ws");
12
8
  const HtmlPlugin_1 = require("../plugins/HtmlPlugin");
9
+ const cleanOutput_1 = require("../utils/cleanOutput");
13
10
  const common_1 = require("../utils/common");
14
11
  const getInitialAssets_1 = require("../utils/getInitialAssets");
15
12
  const htmlEntry_1 = require("../utils/htmlEntry");
16
- const normalize_path_1 = require("../utils/normalize-path");
13
+ const lockfile_1 = require("../utils/lockfile");
14
+ const normalizePath_1 = require("../utils/normalizePath");
17
15
  const runtimePluginStratety_1 = require("../utils/runtimePluginStratety");
18
16
  const validateEntry_1 = require("../utils/validateEntry");
19
17
  const project_1 = require("./project");
@@ -29,35 +27,45 @@ async function createHotReloader(bundleOptions, projectPath, rootPath) {
29
27
  const resolvedRootPath = rootPath || projectPath || process.cwd();
30
28
  (0, htmlEntry_1.processHtmlEntry)(bundleOptions.config, resolvedProjectPath);
31
29
  (0, validateEntry_1.validateEntryPaths)(bundleOptions.config, resolvedProjectPath);
30
+ await (0, cleanOutput_1.cleanOutput)(bundleOptions.config, resolvedProjectPath);
32
31
  const createProject = (0, project_1.projectFactory)();
33
- const project = await createProject({
34
- processEnv: (_a = bundleOptions.processEnv) !== null && _a !== void 0 ? _a : {},
35
- watch: {
36
- enable: true,
37
- },
38
- dev: true,
39
- buildId: bundleOptions.buildId || (0, nanoid_1.nanoid)(),
40
- tracing: (_b = bundleOptions.tracing) !== null && _b !== void 0 ? _b : true,
41
- config: {
42
- ...bundleOptions.config,
43
- mode: "development",
44
- stats: Boolean(process.env.ANALYZE) ||
45
- bundleOptions.config.stats ||
46
- bundleOptions.config.entry.some((e) => !!e.html),
47
- optimization: {
48
- ...bundleOptions.config.optimization,
49
- minify: false,
50
- moduleIds: "named",
32
+ const persistentCaching = (_a = bundleOptions.config.persistentCaching) !== null && _a !== void 0 ? _a : false;
33
+ const persistentCacheLock = await (0, lockfile_1.acquirePersistentCacheLock)(resolvedProjectPath, "utoo pack dev", persistentCaching);
34
+ let project;
35
+ try {
36
+ project = await createProject({
37
+ processEnv: (_b = bundleOptions.processEnv) !== null && _b !== void 0 ? _b : {},
38
+ watch: {
39
+ enable: true,
51
40
  },
52
- persistentCaching: (_d = (_c = bundleOptions === null || bundleOptions === void 0 ? void 0 : bundleOptions.config) === null || _c === void 0 ? void 0 : _c.persistentCaching) !== null && _d !== void 0 ? _d : true,
53
- pluginRuntimeStrategy: (_f = (_e = bundleOptions === null || bundleOptions === void 0 ? void 0 : bundleOptions.config) === null || _e === void 0 ? void 0 : _e.pluginRuntimeStrategy) !== null && _f !== void 0 ? _f : ((0, runtimePluginStratety_1.useWorkerThreads)() ? "workerThreads" : "childProcesses"),
54
- },
55
- projectPath: (0, normalize_path_1.normalizePath)(resolvedProjectPath),
56
- rootPath: resolvedRootPath,
57
- packPath: (0, common_1.getPackPath)(),
58
- }, {
59
- persistentCaching: (_g = bundleOptions.config.persistentCaching) !== null && _g !== void 0 ? _g : false,
60
- });
41
+ dev: true,
42
+ buildId: bundleOptions.buildId || (0, nanoid_1.nanoid)(),
43
+ tracing: (_c = bundleOptions.tracing) !== null && _c !== void 0 ? _c : true,
44
+ config: {
45
+ ...bundleOptions.config,
46
+ mode: "development",
47
+ stats: Boolean(process.env.ANALYZE) ||
48
+ bundleOptions.config.stats ||
49
+ bundleOptions.config.entry.some((e) => !!e.html),
50
+ optimization: {
51
+ ...bundleOptions.config.optimization,
52
+ minify: false,
53
+ moduleIds: "named",
54
+ },
55
+ persistentCaching: (_e = (_d = bundleOptions === null || bundleOptions === void 0 ? void 0 : bundleOptions.config) === null || _d === void 0 ? void 0 : _d.persistentCaching) !== null && _e !== void 0 ? _e : true,
56
+ pluginRuntimeStrategy: (_g = (_f = bundleOptions === null || bundleOptions === void 0 ? void 0 : bundleOptions.config) === null || _f === void 0 ? void 0 : _f.pluginRuntimeStrategy) !== null && _g !== void 0 ? _g : ((0, runtimePluginStratety_1.useWorkerThreads)() ? "workerThreads" : "childProcesses"),
57
+ },
58
+ projectPath: (0, normalizePath_1.normalizePath)(resolvedProjectPath),
59
+ rootPath: resolvedRootPath,
60
+ packPath: (0, common_1.getPackPath)(),
61
+ }, {
62
+ persistentCaching,
63
+ });
64
+ }
65
+ catch (error) {
66
+ persistentCacheLock === null || persistentCacheLock === void 0 ? void 0 : persistentCacheLock.unlockSync();
67
+ throw error;
68
+ }
61
69
  const entrypointsSubscription = project.entrypointsSubscribe();
62
70
  let currentEntriesHandlingResolve;
63
71
  let currentEntriesHandling = new Promise((resolve) => (currentEntriesHandlingResolve = resolve));
@@ -81,6 +89,7 @@ async function createHotReloader(bundleOptions, projectPath, rootPath) {
81
89
  let backgroundWatchGeneration = 0;
82
90
  const backgroundWriteTasks = new Map();
83
91
  let closed = false;
92
+ let closePromise;
84
93
  function sendToClient(client, payload) {
85
94
  client.send(JSON.stringify(payload));
86
95
  }
@@ -115,12 +124,12 @@ async function createHotReloader(bundleOptions, projectPath, rootPath) {
115
124
  sendEnqueuedMessagesDebounce();
116
125
  }
117
126
  async function regenerateHtml() {
118
- var _a, _b;
127
+ var _a;
119
128
  if (htmlConfigs.length === 0) {
120
129
  return;
121
130
  }
122
- const outputDir = ((_a = bundleOptions.config.output) === null || _a === void 0 ? void 0 : _a.path) || path_1.default.join(process.cwd(), "dist");
123
- const publicPath = (_b = bundleOptions.config.output) === null || _b === void 0 ? void 0 : _b.publicPath;
131
+ const outputDir = (0, cleanOutput_1.getOutputPath)(bundleOptions.config, resolvedProjectPath);
132
+ const publicPath = (_a = bundleOptions.config.output) === null || _a === void 0 ? void 0 : _a.publicPath;
124
133
  const assets = (0, getInitialAssets_1.getInitialAssetsFromStats)(outputDir);
125
134
  for (const config of htmlConfigs) {
126
135
  const plugin = new HtmlPlugin_1.HtmlPlugin(config);
@@ -252,6 +261,7 @@ async function createHotReloader(bundleOptions, projectPath, rootPath) {
252
261
  subscription === null || subscription === void 0 ? void 0 : subscription.return();
253
262
  }
254
263
  async function handleEntrypointsSubscription() {
264
+ var _a, _b;
255
265
  for await (const entrypoints of entrypointsSubscription) {
256
266
  if (!currentEntriesHandlingResolve) {
257
267
  currentEntriesHandling = new Promise(
@@ -259,8 +269,8 @@ async function createHotReloader(bundleOptions, projectPath, rootPath) {
259
269
  (resolve) => (currentEntriesHandlingResolve = resolve));
260
270
  }
261
271
  currentWatchedEntrypoints = [
262
- ...entrypoints.apps,
263
- ...entrypoints.libraries,
272
+ ...((_a = entrypoints.apps) !== null && _a !== void 0 ? _a : []),
273
+ ...((_b = entrypoints.libraries) !== null && _b !== void 0 ? _b : []),
264
274
  ];
265
275
  await writeEntrypointsToDisk(currentWatchedEntrypoints);
266
276
  if (backgroundWatchersStarted) {
@@ -445,6 +455,14 @@ async function createHotReloader(bundleOptions, projectPath, rootPath) {
445
455
  close() {
446
456
  closed = true;
447
457
  void disposeBackgroundWatchSubscriptions();
458
+ closePromise !== null && closePromise !== void 0 ? closePromise : (closePromise = project
459
+ .onExit()
460
+ .catch((err) => {
461
+ console.error(err);
462
+ })
463
+ .finally(() => {
464
+ persistentCacheLock === null || persistentCacheLock === void 0 ? void 0 : persistentCacheLock.unlockSync();
465
+ }));
448
466
  for (const wsClient of clients) {
449
467
  wsClient.close();
450
468
  }
@@ -40,7 +40,7 @@ const path_1 = require("path");
40
40
  const util_1 = require("util");
41
41
  const binding = __importStar(require("../binding"));
42
42
  const common_1 = require("../utils/common");
43
- const normalize_path_1 = require("../utils/normalize-path");
43
+ const normalizePath_1 = require("../utils/normalizePath");
44
44
  const loaderWorkerPool_1 = require("./loaderWorkerPool");
45
45
  /**
46
46
  * An error caused by a bug in Turbopack, and not the user's code (e.g. a Rust panic). These should
@@ -240,15 +240,15 @@ async function rustifyPartialProjectOptions(options) {
240
240
  function normalizePathOption(pathLike) {
241
241
  if (pathLike === undefined)
242
242
  return undefined;
243
- return (0, normalize_path_1.normalizePath)(pathLike);
243
+ return (0, normalizePath_1.normalizePath)(pathLike);
244
244
  }
245
245
  async function rustifyProjectOptions(options) {
246
246
  var _a;
247
247
  return {
248
248
  ...options,
249
- rootPath: (0, normalize_path_1.normalizePath)(options.rootPath),
250
- projectPath: (0, normalize_path_1.normalizePath)(options.projectPath),
251
- packPath: (0, normalize_path_1.normalizePath)(options.packPath),
249
+ rootPath: (0, normalizePath_1.normalizePath)(options.rootPath),
250
+ projectPath: (0, normalizePath_1.normalizePath)(options.projectPath),
251
+ packPath: (0, normalizePath_1.normalizePath)(options.packPath),
252
252
  config: await serializeConfig(options.config, options.dev),
253
253
  processEnv: (0, common_1.rustifyEnv)((_a = options.processEnv) !== null && _a !== void 0 ? _a : {}),
254
254
  };
@@ -0,0 +1,3 @@
1
+ import type { ConfigComplete } from "../config/types";
2
+ export declare function getOutputPath(config: ConfigComplete, projectPath: string): string;
3
+ export declare function cleanOutput(config: ConfigComplete, projectPath: string): Promise<void>;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getOutputPath = getOutputPath;
7
+ exports.cleanOutput = cleanOutput;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ function getOutputPath(config, projectPath) {
11
+ var _a;
12
+ return path_1.default.resolve(projectPath, ((_a = config.output) === null || _a === void 0 ? void 0 : _a.path) || "dist");
13
+ }
14
+ async function cleanOutput(config, projectPath) {
15
+ var _a;
16
+ if (!((_a = config.output) === null || _a === void 0 ? void 0 : _a.clean)) {
17
+ return;
18
+ }
19
+ const outputPath = getOutputPath(config, projectPath);
20
+ let entries;
21
+ try {
22
+ entries = await fs_1.default.promises.readdir(outputPath, { withFileTypes: true });
23
+ }
24
+ catch (error) {
25
+ if (isNodeError(error) && error.code === "ENOENT") {
26
+ return;
27
+ }
28
+ throw error;
29
+ }
30
+ await Promise.all(entries.map((entry) => fs_1.default.promises.rm(path_1.default.join(outputPath, entry.name), {
31
+ force: true,
32
+ maxRetries: 3,
33
+ recursive: true,
34
+ retryDelay: 50,
35
+ })));
36
+ }
37
+ function isNodeError(error) {
38
+ return error instanceof Error && "code" in error;
39
+ }
@@ -0,0 +1,10 @@
1
+ export declare class PersistentCacheLock {
2
+ private listener;
3
+ private nativeLockfile;
4
+ private constructor();
5
+ static tryAcquire(lockPath: string, content?: string): PersistentCacheLock | undefined;
6
+ static acquireWithRetries(lockPath: string, processName: string): Promise<PersistentCacheLock>;
7
+ unlock(): Promise<void>;
8
+ unlockSync(): void;
9
+ }
10
+ export declare function acquirePersistentCacheLock(projectPath: string, processName: string, persistentCaching: boolean): Promise<PersistentCacheLock | undefined>;
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.PersistentCacheLock = void 0;
40
+ exports.acquirePersistentCacheLock = acquirePersistentCacheLock;
41
+ const fs_1 = __importDefault(require("fs"));
42
+ const path_1 = __importDefault(require("path"));
43
+ const picocolors_1 = __importDefault(require("picocolors"));
44
+ const binding = __importStar(require("../binding"));
45
+ const RETRY_DELAY_MS = 10;
46
+ const MAX_RETRY_MS = 1000;
47
+ class PersistentCacheLock {
48
+ constructor(nativeLockfile) {
49
+ this.nativeLockfile = nativeLockfile;
50
+ this.listener = () => this.unlockSync();
51
+ process.on("exit", this.listener);
52
+ }
53
+ static tryAcquire(lockPath, content) {
54
+ const nativeLockfile = binding.lockfileTryAcquireSync(lockPath, content);
55
+ return nativeLockfile ? new PersistentCacheLock(nativeLockfile) : undefined;
56
+ }
57
+ static async acquireWithRetries(lockPath, processName) {
58
+ const content = JSON.stringify({
59
+ pid: process.pid,
60
+ processName,
61
+ startedAt: Date.now(),
62
+ });
63
+ const startMs = Date.now();
64
+ let lockfile;
65
+ while (Date.now() - startMs < MAX_RETRY_MS) {
66
+ lockfile = PersistentCacheLock.tryAcquire(lockPath, content);
67
+ if (lockfile) {
68
+ return lockfile;
69
+ }
70
+ await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS));
71
+ }
72
+ throw new Error(formatLockError(lockPath, processName));
73
+ }
74
+ async unlock() {
75
+ const lockfile = this.nativeLockfile;
76
+ this.nativeLockfile = undefined;
77
+ if (this.listener) {
78
+ process.off("exit", this.listener);
79
+ this.listener = undefined;
80
+ }
81
+ if (lockfile) {
82
+ await binding.lockfileUnlock(lockfile);
83
+ }
84
+ }
85
+ unlockSync() {
86
+ const lockfile = this.nativeLockfile;
87
+ this.nativeLockfile = undefined;
88
+ if (this.listener) {
89
+ process.off("exit", this.listener);
90
+ this.listener = undefined;
91
+ }
92
+ if (lockfile) {
93
+ binding.lockfileUnlockSync(lockfile);
94
+ }
95
+ }
96
+ }
97
+ exports.PersistentCacheLock = PersistentCacheLock;
98
+ async function acquirePersistentCacheLock(projectPath, processName, persistentCaching) {
99
+ if (!persistentCaching) {
100
+ return undefined;
101
+ }
102
+ const internalDir = path_1.default.join(path_1.default.resolve(projectPath), ".turbopack");
103
+ fs_1.default.mkdirSync(internalDir, { recursive: true });
104
+ return PersistentCacheLock.acquireWithRetries(path_1.default.join(internalDir, "lock"), processName);
105
+ }
106
+ function formatLockError(lockPath, processName) {
107
+ let owner = "";
108
+ try {
109
+ const data = JSON.parse(fs_1.default.readFileSync(lockPath, "utf-8"));
110
+ if (data.pid) {
111
+ const killCommand = process.platform === "win32"
112
+ ? `taskkill /PID ${data.pid} /F`
113
+ : `kill ${data.pid}`;
114
+ owner = `\nExisting process: ${data.processName || "unknown"} (PID ${data.pid}).\nStop it with ${picocolors_1.default.cyan(killCommand)} if it is stale.`;
115
+ }
116
+ }
117
+ catch (_a) {
118
+ // The lock holder might be a version that did not write metadata.
119
+ }
120
+ return `Unable to acquire ${processName} persistent cache lock at ${picocolors_1.default.cyan(lockPath)}. Another utoo pack process may be using the same .turbopack cache.${owner}`;
121
+ }
package/esm/binding.d.ts CHANGED
@@ -9,6 +9,11 @@ export declare class ExternalObject<T> {
9
9
  [K: symbol]: T
10
10
  }
11
11
  }
12
+ export type Lockfile = { __napiType: "Lockfile" }
13
+ export declare function lockfileTryAcquireSync(path: string, content?: string | undefined | null): Lockfile | null
14
+ export declare function lockfileTryAcquire(path: string, content?: string | undefined | null): Promise<Lockfile | null>
15
+ export declare function lockfileUnlockSync(lockfile: Lockfile): void
16
+ export declare function lockfileUnlock(lockfile: Lockfile): Promise<void>
12
17
  export declare function registerWorkerScheduler(creator: (arg: NapiWorkerCreation) => any, terminator: (arg: NapiWorkerTermination) => any): void
13
18
  export declare function workerCreated(workerId: number): void
14
19
  export interface NapiWorkerCreation {
package/esm/binding.js CHANGED
@@ -319,7 +319,11 @@ if (!nativeBinding) {
319
319
  }
320
320
  throw new Error(`Failed to load native binding`);
321
321
  }
322
- const { registerWorkerScheduler, workerCreated, recvTaskMessageInWorker, sendTaskMessage, endpointWriteToDisk, endpointServerChangedSubscribe, endpointClientChangedSubscribe, projectNew, projectUpdate, projectOnExit, projectShutdown, projectWriteAllEntrypointsToDisk, projectEntrypointsSubscribe, projectHmrEvents, projectHmrIdentifiersSubscribe, projectUpdateInfoSubscribe, projectTraceSource, projectGetSourceForAsset, projectGetSourceMap, projectGetSourceMapSync, rootTaskDispose, initCustomTraceSubscriber, teardownTraceSubscriber } = nativeBinding;
322
+ const { lockfileTryAcquireSync, lockfileTryAcquire, lockfileUnlockSync, lockfileUnlock, registerWorkerScheduler, workerCreated, recvTaskMessageInWorker, sendTaskMessage, endpointWriteToDisk, endpointServerChangedSubscribe, endpointClientChangedSubscribe, projectNew, projectUpdate, projectOnExit, projectShutdown, projectWriteAllEntrypointsToDisk, projectEntrypointsSubscribe, projectHmrEvents, projectHmrIdentifiersSubscribe, projectUpdateInfoSubscribe, projectTraceSource, projectGetSourceForAsset, projectGetSourceMap, projectGetSourceMapSync, rootTaskDispose, initCustomTraceSubscriber, teardownTraceSubscriber } = nativeBinding;
323
+ module.exports.lockfileTryAcquireSync = lockfileTryAcquireSync;
324
+ module.exports.lockfileTryAcquire = lockfileTryAcquire;
325
+ module.exports.lockfileUnlockSync = lockfileUnlockSync;
326
+ module.exports.lockfileUnlock = lockfileUnlock;
323
327
  module.exports.registerWorkerScheduler = registerWorkerScheduler;
324
328
  module.exports.workerCreated = workerCreated;
325
329
  module.exports.recvTaskMessageInWorker = recvTaskMessageInWorker;
@@ -6,11 +6,13 @@ import path from "path";
6
6
  import { resolveBundleOptions } from "../config/webpackCompat.js";
7
7
  import { projectFactory } from "../core/project.js";
8
8
  import { HtmlPlugin } from "../plugins/HtmlPlugin.js";
9
+ import { cleanOutput, getOutputPath } from "../utils/cleanOutput.js";
9
10
  import { blockStdout, getPackPath } from "../utils/common.js";
10
11
  import { findRootDir } from "../utils/findRoot.js";
11
12
  import { getInitialAssetsFromStats } from "../utils/getInitialAssets.js";
12
13
  import { processHtmlEntry } from "../utils/htmlEntry.js";
13
- import { normalizePath } from "../utils/normalize-path.js";
14
+ import { acquirePersistentCacheLock } from "../utils/lockfile.js";
15
+ import { normalizePath } from "../utils/normalizePath.js";
14
16
  import { useWorkerThreads } from "../utils/runtimePluginStratety.js";
15
17
  import { validateEntryPaths } from "../utils/validateEntry.js";
16
18
  import { xcodeProfilingReady } from "../utils/xcodeProfile.js";
@@ -23,7 +25,7 @@ export function build(options, projectPath, rootPath) {
23
25
  return buildInternal(bundleOptions, projectPath, rootPath);
24
26
  }
25
27
  async function buildInternal(bundleOptions, projectPath, rootPath) {
26
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
28
+ var _a, _b, _c, _d, _e, _f, _g, _h;
27
29
  blockStdout();
28
30
  if (process.env.XCODE_PROFILE) {
29
31
  await xcodeProfilingReady();
@@ -33,62 +35,70 @@ async function buildInternal(bundleOptions, projectPath, rootPath) {
33
35
  const persistentCaching = (_a = bundleOptions.config.persistentCaching) !== null && _a !== void 0 ? _a : false;
34
36
  processHtmlEntry(bundleOptions.config, resolvedProjectPath);
35
37
  validateEntryPaths(bundleOptions.config, resolvedProjectPath);
38
+ await cleanOutput(bundleOptions.config, resolvedProjectPath);
36
39
  const createProject = projectFactory();
37
- const project = await createProject({
38
- processEnv: (_b = bundleOptions.processEnv) !== null && _b !== void 0 ? _b : {},
39
- watch: {
40
- enable: false,
41
- },
42
- dev: (_c = bundleOptions.dev) !== null && _c !== void 0 ? _c : false,
43
- buildId: bundleOptions.buildId || nanoid(),
44
- tracing: (_d = bundleOptions.tracing) !== null && _d !== void 0 ? _d : true,
45
- config: {
46
- ...bundleOptions.config,
47
- stats: Boolean(process.env.ANALYZE) ||
48
- bundleOptions.config.stats ||
49
- bundleOptions.config.entry.some((e) => !!e.html),
50
- pluginRuntimeStrategy: (_f = (_e = bundleOptions === null || bundleOptions === void 0 ? void 0 : bundleOptions.config) === null || _e === void 0 ? void 0 : _e.pluginRuntimeStrategy) !== null && _f !== void 0 ? _f : (useWorkerThreads() ? "workerThreads" : "childProcesses"),
51
- },
52
- projectPath: normalizePath(resolvedProjectPath),
53
- rootPath: resolvedRootPath,
54
- packPath: getPackPath(),
55
- }, {
56
- persistentCaching,
57
- // Build mode is a short-lived, one-shot compilation, so avoid paying
58
- // dependency graph bookkeeping cost unless the persistent cache needs it.
59
- dependencyTracking: persistentCaching,
60
- isShortSession: true,
61
- });
62
- const entrypoints = await project.writeAllEntrypointsToDisk();
63
- handleIssues(entrypoints.issues);
64
- const htmlConfigs = [
65
- ...(Array.isArray(bundleOptions.config.html)
66
- ? bundleOptions.config.html
67
- : bundleOptions.config.html
68
- ? [bundleOptions.config.html]
69
- : []),
70
- ...bundleOptions.config.entry
71
- .filter((e) => !!e.html)
72
- .map((e) => e.html),
73
- ];
74
- if (htmlConfigs.length > 0) {
75
- const assets = { js: [], css: [] };
76
- const outputDir = ((_g = bundleOptions.config.output) === null || _g === void 0 ? void 0 : _g.path) || path.join(process.cwd(), "dist");
77
- if (assets.js.length === 0 && assets.css.length === 0) {
78
- const discovered = getInitialAssetsFromStats(outputDir);
79
- assets.js.push(...discovered.js);
80
- assets.css.push(...discovered.css);
40
+ const persistentCacheLock = await acquirePersistentCacheLock(resolvedProjectPath, "utoo pack build", persistentCaching);
41
+ let project;
42
+ try {
43
+ project = await createProject({
44
+ processEnv: (_b = bundleOptions.processEnv) !== null && _b !== void 0 ? _b : {},
45
+ watch: {
46
+ enable: false,
47
+ },
48
+ dev: (_c = bundleOptions.dev) !== null && _c !== void 0 ? _c : false,
49
+ buildId: bundleOptions.buildId || nanoid(),
50
+ tracing: (_d = bundleOptions.tracing) !== null && _d !== void 0 ? _d : true,
51
+ config: {
52
+ ...bundleOptions.config,
53
+ stats: Boolean(process.env.ANALYZE) ||
54
+ bundleOptions.config.stats ||
55
+ bundleOptions.config.entry.some((e) => !!e.html),
56
+ pluginRuntimeStrategy: (_f = (_e = bundleOptions === null || bundleOptions === void 0 ? void 0 : bundleOptions.config) === null || _e === void 0 ? void 0 : _e.pluginRuntimeStrategy) !== null && _f !== void 0 ? _f : (useWorkerThreads() ? "workerThreads" : "childProcesses"),
57
+ },
58
+ projectPath: normalizePath(resolvedProjectPath),
59
+ rootPath: resolvedRootPath,
60
+ packPath: getPackPath(),
61
+ }, {
62
+ persistentCaching,
63
+ // Build mode is a short-lived, one-shot compilation, so avoid paying
64
+ // dependency graph bookkeeping cost unless the persistent cache needs it.
65
+ dependencyTracking: persistentCaching,
66
+ isShortSession: true,
67
+ });
68
+ const entrypoints = await project.writeAllEntrypointsToDisk();
69
+ handleIssues(entrypoints.issues);
70
+ const htmlConfigs = [
71
+ ...(Array.isArray(bundleOptions.config.html)
72
+ ? bundleOptions.config.html
73
+ : bundleOptions.config.html
74
+ ? [bundleOptions.config.html]
75
+ : []),
76
+ ...bundleOptions.config.entry
77
+ .filter((e) => !!e.html)
78
+ .map((e) => e.html),
79
+ ];
80
+ if (htmlConfigs.length > 0) {
81
+ const assets = { js: [], css: [] };
82
+ const outputDir = getOutputPath(bundleOptions.config, resolvedProjectPath);
83
+ if (assets.js.length === 0 && assets.css.length === 0) {
84
+ const discovered = getInitialAssetsFromStats(outputDir);
85
+ assets.js.push(...discovered.js);
86
+ assets.css.push(...discovered.css);
87
+ }
88
+ const publicPath = (_g = bundleOptions.config.output) === null || _g === void 0 ? void 0 : _g.publicPath;
89
+ for (const config of htmlConfigs) {
90
+ const plugin = new HtmlPlugin(config);
91
+ await plugin.generate(outputDir, assets, publicPath);
92
+ }
81
93
  }
82
- const publicPath = (_h = bundleOptions.config.output) === null || _h === void 0 ? void 0 : _h.publicPath;
83
- for (const config of htmlConfigs) {
84
- const plugin = new HtmlPlugin(config);
85
- await plugin.generate(outputDir, assets, publicPath);
94
+ if (process.env.ANALYZE) {
95
+ await analyzeBundle(((_h = bundleOptions.config.output) === null || _h === void 0 ? void 0 : _h.path) || "dist");
86
96
  }
87
97
  }
88
- if (process.env.ANALYZE) {
89
- await analyzeBundle(((_j = bundleOptions.config.output) === null || _j === void 0 ? void 0 : _j.path) || "dist");
98
+ finally {
99
+ await (project === null || project === void 0 ? void 0 : project.shutdown());
100
+ persistentCacheLock === null || persistentCacheLock === void 0 ? void 0 : persistentCacheLock.unlockSync();
90
101
  }
91
- await project.shutdown();
92
102
  // TODO: Maybe run tasks in worker is a better way, see
93
103
  // https://github.com/vercel/next.js/blob/512d8283054407ab92b2583ecce3b253c3be7b85/packages/next/src/lib/worker.ts
94
104
  }
@@ -9,10 +9,10 @@ import fs from "fs";
9
9
  import getPort from "get-port";
10
10
  import { Hono } from "hono";
11
11
  import https from "https";
12
- import path from "path";
13
12
  import { resolveBundleOptions, } from "../config/webpackCompat.js";
14
13
  import { createHotReloader } from "../core/hmr.js";
15
- import { createHttpProxyMiddleware } from "../core/proxy-hono.js";
14
+ import { createHttpProxyMiddleware } from "../core/proxyHono.js";
15
+ import { getOutputPath } from "../utils/cleanOutput.js";
16
16
  import { blockStdout, getPackPath } from "../utils/common.js";
17
17
  import { findRootDir } from "../utils/findRoot.js";
18
18
  import { createSelfSignedCertificate } from "../utils/mkcert.js";
@@ -133,7 +133,7 @@ export function serve(options, projectPath, rootPath, serverOptions) {
133
133
  }
134
134
  const HMR_PATH = "/turbopack-hmr";
135
135
  async function runDev(options, projectPath, rootPath, serverOptions) {
136
- var _a, _b, _d, _e, _f, _g, _h;
136
+ var _a, _b, _d, _e, _f;
137
137
  blockStdout();
138
138
  process.title = "utoopack-dev-server";
139
139
  if (process.env.XCODE_PROFILE) {
@@ -143,8 +143,8 @@ async function runDev(options, projectPath, rootPath, serverOptions) {
143
143
  const { bundleOptions, projectPathResolved, rootPathResolved, serveOptsBase, } = await resolveDevConfig(options, projectPath, rootPath, serverOptions);
144
144
  const hotReloader = await createHotReloader(bundleOptions, projectPathResolved, rootPathResolved);
145
145
  await hotReloader.start();
146
- 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");
147
- const publicPath = (_e = (_d = options.config) === null || _d === void 0 ? void 0 : _d.output) === null || _e === void 0 ? void 0 : _e.publicPath;
146
+ const distRoot = getOutputPath(options.config, projectPathResolved);
147
+ const publicPath = (_b = (_a = options.config) === null || _a === void 0 ? void 0 : _a.output) === null || _b === void 0 ? void 0 : _b.publicPath;
148
148
  // Skip prefix stripping for "runtime" and when publicPath is absent (match dev.ts).
149
149
  const normalizedPrefix = publicPath && publicPath !== "runtime"
150
150
  ? normalizedPublicPath(publicPath)
@@ -185,7 +185,7 @@ async function runDev(options, projectPath, rootPath, serverOptions) {
185
185
  console.error("HMR WebSocket error", err);
186
186
  },
187
187
  })));
188
- const proxyRules = (_g = (_f = bundleOptions.config) === null || _f === void 0 ? void 0 : _f.devServer) === null || _g === void 0 ? void 0 : _g.proxy;
188
+ const proxyRules = (_e = (_d = bundleOptions.config) === null || _d === void 0 ? void 0 : _d.devServer) === null || _e === void 0 ? void 0 : _e.proxy;
189
189
  if (proxyRules && proxyRules.length > 0) {
190
190
  app.use("*", createHttpProxyMiddleware(proxyRules));
191
191
  }
@@ -214,7 +214,7 @@ async function runDev(options, projectPath, rootPath, serverOptions) {
214
214
  server.once("error", onError);
215
215
  });
216
216
  }
217
- await ((_h = serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.onReady) === null || _h === void 0 ? void 0 : _h.call(serverOptions, {
217
+ await ((_f = serverOptions === null || serverOptions === void 0 ? void 0 : serverOptions.onReady) === null || _f === void 0 ? void 0 : _f.call(serverOptions, {
218
218
  port: serveOptsBase.port,
219
219
  hostname: serveOptsBase.hostname,
220
220
  }));
package/esm/core/hmr.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import { HMR_ACTIONS_SENT_TO_BROWSER, } from "@utoo/pack-shared";
2
2
  import { nanoid } from "nanoid";
3
- import path from "path";
4
3
  import { WebSocketServer } from "ws";
5
4
  import { HtmlPlugin } from "../plugins/HtmlPlugin.js";
5
+ import { cleanOutput, getOutputPath } from "../utils/cleanOutput.js";
6
6
  import { debounce, getPackPath, processIssues } from "../utils/common.js";
7
7
  import { getInitialAssetsFromStats } from "../utils/getInitialAssets.js";
8
8
  import { processHtmlEntry } from "../utils/htmlEntry.js";
9
- import { normalizePath } from "../utils/normalize-path.js";
9
+ import { acquirePersistentCacheLock } from "../utils/lockfile.js";
10
+ import { normalizePath } from "../utils/normalizePath.js";
10
11
  import { useWorkerThreads } from "../utils/runtimePluginStratety.js";
11
12
  import { validateEntryPaths } from "../utils/validateEntry.js";
12
13
  import { projectFactory } from "./project.js";
@@ -21,35 +22,45 @@ export async function createHotReloader(bundleOptions, projectPath, rootPath) {
21
22
  const resolvedRootPath = rootPath || projectPath || process.cwd();
22
23
  processHtmlEntry(bundleOptions.config, resolvedProjectPath);
23
24
  validateEntryPaths(bundleOptions.config, resolvedProjectPath);
25
+ await cleanOutput(bundleOptions.config, resolvedProjectPath);
24
26
  const createProject = projectFactory();
25
- const project = await createProject({
26
- processEnv: (_a = bundleOptions.processEnv) !== null && _a !== void 0 ? _a : {},
27
- watch: {
28
- enable: true,
29
- },
30
- dev: true,
31
- buildId: bundleOptions.buildId || nanoid(),
32
- tracing: (_b = bundleOptions.tracing) !== null && _b !== void 0 ? _b : true,
33
- config: {
34
- ...bundleOptions.config,
35
- mode: "development",
36
- stats: Boolean(process.env.ANALYZE) ||
37
- bundleOptions.config.stats ||
38
- bundleOptions.config.entry.some((e) => !!e.html),
39
- optimization: {
40
- ...bundleOptions.config.optimization,
41
- minify: false,
42
- moduleIds: "named",
27
+ const persistentCaching = (_a = bundleOptions.config.persistentCaching) !== null && _a !== void 0 ? _a : false;
28
+ const persistentCacheLock = await acquirePersistentCacheLock(resolvedProjectPath, "utoo pack dev", persistentCaching);
29
+ let project;
30
+ try {
31
+ project = await createProject({
32
+ processEnv: (_b = bundleOptions.processEnv) !== null && _b !== void 0 ? _b : {},
33
+ watch: {
34
+ enable: true,
43
35
  },
44
- persistentCaching: (_d = (_c = bundleOptions === null || bundleOptions === void 0 ? void 0 : bundleOptions.config) === null || _c === void 0 ? void 0 : _c.persistentCaching) !== null && _d !== void 0 ? _d : true,
45
- pluginRuntimeStrategy: (_f = (_e = bundleOptions === null || bundleOptions === void 0 ? void 0 : bundleOptions.config) === null || _e === void 0 ? void 0 : _e.pluginRuntimeStrategy) !== null && _f !== void 0 ? _f : (useWorkerThreads() ? "workerThreads" : "childProcesses"),
46
- },
47
- projectPath: normalizePath(resolvedProjectPath),
48
- rootPath: resolvedRootPath,
49
- packPath: getPackPath(),
50
- }, {
51
- persistentCaching: (_g = bundleOptions.config.persistentCaching) !== null && _g !== void 0 ? _g : false,
52
- });
36
+ dev: true,
37
+ buildId: bundleOptions.buildId || nanoid(),
38
+ tracing: (_c = bundleOptions.tracing) !== null && _c !== void 0 ? _c : true,
39
+ config: {
40
+ ...bundleOptions.config,
41
+ mode: "development",
42
+ stats: Boolean(process.env.ANALYZE) ||
43
+ bundleOptions.config.stats ||
44
+ bundleOptions.config.entry.some((e) => !!e.html),
45
+ optimization: {
46
+ ...bundleOptions.config.optimization,
47
+ minify: false,
48
+ moduleIds: "named",
49
+ },
50
+ persistentCaching: (_e = (_d = bundleOptions === null || bundleOptions === void 0 ? void 0 : bundleOptions.config) === null || _d === void 0 ? void 0 : _d.persistentCaching) !== null && _e !== void 0 ? _e : true,
51
+ pluginRuntimeStrategy: (_g = (_f = bundleOptions === null || bundleOptions === void 0 ? void 0 : bundleOptions.config) === null || _f === void 0 ? void 0 : _f.pluginRuntimeStrategy) !== null && _g !== void 0 ? _g : (useWorkerThreads() ? "workerThreads" : "childProcesses"),
52
+ },
53
+ projectPath: normalizePath(resolvedProjectPath),
54
+ rootPath: resolvedRootPath,
55
+ packPath: getPackPath(),
56
+ }, {
57
+ persistentCaching,
58
+ });
59
+ }
60
+ catch (error) {
61
+ persistentCacheLock === null || persistentCacheLock === void 0 ? void 0 : persistentCacheLock.unlockSync();
62
+ throw error;
63
+ }
53
64
  const entrypointsSubscription = project.entrypointsSubscribe();
54
65
  let currentEntriesHandlingResolve;
55
66
  let currentEntriesHandling = new Promise((resolve) => (currentEntriesHandlingResolve = resolve));
@@ -73,6 +84,7 @@ export async function createHotReloader(bundleOptions, projectPath, rootPath) {
73
84
  let backgroundWatchGeneration = 0;
74
85
  const backgroundWriteTasks = new Map();
75
86
  let closed = false;
87
+ let closePromise;
76
88
  function sendToClient(client, payload) {
77
89
  client.send(JSON.stringify(payload));
78
90
  }
@@ -107,12 +119,12 @@ export async function createHotReloader(bundleOptions, projectPath, rootPath) {
107
119
  sendEnqueuedMessagesDebounce();
108
120
  }
109
121
  async function regenerateHtml() {
110
- var _a, _b;
122
+ var _a;
111
123
  if (htmlConfigs.length === 0) {
112
124
  return;
113
125
  }
114
- const outputDir = ((_a = bundleOptions.config.output) === null || _a === void 0 ? void 0 : _a.path) || path.join(process.cwd(), "dist");
115
- const publicPath = (_b = bundleOptions.config.output) === null || _b === void 0 ? void 0 : _b.publicPath;
126
+ const outputDir = getOutputPath(bundleOptions.config, resolvedProjectPath);
127
+ const publicPath = (_a = bundleOptions.config.output) === null || _a === void 0 ? void 0 : _a.publicPath;
116
128
  const assets = getInitialAssetsFromStats(outputDir);
117
129
  for (const config of htmlConfigs) {
118
130
  const plugin = new HtmlPlugin(config);
@@ -244,6 +256,7 @@ export async function createHotReloader(bundleOptions, projectPath, rootPath) {
244
256
  subscription === null || subscription === void 0 ? void 0 : subscription.return();
245
257
  }
246
258
  async function handleEntrypointsSubscription() {
259
+ var _a, _b;
247
260
  for await (const entrypoints of entrypointsSubscription) {
248
261
  if (!currentEntriesHandlingResolve) {
249
262
  currentEntriesHandling = new Promise(
@@ -251,8 +264,8 @@ export async function createHotReloader(bundleOptions, projectPath, rootPath) {
251
264
  (resolve) => (currentEntriesHandlingResolve = resolve));
252
265
  }
253
266
  currentWatchedEntrypoints = [
254
- ...entrypoints.apps,
255
- ...entrypoints.libraries,
267
+ ...((_a = entrypoints.apps) !== null && _a !== void 0 ? _a : []),
268
+ ...((_b = entrypoints.libraries) !== null && _b !== void 0 ? _b : []),
256
269
  ];
257
270
  await writeEntrypointsToDisk(currentWatchedEntrypoints);
258
271
  if (backgroundWatchersStarted) {
@@ -437,6 +450,14 @@ export async function createHotReloader(bundleOptions, projectPath, rootPath) {
437
450
  close() {
438
451
  closed = true;
439
452
  void disposeBackgroundWatchSubscriptions();
453
+ closePromise !== null && closePromise !== void 0 ? closePromise : (closePromise = project
454
+ .onExit()
455
+ .catch((err) => {
456
+ console.error(err);
457
+ })
458
+ .finally(() => {
459
+ persistentCacheLock === null || persistentCacheLock === void 0 ? void 0 : persistentCacheLock.unlockSync();
460
+ }));
440
461
  for (const wsClient of clients) {
441
462
  wsClient.close();
442
463
  }
@@ -2,7 +2,7 @@ import { resolve } from "path";
2
2
  import { isDeepStrictEqual } from "util";
3
3
  import * as binding from "../binding.js";
4
4
  import { getPackPath, rustifyEnv } from "../utils/common.js";
5
- import { normalizePath } from "../utils/normalize-path.js";
5
+ import { normalizePath } from "../utils/normalizePath.js";
6
6
  import { runLoaderWorkerPool } from "./loaderWorkerPool.js";
7
7
  /**
8
8
  * An error caused by a bug in Turbopack, and not the user's code (e.g. a Rust panic). These should
@@ -0,0 +1,3 @@
1
+ import type { ConfigComplete } from "../config/types";
2
+ export declare function getOutputPath(config: ConfigComplete, projectPath: string): string;
3
+ export declare function cleanOutput(config: ConfigComplete, projectPath: string): Promise<void>;
@@ -0,0 +1,32 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ export function getOutputPath(config, projectPath) {
4
+ var _a;
5
+ return path.resolve(projectPath, ((_a = config.output) === null || _a === void 0 ? void 0 : _a.path) || "dist");
6
+ }
7
+ export async function cleanOutput(config, projectPath) {
8
+ var _a;
9
+ if (!((_a = config.output) === null || _a === void 0 ? void 0 : _a.clean)) {
10
+ return;
11
+ }
12
+ const outputPath = getOutputPath(config, projectPath);
13
+ let entries;
14
+ try {
15
+ entries = await fs.promises.readdir(outputPath, { withFileTypes: true });
16
+ }
17
+ catch (error) {
18
+ if (isNodeError(error) && error.code === "ENOENT") {
19
+ return;
20
+ }
21
+ throw error;
22
+ }
23
+ await Promise.all(entries.map((entry) => fs.promises.rm(path.join(outputPath, entry.name), {
24
+ force: true,
25
+ maxRetries: 3,
26
+ recursive: true,
27
+ retryDelay: 50,
28
+ })));
29
+ }
30
+ function isNodeError(error) {
31
+ return error instanceof Error && "code" in error;
32
+ }
@@ -0,0 +1,10 @@
1
+ export declare class PersistentCacheLock {
2
+ private listener;
3
+ private nativeLockfile;
4
+ private constructor();
5
+ static tryAcquire(lockPath: string, content?: string): PersistentCacheLock | undefined;
6
+ static acquireWithRetries(lockPath: string, processName: string): Promise<PersistentCacheLock>;
7
+ unlock(): Promise<void>;
8
+ unlockSync(): void;
9
+ }
10
+ export declare function acquirePersistentCacheLock(projectPath: string, processName: string, persistentCaching: boolean): Promise<PersistentCacheLock | undefined>;
@@ -0,0 +1,80 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import pc from "picocolors";
4
+ import * as binding from "../binding.js";
5
+ const RETRY_DELAY_MS = 10;
6
+ const MAX_RETRY_MS = 1000;
7
+ export class PersistentCacheLock {
8
+ constructor(nativeLockfile) {
9
+ this.nativeLockfile = nativeLockfile;
10
+ this.listener = () => this.unlockSync();
11
+ process.on("exit", this.listener);
12
+ }
13
+ static tryAcquire(lockPath, content) {
14
+ const nativeLockfile = binding.lockfileTryAcquireSync(lockPath, content);
15
+ return nativeLockfile ? new PersistentCacheLock(nativeLockfile) : undefined;
16
+ }
17
+ static async acquireWithRetries(lockPath, processName) {
18
+ const content = JSON.stringify({
19
+ pid: process.pid,
20
+ processName,
21
+ startedAt: Date.now(),
22
+ });
23
+ const startMs = Date.now();
24
+ let lockfile;
25
+ while (Date.now() - startMs < MAX_RETRY_MS) {
26
+ lockfile = PersistentCacheLock.tryAcquire(lockPath, content);
27
+ if (lockfile) {
28
+ return lockfile;
29
+ }
30
+ await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS));
31
+ }
32
+ throw new Error(formatLockError(lockPath, processName));
33
+ }
34
+ async unlock() {
35
+ const lockfile = this.nativeLockfile;
36
+ this.nativeLockfile = undefined;
37
+ if (this.listener) {
38
+ process.off("exit", this.listener);
39
+ this.listener = undefined;
40
+ }
41
+ if (lockfile) {
42
+ await binding.lockfileUnlock(lockfile);
43
+ }
44
+ }
45
+ unlockSync() {
46
+ const lockfile = this.nativeLockfile;
47
+ this.nativeLockfile = undefined;
48
+ if (this.listener) {
49
+ process.off("exit", this.listener);
50
+ this.listener = undefined;
51
+ }
52
+ if (lockfile) {
53
+ binding.lockfileUnlockSync(lockfile);
54
+ }
55
+ }
56
+ }
57
+ export async function acquirePersistentCacheLock(projectPath, processName, persistentCaching) {
58
+ if (!persistentCaching) {
59
+ return undefined;
60
+ }
61
+ const internalDir = path.join(path.resolve(projectPath), ".turbopack");
62
+ fs.mkdirSync(internalDir, { recursive: true });
63
+ return PersistentCacheLock.acquireWithRetries(path.join(internalDir, "lock"), processName);
64
+ }
65
+ function formatLockError(lockPath, processName) {
66
+ let owner = "";
67
+ try {
68
+ const data = JSON.parse(fs.readFileSync(lockPath, "utf-8"));
69
+ if (data.pid) {
70
+ const killCommand = process.platform === "win32"
71
+ ? `taskkill /PID ${data.pid} /F`
72
+ : `kill ${data.pid}`;
73
+ owner = `\nExisting process: ${data.processName || "unknown"} (PID ${data.pid}).\nStop it with ${pc.cyan(killCommand)} if it is stale.`;
74
+ }
75
+ }
76
+ catch (_a) {
77
+ // The lock holder might be a version that did not write metadata.
78
+ }
79
+ return `Unable to acquire ${processName} persistent cache lock at ${pc.cyan(lockPath)}. Another utoo pack process may be using the same .turbopack cache.${owner}`;
80
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utoo/pack",
3
- "version": "1.4.3",
3
+ "version": "1.4.4-alpha.1",
4
4
  "main": "cjs/index.js",
5
5
  "module": "esm/index.js",
6
6
  "types": "esm/index.d.ts",
@@ -41,7 +41,7 @@
41
41
  "@hono/node-server": "^1.19.11",
42
42
  "@hono/node-ws": "^1.3.0",
43
43
  "@swc/helpers": "0.5.15",
44
- "@utoo/pack-shared": "1.4.3",
44
+ "@utoo/pack-shared": "1.4.4-alpha.1",
45
45
  "domparser-rs": "^0.0.7",
46
46
  "find-up": "4.1.0",
47
47
  "get-port": "5.1.1",
@@ -92,12 +92,12 @@
92
92
  },
93
93
  "repository": "git@github.com:utooland/utoo.git",
94
94
  "optionalDependencies": {
95
- "@utoo/pack-darwin-arm64": "1.4.3",
96
- "@utoo/pack-darwin-x64": "1.4.3",
97
- "@utoo/pack-linux-arm64-gnu": "1.4.3",
98
- "@utoo/pack-linux-arm64-musl": "1.4.3",
99
- "@utoo/pack-linux-x64-gnu": "1.4.3",
100
- "@utoo/pack-linux-x64-musl": "1.4.3",
101
- "@utoo/pack-win32-x64-msvc": "1.4.3"
95
+ "@utoo/pack-darwin-arm64": "1.4.4-alpha.1",
96
+ "@utoo/pack-darwin-x64": "1.4.4-alpha.1",
97
+ "@utoo/pack-linux-arm64-gnu": "1.4.4-alpha.1",
98
+ "@utoo/pack-linux-arm64-musl": "1.4.4-alpha.1",
99
+ "@utoo/pack-linux-x64-gnu": "1.4.4-alpha.1",
100
+ "@utoo/pack-linux-x64-musl": "1.4.4-alpha.1",
101
+ "@utoo/pack-win32-x64-msvc": "1.4.4-alpha.1"
102
102
  }
103
103
  }
File without changes
File without changes
File without changes
File without changes