UNPKG

@agile-team/robot-cli

Version:

🤖 现代化项目脚手架工具,支持多技术栈快速创建项目 - 优先 bun,兼容 npm/pnpm/yarn

1,201 lines (1,048 loc) 33.9 kB
// lib/create.js - 统一包管理器版本,优先 bun import fs from "fs-extra"; import path from "path"; import chalk from "chalk"; import inquirer from "inquirer"; import ora from "ora"; import { execSync } from "child_process"; import { downloadTemplate } from "./download.js"; import { TEMPLATE_CATEGORIES, getAllTemplates, getTemplatesByCategory, searchTemplates, getRecommendedTemplates, } from "./templates.js"; import { validateProjectName, copyTemplate, installDependencies, generateProjectStats, printProjectStats, detectPackageManager, } from "./utils.js"; /** * 创建项目主函数 */ export async function createProject(projectName, options = {}) { console.log(); console.log(chalk.cyan("🚀 Robot CLI - 开始创建项目")); console.log(); // 1. 选择模板 const template = await selectTemplate(options.template); // 2. 处理项目名称 const finalProjectName = await handleProjectName(projectName, template); // 3. 项目配置选项 const projectConfig = await configureProject(options); // 4. 确认创建 await confirmCreation(finalProjectName, template, projectConfig); // 5. 创建项目 await executeCreation(finalProjectName, template, projectConfig); } /** * 处理项目名称 */ async function handleProjectName(projectName, template) { if (projectName) { // 验证项目名称 const validation = validateProjectName(projectName); if (!validation.valid) { console.log(chalk.red("❌ 项目名称不合法:")); validation.errors.forEach((error) => { console.log(chalk.red(` ${error}`)); }); console.log(); const { newName } = await inquirer.prompt([ { type: "input", name: "newName", message: "请输入新的项目名称:", validate: (input) => { const result = validateProjectName(input); return result.valid || result.errors[0]; }, }, ]); return newName; } return projectName; } else { // 根据模板名称生成默认项目名称 const defaultName = generateDefaultProjectName(template); const { name } = await inquirer.prompt([ { type: "input", name: "name", message: "请输入项目名称:", default: defaultName, validate: (input) => { if (!input.trim()) return "项目名称不能为空"; const result = validateProjectName(input); return result.valid || result.errors[0]; }, }, ]); return name; } } /** * 根据模板生成默认项目名称 */ function generateDefaultProjectName(template) { if (!template) return "my-project"; const templateKey = template.key || "project"; const timestamp = Date.now().toString().slice(-4); // 移除版本后缀 (-full, -lite) const baseName = templateKey.replace(/-(full|lite|base)$/, ""); return `my-${baseName}-${timestamp}`; } /** * 选择模板 - 多种方式(带返回功能) */ async function selectTemplate(templateOption) { if (templateOption) { // 命令行指定了模板 const allTemplates = getAllTemplates(); if (allTemplates[templateOption]) { return { key: templateOption, ...allTemplates[templateOption] }; } else { console.log(chalk.yellow(`⚠️ 模板 "${templateOption}" 不存在`)); console.log(); } } // 交互式选择 - 主选择方式 return await selectTemplateMethod(); } /** * 选择模板方式 - 优化主菜单 */ async function selectTemplateMethod() { console.log(); console.log(chalk.blue.bold("🎯 选择模板创建方式")); console.log(chalk.dim("请选择最适合你的模板浏览方式")); console.log(); const { selectionMode } = await inquirer.prompt([ { type: "list", name: "selectionMode", message: "模板选择方式:", choices: [ { name: `● ${chalk.bold('推荐模板')} ${chalk.dim('(常用模板快速选择) - 基于团队使用频率推荐的热门模板')}`, value: "recommended" }, { name: chalk.dim("─".repeat(70)), value: "sep1", disabled: true }, { name: `● ${chalk.bold('分类模板')} ${chalk.dim('(按项目类型分类选择) - 前端、后端、移动端、桌面端分类浏览')}`, value: "category" }, { name: chalk.dim("─".repeat(70)), value: "sep2", disabled: true }, { name: `● ${chalk.bold('搜索模板')} ${chalk.dim('(关键词搜索) - 通过技术栈、功能特性等关键词快速查找')}`, value: "search" }, { name: chalk.dim("─".repeat(70)), value: "sep3", disabled: true }, { name: `● ${chalk.bold('全部模板')} ${chalk.dim('(查看所有可用模板) - 按分类展示所有可用的项目模板')}`, value: "all" }, ], pageSize: 10 }, ]); switch (selectionMode) { case "recommended": return await selectFromRecommended(); case "category": return await selectByCategory(); case "search": return await selectBySearch(); case "all": return await selectFromAll(); default: return await selectByCategory(); } } /** * 从推荐模板中选择 - 优化后的展示 */ async function selectFromRecommended() { const recommended = getRecommendedTemplates(); if (Object.keys(recommended).length === 0) { console.log(chalk.yellow("⚠️ 暂无推荐模板")); return await selectTemplateMethod(); } console.log(); console.log(chalk.blue.bold("🎯 推荐模板")); console.log(chalk.dim("基于团队使用频率和项目成熟度推荐")); console.log(); // 创建更美观的选择项 const choices = Object.entries(recommended).map(([key, template]) => { // 构建特性标签 const featureTags = template.features.slice(0, 3).map(f => chalk.dim(`[${f}]`) ).join(' '); // 版本标签 - 只保留方括号内的版本 const versionTag = template.version === 'full' ? chalk.green('[完整版]') : chalk.yellow('[精简版]'); return { name: `${chalk.bold.white(template.name.replace(/\s*(完整版|精简版)\s*$/, ''))} ${versionTag} - ${chalk.dim(template.description)} ${chalk.dim(featureTags)}${template.features.length > 3 ? chalk.dim(` +${template.features.length - 3}more`) : ''}`, value: { key, ...template }, short: template.name, }; }); // 为每个推荐模板添加分隔符 const choicesWithSeparators = []; choices.forEach((choice, index) => { choicesWithSeparators.push(choice); if (index < choices.length - 1) { choicesWithSeparators.push({ name: chalk.dim("─".repeat(70)), value: `sep_${index}`, disabled: true }); } }); choicesWithSeparators.push({ name: chalk.dim("⬅️ 返回选择其他方式"), value: "back", }); const { selectedTemplate } = await inquirer.prompt([ { type: "list", name: "selectedTemplate", message: "选择推荐模板:", choices: choicesWithSeparators, pageSize: 15, loop: false }, ]); if (selectedTemplate === "back") { return await selectTemplateMethod(); } return selectedTemplate; } /** * 按分类选择模板(完整的返回功能) */ async function selectByCategory() { // 1. 选择项目类型 while (true) { const categoryResult = await selectCategory(); if (categoryResult === "back_to_method") { return await selectTemplateMethod(); } // 2. 选择技术栈 const stackResult = await selectStack(categoryResult); if (stackResult === "back_to_category") { continue; // 返回到项目类型选择 } if (stackResult === "back_to_method") { return await selectTemplateMethod(); } // 3. 选择架构模式 const patternResult = await selectPattern(categoryResult, stackResult); if (patternResult === "back_to_stack") { continue; // 返回到技术栈选择,会重新开始while循环 } if (patternResult === "back_to_category") { continue; // 返回到项目类型选择 } if (patternResult === "back_to_method") { return await selectTemplateMethod(); } // 4. 选择具体模板 const templateResult = await selectSpecificTemplate( categoryResult, stackResult, patternResult ); if (templateResult === "back_to_pattern") { continue; // 返回到架构模式选择 } if (templateResult === "back_to_stack") { continue; // 返回到技术栈选择 } if (templateResult === "back_to_category") { continue; // 返回到项目类型选择 } if (templateResult === "back_to_method") { return await selectTemplateMethod(); } // 成功选择了模板 return templateResult; } } /** * 选择项目类型 */ async function selectCategory() { const categoryChoices = Object.entries(TEMPLATE_CATEGORIES).map( ([key, category]) => ({ name: category.name, value: key, }) ); categoryChoices.push({ name: chalk.dim("← 返回模板选择方式"), value: "back_to_method", }); const { categoryKey } = await inquirer.prompt([ { type: "list", name: "categoryKey", message: "请选择项目类型:", choices: categoryChoices, }, ]); return categoryKey; } /** * 选择技术栈 */ async function selectStack(categoryKey) { if (categoryKey === "back_to_method") return categoryKey; const category = TEMPLATE_CATEGORIES[categoryKey]; const stackChoices = Object.entries(category.stacks).map(([key, stack]) => ({ name: stack.name, value: key, })); stackChoices.push( { name: chalk.dim("─────────────────────"), value: "separator", disabled: true, }, { name: chalk.dim("← 返回项目类型选择"), value: "back_to_category" }, { name: chalk.dim("← 返回模板选择方式"), value: "back_to_method" } ); if (stackChoices.length === 3) { // 只有一个技术栈 + 分隔线 + 返回选项 return stackChoices[0].value; } const { stackKey } = await inquirer.prompt([ { type: "list", name: "stackKey", message: "请选择技术栈:", choices: stackChoices, }, ]); return stackKey; } /** * 选择架构模式 */ async function selectPattern(categoryKey, stackKey) { if (["back_to_category", "back_to_method"].includes(stackKey)) return stackKey; const category = TEMPLATE_CATEGORIES[categoryKey]; const stack = category.stacks[stackKey]; const patternChoices = Object.entries(stack.patterns).map( ([key, pattern]) => ({ name: pattern.name, value: key, }) ); patternChoices.push( { name: chalk.dim("─────────────────────"), value: "separator", disabled: true, }, { name: chalk.dim("← 返回技术栈选择"), value: "back_to_stack" }, { name: chalk.dim("← 返回项目类型选择"), value: "back_to_category" }, { name: chalk.dim("← 返回模板选择方式"), value: "back_to_method" } ); if (patternChoices.length === 4) { // 只有一个模式 + 分隔线 + 返回选项 return patternChoices[0].value; } const { patternKey } = await inquirer.prompt([ { type: "list", name: "patternKey", message: "请选择架构模式:", choices: patternChoices, }, ]); return patternKey; } /** * 选择具体模板 */ async function selectSpecificTemplate(categoryKey, stackKey, patternKey) { if ( ["back_to_stack", "back_to_category", "back_to_method"].includes(patternKey) ) { return patternKey; } const templates = getTemplatesByCategory(categoryKey, stackKey, patternKey); const templateChoices = Object.entries(templates).map(([key, template]) => ({ name: `${template.name} - ${chalk.dim(template.description)}`, value: { key, ...template }, short: template.name, })); templateChoices.push( { name: chalk.dim("─────────────────────"), value: "separator", disabled: true, }, { name: chalk.dim("← 返回架构模式选择"), value: "back_to_pattern" }, { name: chalk.dim("← 返回技术栈选择"), value: "back_to_stack" }, { name: chalk.dim("← 返回项目类型选择"), value: "back_to_category" }, { name: chalk.dim("← 返回模板选择方式"), value: "back_to_method" } ); const { selectedTemplate } = await inquirer.prompt([ { type: "list", name: "selectedTemplate", message: "请选择模板版本:", choices: templateChoices, }, ]); return selectedTemplate; } /** * 搜索选择模板 - 优化结果展示 */ async function selectBySearch() { while (true) { const { keyword } = await inquirer.prompt([ { type: "input", name: "keyword", message: "请输入搜索关键词 (名称、描述、技术栈):", validate: (input) => (input.trim() ? true : "关键词不能为空"), }, ]); const results = searchTemplates(keyword); if (Object.keys(results).length === 0) { console.log(); console.log(chalk.yellow("🔍 没有找到匹配的模板")); console.log(chalk.dim(`搜索关键词: "${keyword}"`)); console.log(); const { action } = await inquirer.prompt([ { type: "list", name: "action", message: "请选择下一步操作:", choices: [ { name: "🔍 重新搜索", value: "retry" }, { name: "⬅️ 返回模板选择方式", value: "back" }, ], }, ]); if (action === "retry") { continue; } else { return await selectTemplateMethod(); } } console.log(); console.log(chalk.green.bold(`🔍 搜索结果`)); console.log(chalk.dim(`关键词: "${keyword}" • 找到 ${Object.keys(results).length} 个匹配模板`)); console.log(); const choices = Object.entries(results).map(([key, template]) => { // 高亮匹配的关键词 const highlightText = (text) => { const regex = new RegExp(`(${keyword})`, 'gi'); return text.replace(regex, chalk.bgYellow.black('$1')); }; const techInfo = template.features.slice(0, 2).join(' • '); const versionTag = template.version === 'full' ? chalk.green('[完整版]') : chalk.yellow('[精简版]'); return { name: `${chalk.bold(highlightText(template.name.replace(/\s*(完整版|精简版)\s*$/, '')))} ${versionTag} ${chalk.dim(highlightText(template.description))} ${chalk.dim(`${techInfo} • 模板key: ${key}`)} ${chalk.dim('─'.repeat(60))}`, value: { key, ...template }, short: template.name, }; }); choices.push( { name: chalk.dim("━".repeat(70)), value: "separator", disabled: true, }, { name: "🔍 重新搜索", value: "search_again" }, { name: "⬅️ 返回模板选择方式", value: "back_to_mode" } ); const { selectedTemplate } = await inquirer.prompt([ { type: "list", name: "selectedTemplate", message: "选择模板:", choices, pageSize: 15, loop: false }, ]); if (selectedTemplate === "search_again") { continue; } else if (selectedTemplate === "back_to_mode") { return await selectTemplateMethod(); } else { return selectedTemplate; } } } /** * 从全部模板中选择 - 优化后的展示 */ async function selectFromAll() { const allTemplates = getAllTemplates(); console.log(); console.log(chalk.blue.bold(`📋 所有可用模板`)); console.log(chalk.dim(`共 ${Object.keys(allTemplates).length} 个模板可选`)); console.log(); // 按分类组织模板 const categorizedChoices = []; // 前端模板 categorizedChoices.push({ name: chalk.yellow.bold("🎨 前端项目"), value: "frontend_header", disabled: true }); Object.entries(allTemplates) .filter(([key]) => key.includes('admin') || key.includes('react') || key.includes('micro')) .forEach(([key, template]) => { const techStack = key.includes('admin') ? 'Vue3' : key.includes('react') ? 'React' : 'Vue3'; const versionTag = template.version === 'full' ? chalk.green('[完整版]') : chalk.yellow('[精简版]'); categorizedChoices.push({ name: ` ● ${chalk.bold(template.name.replace(/\s*(完整版|精简版)\s*$/, ''))} ${versionTag} - ${chalk.dim(template.description)} ${chalk.dim(`技术栈: ${techStack} • 命令: robot create my-app -t ${key}`)}`, value: { key, ...template }, short: template.name }); // 添加分隔符 categorizedChoices.push({ name: chalk.dim(" " + "─".repeat(66)), value: `sep_${key}`, disabled: true }); }); // 移动端模板 categorizedChoices.push({ name: chalk.yellow.bold("📱 移动端项目"), value: "mobile_header", disabled: true }); Object.entries(allTemplates) .filter(([key]) => key.includes('uniapp') || key.includes('tarao')) .forEach(([key, template]) => { const versionTag = template.version === 'full' ? chalk.green('[完整版]') : chalk.yellow('[精简版]'); categorizedChoices.push({ name: ` ● ${chalk.bold(template.name.replace(/\s*(完整版|精简版)\s*$/, ''))} ${versionTag} - ${chalk.dim(template.description)} ${chalk.dim(`跨平台: 小程序/H5/App • 命令: robot create my-app -t ${key}`)}`, value: { key, ...template }, short: template.name }); // 添加分隔符 categorizedChoices.push({ name: chalk.dim(" " + "─".repeat(66)), value: `sep_${key}`, disabled: true }); }); // 后端模板 categorizedChoices.push({ name: chalk.yellow.bold("🚀 后端项目"), value: "backend_header", disabled: true }); Object.entries(allTemplates) .filter(([key]) => key.includes('nest') || key.includes('koa')) .forEach(([key, template]) => { const framework = key.includes('nest') ? 'NestJS' : 'Koa3'; const versionTag = template.version === 'full' ? chalk.green('[完整版]') : chalk.yellow('[精简版]'); categorizedChoices.push({ name: ` ● ${chalk.bold(template.name.replace(/\s*(完整版|精简版)\s*$/, ''))} ${versionTag} - ${chalk.dim(template.description)} ${chalk.dim(`框架: ${framework} • 命令: robot create my-app -t ${key}`)}`, value: { key, ...template }, short: template.name }); // 添加分隔符 categorizedChoices.push({ name: chalk.dim(" " + "─".repeat(66)), value: `sep_${key}`, disabled: true }); }); // 桌面端模板 categorizedChoices.push({ name: chalk.yellow.bold("💻 桌面端项目"), value: "desktop_header", disabled: true }); Object.entries(allTemplates) .filter(([key]) => key.includes('electron') || key.includes('tauri')) .forEach(([key, template]) => { const framework = key.includes('electron') ? 'Electron' : 'Tauri'; const versionTag = template.version === 'full' ? chalk.green('[完整版]') : chalk.yellow('[精简版]'); categorizedChoices.push({ name: ` ● ${chalk.bold(template.name.replace(/\s*(完整版|精简版)\s*$/, ''))} ${versionTag} - ${chalk.dim(template.description)} ${chalk.dim(`框架: ${framework} • 命令: robot create my-app -t ${key}`)}`, value: { key, ...template }, short: template.name }); // 添加分隔符 categorizedChoices.push({ name: chalk.dim(" " + "─".repeat(66)), value: `sep_${key}`, disabled: true }); }); // 添加返回选项 categorizedChoices.push( { name: chalk.dim("━".repeat(70)), value: "separator", disabled: true, }, { name: chalk.dim("⬅️ 返回模板选择方式"), value: "back_to_mode" } ); const { selectedTemplate } = await inquirer.prompt([ { type: "list", name: "selectedTemplate", message: "选择模板:", choices: categorizedChoices, pageSize: 25, loop: false }, ]); if (selectedTemplate === "back_to_mode") { return await selectTemplateMethod(); } return selectedTemplate; } /** * 项目配置选项 - 智能检测并优先推荐 bun */ async function configureProject(options) { console.log(); console.log(chalk.blue("⚙️ 项目配置")); console.log(); // 检测可用的包管理器 const availableManagers = detectPackageManager(); const hasBun = availableManagers.includes('bun'); const hasPnpm = availableManagers.includes('pnpm'); const hasYarn = availableManagers.includes('yarn'); const hasNpm = availableManagers.includes('npm'); // 构建包管理器选择列表 const managerChoices = []; if (hasBun) { managerChoices.push({ name: "bun (推荐 - 极速安装,现代化,性能最佳)", value: "bun" }); } if (hasPnpm) { managerChoices.push({ name: "pnpm (推荐 - 快速安装,节省磁盘空间)", value: "pnpm" }); } if (hasYarn) { managerChoices.push({ name: "yarn (兼容性好 - 适用于现有yarn项目)", value: "yarn" }); } if (hasNpm) { managerChoices.push({ name: "npm (默认 - Node.js内置,兼容性最好)", value: "npm" }); } // 如果没有检测到任何包管理器,使用默认列表 if (managerChoices.length === 0) { managerChoices.push( { name: "npm (默认)", value: "npm" }, { name: "bun (如已安装)", value: "bun" }, { name: "pnpm (如已安装)", value: "pnpm" }, { name: "yarn (如已安装)", value: "yarn" } ); } const config = await inquirer.prompt([ { type: "confirm", name: "initGit", message: "是否初始化 Git 仓库?", default: true, }, { type: "confirm", name: "installDeps", message: "是否立即安装依赖?", default: !options.skipInstall, }, { type: "list", name: "packageManager", message: "选择包管理器:", choices: managerChoices, default: hasBun ? "bun" : (hasPnpm ? "pnpm" : "npm"), when: (answers) => answers.installDeps, }, { type: "input", name: "description", message: "项目描述 (可选):", default: "", }, { type: "input", name: "author", message: "作者 (可选):", default: "CHENY", }, { type: "confirm", name: "confirmConfig", message: "确认以上配置?", default: true, }, ]); if (!config.confirmConfig) { const { action } = await inquirer.prompt([ { type: "list", name: "action", message: "请选择操作:", choices: [ { name: "🔄 重新配置", value: "reconfigure" }, { name: "❌ 取消创建", value: "cancel" }, ], }, ]); if (action === "reconfigure") { return await configureProject(options); } else { console.log(chalk.yellow("❌ 取消创建项目")); process.exit(0); } } return config; } /** * 确认创建 - 统一显示包管理器 */ async function confirmCreation(projectName, template, projectConfig) { console.log(); console.log(chalk.blue("📋 项目创建信息确认:")); console.log(); console.log(` 项目名称: ${chalk.cyan(projectName)}`); console.log(` 选择模板: ${chalk.cyan(template.name)}`); console.log(` 模板描述: ${chalk.dim(template.description)}`); console.log(` 包含功能: ${chalk.dim(template.features.join(", "))}`); if (projectConfig.description) { console.log(` 项目描述: ${chalk.dim(projectConfig.description)}`); } if (projectConfig.author) { console.log(` 作  者: ${chalk.dim(projectConfig.author)}`); } console.log( ` 初始化Git: ${ projectConfig.initGit ? chalk.green("是") : chalk.dim("否") }` ); console.log( ` 安装依赖: ${ projectConfig.installDeps ? chalk.green("是") + chalk.dim(` (${projectConfig.packageManager})`) : chalk.dim("否") }` ); console.log(` 源码仓库: ${chalk.dim(template.repoUrl)}`); console.log(); const { confirmed } = await inquirer.prompt([ { type: "confirm", name: "confirmed", message: "确认创建项目?", default: true, }, ]); if (!confirmed) { console.log(chalk.yellow("❌ 取消创建")); process.exit(0); } } /** * 执行创建流程 - 统一包管理器使用 */ async function executeCreation(projectName, template, projectConfig) { if (!projectName || typeof projectName !== "string") { throw new Error(`项目名称无效: ${projectName}`); } if (!template || !template.name) { throw new Error(`模板数据无效: ${JSON.stringify(template)}`); } const spinner = ora({ text: "🚀 准备创建项目...", spinner: 'dots', color: 'cyan' }).start(); let tempTemplatePath; // 用于清理临时文件 try { // 1. 检查目录是否存在 spinner.text = "📁 检查项目目录..."; const projectPath = path.resolve(projectName); if (fs.existsSync(projectPath)) { spinner.stop(); console.log(chalk.yellow("⚠️ 项目目录已存在")); const { overwrite } = await inquirer.prompt([ { type: "confirm", name: "overwrite", message: "目录已存在,是否覆盖?", default: false, }, ]); if (!overwrite) { console.log(chalk.yellow("❌ 取消创建")); process.exit(0); } spinner.start("🗑️ 清理现有目录..."); await fs.remove(projectPath); spinner.text = "📁 准备创建新目录..."; } // 2. 下载最新模板 spinner.text = "🌐 下载最新模板..."; try { tempTemplatePath = await downloadTemplate(template, { spinner }); if (!tempTemplatePath || !fs.existsSync(tempTemplatePath)) { throw new Error(`模板路径无效: ${tempTemplatePath}`); } } catch (error) { spinner.fail("模板下载失败"); console.log(); console.log(chalk.red("❌ 模板下载错误:")); console.log(chalk.dim(` ${error.message}`)); console.log(); console.log(chalk.blue("💡 可能的解决方案:")); console.log(chalk.dim(" 1. 检查网络连接")); console.log(chalk.dim(" 2. 重试命令")); console.log(chalk.dim(" 3. 检查仓库地址是否正确")); console.log(); throw error; } // 3. 复制模板文件 - 带详细进度 await copyTemplate(tempTemplatePath, projectPath, spinner); // 4. 处理项目配置 spinner.text = "⚙️ 处理项目配置..."; await processProjectConfig( projectPath, projectName, template, projectConfig ); // 5. 初始化Git仓库 if (projectConfig.initGit) { spinner.text = "📝 初始化 Git 仓库..."; await initializeGitRepository(projectPath); } // 6. 安装依赖 if (projectConfig.installDeps) { spinner.text = `📦 使用 ${projectConfig.packageManager} 安装依赖...`; await installDependencies( projectPath, spinner, projectConfig.packageManager ); } // 7. 清理临时文件 if (tempTemplatePath) { spinner.text = "🧹 清理临时文件..."; await fs.remove(tempTemplatePath).catch(() => {}); } // 8. 创建成功 spinner.succeed(chalk.green("🎉 项目创建成功!")); console.log(); console.log(chalk.green("🎉 项目创建完成!")); console.log(); console.log(chalk.blue("📁 项目信息:")); console.log(` 位置: ${chalk.cyan(projectPath)}`); console.log(` 模板: ${chalk.cyan(template.name)}`); console.log( ` Git仓库: ${ projectConfig.initGit ? chalk.green("已初始化") : chalk.dim("未初始化") }` ); console.log( ` 依赖安装: ${ projectConfig.installDeps ? chalk.green("已完成") : chalk.dim("需手动安装") }` ); console.log(); console.log(chalk.blue("🚀 快速开始:")); console.log(chalk.cyan(` cd ${projectName}`)); // 🎯 修复:统一使用选择的包管理器 if (!projectConfig.installDeps) { const packageManager = projectConfig.packageManager || "bun"; console.log(chalk.cyan(` ${packageManager} install`)); } const startCommand = getStartCommand(template, projectConfig.packageManager); if (startCommand) { console.log(chalk.cyan(` ${startCommand}`)); } // 添加替代方案提示 const selectedManager = projectConfig.packageManager || "bun"; if (selectedManager === "bun") { console.log(chalk.dim(` ${chalk.gray("# 或使用 npm: npm install && npm run dev")}`)); } else if (selectedManager === "npm") { console.log(chalk.dim(` ${chalk.gray("# 或使用 bun: bun install && bun run dev (如已安装)")}`)); } console.log(); // 显示项目统计 spinner.start("📊 统计项目信息..."); const stats = await generateProjectStats(projectPath); spinner.stop(); if (stats) { printProjectStats(stats); console.log(); } } catch (error) { // 清理临时文件 if (tempTemplatePath) { await fs.remove(tempTemplatePath).catch(() => {}); } spinner.fail("创建项目失败"); throw error; } } /** * 处理项目配置 */ async function processProjectConfig( projectPath, projectName, template, projectConfig ) { // 更新 package.json const packageJsonPath = path.join(projectPath, "package.json"); if (fs.existsSync(packageJsonPath)) { const packageJson = await fs.readJson(packageJsonPath); packageJson.name = projectName; if (projectConfig.description) { packageJson.description = projectConfig.description; } else { packageJson.description = `基于 ${template.name} 创建的项目`; } if (projectConfig.author) { packageJson.author = projectConfig.author; } await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 }); } // 处理 README.md const readmePath = path.join(projectPath, "README.md"); if (fs.existsSync(readmePath)) { let readme = await fs.readFile(readmePath, "utf8"); readme = readme.replace(/# .+/, `# ${projectName}`); const description = projectConfig.description || `基于 ${template.name} 创建的项目`; readme = readme.replace(/项目描述.*/, description); if (projectConfig.author) { readme += `\n\n## 作者\n\n${projectConfig.author}\n`; } await fs.writeFile(readmePath, readme); } // 处理 .gitignore (如果是 _gitignore) const gitignoreSource = path.join(projectPath, "_gitignore"); const gitignoreDest = path.join(projectPath, ".gitignore"); if (fs.existsSync(gitignoreSource)) { await fs.move(gitignoreSource, gitignoreDest); } // 处理 .env.example const envSource = path.join(projectPath, "_env.example"); const envDest = path.join(projectPath, ".env.example"); if (fs.existsSync(envSource)) { await fs.move(envSource, envDest); } } /** * 初始化Git仓库 */ async function initializeGitRepository(projectPath) { const originalCwd = process.cwd(); try { process.chdir(projectPath); // 检查是否安装了git execSync("git --version", { stdio: "ignore" }); // 初始化git仓库 execSync("git init", { stdio: "ignore" }); execSync("git add .", { stdio: "ignore" }); execSync('git commit -m "feat: 初始化项目"', { stdio: "ignore" }); } catch (error) { // Git不可用,跳过初始化 console.log(chalk.yellow("⚠️ Git 不可用,跳过仓库初始化")); } finally { process.chdir(originalCwd); } } /** * 获取启动命令 - 🎯 修复:统一使用包管理器 */ function getStartCommand(template, packageManager = "bun") { if (!template.key) return `${packageManager} run dev`; const startCommands = { // Vue前端项目 "robot-admin": "dev", "robot-admin-base": "dev", "robot-monorepo": "dev:packages", "robot-monorepo-base": "dev", "robot-micro": "dev:main", "robot-micro-base": "dev", // React前端项目 "robot-react": "start", "robot-react-base": "start", // 移动端项目 "robot-uniapp": "dev:h5", "robot-uniapp-base": "dev:h5", "robot-tarao": "android", "robot-tarao-base": "android", // 后端项目 "robot-nest": "start:dev", "robot-nest-base": "start:dev", "robot-nest-micro": "start:dev", "robot-koa": "dev", "robot-koa-base": "dev", // 桌面端项目 "robot-electron": "electron:dev", "robot-electron-base": "electron:dev", "robot-tauri": "tauri dev", "robot-tauri-base": "tauri dev", }; const scriptName = startCommands[template.key] || "dev"; return `${packageManager} run ${scriptName}`; }