archgenie
Version:
A powerful Node.js CLI tool to quickly scaffold project folder structures based on popular software architecture patterns.
449 lines (423 loc) • 8.47 kB
JavaScript
const fs = require("fs").promises;
const path = require("path");
const readline = require("readline");
const { exec } = require("child_process");
// Created by Praveen KR
// https://www.linkedin.com/in/mepraveenkr/
// https://github.com/frontenddeveloperpraveen
const CLI_FILE = ".cli";
const VERSION = "1.1.3";
const file = {
"--mvc": [
"models",
"views",
"controllers",
"services",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--mvp": [
"models",
"views",
"presenters",
"services",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--mvvm": [
"models",
"views",
"viewmodels",
"services",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--pac": [
"presentation",
"abstraction",
"control",
"services",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--hmvc": [
"modules",
"models",
"views",
"controllers",
"services",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--layered": [
"presentation_layer",
"application_layer",
"business_layer",
"persistence_layer",
"database",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--monolithic": [
"controllers",
"models",
"views",
"services",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--microservices": [
"service_A",
"service_B",
"service_C",
"shared_libs",
"gateway",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--soa": [
"services",
"service_bus",
"adapters",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--serverless": [
"functions",
"triggers",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--clean": [
"entities",
"use_cases",
"interfaces",
"frameworks",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--hexagonal": [
"core",
"ports",
"adapters",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--onion": [
"domain",
"application",
"infrastructure",
"interfaces",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--rest": [
"routes",
"controllers",
"models",
"services",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--graphql": [
"schemas",
"resolvers",
"models",
"services",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--grpc": [
"proto_files",
"services",
"handlers",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--event_driven": [
"event_producers",
"event_consumers",
"event_bus",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--cqrs": [
"commands",
"queries",
"models",
"services",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--pub_sub": [
"publishers",
"subscribers",
"message_broker",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--client_server": [
"client",
"server",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--p2p": [
"nodes",
"peer_connections",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--master_slave": [
"master",
"slaves",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--leader_follower": [
"leader",
"followers",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--load_balanced_cluster": [
"load_balancer",
"nodes",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--blackboard": [
"blackboard",
"knowledge_sources",
"controllers",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--pipes_filters": [
"pipes",
"filters",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--microkernel": [
"core_system",
"plugins",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--service_mesh": [
"control_plane",
"data_plane",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
"--reactive": [
"event_streams",
"reactive_components",
"middlewares",
"config",
"utils",
"tests",
"docs",
],
};
function promptUser(question) {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
return new Promise((resolve) =>
rl.question(question, (ans) => {
rl.close();
resolve(ans.trim().toLowerCase());
})
);
}
async function getCurrentArch() {
try {
const data = await fs.readFile(CLI_FILE, "utf8");
return data.trim();
} catch {
return null; // No file yet
}
}
async function setCurrentArch(arch) {
await fs.writeFile(CLI_FILE, arch);
}
async function deleteAllExceptNodeModulesAndCli() {
const items = await fs.readdir(process.cwd());
for (const item of items) {
if (item === "node_modules" || item === CLI_FILE) continue;
await fs.rm(path.join(process.cwd(), item), {
recursive: true,
force: true,
});
}
}
async function backupCurrentArch(currentArch) {
const backupDir = `backup-${currentArch}`;
await fs.mkdir(backupDir, { recursive: true });
const items = await fs.readdir(process.cwd());
for (const item of items) {
if (
item === "node_modules" ||
item === CLI_FILE ||
item === backupDir // prevent moving backup into itself
)
continue;
await fs.rename(path.join(process.cwd(), item), path.join(backupDir, item));
}
}
async function createFolder(folderList) {
const files_name = [".gitignore", "server.js"];
for (const filename of files_name) {
const f = await fs.open(filename, "a");
if (filename === ".gitignore") await f.write("node_modules\n.cli\n");
await f.close();
}
const commands = ["git init", "npm init -y", "npm i express nodemon"];
for (const cmd of commands) {
exec(cmd, (error, stdout, stderr) => {
if (error) return console.error(`Error: ${error.message}`);
if (stderr) return console.error(`Stderr: ${stderr}`);
console.log(`${cmd}:\n${stdout}`);
});
}
for (const folder of folderList) {
try {
await fs.mkdir(folder, { recursive: true });
console.log(`${folder} created successfully!`);
} catch (err) {
console.error("Error creating folder:", err);
}
}
}
function showHelp() {
console.log(`
Usage:
cli-tool <architecture-flag>
Available Flags:
${Object.keys(file).join("\n ")}
--help
--version
`);
}
(async () => {
const args = process.argv[2];
if (!args || args === "--help") {
showHelp();
process.exit(0);
}
if (args === "--version") {
console.log(`Version: ${VERSION}`);
process.exit(0);
}
if (!file[args]) {
console.error("Invalid architecture flag. Use --help to see options.");
process.exit(1);
}
const currentArch = await getCurrentArch();
if (currentArch && currentArch !== args) {
console.log(`Detected previous architecture: ${currentArch}`);
const choice = await promptUser(
"Do you want to delete all files or take a backup? (delete/backup): "
);
if (choice === "delete") {
await deleteAllExceptNodeModulesAndCli();
console.log("All files deleted.");
} else if (choice === "backup") {
await backupCurrentArch(currentArch);
console.log(`Backup created in folder: backup-${currentArch}`);
} else {
console.log("Invalid choice. Exiting.");
process.exit(1);
}
}
await createFolder(file[args]);
await setCurrentArch(args);
})();