4bnode
Version:
4bnode is a CLI-powered backend development platform with a built-in visual dashboard to generate, manage, and test Node.js/Express APIs faster.
175 lines (146 loc) • 7.3 kB
JavaScript
import fs from "fs";
import path from "path";
import chalk from "chalk";
import gradient from "gradient-string";
import ora from "ora";
import { addDepsToPackageJson } from "./codegen.js";
// Brand gradients
const brand = gradient(["#00c6ff", "#0072ff", "#7b2ff7"]);
const warm = gradient(["#f7971e", "#ffd200"]);
const cool = gradient(["#00f260", "#0575e6"]);
const fire = gradient(["#ff416c", "#ff4b2b"]);
// Colors
const dim = chalk.gray;
const accent = chalk.hex("#00c6ff");
const highlight = chalk.hex("#ffd200").bold;
const success = chalk.hex("#00f260").bold;
const warn = chalk.hex("#ffd200");
const err = chalk.hex("#ff416c").bold;
const info = chalk.hex("#0575e6");
const muted = chalk.hex("#888888");
const white = chalk.white;
const bold = chalk.white.bold;
const line = dim(" ─────────────────────────────────────────────────────");
const banner = `
██╗ ██╗██████╗ ███╗ ██╗ ██████╗ ██████╗ ███████╗
██║ ██║██╔══██╗████╗ ██║██╔═══██╗██╔══██╗██╔════╝
███████║██████╔╝██╔██╗ ██║██║ ██║██║ ██║█████╗
╚════██║██╔══██╗██║╚██╗██║██║ ██║██║ ██║██╔══╝
██║██████╔╝██║ ╚████║╚██████╔╝██████╔╝███████╗
╚═╝╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚══════╝`;
// ── Banner ──────────────────────────────────────────
export function showBanner() {
console.log(brand(banner));
console.log(line);
console.log(
dim(" ") + bold("4Brains Technologies") + dim(" | Node.js Project Generator")
);
console.log(line + "\n");
}
// ── Section Header ──────────────────────────────────
export function showHeader(command, description) {
console.log();
console.log(dim(" ┌─────────────────────────────────────────────────┐"));
console.log(
dim(" │ ") + accent("◆ ") + highlight(command) + dim(" ") + white(description) + dim(" │")
);
console.log(dim(" └─────────────────────────────────────────────────┘"));
console.log();
}
// ── Spinners ────────────────────────────────────────
export function createSpinner(text) {
return ora({
text: muted(text),
spinner: "dots12",
color: "cyan",
indent: 2,
});
}
// ── Step indicators ─────────────────────────────────
export function showStep(step, total, text) {
const progress = accent(`[${step}/${total}]`);
console.log(` ${progress} ${white(text)}`);
}
// ── Success ─────────────────────────────────────────
export function showSuccess(lines) {
console.log();
console.log(dim(" ┌─────────────────────────────────────────────────┐"));
console.log(dim(" │") + success(" ✔ Done!") + dim(new Array(42).join(" ") + "│"));
console.log(dim(" └─────────────────────────────────────────────────┘"));
console.log();
for (const l of lines) {
console.log(dim(" │ ") + white(l));
}
console.log();
}
// ── Task complete (for add-* commands) ──────────────
export function showTaskDone(title, details = []) {
console.log();
console.log(` ${success("✔")} ${cool(title)}`);
for (const d of details) {
console.log(` ${dim("→")} ${muted(d)}`);
}
console.log();
}
// ── Warning ─────────────────────────────────────────
export function showWarning(message) {
console.log(`\n ${warn("⚠")} ${warn(message)}\n`);
}
// ── Error ───────────────────────────────────────────
export function showError(message) {
console.log(`\n ${err("✖")} ${err(message)}\n`);
}
// ── Info ────────────────────────────────────────────
export function showInfo(message) {
console.log(` ${info("ℹ")} ${muted(message)}`);
}
// ── Next Steps ──────────────────────────────────────
export function showNextSteps(steps) {
console.log(accent.bold(" Next steps:\n"));
for (let i = 0; i < steps.length; i++) {
const num = highlight(` ${i + 1} `);
console.log(` ${num} ${white(steps[i])}`);
}
console.log();
console.log(line);
console.log(dim(" Happy coding! ") + brand("★ 4Brains"));
console.log(line + "\n");
}
// ── Installing deps (with spinner) ──────────────────
export async function installDeps(packages, opts = {}) {
const { execSync } = await import("child_process");
const { dev = false, ...execOpts } = opts;
const names = Array.isArray(packages) ? packages : [packages];
const label = names.join(", ");
// Record in package.json first, so the app is installable even if the live
// install fails (offline) — a later `npm install` then resolves everything.
try {
const pkgPath = path.join(execOpts.cwd || process.cwd(), "package.json");
if (fs.existsSync(pkgPath)) {
const { content, changed } = addDepsToPackageJson(fs.readFileSync(pkgPath, "utf8"), names, { dev });
if (changed) fs.writeFileSync(pkgPath, content, "utf8");
}
} catch {}
const spinner = createSpinner(`Installing ${label}...`);
spinner.start();
try {
execSync(`npm install ${dev ? "-D " : ""}${names.join(" ")}`, {
stdio: "pipe",
...execOpts,
});
spinner.succeed(success(`Installed ${label}`));
} catch (e) {
spinner.fail(err(`Failed to install ${label}`));
throw e;
}
}
// ── File action log ─────────────────────────────────
export function showFileAction(action, filePath) {
const icons = {
created: chalk.hex("#00f260")("+ "),
updated: chalk.hex("#0575e6")("~ "),
deleted: chalk.hex("#ff416c")("- "),
};
const icon = icons[action] || dim(" ");
console.log(` ${icon}${muted(filePath)}`);
}