astro
Version:
Astro is a modern site builder with web best practices, performance, and DX front-of-mind.
126 lines (125 loc) • 3.22 kB
JavaScript
import { existsSync, readFileSync, unlinkSync, writeFileSync, mkdirSync } from "node:fs";
import { fileURLToPath } from "node:url";
const GRACEFUL_SHUTDOWN_TIMEOUT = 5e3;
function getLockFileURL(root) {
return new URL(".astro/dev.json", root);
}
function getLogFileURL(root) {
return new URL(".astro/dev.log", root);
}
function isStringArray(value) {
return Array.isArray(value) && value.every((item) => typeof item === "string");
}
function isResolvedServerUrls(value) {
if (typeof value !== "object" || value === null) {
return false;
}
const { local, network } = value;
return isStringArray(local) && isStringArray(network);
}
function parseLockFile(content) {
try {
const data = JSON.parse(content);
if (typeof data.pid !== "number" || typeof data.port !== "number" || typeof data.url !== "string" || typeof data.background !== "boolean" || typeof data.startedAt !== "string") {
return null;
}
if (data.urls !== void 0 && !isResolvedServerUrls(data.urls)) {
return null;
}
return data;
} catch {
return null;
}
}
function serializeLockFile(data) {
return JSON.stringify(data, null, " ");
}
function isProcessAlive(pid) {
try {
process.kill(pid, 0);
return true;
} catch {
return false;
}
}
function readLockFile(root) {
const lockFileURL = getLockFileURL(root);
try {
const content = readFileSync(lockFileURL, "utf-8");
return parseLockFile(content);
} catch {
return null;
}
}
function writeLockFile(root, data) {
const lockFileURL = getLockFileURL(root);
const dirPath = fileURLToPath(new URL(".astro/", root));
try {
if (!existsSync(dirPath)) {
mkdirSync(dirPath, { recursive: true });
}
writeFileSync(lockFileURL, serializeLockFile(data), "utf-8");
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
throw new Error(`Failed to write lock file: ${message}`);
}
}
function removeLockFile(root) {
const lockFileURL = getLockFileURL(root);
try {
unlinkSync(lockFileURL);
} catch (err) {
if (err?.code !== "ENOENT") {
throw err;
}
}
}
function evaluateExistingServer(data, alive) {
if (data === null) {
return null;
}
return { data, stale: !alive };
}
async function killDevServer(root, data) {
try {
process.kill(data.pid, "SIGTERM");
} catch {
}
const deadline = Date.now() + GRACEFUL_SHUTDOWN_TIMEOUT;
while (Date.now() < deadline) {
if (!isProcessAlive(data.pid)) break;
await new Promise((r) => setTimeout(r, 100));
}
if (isProcessAlive(data.pid)) {
try {
process.kill(data.pid, "SIGKILL");
} catch {
}
}
removeLockFile(root);
}
function checkExistingServer(root) {
const data = readLockFile(root);
const result = evaluateExistingServer(data, data !== null && isProcessAlive(data.pid));
if (result === null) {
return null;
}
if (result.stale) {
removeLockFile(root);
return null;
}
return result.data;
}
export {
GRACEFUL_SHUTDOWN_TIMEOUT,
checkExistingServer,
evaluateExistingServer,
getLogFileURL,
isProcessAlive,
killDevServer,
parseLockFile,
readLockFile,
removeLockFile,
serializeLockFile,
writeLockFile
};