UNPKG

nuxi

Version:
194 lines (193 loc) 6.17 kB
import { n as colors } from "./consola.DXBYu-KD-qSGefJ79.mjs"; import "./utils-MaFlCoS1.mjs"; import { n as c } from "./dist-BkNIIgFa.mjs"; import { p as oe } from "./logger-CtlB9piy.mjs"; import { a as join, s as relative } from "./pathe.M-eThtNZ-BfnU2wdd.mjs"; import { n as themeColor } from "./ascii-ByocWiRd.mjs"; import process from "node:process"; import { mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs"; //#region src/utils/profile.ts const RELATIVE_PATH_RE = /^(?![^.]{1,2}\/)/; let session; let profileCount = 0; async function startCpuProfile() { const cli = globalThis.__nuxt_cli__; if (cli?.cpuProfileSession) { session = cli.cpuProfileSession; delete cli.cpuProfileSession; return; } session = new (await (import("node:inspector"))).Session(); session.connect(); try { await new Promise((res, rej) => { session.post("Profiler.enable", (err) => { if (err) return rej(err); session.post("Profiler.start", (err) => { if (err) return rej(err); res(); }); }); }); } catch (err) { session.disconnect(); session = void 0; throw err; } } async function stopCpuProfile(outDir, command) { if (!session) return; const s = session; session = void 0; const count = profileCount++; const outPath = join(outDir, `nuxt-${command}${count ? `-${count}` : ""}.cpuprofile`); const relativeOutPath = relative(process.cwd(), outPath).replace(RELATIVE_PATH_RE, "./"); try { await new Promise((resolve, reject) => { s.post("Profiler.stop", (err, params) => { if (err) return reject(err); if (!params?.profile) return resolve(params); try { mkdirSync(outDir, { recursive: true }); writeFileSync(outPath, JSON.stringify(params.profile)); oe(`\n${[`CPU profile written to ${colors.cyan(relativeOutPath)}.`, `Open it in a CPU profile viewer like your IDE, or ${colors.cyan("https://discoveryjs.github.io/cpupro")}.`].map((step) => ` › ${step}`).join("\n")}\n`, "", { contentAlign: "left", titleAlign: "left", width: "auto", titlePadding: 2, contentPadding: 2, rounded: true, withGuide: false, formatBorder: (text) => `${themeColor + text}\x1B[0m` }); } catch {} resolve(params); }); }); } finally { s.disconnect(); } } //#endregion //#region src/utils/lockfile.ts const LOCK_FILENAME = "nuxt.lock"; const MAX_LOCK_AGE_MS = 1440 * 60 * 1e3; function isProcessAlive(pid) { try { process.kill(pid, 0); return true; } catch (err) { return err.code === "EPERM"; } } function readLockFile(lockPath) { try { return JSON.parse(readFileSync(lockPath, "utf-8")); } catch { return; } } function tryUnlink(lockPath) { try { unlinkSync(lockPath); } catch {} } function isLockActive(info) { if (info.pid === process.pid) return false; if (!isProcessAlive(info.pid)) return false; if (Date.now() - info.startedAt > MAX_LOCK_AGE_MS) return false; return true; } /** * Locking is enabled for agents by default. `NUXT_LOCK=1` forces it on for * non-agents; `NUXT_IGNORE_LOCK=1` forces it off. */ function isLockEnabled() { if (process.env.NUXT_IGNORE_LOCK) return false; if (process.env.NUXT_LOCK === "1" || process.env.NUXT_LOCK === "true") return true; return c; } /** * Atomically acquire a build/dev lock. * Returns `{ existing }` if another live process holds the lock, otherwise * `{ release }` to be invoked on shutdown. No-op when locking is disabled. */ function acquireLock(buildDir, info) { if (!isLockEnabled()) return { release: () => {} }; const lockPath = join(buildDir, LOCK_FILENAME); const fullInfo = { pid: process.pid, startedAt: Date.now(), ...info }; try { mkdirSync(buildDir, { recursive: true }); } catch {} for (let attempt = 0; attempt < 2; attempt++) try { writeFileSync(lockPath, JSON.stringify(fullInfo, null, 2), { flag: "wx" }); return { release: makeRelease(lockPath) }; } catch (err) { if (err.code !== "EEXIST") throw err; const existing = readLockFile(lockPath); if (existing && isLockActive(existing)) return { existing }; tryUnlink(lockPath); } const existing = readLockFile(lockPath); if (existing && isLockActive(existing)) return { existing }; return { release: () => {} }; } /** * Overwrite an existing lock we already own with updated metadata (e.g. port * information learned after the listener binds). Callers must hold the lock * via a prior successful `acquireLock`. Does nothing when locking is disabled. */ function updateLock(buildDir, info) { if (!isLockEnabled()) return; const lockPath = join(buildDir, LOCK_FILENAME); const current = readLockFile(lockPath); if (current && current.pid !== process.pid) return; const next = { pid: process.pid, startedAt: current?.startedAt ?? Date.now(), ...info }; try { writeFileSync(lockPath, JSON.stringify(next, null, 2)); } catch {} } function makeRelease(lockPath) { let released = false; function release() { if (released) return; released = true; process.off("exit", release); const current = readLockFile(lockPath); if (!current || current.pid === process.pid) tryUnlink(lockPath); } process.on("exit", release); return release; } /** * Format an error message when a Nuxt process is already running. * Designed to be actionable for both humans and LLM agents. */ function formatLockError(info) { const killCmd = process.platform === "win32" ? `taskkill /PID ${info.pid} /F` : `kill ${info.pid}`; const lines = [ "", `Another Nuxt ${info.command === "dev" ? "dev server" : "build"} is already running:`, "" ]; if (info.url) lines.push(` URL: ${info.url}`); lines.push(` PID: ${info.pid}`); lines.push(` Dir: ${info.cwd}`); lines.push(` Started: ${new Date(info.startedAt).toLocaleString()}`); lines.push(""); if (info.command === "dev" && info.url) lines.push(`Run \`${killCmd}\` to stop it, or connect to ${info.url}`); else lines.push(`Run \`${killCmd}\` to stop it.`); lines.push(`Set NUXT_IGNORE_LOCK=1 to bypass this check.`); lines.push(""); return lines.join("\n"); } //#endregion export { stopCpuProfile as a, startCpuProfile as i, formatLockError as n, updateLock as r, acquireLock as t };