UNPKG

peezy-cli

Version:

Production-ready CLI for scaffolding modern applications with curated full-stack templates, intelligent migrations, and enterprise security.

158 lines 4.89 kB
import { execSync } from "node:child_process"; import fs from "node:fs"; import net from "node:net"; import path from "node:path"; import { log } from "../utils/logger.js"; function checkCmd(cmd) { try { const out = execSync(cmd, { stdio: ["ignore", "pipe", "pipe"] }) .toString() .trim(); return { ok: true, version: out }; } catch (e) { return { ok: false, error: e?.message || String(e) }; } } function checkPort(port) { return new Promise((resolve) => { const server = net.createServer(); server.once("error", () => resolve(false)); server.once("listening", () => { server.close(() => resolve(true)); }); server.listen(port, "127.0.0.1"); }); } function findProjectRoot(cwd) { let dir = cwd; while (dir !== path.parse(dir).root) { if (fs.existsSync(path.join(dir, "package.json")) || fs.existsSync(path.join(dir, "pyproject.toml")) || fs.existsSync(path.join(dir, "requirements.txt"))) { return dir; } dir = path.dirname(dir); } return cwd; } export async function doctor(opts = {}) { let ok = true; const errors = []; const warnings = []; const checks = {}; const cwd = process.cwd(); const root = findProjectRoot(cwd); if (!opts.json) { log.info(`Doctor running in ${root}`); } // Versions const node = checkCmd("node -v"); const bun = checkCmd("bun --version"); const python = checkCmd("python3 --version"); checks.versions = { node, bun, python }; if (!opts.json) { log.info(`Node: ${node.ok ? node.version : "missing"}`); log.info(`Bun: ${bun.ok ? bun.version : "missing"}`); log.info(`Python: ${python.ok ? python.version : "missing"}`); } if (!node.ok) { ok = false; errors.push("Node.js is not available"); } // Env const envFile = path.join(root, ".env"); const envExample = path.join(root, ".env.example"); if (fs.existsSync(envExample)) { const example = fs .readFileSync(envExample, "utf8") .split(/\r?\n/) .filter(Boolean); const missing = []; const have = fs.existsSync(envFile) ? fs.readFileSync(envFile, "utf8") : ""; for (const line of example) { const key = line.split("=")[0]; if (key && !have.includes(`${key}=`)) missing.push(key); } if (missing.length) { ok = false; log.warn(`Missing env vars in .env: ${missing.join(", ")}`); if (opts.fixEnvExamples && !fs.existsSync(envExample)) { fs.writeFileSync(envExample, "", "utf8"); } } else { log.ok("Environment variables present (based on .env.example)"); } } else { log.warn(".env.example not found"); } // DB connectivity placeholder (Prisma/Peewee) try { const hasPrisma = fs.existsSync(path.join(root, "prisma/schema.prisma")); if (hasPrisma) { // Best-effort check: ensure DATABASE_URL is present if (!process.env.DATABASE_URL) { ok = false; log.warn("DATABASE_URL not set. Skipping DB connectivity check."); } } } catch { } // Pending migrations placeholder if (fs.existsSync(path.join(root, "prisma/migrations"))) { log.info("Prisma migrations folder present. Run your package manager to check pending migrations."); } // Broken imports (TypeScript/JS only: try tsc --noEmit) try { if (fs.existsSync(path.join(root, "tsconfig.json"))) { execSync("npx tsc -p tsconfig.json --noEmit", { cwd: root, stdio: "pipe", }); log.ok("TypeScript typecheck passed"); } } catch (e) { ok = false; log.err("TypeScript typecheck failed"); } // Lint autofix if (opts.fixLint) { try { execSync("npm run lint --silent -- --fix", { cwd: root, stdio: "inherit", }); } catch { // ignore, do not flip ok } } // Port conflicts const ports = opts.ports && opts.ports.length ? opts.ports : [3000, 5173, 8000]; for (const p of ports) { const free = await checkPort(p); if (!free) { ok = false; log.warn(`Port ${p} appears to be in use`); } } if (opts.json) { return { ok, errors, warnings, checks, }; } if (!ok) { log.err("Doctor found issues"); return 1; } log.ok("All checks passed"); return 0; } //# sourceMappingURL=doctor.js.map