@utoo/pack 1.4.3-alpha.2 → 1.4.4-alpha.0

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;
@@ -16,6 +16,7 @@ const common_1 = require("../utils/common");
16
16
  const findRoot_1 = require("../utils/findRoot");
17
17
  const getInitialAssets_1 = require("../utils/getInitialAssets");
18
18
  const htmlEntry_1 = require("../utils/htmlEntry");
19
+ const lockfile_1 = require("../utils/lockfile");
19
20
  const normalize_path_1 = require("../utils/normalize-path");
20
21
  const runtimePluginStratety_1 = require("../utils/runtimePluginStratety");
21
22
  const validateEntry_1 = require("../utils/validateEntry");
@@ -40,61 +41,68 @@ async function buildInternal(bundleOptions, projectPath, rootPath) {
40
41
  (0, htmlEntry_1.processHtmlEntry)(bundleOptions.config, resolvedProjectPath);
41
42
  (0, validateEntry_1.validateEntryPaths)(bundleOptions.config, resolvedProjectPath);
42
43
  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);
44
+ const persistentCacheLock = await (0, lockfile_1.acquirePersistentCacheLock)(resolvedProjectPath, "utoo pack build", persistentCaching);
45
+ let project;
46
+ try {
47
+ project = await createProject({
48
+ processEnv: (_b = bundleOptions.processEnv) !== null && _b !== void 0 ? _b : {},
49
+ watch: {
50
+ enable: false,
51
+ },
52
+ dev: (_c = bundleOptions.dev) !== null && _c !== void 0 ? _c : false,
53
+ buildId: bundleOptions.buildId || (0, nanoid_1.nanoid)(),
54
+ tracing: (_d = bundleOptions.tracing) !== null && _d !== void 0 ? _d : true,
55
+ config: {
56
+ ...bundleOptions.config,
57
+ stats: Boolean(process.env.ANALYZE) ||
58
+ bundleOptions.config.stats ||
59
+ bundleOptions.config.entry.some((e) => !!e.html),
60
+ 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"),
61
+ },
62
+ projectPath: (0, normalize_path_1.normalizePath)(resolvedProjectPath),
63
+ rootPath: resolvedRootPath,
64
+ packPath: (0, common_1.getPackPath)(),
65
+ }, {
66
+ persistentCaching,
67
+ // Build mode is a short-lived, one-shot compilation, so avoid paying
68
+ // dependency graph bookkeeping cost unless the persistent cache needs it.
69
+ dependencyTracking: persistentCaching,
70
+ isShortSession: true,
71
+ });
72
+ const entrypoints = await project.writeAllEntrypointsToDisk();
73
+ (0, pack_shared_1.handleIssues)(entrypoints.issues);
74
+ const htmlConfigs = [
75
+ ...(Array.isArray(bundleOptions.config.html)
76
+ ? bundleOptions.config.html
77
+ : bundleOptions.config.html
78
+ ? [bundleOptions.config.html]
79
+ : []),
80
+ ...bundleOptions.config.entry
81
+ .filter((e) => !!e.html)
82
+ .map((e) => e.html),
83
+ ];
84
+ if (htmlConfigs.length > 0) {
85
+ const assets = { js: [], css: [] };
86
+ const outputDir = ((_g = bundleOptions.config.output) === null || _g === void 0 ? void 0 : _g.path) || path_1.default.join(process.cwd(), "dist");
87
+ if (assets.js.length === 0 && assets.css.length === 0) {
88
+ const discovered = (0, getInitialAssets_1.getInitialAssetsFromStats)(outputDir);
89
+ assets.js.push(...discovered.js);
90
+ assets.css.push(...discovered.css);
91
+ }
92
+ const publicPath = (_h = bundleOptions.config.output) === null || _h === void 0 ? void 0 : _h.publicPath;
93
+ for (const config of htmlConfigs) {
94
+ const plugin = new HtmlPlugin_1.HtmlPlugin(config);
95
+ await plugin.generate(outputDir, assets, publicPath);
96
+ }
87
97
  }
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);
98
+ if (process.env.ANALYZE) {
99
+ await analyzeBundle(((_j = bundleOptions.config.output) === null || _j === void 0 ? void 0 : _j.path) || "dist");
92
100
  }
93
101
  }
94
- if (process.env.ANALYZE) {
95
- await analyzeBundle(((_j = bundleOptions.config.output) === null || _j === void 0 ? void 0 : _j.path) || "dist");
102
+ finally {
103
+ await (project === null || project === void 0 ? void 0 : project.shutdown());
104
+ persistentCacheLock === null || persistentCacheLock === void 0 ? void 0 : persistentCacheLock.unlockSync();
96
105
  }
97
- await project.shutdown();
98
106
  // TODO: Maybe run tasks in worker is a better way, see
99
107
  // https://github.com/vercel/next.js/blob/512d8283054407ab92b2583ecce3b253c3be7b85/packages/next/src/lib/worker.ts
100
108
  }
package/cjs/core/hmr.js CHANGED
@@ -13,6 +13,7 @@ const HtmlPlugin_1 = require("../plugins/HtmlPlugin");
13
13
  const common_1 = require("../utils/common");
14
14
  const getInitialAssets_1 = require("../utils/getInitialAssets");
15
15
  const htmlEntry_1 = require("../utils/htmlEntry");
16
+ const lockfile_1 = require("../utils/lockfile");
16
17
  const normalize_path_1 = require("../utils/normalize-path");
17
18
  const runtimePluginStratety_1 = require("../utils/runtimePluginStratety");
18
19
  const validateEntry_1 = require("../utils/validateEntry");
@@ -30,34 +31,43 @@ async function createHotReloader(bundleOptions, projectPath, rootPath) {
30
31
  (0, htmlEntry_1.processHtmlEntry)(bundleOptions.config, resolvedProjectPath);
31
32
  (0, validateEntry_1.validateEntryPaths)(bundleOptions.config, resolvedProjectPath);
32
33
  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",
34
+ const persistentCaching = (_a = bundleOptions.config.persistentCaching) !== null && _a !== void 0 ? _a : false;
35
+ const persistentCacheLock = await (0, lockfile_1.acquirePersistentCacheLock)(resolvedProjectPath, "utoo pack dev", persistentCaching);
36
+ let project;
37
+ try {
38
+ project = await createProject({
39
+ processEnv: (_b = bundleOptions.processEnv) !== null && _b !== void 0 ? _b : {},
40
+ watch: {
41
+ enable: true,
51
42
  },
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
- });
43
+ dev: true,
44
+ buildId: bundleOptions.buildId || (0, nanoid_1.nanoid)(),
45
+ tracing: (_c = bundleOptions.tracing) !== null && _c !== void 0 ? _c : true,
46
+ config: {
47
+ ...bundleOptions.config,
48
+ mode: "development",
49
+ stats: Boolean(process.env.ANALYZE) ||
50
+ bundleOptions.config.stats ||
51
+ bundleOptions.config.entry.some((e) => !!e.html),
52
+ optimization: {
53
+ ...bundleOptions.config.optimization,
54
+ minify: false,
55
+ moduleIds: "named",
56
+ },
57
+ 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,
58
+ 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"),
59
+ },
60
+ projectPath: (0, normalize_path_1.normalizePath)(resolvedProjectPath),
61
+ rootPath: resolvedRootPath,
62
+ packPath: (0, common_1.getPackPath)(),
63
+ }, {
64
+ persistentCaching,
65
+ });
66
+ }
67
+ catch (error) {
68
+ persistentCacheLock === null || persistentCacheLock === void 0 ? void 0 : persistentCacheLock.unlockSync();
69
+ throw error;
70
+ }
61
71
  const entrypointsSubscription = project.entrypointsSubscribe();
62
72
  let currentEntriesHandlingResolve;
63
73
  let currentEntriesHandling = new Promise((resolve) => (currentEntriesHandlingResolve = resolve));
@@ -81,6 +91,7 @@ async function createHotReloader(bundleOptions, projectPath, rootPath) {
81
91
  let backgroundWatchGeneration = 0;
82
92
  const backgroundWriteTasks = new Map();
83
93
  let closed = false;
94
+ let closePromise;
84
95
  function sendToClient(client, payload) {
85
96
  client.send(JSON.stringify(payload));
86
97
  }
@@ -252,6 +263,7 @@ async function createHotReloader(bundleOptions, projectPath, rootPath) {
252
263
  subscription === null || subscription === void 0 ? void 0 : subscription.return();
253
264
  }
254
265
  async function handleEntrypointsSubscription() {
266
+ var _a, _b;
255
267
  for await (const entrypoints of entrypointsSubscription) {
256
268
  if (!currentEntriesHandlingResolve) {
257
269
  currentEntriesHandling = new Promise(
@@ -259,8 +271,8 @@ async function createHotReloader(bundleOptions, projectPath, rootPath) {
259
271
  (resolve) => (currentEntriesHandlingResolve = resolve));
260
272
  }
261
273
  currentWatchedEntrypoints = [
262
- ...entrypoints.apps,
263
- ...entrypoints.libraries,
274
+ ...((_a = entrypoints.apps) !== null && _a !== void 0 ? _a : []),
275
+ ...((_b = entrypoints.libraries) !== null && _b !== void 0 ? _b : []),
264
276
  ];
265
277
  await writeEntrypointsToDisk(currentWatchedEntrypoints);
266
278
  if (backgroundWatchersStarted) {
@@ -445,6 +457,14 @@ async function createHotReloader(bundleOptions, projectPath, rootPath) {
445
457
  close() {
446
458
  closed = true;
447
459
  void disposeBackgroundWatchSubscriptions();
460
+ closePromise !== null && closePromise !== void 0 ? closePromise : (closePromise = project
461
+ .onExit()
462
+ .catch((err) => {
463
+ console.error(err);
464
+ })
465
+ .finally(() => {
466
+ persistentCacheLock === null || persistentCacheLock === void 0 ? void 0 : persistentCacheLock.unlockSync();
467
+ }));
448
468
  for (const wsClient of clients) {
449
469
  wsClient.close();
450
470
  }
@@ -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;
@@ -10,6 +10,7 @@ import { blockStdout, getPackPath } from "../utils/common.js";
10
10
  import { findRootDir } from "../utils/findRoot.js";
11
11
  import { getInitialAssetsFromStats } from "../utils/getInitialAssets.js";
12
12
  import { processHtmlEntry } from "../utils/htmlEntry.js";
13
+ import { acquirePersistentCacheLock } from "../utils/lockfile.js";
13
14
  import { normalizePath } from "../utils/normalize-path.js";
14
15
  import { useWorkerThreads } from "../utils/runtimePluginStratety.js";
15
16
  import { validateEntryPaths } from "../utils/validateEntry.js";
@@ -34,61 +35,68 @@ async function buildInternal(bundleOptions, projectPath, rootPath) {
34
35
  processHtmlEntry(bundleOptions.config, resolvedProjectPath);
35
36
  validateEntryPaths(bundleOptions.config, resolvedProjectPath);
36
37
  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);
38
+ const persistentCacheLock = await acquirePersistentCacheLock(resolvedProjectPath, "utoo pack build", persistentCaching);
39
+ let project;
40
+ try {
41
+ project = await createProject({
42
+ processEnv: (_b = bundleOptions.processEnv) !== null && _b !== void 0 ? _b : {},
43
+ watch: {
44
+ enable: false,
45
+ },
46
+ dev: (_c = bundleOptions.dev) !== null && _c !== void 0 ? _c : false,
47
+ buildId: bundleOptions.buildId || nanoid(),
48
+ tracing: (_d = bundleOptions.tracing) !== null && _d !== void 0 ? _d : true,
49
+ config: {
50
+ ...bundleOptions.config,
51
+ stats: Boolean(process.env.ANALYZE) ||
52
+ bundleOptions.config.stats ||
53
+ bundleOptions.config.entry.some((e) => !!e.html),
54
+ 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"),
55
+ },
56
+ projectPath: normalizePath(resolvedProjectPath),
57
+ rootPath: resolvedRootPath,
58
+ packPath: getPackPath(),
59
+ }, {
60
+ persistentCaching,
61
+ // Build mode is a short-lived, one-shot compilation, so avoid paying
62
+ // dependency graph bookkeeping cost unless the persistent cache needs it.
63
+ dependencyTracking: persistentCaching,
64
+ isShortSession: true,
65
+ });
66
+ const entrypoints = await project.writeAllEntrypointsToDisk();
67
+ handleIssues(entrypoints.issues);
68
+ const htmlConfigs = [
69
+ ...(Array.isArray(bundleOptions.config.html)
70
+ ? bundleOptions.config.html
71
+ : bundleOptions.config.html
72
+ ? [bundleOptions.config.html]
73
+ : []),
74
+ ...bundleOptions.config.entry
75
+ .filter((e) => !!e.html)
76
+ .map((e) => e.html),
77
+ ];
78
+ if (htmlConfigs.length > 0) {
79
+ const assets = { js: [], css: [] };
80
+ const outputDir = ((_g = bundleOptions.config.output) === null || _g === void 0 ? void 0 : _g.path) || path.join(process.cwd(), "dist");
81
+ if (assets.js.length === 0 && assets.css.length === 0) {
82
+ const discovered = getInitialAssetsFromStats(outputDir);
83
+ assets.js.push(...discovered.js);
84
+ assets.css.push(...discovered.css);
85
+ }
86
+ const publicPath = (_h = bundleOptions.config.output) === null || _h === void 0 ? void 0 : _h.publicPath;
87
+ for (const config of htmlConfigs) {
88
+ const plugin = new HtmlPlugin(config);
89
+ await plugin.generate(outputDir, assets, publicPath);
90
+ }
81
91
  }
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);
92
+ if (process.env.ANALYZE) {
93
+ await analyzeBundle(((_j = bundleOptions.config.output) === null || _j === void 0 ? void 0 : _j.path) || "dist");
86
94
  }
87
95
  }
88
- if (process.env.ANALYZE) {
89
- await analyzeBundle(((_j = bundleOptions.config.output) === null || _j === void 0 ? void 0 : _j.path) || "dist");
96
+ finally {
97
+ await (project === null || project === void 0 ? void 0 : project.shutdown());
98
+ persistentCacheLock === null || persistentCacheLock === void 0 ? void 0 : persistentCacheLock.unlockSync();
90
99
  }
91
- await project.shutdown();
92
100
  // TODO: Maybe run tasks in worker is a better way, see
93
101
  // https://github.com/vercel/next.js/blob/512d8283054407ab92b2583ecce3b253c3be7b85/packages/next/src/lib/worker.ts
94
102
  }
package/esm/core/hmr.js CHANGED
@@ -6,6 +6,7 @@ import { HtmlPlugin } from "../plugins/HtmlPlugin.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 { acquirePersistentCacheLock } from "../utils/lockfile.js";
9
10
  import { normalizePath } from "../utils/normalize-path.js";
10
11
  import { useWorkerThreads } from "../utils/runtimePluginStratety.js";
11
12
  import { validateEntryPaths } from "../utils/validateEntry.js";
@@ -22,34 +23,43 @@ export async function createHotReloader(bundleOptions, projectPath, rootPath) {
22
23
  processHtmlEntry(bundleOptions.config, resolvedProjectPath);
23
24
  validateEntryPaths(bundleOptions.config, resolvedProjectPath);
24
25
  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",
26
+ const persistentCaching = (_a = bundleOptions.config.persistentCaching) !== null && _a !== void 0 ? _a : false;
27
+ const persistentCacheLock = await acquirePersistentCacheLock(resolvedProjectPath, "utoo pack dev", persistentCaching);
28
+ let project;
29
+ try {
30
+ project = await createProject({
31
+ processEnv: (_b = bundleOptions.processEnv) !== null && _b !== void 0 ? _b : {},
32
+ watch: {
33
+ enable: true,
43
34
  },
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
- });
35
+ dev: true,
36
+ buildId: bundleOptions.buildId || nanoid(),
37
+ tracing: (_c = bundleOptions.tracing) !== null && _c !== void 0 ? _c : true,
38
+ config: {
39
+ ...bundleOptions.config,
40
+ mode: "development",
41
+ stats: Boolean(process.env.ANALYZE) ||
42
+ bundleOptions.config.stats ||
43
+ bundleOptions.config.entry.some((e) => !!e.html),
44
+ optimization: {
45
+ ...bundleOptions.config.optimization,
46
+ minify: false,
47
+ moduleIds: "named",
48
+ },
49
+ 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,
50
+ 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"),
51
+ },
52
+ projectPath: normalizePath(resolvedProjectPath),
53
+ rootPath: resolvedRootPath,
54
+ packPath: getPackPath(),
55
+ }, {
56
+ persistentCaching,
57
+ });
58
+ }
59
+ catch (error) {
60
+ persistentCacheLock === null || persistentCacheLock === void 0 ? void 0 : persistentCacheLock.unlockSync();
61
+ throw error;
62
+ }
53
63
  const entrypointsSubscription = project.entrypointsSubscribe();
54
64
  let currentEntriesHandlingResolve;
55
65
  let currentEntriesHandling = new Promise((resolve) => (currentEntriesHandlingResolve = resolve));
@@ -73,6 +83,7 @@ export async function createHotReloader(bundleOptions, projectPath, rootPath) {
73
83
  let backgroundWatchGeneration = 0;
74
84
  const backgroundWriteTasks = new Map();
75
85
  let closed = false;
86
+ let closePromise;
76
87
  function sendToClient(client, payload) {
77
88
  client.send(JSON.stringify(payload));
78
89
  }
@@ -244,6 +255,7 @@ export async function createHotReloader(bundleOptions, projectPath, rootPath) {
244
255
  subscription === null || subscription === void 0 ? void 0 : subscription.return();
245
256
  }
246
257
  async function handleEntrypointsSubscription() {
258
+ var _a, _b;
247
259
  for await (const entrypoints of entrypointsSubscription) {
248
260
  if (!currentEntriesHandlingResolve) {
249
261
  currentEntriesHandling = new Promise(
@@ -251,8 +263,8 @@ export async function createHotReloader(bundleOptions, projectPath, rootPath) {
251
263
  (resolve) => (currentEntriesHandlingResolve = resolve));
252
264
  }
253
265
  currentWatchedEntrypoints = [
254
- ...entrypoints.apps,
255
- ...entrypoints.libraries,
266
+ ...((_a = entrypoints.apps) !== null && _a !== void 0 ? _a : []),
267
+ ...((_b = entrypoints.libraries) !== null && _b !== void 0 ? _b : []),
256
268
  ];
257
269
  await writeEntrypointsToDisk(currentWatchedEntrypoints);
258
270
  if (backgroundWatchersStarted) {
@@ -437,6 +449,14 @@ export async function createHotReloader(bundleOptions, projectPath, rootPath) {
437
449
  close() {
438
450
  closed = true;
439
451
  void disposeBackgroundWatchSubscriptions();
452
+ closePromise !== null && closePromise !== void 0 ? closePromise : (closePromise = project
453
+ .onExit()
454
+ .catch((err) => {
455
+ console.error(err);
456
+ })
457
+ .finally(() => {
458
+ persistentCacheLock === null || persistentCacheLock === void 0 ? void 0 : persistentCacheLock.unlockSync();
459
+ }));
440
460
  for (const wsClient of clients) {
441
461
  wsClient.close();
442
462
  }
@@ -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-alpha.2",
3
+ "version": "1.4.4-alpha.0",
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-alpha.2",
44
+ "@utoo/pack-shared": "1.4.4-alpha.0",
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-alpha.2",
96
- "@utoo/pack-darwin-x64": "1.4.3-alpha.2",
97
- "@utoo/pack-linux-arm64-gnu": "1.4.3-alpha.2",
98
- "@utoo/pack-linux-arm64-musl": "1.4.3-alpha.2",
99
- "@utoo/pack-linux-x64-gnu": "1.4.3-alpha.2",
100
- "@utoo/pack-linux-x64-musl": "1.4.3-alpha.2",
101
- "@utoo/pack-win32-x64-msvc": "1.4.3-alpha.2"
95
+ "@utoo/pack-darwin-arm64": "1.4.4-alpha.0",
96
+ "@utoo/pack-darwin-x64": "1.4.4-alpha.0",
97
+ "@utoo/pack-linux-arm64-gnu": "1.4.4-alpha.0",
98
+ "@utoo/pack-linux-arm64-musl": "1.4.4-alpha.0",
99
+ "@utoo/pack-linux-x64-gnu": "1.4.4-alpha.0",
100
+ "@utoo/pack-linux-x64-musl": "1.4.4-alpha.0",
101
+ "@utoo/pack-win32-x64-msvc": "1.4.4-alpha.0"
102
102
  }
103
103
  }