UNPKG

create-exam-project

Version:

Create exam projects with React + Express + PostgreSQL in seconds

274 lines (228 loc) 9.97 kB
#!/usr/bin/env node const path = require('path'); const fs = require('fs-extra'); const { program } = require('commander'); const inquirer = require('inquirer'); const chalk = require('chalk'); const ora = require('ora'); const { execSync } = require('child_process'); const validateProjectName = require('validate-npm-package-name'); const packageJson = require('../package.json'); // Варианты проектов const variants = { variant1: { name: 'Буквоежка', description: 'Система обмена книгами', admin: { login: 'admin', password: 'bookworm' } }, variant2: { name: 'Грузовозофф', description: 'Система грузоперевозок', admin: { login: 'admin', password: 'gruzovik2024' } }, variant3: { name: 'Корочки.есть', description: 'Система онлайн курсов', admin: { login: 'admin', password: 'education' } }, variant4: { name: 'Я буду кушац', description: 'Система бронирования столиков', admin: { login: 'admin', password: 'restaurant' } }, universal: { name: 'Универсальный', description: 'Базовый шаблон', admin: { login: 'admin', password: 'admin123' } } }; console.log(chalk.blue(` ╔═══════════════════════════════════════╗ ║ CREATE EXAM PROJECT v${packageJson.version} ║ ║ Быстрый старт для экзамена ║ ╚═══════════════════════════════════════╝ `)); // Если запущено без аргументов, показываем интерактивное меню if (process.argv.length === 2) { console.log(chalk.yellow('💡 Совет: для быстрого создания используйте:')); console.log(chalk.gray(' npx create-exam-project my-project --variant variant1 --yes\n')); } program .name('create-exam-project') .version(packageJson.version) .argument('[project-name]', 'имя проекта') .option('-v, --variant <type>', 'вариант проекта') .option('-y, --yes', 'использовать настройки по умолчанию') .option('--skip-install', 'пропустить установку зависимостей') .option('--skip-git', 'не инициализировать git') .helpOption('-h, --help', 'показать помощь') .parse(); async function init() { const options = program.opts(); let projectName = program.args[0]; let variant = options.variant; // Интерактивный режим if (!options.yes && (!projectName || !variant)) { const answers = await inquirer.prompt([ { type: 'input', name: 'projectName', message: 'Название проекта:', default: 'my-exam-app', when: !projectName, validate: (input) => { const validation = validateProjectName(input); return validation.validForNewPackages || 'Некорректное название проекта'; } }, { type: 'list', name: 'variant', message: 'Выберите вариант:', when: !variant, choices: [ new inquirer.Separator('═══ Варианты заданий ═══'), { name: chalk.green('📚 Вариант 1: Буквоежка') + chalk.gray(' (обмен книгами)'), value: 'variant1', short: 'Буквоежка' }, { name: chalk.yellow('🚚 Вариант 2: Грузовозофф') + chalk.gray(' (грузоперевозки)'), value: 'variant2', short: 'Грузовозофф' }, { name: chalk.blue('🎓 Вариант 3: Корочки.есть') + chalk.gray(' (онлайн курсы)'), value: 'variant3', short: 'Корочки.есть' }, { name: chalk.magenta('🍽️ Вариант 4: Я буду кушац') + chalk.gray(' (бронирование)'), value: 'variant4', short: 'Я буду кушац' }, new inquirer.Separator('═════════════════════════'), { name: chalk.cyan('🚀 Универсальный шаблон'), value: 'universal', short: 'Универсальный' } ], default: 0 } ]); projectName = projectName || answers.projectName; variant = variant || answers.variant; // Показываем информацию о выбранном варианте if (answers.variant) { const selectedVariant = variants[answers.variant]; console.log(''); console.log(chalk.green('✅ Выбран: ') + chalk.yellow(selectedVariant.name)); console.log(chalk.gray('🔐 Админ: ') + chalk.cyan(`${selectedVariant.admin.login} / ${selectedVariant.admin.password}`)); console.log(''); } } // Значения по умолчанию projectName = projectName || 'my-exam-app'; variant = variant || 'universal'; const projectPath = path.join(process.cwd(), projectName); // Проверка существования папки if (fs.existsSync(projectPath)) { console.error(chalk.red(`\n❌ Папка ${projectName} уже существует!`)); process.exit(1); } const spinner = ora('Создание проекта...').start(); try { // Создаем папку проекта fs.ensureDirSync(projectPath); // Копируем файлы const templatePath = path.join(__dirname, '..', 'templates', 'base'); // Копируем основные файлы spinner.text = 'Копирование файлов...'; // Структура проекта const templateBase = path.join(__dirname, '..', 'templates', 'base'); // Копируем все из templates/base if (fs.existsSync(templateBase)) { fs.copySync(templateBase, projectPath); } else { throw new Error('Шаблоны не найдены'); } // Настройка варианта spinner.text = 'Настройка варианта...'; const variantConfig = variants[variant]; // Создаем .env файл const envContent = `# Database DB_HOST=localhost DB_PORT=5432 DB_NAME=exam_db DB_USER=postgres DB_PASSWORD=your_password # Server PORT=5000 # JWT JWT_SECRET=your_secret_key_${Date.now()} # Admin ADMIN_LOGIN=${variantConfig.admin.login} ADMIN_PASSWORD=${variantConfig.admin.password} `; fs.writeFileSync(path.join(projectPath, 'server', '.env'), envContent); fs.writeFileSync(path.join(projectPath, 'server', '.env.example'), envContent.replace('your_password', 'your_password_here')); // Обновляем конфигурацию клиента const clientConfigPath = path.join(projectPath, 'client', 'src', 'contexts', 'ConfigContext.jsx'); if (fs.existsSync(clientConfigPath)) { let configContent = fs.readFileSync(clientConfigPath, 'utf8'); configContent = configContent.replace( "localStorage.getItem('currentVariant') || 'universal'", `localStorage.getItem('currentVariant') || '${variant}'` ); fs.writeFileSync(clientConfigPath, configContent); } // Git init if (!options.skipGit) { spinner.text = 'Инициализация git...'; try { execSync('git init', { cwd: projectPath, stdio: 'ignore' }); } catch (e) { // Игнорируем ошибки git } } // Установка зависимостей if (!options.skipInstall) { spinner.text = 'Установка зависимостей (это может занять несколько минут)...'; const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm'; // Устанавливаем корневые зависимости execSync(`${npmCommand} install`, { cwd: projectPath, stdio: 'ignore' }); // Устанавливаем серверные зависимости spinner.text = 'Установка серверных зависимостей...'; execSync(`${npmCommand} install`, { cwd: path.join(projectPath, 'server'), stdio: 'ignore' }); // Устанавливаем клиентские зависимости spinner.text = 'Установка клиентских зависимостей...'; execSync(`${npmCommand} install`, { cwd: path.join(projectPath, 'client'), stdio: 'ignore' }); } spinner.succeed('Проект создан успешно!'); console.log(` ${chalk.green('✅ Готово!')} ${chalk.blue('Следующие шаги:')} ${chalk.yellow(`cd ${projectName}`)} ${chalk.gray('# Настройте PostgreSQL в server/.env')} ${chalk.yellow('npm run dev')} ${chalk.cyan('Данные администратора:')} Логин: ${chalk.green(variantConfig.admin.login)} Пароль: ${chalk.green(variantConfig.admin.password)} ${chalk.gray('Frontend: http://localhost:3000')} ${chalk.gray('Backend: http://localhost:5000')} ${chalk.magenta('Удачи на экзамене! 🎉')} `); } catch (error) { spinner.fail('Ошибка при создании проекта'); console.error(chalk.red(error.message)); // Удаляем папку при ошибке if (fs.existsSync(projectPath)) { fs.removeSync(projectPath); } process.exit(1); } } // Запускаем init().catch(console.error);