astro
Version:
Astro is a modern site builder with web best practices, performance, and DX front-of-mind.
180 lines (178 loc) • 6.13 kB
JavaScript
import { detectAgenticEnvironment } from "am-i-vibing";
import colors from "piccolore";
import devServer from "../../core/dev/index.js";
import { pathToFileURL } from "node:url";
import {
checkExistingServer,
killDevServer,
removeLockFile,
writeLockFile
} from "../../core/dev/lockfile.js";
import { resolveRoot } from "../../core/config/config.js";
import { printHelp } from "../../core/messages/runtime.js";
import { createLoggerFromFlags, flagsToAstroInlineConfig } from "../flags.js";
function isRunByAgent() {
try {
return detectAgenticEnvironment().type === "agent";
} catch {
return false;
}
}
function isIgnoreLock(flags) {
return flags.ignoreLock === true;
}
function getBackgroundIgnoreLockConflict(flags, wantsBackground) {
if (!wantsBackground) {
return null;
}
const reason = flags.background ? "`--background`" : "an auto-detected AI agent environment, which runs the dev server in the background automatically";
return [
`\`--ignore-lock\` cannot be used together with ${reason}.`,
"",
"Background dev servers rely on the lock file so `astro dev stop`, `astro dev status`, and `astro dev logs` can find them.",
"Run the dev server in the foreground to use --ignore-lock."
].join("\n");
}
function getForceIgnoreLockConflict(flags) {
if (!flags.force) {
return null;
}
return [
"`--force` and `--ignore-lock` cannot be used together.",
"",
"`--force` replaces the existing dev server; `--ignore-lock` starts a new one alongside it without touching the lock file. Choose one."
].join("\n");
}
async function dev({ flags }) {
if (flags.help || flags.h) {
printHelp({
commandName: "astro dev",
usage: "[command] [...flags]",
tables: {
Commands: [
["stop", "Stop a running background dev server."],
["status", "Check if a dev server is running."],
["logs [--follow]", "View logs from a background dev server."]
],
Flags: [
["--background", "Start the dev server as a background process."],
["--mode", `Specify the mode of the project. Defaults to "development".`],
["--port", `Specify which port to run on. Defaults to 4321.`],
["--host", `Listen on all addresses, including LAN and public addresses.`],
["--host <custom-address>", `Expose on a network IP address at <custom-address>`],
["--open", "Automatically open the app in the browser on server start"],
["--force", "Clear the content layer cache, forcing a full rebuild."],
[
"--ignore-lock",
"Start the dev server even if another one is already running, without checking or writing the lock file."
],
[
"--allowed-hosts",
"Specify a comma-separated list of allowed hosts or allow any hostname."
],
["--help (-h)", "See all available flags."]
]
},
description: `Check ${colors.cyan(
"https://docs.astro.build/en/reference/cli-reference/#astro-dev"
)} for more information.`
});
return;
}
const agentDetected = !process.env.ASTRO_DEV_BACKGROUND && isRunByAgent();
if (agentDetected) {
flags.json = true;
}
const ignoreLock = isIgnoreLock(flags);
const wantsBackground = !!flags.background || agentDetected;
const logger = createLoggerFromFlags(flags);
const subcommand = flags._[3]?.toString();
if (subcommand === "stop") {
const { stop } = await import("./stop.js");
await stop({ flags, logger });
return;
}
if (subcommand === "status") {
const { status } = await import("./status.js");
await status({ flags, logger });
return;
}
if (subcommand === "logs") {
const { logs } = await import("./logs.js");
await logs({ flags, logger });
return;
}
if (ignoreLock) {
const conflict = getBackgroundIgnoreLockConflict(flags, wantsBackground) ?? getForceIgnoreLockConflict(flags);
if (conflict) {
throw new Error(conflict);
}
}
if (wantsBackground) {
const { background } = await import("./background.js");
await background({ flags, logger });
return;
}
if (subcommand) {
logger.error(
"SKIP_FORMAT",
`Unknown command: astro dev ${subcommand}
Run \`astro dev --help\` to see available commands.`
);
process.exit(1);
}
const root = pathToFileURL(resolveRoot(flags.root) + "/");
if (ignoreLock) {
const existingServer2 = checkExistingServer(root);
if (existingServer2) {
logger.info(
"SKIP_FORMAT",
[
`Starting a new dev server alongside the one already running at ${existingServer2.url} (pid ${existingServer2.pid}).`,
"This instance is not tracked by `astro dev stop`, `astro dev status`, or `astro dev logs`."
].join("\n")
);
}
const inlineConfig2 = flagsToAstroInlineConfig(flags);
return await devServer(inlineConfig2);
}
const existingServer = checkExistingServer(root);
if (existingServer) {
if (flags.force) {
await killDevServer(root, existingServer);
} else {
const message = [
"Another astro dev server is already running.",
"",
` URL: ${existingServer.url}`,
` PID: ${existingServer.pid}`,
"",
`Run \`astro dev stop\` to stop it, or use \`astro dev --force\` to replace it.`
].join("\n");
throw new Error(message);
}
}
const inlineConfig = flagsToAstroInlineConfig(flags);
const server = await devServer(inlineConfig);
const serverUrl = new URL(server.resolvedUrls.local[0]).origin;
writeLockFile(root, {
pid: process.pid,
port: server.address.port,
url: serverUrl,
urls: server.resolvedUrls,
background: !!process.env.ASTRO_DEV_BACKGROUND,
startedAt: (/* @__PURE__ */ new Date()).toISOString()
});
const originalStop = server.stop.bind(server);
server.stop = async () => {
removeLockFile(root);
await originalStop();
};
return server;
}
export {
dev,
getBackgroundIgnoreLockConflict,
getForceIgnoreLockConflict,
isIgnoreLock
};