UNPKG

skelecli

Version:

πŸš€πŸ‘β€οΈA simple CLI to generate a blueprint for a new projectπŸš€βœ…πŸ‘¨β€πŸ­

193 lines (171 loc) β€’ 6.27 kB
#!/usr/bin/env node import inquirer from 'inquirer'; import fs from 'fs'; import path, { dirname } from 'path'; import { fileURLToPath } from 'url'; import welcome from 'conwelcome'; import createDirectoryContents from './createDirectoryContents.js'; import installDependencies from './installDependencies.js'; import checkForUpdates from './checkUpdates.js'; import { createRequire } from 'module'; const require = createRequire(import.meta.url); const packageJson = require('./package.json'); const CURRENT_VERSION = packageJson.version; const PACKAGE_NAME = packageJson.name; const CURR_DIR = process.cwd(); const __dirname = dirname(fileURLToPath(import.meta.url)); /** * Fetches available template choices from the templates directory. * @returns {string[]} List of available template choices. */ const getTemplateChoices = () => { const templatePath = path.join(__dirname, 'templates'); if (!fs.existsSync(templatePath)) { console.error('❌ Templates directory not found. Please ensure templates exist.'); process.exit(0); } const templates = fs.readdirSync(templatePath); if (templates.length === 0) { console.error('❌ No templates available. Please add templates to the templates folder.'); process.exit(0); } return templates; }; /** * Displays a welcome message. */ const displayWelcomeMessage = () => { welcome({ title:`πŸ”₯ ${PACKAGE_NAME} πŸ”₯`, tagLine: 'by Sumangal Karan πŸ’»', description: 'β€οΈπŸš€ SkeleCLI is the best CLI tool for creating new projects! 🎯✨', bgColor: '#fadc5e', color: '#000000', bold: true, clear: true, version: `${CURRENT_VERSION}`, }); }; /** * Prompts user for project details. * @returns {Promise<Object>} User's selected project template and name. */ const askProjectDetails = async () => { return await inquirer.prompt([ { name: 'projectChoice', type: 'list', message: 'πŸ“‚ What project template would you like to generate?', choices: getTemplateChoices(), }, { name: 'projectName', type: 'input', message: 'πŸ“ Enter a project name:', validate: input => /^([A-Za-z\-\_\d\.])+$/.test(input) || '❌ Project name may only include letters, numbers, underscores, and dashes.', }, ]); }; /** * Handles existing project folder. * @param {string} projectPath - The project directory path. * @returns {Promise<boolean>} Whether to continue creating the project. */ const handleExistingProject = async (projectPath) => { const { action } = await inquirer.prompt([ { name: 'action', type: 'list', message: `🚨 The folder "${path.basename(projectPath)}" already exists. What would you like to do?`, choices: [ { name: '❌ Remove all files and create a new project', value: 'remove' }, { name: '✏️ Overwrite existing files (keep other files)', value: 'overwrite' }, { name: 'πŸšͺ Stop and exit', value: 'stop' }, ], }, ]); if (action === 'remove') { console.log(`πŸ—‘οΈ Removing existing project folder: ${path.basename(projectPath)}`); fs.rmSync(projectPath, { recursive: true, force: true }); fs.mkdirSync(projectPath, { recursive: true }); return true; } if (action === 'overwrite') { console.log('✏️ Overwriting files while keeping the existing structure.'); return true; } console.log('🚫 Operation canceled.'); process.exit(0); }; /** * Asks user whether to install dependencies automatically. * @returns {Promise<boolean>} Whether to install dependencies. */ const askInstallDependencies = async () => { const { install } = await inquirer.prompt([ { name: 'install', type: 'confirm', message: 'πŸ“¦ Do you want to install dependencies automatically?', default: true, // Default to Yes }, ]); return install; }; /** * Creates the project from the selected template. * @param {string} templateName - The chosen template name. * @param {string} projectName - The project name. */ const createProject = async (templateName, projectName) => { const templatePath = path.join(__dirname, 'templates', templateName); const projectPath = projectName === '.' ? CURR_DIR : path.join(CURR_DIR, projectName); if (!fs.existsSync(templatePath)) { console.error('❌ Selected template does not exist. Please check your πŸ“ templates folder.'); process.exit(0); } if (fs.existsSync(projectPath)) { await handleExistingProject(projectPath); } else { fs.mkdirSync(projectPath, { recursive: true }); } try { createDirectoryContents(templatePath, projectPath); console.log(`\nβœ… Project ${projectName === '.' ? 'created in current directory' : projectName} created successfully!`); const installDeps = await askInstallDependencies(); if (installDeps) { console.log('πŸ“¦ Installing dependencies... ⏳ '); installDependencies(projectPath); } else { console.log(`\nπŸ“Œ To install dependencies manually, run:\n`); console.log(` πŸ“‚ cd ${projectName}\n`); console.log(` πŸš€ pnpm install\n`); console.log(` πŸš€ bun install\n`); console.log(` πŸš€ npm install\n`); console.log(` πŸš€ yarn install\n`); } } catch (err) { console.error('❌ Error creating project:', err); process.exit(0); } }; // 🌟 Run CLI Tool (async () => { try { console.log('πŸ” Checking for updates... ⏳'); const updateAvailable = await checkForUpdates(); // Wait for update check if (updateAvailable) { console.log('πŸ”„ Update completed. Please restart the CLI.'); process.exit(0); // Exit after update } displayWelcomeMessage(); const { projectChoice, projectName } = await askProjectDetails(); await createProject(projectChoice, projectName); } catch (error) { if (error.message.includes('User force closed the prompt')) { console.log('🚫 Operation canceled by user.'); process.exit(0); // βœ… Exit normally without error } } })();