Files
kandev/apps/cli/src/cli.ts
T

179 lines
4.9 KiB
TypeScript

import path from "node:path";
import fs from "node:fs";
import pkg from "../package.json";
import { runDev } from "./dev";
import { runRelease } from "./run";
import { runStart } from "./start";
import { ensureValidPort } from "./ports";
import { maybePromptForUpdate } from "./update";
type Command = "run" | "dev" | "start";
type CliOptions = {
command: Command;
version?: string;
backendPort?: number;
webPort?: number;
verbose?: boolean;
debug?: boolean;
};
function printHelp() {
console.log(`kandev launcher
Usage:
kandev run [--version <tag>] [--backend-port <port>] [--web-port <port>] [--verbose] [--debug]
kandev dev [--backend-port <port>] [--web-port <port>]
kandev start [--backend-port <port>] [--web-port <port>] [--verbose] [--debug]
kandev [--version <tag>] [--backend-port <port>] [--web-port <port>] [--verbose] [--debug]
kandev --dev [--backend-port <port>] [--web-port <port>]
Examples:
kandev
kandev run
kandev --dev
kandev dev
kandev start
kandev --version v0.1.0
kandev --backend-port 18080 --web-port 13000
kandev --debug
Options:
dev Use local repo for dev (make dev + next dev) if available.
start Use local production build (make build + next start).
run Use release bundles (default).
--dev Alias for "dev".
--version Release tag to install (default: latest).
--backend-port Override backend port (or KANDEV_BACKEND_PORT env var).
--web-port Override web port (or KANDEV_WEB_PORT env var).
--verbose, -v Show info logs from backend + web.
--debug Show debug logs + agent message dumps.
--help, -h Show help.
`);
}
function parseArgs(argv: string[]): CliOptions {
const opts: CliOptions = { command: "run" };
for (let i = 0; i < argv.length; i += 1) {
const arg = argv[i];
if (arg === "--help" || arg === "-h") {
printHelp();
process.exit(0);
}
if (arg === "dev" || arg === "run" || arg === "start") {
opts.command = arg;
continue;
}
if (arg === "--version") {
opts.version = argv[i + 1];
i += 1;
continue;
}
if (arg.startsWith("--version=")) {
opts.version = arg.split("=")[1];
continue;
}
if (arg === "--dev") {
opts.command = "dev";
continue;
}
if (arg === "--backend-port") {
opts.backendPort = Number(argv[i + 1]);
i += 1;
continue;
}
if (arg.startsWith("--backend-port=")) {
opts.backendPort = Number(arg.split("=")[1]);
continue;
}
if (arg === "--web-port") {
opts.webPort = Number(argv[i + 1]);
i += 1;
continue;
}
if (arg.startsWith("--web-port=")) {
opts.webPort = Number(arg.split("=")[1]);
continue;
}
if (arg === "--verbose" || arg === "-v") {
opts.verbose = true;
continue;
}
if (arg === "--debug") {
opts.debug = true;
continue;
}
}
return opts;
}
function envPort(name: string): number | undefined {
const val = process.env[name];
return val ? Number(val) : undefined;
}
function findRepoRoot(startDir: string): string | null {
let current = path.resolve(startDir);
while (true) {
if (current.endsWith(`${path.sep}apps`)) {
const backendInApps = path.join(current, "backend");
const webInApps = path.join(current, "web");
if (fs.existsSync(backendInApps) && fs.existsSync(webInApps)) {
return path.dirname(current);
}
}
const backendDir = path.join(current, "apps", "backend");
const webDir = path.join(current, "apps", "web");
if (fs.existsSync(backendDir) && fs.existsSync(webDir)) {
return current;
}
const parent = path.dirname(current);
if (parent === current) {
return null;
}
current = parent;
}
}
async function main(): Promise<void> {
const raw = parseArgs(process.argv.slice(2));
const backendPort = ensureValidPort(
raw.backendPort ?? envPort("KANDEV_BACKEND_PORT"),
"backend port",
);
const webPort = ensureValidPort(raw.webPort ?? envPort("KANDEV_WEB_PORT"), "web port");
if (raw.command === "dev") {
const repoRoot = findRepoRoot(process.cwd());
if (!repoRoot) {
throw new Error("Unable to locate repo root for dev. Run from the repo.");
}
await runDev({ repoRoot, backendPort, webPort });
return;
}
if (raw.command === "start") {
const repoRoot = findRepoRoot(process.cwd());
if (!repoRoot) {
throw new Error("Unable to locate repo root for start. Run from the repo.");
}
await runStart({ repoRoot, backendPort, webPort, verbose: raw.verbose, debug: raw.debug });
return;
}
await maybePromptForUpdate(pkg.version, process.argv.slice(2));
await runRelease({
version: raw.version,
backendPort,
webPort,
verbose: raw.verbose,
debug: raw.debug,
});
}
main().catch((err) => {
console.error(`[kandev] ${err instanceof Error ? err.message : String(err)}`);
process.exit(1);
});