feature-hub
Version:
Feature-based CLI tool to install backend features easily like auth, cron-job, file-upload, etc.
158 lines (147 loc) • 4.54 kB
JavaScript
import path from "node:path";
import readline from "node:readline";
import { fileURLToPath } from "url";
import fs from "node:fs";
import fse from "fs-extra";
import { exec } from "child_process";
import { execSync } from "node:child_process";
// Features list
const features = [
"auth",
"cron-job",
"file-upload",
"payment",
"queue",
"redis",
"payments",
];
const featurepkgs = {
auth: [
"bcryptjs",
"mongoose",
"jsonwebtoken",
"fs-extra",
"fastest-validator",
"express",
"cookie-parser",
],
cronJob: ["node-cron", "redis"],
queue: ["bullmq", "ioredis"],
};
// CLI Arguments
const args = process.argv.slice(2);
const command = args[0];
const pkgname = args[1];
// Console colors
const green = (text) => `\x1b[32m${text}\x1b[0m`;
const red = (text) => `\x1b[31m${text}\x1b[0m`;
const yellow = (text) => `\x1b[33m${text}\x1b[0m`;
// Create readline interface
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
// Helper for logging available features
function showAvailableFeatures() {
console.log(yellow("\nAvailable Features:"));
features.forEach((f) => console.log(` - ${f}`));
}
// Start CLI logic
if (command === "install" || command === "i") {
if (!pkgname || !features.includes(pkgname)) {
console.log(red("❌ Invalid or missing package name!\n"));
showAvailableFeatures();
rl.close();
process.exit(1);
}
rl.question(
green(
`\n✅ Where do you want to install "${pkgname}"? Provide the path:\n> `
),
(inputPath) => {
rl.close();
// Resolve paths
const projectPath = path.resolve(process.cwd(), inputPath);
const targetPath = path.join(projectPath, `feature-hub/${pkgname}`);
editEnvfile(projectPath, pkgname);
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const sourcePath = path.join(__dirname, "../features", pkgname);
// Check if already exists
if (fs.existsSync(targetPath)) {
console.log(red("\n❌ Feature already exists at target location!\n"));
process.exit(1);
}
// Copy folder
try {
fse.copySync(sourcePath, targetPath, { overwrite: true });
editPackageJson(projectPath, featurepkgs[pkgname]);
console.log(
green(`\n✅ Successfully installed "${pkgname}" at ${targetPath}`)
);
} catch (err) {
console.log(red("❌ Failed to copy feature folder: "), err.message);
}
process.exit(0);
}
);
} else {
console.log(red("❌ Invalid command!\n"));
console.log(yellow("Usage:"));
console.log(" feature-hub install <feature>");
console.log(" feature-hub i <feature>");
showAvailableFeatures();
rl.close();
}
function editEnvfile(projectpath, pkgname) {
const envfileExists = fs.existsSync(path.join(projectpath, ".env"));
if (!envfileExists) {
fs.writeFileSync(path.join(projectpath, ".env"), "", "utf-8");
}
switch (pkgname) {
case "auth":
aditenv(projectpath, { JWT_SECRET: "PleaseChangeMe" });
break;
case "queue":
aditenv(projectpath,{ REDIS_HOST: "redis://localhost", REDIS_PORT: 6379 ,password:"" });
break;
case "cron-job":
aditenv(projectpath,{ key: "CRON_JOB_URL", value: "redis://localhost:6379" });
break;
}
}
function editPackageJson(projectPath, pkgname = []) {
console.log(pkgname.join(" "));
const packageJsonExists = fs.existsSync(
path.join(projectPath, "package.json")
);
if (packageJsonExists) {
const packageJsonContent = fs.readFileSync(
path.join(projectPath, "package.json"),
"utf-8"
);
const packageJson = JSON.parse(packageJsonContent);
let packageManager = packageJson.packageManager || "npm";
if (packageManager.includes("pnpm")) {
execSync(`pnpm install ${pkgname.join(" ")}`);
console.log("required packages installed by pnpm");
} else if (packageManager.includes("yarn")) {
execSync(`yarn add ${pkgname.join(" ")}`);
console.log("required packages installed by yarn");
} else if (packageManager.includes("npm")) {
execSync(`npm install ${pkgname.join(" ")}`);
console.log("required packages installed by npm");
}
} else {
console.log("package.json not found");
}
}
function aditenv(projectpath, keyValue = {}) {
for (const [key, value] of Object.entries(keyValue)) {
fs.appendFileSync(
path.join(projectpath, ".env"),
`${key}=${value}\n`,
"utf-8"
);
}
}