create-arena-project
Version:
神奇代码岛<->VSCode,从这里开始,创建一个神岛代码项目的脚手架。
423 lines (388 loc) • 13.3 kB
JavaScript
/**
* @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 };