UNPKG

express-autotemplates

Version:
448 lines (397 loc) • 17.7 kB
#!/usr/bin/env node const { Command } = require("commander"); const inquirer = require("inquirer"); const chalk = require("chalk"); const { spawn } = require("child_process"); const path = require("path"); const { generateProject } = require("../src/generator"); // Handle unhandled promise rejections process.on("unhandledRejection", (err) => { console.error(chalk.red("šŸ’„ Unhandled promise rejection:"), err); process.exit(1); }); // Handle uncaught exceptions process.on("uncaughtException", (err) => { console.error(chalk.red("šŸ’„ Uncaught exception:"), err); process.exit(1); }); // ASCII Art Banner const showBanner = () => { console.log( chalk.cyan(`╔═══════════════════════════════════════════════════════════════╗ ā•‘ ā•‘ ā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•‘ ā•‘ ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā•šā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā•”ā•ā•ā•ā•ā• ā•‘ ā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•šā–ˆā–ˆā–ˆā•”ā• ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•‘ ā•‘ ā–ˆā–ˆā•”ā•ā•ā• ā–ˆā–ˆā•”ā–ˆā–ˆā•— ā–ˆā–ˆā•”ā•ā•ā•ā• ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā• ā•šā•ā•ā•ā•ā–ˆā–ˆā•‘ā•šā•ā•ā•ā•ā–ˆā–ˆā•‘ ā•‘ ā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•”ā• ā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ ā•‘ ā•‘ ā•šā•ā•ā•ā•ā•ā•ā•ā•šā•ā• ā•šā•ā•ā•šā•ā• ā•šā•ā• ā•šā•ā•ā•šā•ā•ā•ā•ā•ā•ā•ā•šā•ā•ā•ā•ā•ā•ā•ā•šā•ā•ā•ā•ā•ā•ā• ā•‘ ā•‘ ā•‘ ā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā•—ā•‘ ā•‘ ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā•šā•ā•ā–ˆā–ˆā•”ā•ā•ā•ā–ˆā–ˆā•”ā•ā•ā•ā–ˆā–ˆā•—ā•šā•ā•ā–ˆā–ˆā•”ā•ā•ā•ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā•‘ā•‘ ā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•”ā–ˆā–ˆā–ˆā–ˆā•”ā–ˆā–ˆā•‘ā•‘ ā•‘ ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•”ā•ā•ā• ā–ˆā–ˆā•‘ā•šā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ā•‘ ā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā• ā–ˆā–ˆā•‘ ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā• ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā•šā•ā• ā–ˆā–ˆā•‘ā•‘ ā•‘ ā•šā•ā• ā•šā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā• ā•šā•ā•ā•ā•ā•ā•ā•ā•šā•ā• ā•šā•ā•ā•‘ ā•‘ ā•‘ ā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•‘ ā•‘ ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā•šā•ā•ā–ˆā–ˆā•”ā•ā•ā•ā–ˆā–ˆā•”ā•ā•ā•ā•ā• ā•‘ ā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•‘ ā•‘ ā–ˆā–ˆā•”ā•ā•ā•ā• ā–ˆā–ˆā•‘ ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•”ā•ā•ā• ā•‘ ā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•‘ ā•‘ ā•šā•ā• ā•šā•ā•ā•ā•ā•ā•ā•ā•šā•ā• ā•šā•ā• ā•šā•ā• ā•šā•ā•ā•ā•ā•ā•ā• ā•‘ ā•‘ ā•‘ ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•`) ); console.log( chalk.magenta.bold( " šŸš€ Your automated Express backend template generator! ✨\n" ) ); console.log( chalk.gray(" Generate professional Express.js backends in seconds\n") ); }; // Loading animation const showLoading = (message, duration = 2000) => { return new Promise((resolve) => { const frames = ["ā ‹", "ā ™", "ā ¹", "ā ø", "ā ¼", "ā “", "ā ¦", "ā §", "ā ‡", "ā "]; let i = 0; const interval = setInterval(() => { process.stdout.write( `\r${chalk.cyan(frames[i])} ${chalk.white(message)}` ); i = (i + 1) % frames.length; }, 100); setTimeout(() => { clearInterval(interval); process.stdout.write(`\r${chalk.green("āœ…")} ${chalk.white(message)}\n`); resolve(); }, duration); }); }; // Install dependencies automatically const installDependencies = (projectPath) => { return new Promise((resolve) => { console.log(chalk.cyan("šŸ“¦ Installing dependencies automatically...\n")); // Show a simple progress indicator const progressFrames = ["ā ‹", "ā ™", "ā ¹", "ā ø", "ā ¼", "ā “", "ā ¦", "ā §", "ā ‡", "ā "]; let progressIndex = 0; let progressMessage = "Installing packages..."; const progressInterval = setInterval(() => { process.stdout.write( `\r${chalk.cyan(progressFrames[progressIndex])} ${chalk.white( progressMessage )}` ); progressIndex = (progressIndex + 1) % progressFrames.length; }, 100); // Determine the correct npm command for the platform const isWindows = process.platform === "win32"; const npmCommand = isWindows ? "npm.cmd" : "npm"; const npmInstall = spawn(npmCommand, ["install"], { cwd: projectPath, stdio: ["pipe", "pipe", "pipe"], shell: isWindows, }); // Handle stdout (but don't show it to keep output clean) npmInstall.stdout.on("data", (data) => { const output = data.toString(); // Update progress message based on npm output if (output.includes("added")) { progressMessage = "Adding packages..."; } else if (output.includes("found")) { progressMessage = "Resolving dependencies..."; } else if (output.includes("audited")) { progressMessage = "Auditing packages..."; } }); // Handle stderr npmInstall.stderr.on("data", (data) => { // Only show critical errors, not warnings const errorOutput = data.toString(); if (errorOutput.includes("ERROR") || errorOutput.includes("ENOENT")) { clearInterval(progressInterval); process.stdout.write(`\r${chalk.red("āŒ")} Installation error\n`); console.log(chalk.yellow(errorOutput)); } }); npmInstall.on("close", (code) => { clearInterval(progressInterval); if (code === 0) { process.stdout.write( `\r${chalk.green("āœ…")} Dependencies installed successfully!\n\n` ); } else { process.stdout.write( `\r${chalk.yellow("āš ļø")} Dependencies installation failed\n` ); console.log( chalk.gray( "You can install them manually by running 'npm install' in your project directory\n" ) ); } resolve(); }); npmInstall.on("error", (error) => { clearInterval(progressInterval); process.stdout.write( `\r${chalk.yellow("āš ļø")} Could not install dependencies automatically\n` ); console.log(chalk.gray(`Error: ${error.message}`)); console.log( chalk.gray( "You can install them manually by running 'npm install' in your project directory\n" ) ); resolve(); }); // Add timeout to prevent hanging setTimeout(() => { if (!npmInstall.killed) { clearInterval(progressInterval); process.stdout.write(`\r${chalk.yellow("āš ļø")} Installation timeout\n`); console.log( chalk.gray( "Installation is taking too long, continuing without automatic installation" ) ); console.log( chalk.gray( "You can install dependencies manually with 'npm install'\n" ) ); npmInstall.kill(); resolve(); } }, 120000); // 2 minutes timeout }); }; // Success celebration const showSuccess = (projectName) => { console.log( chalk.green(` ╔══════════════════════════════════════════════╗ ā•‘ ā•‘ ā•‘ šŸŽ‰ SUCCESS! Your project is ready! šŸŽ‰ ā•‘ ā•‘ ā•‘ ā•‘ Project: ${chalk.cyan.bold(projectName.padEnd(28))} ā•‘ ā•‘ ā•‘ ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā• `) ); }; const program = new Command(); program .name("express-autotemplates") .description( chalk.gray("šŸš€ Generate Express backend projects with various templates") ) .version("1.0.0"); program .command("create [project-name]") .description(chalk.gray("Create a new Express backend project")) .action(async (projectName) => { try { // Show banner showBanner(); // Get project name if not provided if (!projectName) { console.log(chalk.yellow("šŸ“ Let's start by naming your project...\n")); const answers = await inquirer.prompt([ { type: "input", name: "projectName", message: chalk.cyan("šŸ·ļø What is your project name?"), validate: (input) => { if (!input.trim()) { return chalk.red("āŒ Project name is required"); } if (input.length > 50) { return chalk.red( "āŒ Project name must be less than 50 characters" ); } if (!/^[a-zA-Z0-9-_]+$/.test(input)) { return chalk.red( "āŒ Project name can only contain letters, numbers, hyphens, and underscores" ); } return true; }, transformer: (input) => chalk.cyan.bold(input), }, ]); projectName = answers.projectName; } console.log( chalk.green( `\n✨ Great choice! Creating "${chalk.bold(projectName)}"...\n` ) ); // Template selection with enhanced styling const templateChoices = [ { name: chalk.cyan("šŸš€ Basic Backend") + chalk.gray(" - Simple Express server with organized structure"), value: "basic", short: "Basic Backend", }, { name: chalk.blue("šŸ’¬ Chat App Backend") + chalk.gray(" - Real-time chat with Socket.io"), value: "chatapp", short: "Chat App", }, { name: chalk.green("šŸ›’ E-commerce Backend") + chalk.gray(" - Product management, cart, orders"), value: "ecom", short: "E-commerce", }, { name: chalk.magenta("šŸ“ Blog Backend") + chalk.gray(" - Posts, comments, user management"), value: "blog", short: "Blog", }, { name: chalk.magenta("šŸ¤– AI Chat Backend") + chalk.gray(" - AI-powered chat application"), value: "aichat", short: "AI Chat", }, ]; console.log(chalk.yellow("šŸŽØ Choose your backend template:\n")); const { template } = await inquirer.prompt([ { type: "list", name: "template", message: chalk.cyan("šŸŽÆ Select a template:"), choices: templateChoices, pageSize: 10, }, ]); // Show template info const templateInfo = { basic: { emoji: "šŸš€", name: "Basic Backend", color: "cyan" }, chatapp: { emoji: "šŸ’¬", name: "Chat App Backend", color: "blue" }, ecom: { emoji: "šŸ›’", name: "E-commerce Backend", color: "green" }, blog: { emoji: "šŸ“", name: "Blog Backend", color: "magenta" }, aichat: { emoji: "šŸ¤–", name: "AI Chat Backend", color: "magenta" }, }; const info = templateInfo[template]; console.log( chalk[info.color](`\n${info.emoji} Selected: ${info.name}\n`) ); // Loading animation await showLoading("šŸ—ļø Setting up project structure...", 1500); await showLoading("šŸ“¦ Installing dependencies configuration...", 1000); await showLoading("šŸ”§ Configuring middleware and security...", 1200); await showLoading("šŸ“ Generating documentation...", 800); // Generate project await generateProject(projectName, template); // Automatically install dependencies const projectPath = path.join(process.cwd(), projectName); await installDependencies(projectPath); // Success message showSuccess(projectName); // Next steps with beautiful formatting (updated since dependencies are already installed) console.log(chalk.yellow.bold("šŸš€ Your project is ready! Next steps:\n")); const steps = [ { icon: "šŸ“", text: `cd ${projectName}`, desc: "Navigate to your project", }, { icon: "šŸš€", text: "npm start", desc: "Start your development server", }, { icon: "🌐", text: "Open http://localhost:3000", desc: "View your app in the browser", }, { icon: "šŸ“", text: "code .", desc: "Open in your favorite editor", }, ]; steps.forEach((step, index) => { console.log( chalk.cyan(` ${index + 1}. ${step.icon} ${chalk.bold(step.text)}`) ); console.log(chalk.gray(` ${step.desc}\n`)); }); console.log( chalk.green.bold( "šŸŽ‰ Happy coding! Your Express backend is ready to go! ✨\n" ) ); // Show helpful commands for the project directory console.log(chalk.cyan.bold("šŸ’” Quick commands to get started:\n")); console.log(chalk.white(` cd ${projectName} && npm start`)); console.log(chalk.gray(" Start your server immediately\n")); console.log(chalk.white(` cd ${projectName} && npm run dev`)); console.log(chalk.gray(" Start with auto-reload for development\n")); // Footer console.log( chalk.gray( "───────────────────────────────────────────────────────────────" ) ); console.log( chalk.gray("šŸ’” Need help? Check the README.md in your project folder") ); console.log(chalk.gray("šŸ› Found a bug? Report it on GitHub")); console.log(chalk.gray("⭐ Like Express Genie? Give us a star!")); console.log( chalk.gray( "───────────────────────────────────────────────────────────────\n" ) ); } catch (error) { console.log(chalk.red(`\nšŸ’„ Oops! Something went wrong:\n`)); console.log(chalk.red(` āŒ ${error.message}\n`)); console.log(chalk.yellow("šŸ”§ Troubleshooting tips:")); console.log(chalk.gray(" • Make sure you have Node.js 16+ installed")); console.log(chalk.gray(" • Check if the project name is valid")); console.log( chalk.gray(" • Ensure you have write permissions in this directory") ); console.log( chalk.gray(" • Try running with administrator/sudo privileges\n") ); process.exit(1); } }); // Add help command with styling program .command("help") .description(chalk.gray("Show help information")) .action(() => { showBanner(); console.log(chalk.yellow.bold("šŸ“š Available Commands:\n")); console.log(chalk.cyan(" express-autotemplates create [project-name]")); console.log(chalk.gray(" Create a new Express backend project\n")); console.log(chalk.cyan(" express-autotemplates --version")); console.log(chalk.gray(" Show version information\n")); console.log(chalk.cyan(" express-autotemplates --help")); console.log(chalk.gray(" Show this help message\n")); console.log(chalk.yellow.bold("šŸŽÆ Examples:\n")); console.log( chalk.green(" express-autotemplates create my-awesome-backend") ); console.log(chalk.green(" express-autotemplates create")); console.log(chalk.gray(" (will prompt for project name)\n")); }); program.parse();