UNPKG

create-arena-project

Version:

神奇代码岛<->VSCode,从这里开始,创建一个神岛代码项目的脚手架。

423 lines (388 loc) 13.3 kB
/** * @fileoverview 项目配置向导优化模块 * 提供更丰富的配置选项和详细说明,增强用户交互体验 */ import { default as chalk } from "chalk"; import { resolve, isAbsolute } from "path"; import inquirer from "inquirer"; import { errorHandler } from "./error-handler.js"; /** * 配置向导选项 * @typedef {Object} WizardOptions * @property {boolean} [useDefaultsOnError=true] - 发生错误时是否使用默认值 */ /** * 配置向导结果 * @typedef {Object} WizardResult * @property {Object} answers - 用户回答的所有配置项 * @property {string} basepath - 项目基础路径 * @property {null} selectedTemplate - 选择的项目模板,始终为null */ /** * 配置向导类 */ class ConfigWizard { /** * 创建配置向导实例 * @param {WizardOptions} options - 配置选项 */ constructor(options = {}) { this.options = { showTemplates: false, // 禁用模板选择 showAdvancedOptions: false, // 禁用高级选项 useDefaultsOnError: options.useDefaultsOnError !== false, }; } /** * 运行配置向导 * @returns {Promise<WizardResult>} 配置向导结果 */ async run() { try { // 显示欢迎信息 this._showWelcomeMessage(); // 收集基础配置 const answers = await this._collectBasicConfig(); // 根据项目类型决定是否显示服务端/客户端选择 await this._collectServerClientConfig(answers); // 收集项目路径 const basepath = await this._collectProjectPath(); // 显示配置摘要 this._showConfigSummary(answers, basepath, null); return { answers, basepath, selectedTemplate: null, }; } catch (error) { // 只有在真正发生错误时才记录错误日志 await errorHandler.handleError( error, "CONFIGURATION", "运行配置向导", () => { console.error(chalk.red("配置向导出错:"), error.message); } ); // 如果配置为出错时使用默认值,则返回默认配置 if (this.options.useDefaultsOnError) { console.log(chalk.yellow("\n⚠️ 使用默认配置继续...")); return this._getDefaultConfig(); } throw error; } } /** * 显示欢迎信息 * @private */ _showWelcomeMessage() { console.log(chalk.bold.cyan("\n🎮 欢迎使用神奇代码岛项目创建向导!")); console.log(chalk.cyan("让我们一起定制你的神岛代码项目吧!")); console.log( chalk.dim("\n📚 在线文档:") + chalk.blue("https://docs.dao3.fun/arenapro/") + "\n" ); } /** * 收集基础配置 * @returns {Promise<Object>} 基础配置对象 * @private */ async _collectBasicConfig() { const basicQuestions = [ { type: "list", name: "npmPackage", message: "🎯 选择项目类型", choices: [ { name: "神岛地图项目", value: "神岛地图项目", short: "神岛地图项目", }, { name: "神岛组件库", value: "神岛组件库", short: "神岛组件库", }, ], default: "神岛地图项目", description: "神岛地图项目适合开发游戏地图,神岛组件库则用于开发可复用的功能模块。", }, { type: "list", name: "languageType", message: "🔧 选择开发语言", choices: [ { name: "TypeScript", value: "TypeScript", short: "TypeScript", }, { name: "JavaScript", value: "JavaScript", short: "JavaScript", }, ], default: "TypeScript", description: "TypeScript提供类型检查,帮助捕获常见错误并提升开发体验;JavaScript更加灵活,但缺少静态类型检查。", }, { type: "list", name: "prettier", message: "📝 是否配置代码规范工具", choices: [ { name: "配置 - 包含ESLint和Prettier", value: "配置", short: "配置", }, { name: "跳过 - 不使用代码规范工具", value: "跳过", short: "跳过", }, ], description: "ESLint和Prettier可以帮助保持一致的代码风格,提高代码质量和可读性。", }, { type: "list", name: "i18n", message: "💬 是否配置国际化", choices: [ { name: "配置 - 包含i18n", value: "配置", short: "配置", }, { name: "跳过 - 不使用i18n", value: "跳过", short: "跳过", }, ], description: "国际化 (i18n) 方案将帮助你的项目支持多种语言,触及更广泛的用户群体。", }, { type: "list", name: "dependentManner", message: "📦 项目依赖方式", choices: [ { name: "npm - 默认", value: "npm", short: "npm", }, { name: "yarn", value: "yarn", short: "yarn", }, { name: "pnpm", value: "pnpm", short: "pnpm", }, { name: "bun", value: "bun", short: "bun", }, ], default: "npm", description: "请选择包管理器。npm 已内置,推荐使用 yarn 或 pnpm 以获得更佳性能,bun 为最新包管理器,性能更佳。", }, ]; const answers = {}; for (let i = 0; i < basicQuestions.length; i++) { const question = basicQuestions[i]; const { name, ...questionConfig } = question; // 显示问题描述 if (question.description) { console.log(chalk.dim(`💡 ${question.description}`)); } // 提问并收集答案 const response = await inquirer.prompt({ ...questionConfig, name: "value", }); answers[name] = response; // 显示选择结果和提示 console.log(chalk.green(`✓ 已选择: ${response.value}`)); // 如果选择JavaScript,提供TypeScript的建议 if (name === "languageType" && response.value === "JavaScript") { console.log(chalk.yellow("\n💡 温馨提示:")); console.log(chalk.dim("TypeScript 能为你的项目带来以下好处:")); console.log( chalk.dim("• 更好的类型检查,帮助你在开发阶段发现潜在问题") ); console.log(chalk.dim("• 更强大的IDE支持,提供更准确的代码补全和重构")); console.log(chalk.dim("• 更容易维护的代码库,特别是在团队协作中")); console.log(chalk.dim("• 更好的类型提示和API文档集成")); console.log(chalk.dim("• 更强大的代码重构和重组能力")); console.log( chalk.dim( "这次使用 JavaScript 也没关系,但强烈建议你下次尝试 TypeScript,以获得更好的开发体验!\n" ) ); } // 如果选择配置代码规范,提供相关信息 if (name === "prettier" && response.value === "配置") { console.log(chalk.cyan("\n💡 代码规范工具包括:")); console.log( chalk.dim("• ESLint:静态代码分析工具,帮助发现和修复代码问题") ); console.log(chalk.dim("• Prettier:代码格式化工具,统一代码风格")); console.log( chalk.dim("• Husky + lint-staged:Git提交前自动检查和格式化代码") ); console.log( chalk.dim("• 配置文件:.eslintrc, .prettierrc, .lintstagedrc\n") ); } // 如果选择配置国际化,提供相关信息 if (name === "i18n" && response.value === "配置") { console.log(chalk.cyan("\n💡 国际化 (i18n) 方案包括:")); console.log(chalk.dim("• i18next:国际化框架,支持多种语言切换")); console.log(chalk.dim("• 配置文件:i18n/\n")); } } return answers; } /** * 收集服务端/客户端配置 * @param {Object} answers - 当前收集的配置 * @returns {Promise<void>} * @private */ async _collectServerClientConfig(answers) { // 初始化服务端/客户端配置 answers.isServer = { value: "" }; // 仅当项目类型为组件库时才需要选择 if (answers.npmPackage.value === "神岛组件库") { console.log(chalk.cyan("\n🔍 组件库服务对象选择:")); console.log( chalk.dim("• 服务端组件:运行在游戏服务器上,处理游戏逻辑、数据和规则") ); console.log( chalk.dim("• 客户端组件:运行在玩家浏览器中,负责UI展示和用户交互") ); answers.isServer = await inquirer.prompt({ type: "list", name: "value", message: "请选择组件库的主要服务对象:", choices: [ { name: "服务端 - 游戏逻辑和数据处理", value: "服务端", short: "服务端", }, { name: "客户端 - 界面和用户交互", value: "客户端", short: "客户端", }, ], }); // 提供选择后的额外信息 if (answers.isServer.value === "服务端") { console.log(chalk.cyan("\n💡 服务端组件开发提示:")); console.log(chalk.dim("• 服务端组件可以访问游戏的完整数据和API")); console.log(chalk.dim("• 适合实现游戏规则、AI行为和数据处理")); console.log(chalk.dim("• 可以使用GameAPI服务端专用API\n")); } else { console.log(chalk.cyan("\n💡 客户端组件开发提示:")); console.log(chalk.dim("• 客户端组件主要用于实现用户界面和交互")); console.log(chalk.dim("• 适合开发UI控件、视觉效果和用户输入处理")); console.log(chalk.dim("• 可以使用ClientAPI客户端专用API\n")); } console.log(chalk.green(`✓ 已选择: ${answers.isServer.value}`)); } } /** * 收集项目路径 * @returns {Promise<string>} 项目路径 * @private */ async _collectProjectPath() { console.log(chalk.cyan("\n[路径设置]")); console.log(chalk.dim("• 请输入项目名称,作为项目目录")); const { path } = await inquirer.prompt([ { type: "input", name: "path", message: "📂 请输入项目创建的路径:", validate: (input) => { if (!input) { return "项目路径不能为空"; } // 限制项目路径只能包含英文字母、数字、下划线、连字符、点和斜杠 if (!/^[a-zA-Z0-9_./-]+$/.test(input)) { return "项目路径只能包含英文字母、数字、下划线、连字符、点 (.) 和斜杠 (/)"; } return true; }, }, ]); return path; } /** * 显示配置摘要 * @param {Object} answers - 收集的配置 * @param {string} basepath - 项目路径 * @param {null} selectedTemplate - 选择的项目模板 * @private */ _showConfigSummary(answers, basepath, selectedTemplate) { const fullPath = isAbsolute(basepath) ? basepath : resolve(process.cwd(), basepath); console.log(chalk.bold.greenBright("\n✅ 配置收集完成!")); console.log(chalk.cyan("以下是你的项目配置摘要:")); console.log(chalk.dim("----------------------------------------")); console.log(chalk.cyan("📁 项目路径:") + " " + fullPath); console.log(chalk.cyan("🔧 基础配置:")); console.log(` • 项目类型:${answers.npmPackage.value}`); if (answers.isServer.value) { console.log(` • 组件类型:${answers.isServer.value}`); } console.log(` • 开发语言:${answers.languageType.value}`); console.log(` • 代码规范:${answers.prettier.value}`); console.log(` • 国际化:${answers.i18n.value}`); console.log(chalk.dim("----------------------------------------")); console.log( chalk.cyan("正在自动配置你的项目,请稍候...") + chalk.dim(" (这可能需要几分钟时间)\n") ); } /** * 获取默认配置 * @returns {WizardResult} 默认配置结果 * @private */ _getDefaultConfig() { const answers = { npmPackage: { value: "神岛地图项目" }, languageType: { value: "TypeScript" }, strict: { value: "启用" }, prettier: { value: "配置" }, isServer: { value: "" }, }; finalSources = getSources(answers); return { answers, basepath: ".", selectedTemplate: null, }; } } // 导出配置向导类 export { ConfigWizard };