import { execFileSync } from "node:child_process"; import { existsSync, readFileSync, rmSync } from "node:fs"; import { resolve } from "node:path"; const lockPath = ".next/dev/lock"; const devDirPath = ".next/dev"; const webpackRuntimePath = ".next/dev/server/webpack-runtime.js"; const vendorChunkPath = ".next/dev/server/vendor-chunks/next.js"; const projectRoot = process.cwd(); function run(cmd, args) { try { return execFileSync(cmd, args, { encoding: "utf8" }).trim(); } catch { return ""; } } function pidExists(pid) { try { process.kill(pid, 0); return true; } catch { return false; } } function getProcessCwd(pid) { const output = run("lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"]); const cwdLine = output .split("\n") .find((line) => line.startsWith("n")); return cwdLine ? cwdLine.slice(1) : ""; } function getListeningPids(port) { const output = run("lsof", ["-ti", `tcp:${port}`, "-sTCP:LISTEN"]); if (!output) return []; return output .split("\n") .map((line) => Number(line.trim())) .filter((v) => Number.isInteger(v) && v > 0); } function removeLock(reason) { rmSync(lockPath, { force: true }); console.log(`Removed stale ${lockPath}: ${reason}`); } function resetDevCache(reason) { rmSync(devDirPath, { force: true, recursive: true }); console.log(`Reset ${devDirPath}: ${reason}`); } if (existsSync(webpackRuntimePath) && !existsSync(vendorChunkPath)) { resetDevCache("missing vendor-chunks/next.js"); process.exit(0); } if (!existsSync(lockPath)) process.exit(0); let lock; try { lock = JSON.parse(readFileSync(lockPath, "utf8")); } catch { removeLock("invalid JSON"); process.exit(0); } const pid = Number(lock?.pid ?? 0); const port = Number(lock?.port ?? 3000); if (!pid) { removeLock("missing pid"); process.exit(0); } if (!pidExists(pid)) { removeLock(`pid ${pid} not running`); process.exit(0); } const processCwd = getProcessCwd(pid); if (!processCwd) { removeLock(`cannot resolve cwd for pid ${pid}`); process.exit(0); } if (resolve(processCwd) !== resolve(projectRoot)) { removeLock(`pid ${pid} belongs to another directory`); process.exit(0); } const portPids = getListeningPids(port); if (portPids.length > 0 && !portPids.includes(pid)) { removeLock(`pid ${pid} is not listening on port ${port}`); }