# via-cli-dev **Repository Path**: wangjinmeng/via-cli-dev ## Basic Information - **Project Name**: via-cli-dev - **Description**: 我的脚手架工具集合和ts工具集合 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-04-26 - **Last Updated**: 2025-11-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Via CLI 脚手架工具 - 完整指南 > 🚀 一个现代化、模块化的 CLI 脚手架工具,支持项目初始化、Git 工作流管理和缓存管理。 --- ## 📋 目录 - [项目概述](#项目概述) - [架构设计](#架构设计) - [核心架构](#核心架构) - [命令加载机制](#命令加载机制) - [模板系统](#模板系统) - [命令详解](#命令详解) - [init 命令](#init-命令) - [git 命令](#git-命令) - [cache 命令](#cache-命令) - [add 命令(已弃用)](#add-命令已弃用) - [模板库系统](#模板库系统) - [开发指南](#开发指南) - [设计决策](#设计决策) --- ## 项目概述 ### 💡 核心定位 Via CLI 是一个基于 **Monorepo** 架构的现代化脚手架工具,采用模块化设计,提供: - ✅ **项目初始化** - 多种项目模板的快速生成 - ✅ **Git 工作流** - 标准化的 Git Flow 分支管理 - ✅ **缓存管理** - 智能的包缓存和依赖管理 - ✅ **高度可扩展** - 易于添加新命令和模板 ### 📊 技术栈 | 类型 | 技术 | |------|------| | **开发语言** | TypeScript 5.x | | **构建工具** | tsup (基于 esbuild) | | **包管理器** | pnpm (workspace) | | **命令行框架** | Commander.js | | **Git 操作** | simple-git | | **交互式输入** | inquirer | | **日志工具** | 自研 @via-cli/log-tools | ### 📦 Monorepo 结构 ``` via-cli-dev/ ├── packages/ # 核心包集合 │ ├── core/ # 核心引擎(入口点) │ ├── init-command/ # 初始化命令 │ ├── git-command/ # Git 工作流命令 │ ├── cache-command/ # 缓存管理命令 │ └── pkg-manager/ # 包管理器 ├── utils/ # 工具库集合 │ ├── common-tools/ # 通用工具(含 Command 基类) │ ├── log-tools/ # 日志工具 │ ├── npm-tools/ # NPM 工具 │ └── ... # 其他工具库 ├── templates/ # 项目模板集合 │ ├── nuxt3-project-temp/ # Nuxt 3 项目模板 │ ├── ts-utils-temp/ # TypeScript 工具库模板 │ └── vue-bu-temp/ # Vue 组件库模板 ├── docs/ # 项目文档 └── package.json # 根配置 ``` --- ## 架构设计 ### 核心架构 Via CLI 采用 **分层架构 + 插件化命令** 的设计模式: ``` ┌─────────────────────────────────────────┐ │ CLI Entry Point (bin) │ │ packages/core/bin/index.js │ └──────────────────┬──────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ Core Engine (packages/core) │ │ ┌───────────────────────────────────┐ │ │ │ 1. 环境检查 (checkEnv) │ │ │ │ 2. 参数解析 (Commander) │ │ │ │ 3. 命令路由 (commands.ts) │ │ │ └───────────────────────────────────┘ │ └──────────────────┬──────────────────────┘ │ ┌─────────┴─────────┐ │ │ ▼ ▼ ┌─────────────────┐ ┌──────────────────┐ │ 内置命令模块 │ │ 外部动态加载 │ │ (Builtin) │ │ (External) │ ├─────────────────┤ ├──────────────────┤ │ • init │ │ • 模板包 │ │ • git │ │ • 插件包 │ │ • cache │ │ │ └─────────────────┘ └──────────────────┘ │ │ ▼ ▼ ┌─────────────────────────────────────────┐ │ Command Base Class │ │ utils/common-tools/command.ts │ │ ┌───────────────────────────────────┐ │ │ │ • checkNodeVersion() │ │ │ │ • initArgs() │ │ │ │ • init() (需子类实现) │ │ │ │ • exec() (需子类实现) │ │ │ └───────────────────────────────────┘ │ └─────────────────────────────────────────┘ ``` ### 命令加载机制 #### 1. 入口点 (`packages/core/bin/index.js`) ```javascript #! /usr/bin/env node const { default: cliMain } = await import("../dist/index.js"); await cliMain(process.argv.slice(2)); ``` - **作用**: Node.js 可执行入口 - **流程**: 动态加载编译后的 core 模块 #### 2. Core Engine (`packages/core/src/index.ts`) **核心职责**: 1. **环境准备** (`prepare` 函数) ```typescript async function prepare(): Promise { checkNodeVersion(); // 检查 Node.js 版本 checkRoot(); // 检查 root 权限 checkUserHome(); // 检查用户主目录 checkEnv(); // 加载环境变量 await checkGlobalUpdate(); // 检查 CLI 更新 } ``` 2. **命令注册** (`registryCommand` 函数) ```typescript function registryCommond(): void { program .name("via-cli") .version(pkg.version) .option("-d, --debug", "是否开启调试模式", false); // 注册 init 命令 program.command("init [projectName]") .option("-f --force", "强制模式", false) .option("-p --packageName ", "指定模板包名") .option("-tp, --targetPath ", "本地模板路径") .action(executeCommand); // 注册 git 命令 program.command("git [action]") .option("-m --message ", "提交信息") .option("-v --version ", "版本号") .option("-b --branch ", "分支名") .action(executeCommand); // 注册 cache 命令 program.command("cache [action]") .option("-p --package ", "指定包名") .action(executeCommand); } ``` 3. **命令执行** (`executeCommand` 函数) ```typescript async function executeCommand(projectName?: string, options?: any, cmd?: any): Promise { const cmdName = cmd?.name(); // 判断是内置命令还是外部命令 if (isBuiltinCommand(cmdName)) { // 内置命令:直接实例化并执行 return executeBuiltinCommand(cmdName, projectName, options); } else { // 外部命令:动态下载并执行 const packageName = EXTERNAL_SETTINGS[cmdName]; return executePackage(packageName, "latest", projectName, options); } } ``` #### 3. 命令管理器 (`packages/core/src/commands.ts`) ```typescript import init from "@via-cli/init-command"; import git from "@via-cli/git-command"; import cache from "@via-cli/cache-command"; // 内置命令映射表 const BUILTIN_COMMANDS = { init, git, cache } as const; /** * 执行内置命令 */ export function executeBuiltinCommand(commandName: string, ...args: any[]): any { const CommandClass = BUILTIN_COMMANDS[commandName]; if (!CommandClass) { throw new Error(`未找到命令: ${commandName}`); } // 实例化命令类,触发执行流程 return new CommandClass(args); } /** * 检查是否为内置命令 */ export function isBuiltinCommand(commandName: string): boolean { return Object.keys(BUILTIN_COMMANDS).includes(commandName); } ``` **设计要点**: - ✅ **集中管理**: 所有内置命令在一个文件中注册 - ✅ **类型安全**: 使用 TypeScript 的 `as const` 确保类型推断 - ✅ **易于扩展**: 添加新命令只需在映射表中添加一行 #### 4. 命令基类 (`utils/common-tools/src/command.ts`) 所有命令都继承自 `Command` 基类,确保统一的执行流程: ```typescript export default class Command { protected _argv: any[]; // 命令参数 protected _cmd: any; // Commander 对象 constructor(argv: any[]) { this._argv = argv; // 异步初始化流程 this.initialize().catch((err) => { LogTools.error('Command', err.message); throw err; }); } /** * 标准执行流程 */ private async initialize(): Promise { await this.checkNodeVersion(); // 1. 检查 Node 版本 await this.initArgs(); // 2. 初始化参数 await this.init(); // 3. 子类初始化 await this.exec(); // 4. 执行命令逻辑 } // 子类必须实现的方法 protected init(): void | Promise { throw new Error("init 方法必须在子类中实现!"); } protected exec(): void | Promise { throw new Error("exec 方法必须在子类中实现!"); } } ``` **生命周期钩子**: 1. `constructor()` - 接收参数 2. `checkNodeVersion()` - 版本检查 3. `initArgs()` - 参数解析 4. `init()` - **子类实现** - 初始化逻辑 5. `exec()` - **子类实现** - 执行逻辑 ### 模板系统 #### 动态执行器 (`packages/core/src/executor.ts`) 用于执行模板生成器(未来可用于插件系统): ```typescript export async function executePackage( packageName: string, packageVersion: string, ...args: any[] ): Promise { let targetPath = process.env.CLI_TARGET_PATH; const homePath = process.env.CLI_HOME_PATH; let pkg; if (!targetPath) { // 使用缓存模式:下载到 ~/.via-cli/dependencies targetPath = path.resolve(homePath!, "dependencies"); storeDir = path.resolve(targetPath, "node_modules"); pkg = new Package({ targetPath, storeDir, packageName, packageVersion, }); // 智能更新或安装 if (await pkg.exists()) { await pkg.update(); } else { await pkg.install(); } } else { // 使用本地路径(开发模式) pkg = new Package({ targetPath, packageName, packageVersion }); } // 获取包的入口文件 let rootFile = pkg.getRootFile(); // 动态导入并执行 const module = await import(rootFile); await module.default(args); } ``` --- ## 命令详解 ### init 命令 **作用**: 初始化新项目,支持多种项目模板。 #### 核心实现 (`packages/init-command/src/index.ts`) ```typescript class InitCommand extends Command { private projectName: string = ""; private options: InitOptions = {}; private projectPath: string = ""; private templateType: string = ""; // 模板配置表 private templates: TemplateConfig[] = [ { name: "TypeScript 工具库", packageName: "@via-cli/ts-utils-temp", description: "创建一个 TypeScript 工具函数库" }, { name: "Vue 组件库", packageName: "@via-cli/vue-bu-temp", description: "创建一个 Vue 3 组件库" }, { name: "Nuxt 项目", packageName: "@via-cli/nuxt3-project-temp", description: "创建一个 Nuxt 3 应用项目" } ]; init(): void { this.projectName = this._argv[0] || ""; this.options = this._cmd || {}; this.projectPath = path.resolve(this.projectName); this.packageName = this.options.packageName || this.options.p || ""; } async exec(): Promise { // 1. 检查项目名称 if (!this.projectName) { await this.promptProjectName(); } // 2. 准备项目目录 await this.prepareProjectDirectory(); // 3. 选择模板(如未指定) if (!this.options.packageName && !this.options.targetPath) { await this.selectTemplate(); } // 4. 执行模板生成器 await this.executeTemplate(); } } ``` #### 执行流程 ``` 用户输入 │ ▼ via-cli init my-project │ ├─> 检查项目名称 │ └─> 无 → 交互式询问 │ ├─> 准备目录 │ ├─> 目录不存在 → 创建 │ └─> 目录存在 │ ├─> force=true → 清空 │ └─> force=false → 询问用户 │ ├─> 选择模板 │ ├─> 已指定 --packageName → 跳过 │ ├─> 已指定 --targetPath → 跳过 │ └─> 未指定 → 交互式选择 │ ├─ TypeScript 工具库 │ ├─ Vue 组件库 │ └─ Nuxt 项目 │ └─> 执行模板生成器 ├─> 本地模式 (targetPath) │ └─> 直接执行本地 index.js │ └─> 远程模式 (packageName) ├─> 检查缓存 ├─> 下载/更新包 └─> 执行包的 index.js ``` #### 缓存管理 ``` ~/.via-cli/ └── dependencies/ # 旧版依赖缓存(模板专用) └── node_modules/ ├── @via-cli/ts-utils-temp/ ├── @via-cli/vue-bu-temp/ └── @via-cli/nuxt3-project-temp/ ``` #### 使用示例 ```bash # 1. 交互式创建 via-cli init my-project # 2. 指定模板包 via-cli init my-app -p @via-cli/nuxt3-project-temp # 3. 使用本地模板(开发模式) via-cli init test-project --targetPath /path/to/templates/nuxt3-project-temp/index.js # 4. 强制覆盖 via-cli init existing-project --force ``` --- ### git 命令 **作用**: 标准化 Git Flow 工作流管理。 #### 核心实现 (`packages/git-command/src/index.ts`) ```typescript class GitCommand extends Command { private action: string = ""; private message: string = ""; private version: string = ""; private branch: string = ""; private git!: SimpleGit; init(): void { this.action = this._argv[0] || ""; const opts = this._cmd?.opts?.() || {}; this.message = opts.message || ""; this.version = opts.version || ""; this.branch = opts.branch || ""; this.git = simpleGit(process.cwd()); } async exec(): Promise { if (!this.action) { await this.showHelp(); return; } await this.ensureGitRepo(); await this.executeAction(this.action); } private async executeAction(action: GitAction): Promise { const actionMap: Record Promise> = { feature: () => this.createFeatureBranch(), dev: () => this.mergeToDevBranch(), pre: () => this.mergeToPreBranch(), release: () => this.createRelease(), hotfix: () => this.createHotfixBranch(), 'hotfix-finish': () => this.finishHotfix(), commit: () => this.smartCommit(), status: () => this.showStatus(), undo: () => this.undoLastCommit(), }; const handler = actionMap[action]; if (!handler) { throw new Error(`未知操作: ${action}`); } await handler(); } } ``` #### Git Flow 分支模型 ``` master/main (生产) ↑ │ merge + tag │ release/YYYYMMDD (发布分支,永久保留) ↑ │ create from │ pre (预发布) ↑ │ rebase + fast-forward │ dev (开发) ↑ │ rebase + fast-forward │ feature/* (功能分支) ``` #### 核心操作 **1. feature → dev (功能开发)** ```bash # 创建功能分支 via-cli git feature user-login # → 创建 feature/user-login 分支 # 开发完成后合并到 dev via-cli git dev # → 在 feature 分支执行 rebase dev # → 切换到 dev # → 快速合并 feature 分支 # → 推送到远程 ``` **实现原理**: ```typescript async mergeToDevBranch(): Promise { const currentBranch = await this.getCurrentBranch(); // 1. 在当前分支执行 rebase dev await this.git.rebase(['dev']); // 2. 切换到 dev 分支 await this.git.checkout('dev'); // 3. 快速合并(保持线性历史) await this.git.merge([currentBranch, '--ff-only']); // 4. 推送到远程 await this.git.push('origin', 'dev'); // 5. 询问是否删除 feature 分支 const shouldDelete = await this.promptDeleteBranch(); if (shouldDelete) { await this.git.deleteLocalBranch(currentBranch, true); } } ``` **2. dev → pre (预发布)** ```bash via-cli git pre # → 在 dev 分支执行 rebase pre # → 切换到 pre # → 快速合并 dev # → 推送到远程 ``` **3. pre → master (生产发布)** ```bash via-cli git release -v 1.2.0 # → 切换到 master # → 合并 pre 分支 # → 创建 tag v1.2.0 # → 创建 release/20251030 分支 # → 推送所有更改 ``` **4. 智能提交 (commit)** ```bash via-cli git commit # → 检查未暂存文件 # → 选择提交类型 (feat/fix/docs/...) # → 输入提交信息 # → 可选添加 #release# 标记 # → 执行提交 ``` **提交类型**: - `feat` ✨ 新功能 - `fix` 🐛 修复 bug - `docs` 📝 文档更新 - `style` 💄 代码格式 - `refactor` ♻️ 重构 - `perf` ⚡️ 性能优化 - `test` ✅ 测试 - `build` 📦 构建 - `ci` 👷 CI 配置 - `chore` 🔧 其他 #### 使用示例 ```bash # 创建功能分支 via-cli git feature user-auth # 智能提交 via-cli git commit # 选择: feat # 输入: 添加用户认证功能 # 结果: feat: 添加用户认证功能 # 合并到 dev via-cli git dev # 发布到 pre via-cli git pre # 上线发布 via-cli git release -v 1.0.0 # 创建热修复 via-cli git hotfix urgent-fix # 完成热修复 via-cli git hotfix-finish # 查看状态 via-cli git status # 撤销提交 via-cli git undo ``` --- ### cache 命令 **作用**: 管理 CLI 工具的缓存(包括模板缓存和包缓存)。 #### 双缓存系统 Via CLI 支持两种缓存类型: ``` ~/.via-cli/ ├── dependencies/ # 旧版依赖缓存(模板专用) │ └── node_modules/ │ └── @via-cli/xxx-temp/ │ └── npm-cache/ # 统一包缓存(pkg-manager v1.0+) ├── lodash/ │ └── 4.17.21/ └── axios/ └── 1.6.0/ ``` #### 核心实现 (`packages/cache-command/src/index.ts`) ```typescript class CacheCommand extends Command { private legacyCachePath: string = ""; // 旧缓存 private unifiedCachePath: string = ""; // 统一缓存 protected init(): void { this.projectName = this._argv[0] || ""; this.legacyCachePath = getDependenciesCacheDir(); this.unifiedCachePath = this.getUnifiedCachePath(); } protected async exec(): Promise { const action = this.projectName; switch (action) { case "list": case "ls": await this.listCache(); break; case "clear": case "clean": await this.clearCache(); break; case "remove": case "rm": await this.removePackage(); break; case "info": case "status": await this.showCacheInfo(); break; default: await this.showHelp(); } } } ``` #### 主要功能 **1. 查看缓存列表** ```bash via-cli cache list # 输出: # 📦 旧版依赖缓存 (/path/to/dependencies): # 1. @via-cli/ts-utils-temp@1.0.0 # 2. @via-cli/nuxt3-project-temp@1.2.0 # # 🚀 统一包缓存 (~/.via-cli/npm-cache): # 1. lodash@4.17.21 # 2. axios@1.6.0 # # 总缓存: 4 个 # 总大小: 125.67 MB ``` **2. 清理缓存** ```bash via-cli cache clear # 交互式选择: # ? 选择清理选项: # ○ 仅清理旧版依赖缓存 # ○ 仅清理统一包缓存 # ● 清理所有缓存 # ○ 取消 ``` **3. 删除指定包** ```bash via-cli cache remove # 多选删除: # ? 选择要删除的包: # ◉ 📦 [旧版] @via-cli/ts-utils-temp@1.0.0 (12.5 MB) # ◯ 🚀 [统一] lodash@4.17.21 (8.3 MB) # ◉ 🚀 [统一] axios@1.6.0 (5.2 MB) ``` **4. 查看缓存信息** ```bash via-cli cache info # 输出详细统计: # ℹ 缓存配置 # 旧版缓存目录: /path/to/dependencies # 统一缓存目录: ~/.via-cli/npm-cache # # 📊 缓存统计 # 旧版缓存: 2 个包, 45.2 MB # 统一缓存: 15 个包, 180.5 MB # 总计: 17 个包, 225.7 MB ``` --- ### add 命令(已弃用) #### ⚠️ 弃用原因 在 v0.1.0 之前,Via CLI 提供了 `via-cli add` 命令用于在项目中添加组件、页面等。 **存在的问题**: 1. **上下文绑定性强** - 不同项目类型(Vue/React/Nuxt)的结构差异大 2. **维护成本高** - 全局 CLI 需要了解所有项目类型的细节 3. **耦合度过高** - 违反单一职责原则 4. **扩展性差** - 难以适应项目特定需求 #### ✅ 新设计:项目内置 CLI 将代码生成功能迁移到项目模板内部: **旧方式(已弃用)**: ```bash cd my-nuxt-app via-cli add component UserCard via-cli add page about ``` **新方式(推荐)**: ```bash cd my-nuxt-app pnpm add:component UserCard pnpm add:page about pnpm add:composable user-data ``` #### 项目内置 CLI 结构 ``` my-nuxt-project/ ├── cli/ # 内置 CLI 工具 │ ├── add-component.js # 组件生成命令 │ ├── add-page.js # 页面生成命令 │ ├── add-composable.js # 组合函数生成命令 │ ├── generators/ # 生成器实现 │ │ ├── component.js │ │ ├── page.js │ │ └── composable.js │ ├── templates/ # 代码模板 │ │ ├── component.vue │ │ ├── page.vue │ │ └── composable.ts │ └── utils/ # 工具函数 │ ├── logger.js │ ├── file-helper.js │ └── name-helper.js ├── package.json │ └── scripts: │ ├── "add:component": "node cli/add-component.js" │ ├── "add:page": "node cli/add-page.js" │ └── "add:composable": "node cli/add-composable.js" └── ... ``` **优势**: - ✅ **降低耦合**: 全局 CLI 不再需要了解项目细节 - ✅ **提高灵活性**: 项目可以自定义生成器行为 - ✅ **简化维护**: 生成器逻辑与模板一起维护 - ✅ **更好体验**: 项目内命令响应更快,不依赖全局 CLI 版本 --- ## 模板库系统 ### 模板架构 Via CLI 的模板系统采用 **标准 NPM 包 + 生成器函数** 的设计: ``` 模板包 (@via-cli/xxx-temp) ├── package.json # NPM 包配置 ├── index.js # 生成器入口(ES Module) ├── template/ # 模板文件 │ ├── package.json.ejs # EJS 模板 │ ├── src/ │ └── ... ├── scripts/ # 构建脚本 └── source/ # 源码(可选) ``` ### 模板开发规范 #### 1. package.json 配置 ```json { "name": "@via-cli/nuxt3-project-temp", "version": "1.0.0", "type": "module", // ⚠️ 必须使用 ES Module "main": "index.js", // 入口文件 "dependencies": { "inquirer": "^9.0.0", // 交互式输入 "ejs": "^3.1.9", // 模板渲染 "@via-cli/utils-core": "workspace:*" } } ``` #### 2. index.js 生成器 ```javascript #!/usr/bin/env node import inquirer from 'inquirer'; import ejs from 'ejs'; import { mkdir, writeFile, readFile, readdir, stat } from 'fs/promises'; import { join } from 'path'; /** * 递归复制并渲染模板 */ async function copyTemplate(templateDir, targetDir, data) { const items = await readdir(templateDir); for (const item of items) { const sourcePath = join(templateDir, item); const itemStat = await stat(sourcePath); if (itemStat.isDirectory()) { // 递归处理目录 await mkdir(join(targetDir, item), { recursive: true }); await copyTemplate(sourcePath, join(targetDir, item), data); } else { let targetPath = join(targetDir, item); // 处理 .ejs 文件:渲染并去掉后缀 if (item.endsWith('.ejs')) { targetPath = join(targetDir, item.replace(/\.ejs$/, '')); const content = await readFile(sourcePath, 'utf-8'); const renderedContent = ejs.render(content, data); await writeFile(targetPath, renderedContent, 'utf-8'); } else { // 非 .ejs 文件直接复制 const content = await readFile(sourcePath, 'utf-8'); await writeFile(targetPath, content, 'utf-8'); } } } } /** * 主函数 - 必须导出为 default */ async function main(options = {}) { console.log('🚀 创建新的 Nuxt 3 项目'); // 从参数中获取目标目录和项目名称 const targetDir = options.targetDir; const projectName = options.projectName; // 交互式询问项目信息 const answers = await inquirer.prompt([ { type: 'input', name: 'projectName', message: '项目名称:', default: projectName, validate: input => input.trim() ? true : '项目名称不能为空' }, { type: 'input', name: 'version', message: '初始版本:', default: '0.0.1' }, { type: 'confirm', name: 'useTailwind', message: '是否使用 Tailwind CSS?', default: true } ]); // 准备模板数据 const projectData = { projectName: answers.projectName, version: answers.version, useTailwind: answers.useTailwind, year: new Date().getFullYear() }; // 复制并渲染模板 const templateDir = join(__dirname, 'template'); await copyTemplate(templateDir, targetDir, projectData); console.log('✅ 项目创建成功!'); } // ⚠️ 必须导出为 default export default main; ``` #### 3. 模板文件示例 **package.json.ejs**: ```json { "name": "<%= projectName %>", "version": "<%= version %>", "description": "A Nuxt 3 project", "scripts": { "dev": "nuxt dev", "build": "nuxt build", "start": "node .output/server/index.mjs" }, "dependencies": { "nuxt": "^3.15.0"<% if (useTailwind) { %>, "@nuxtjs/tailwindcss": "^6.0.0"<% } %> } } ``` ### 已有模板 #### 1. TypeScript 工具库 (`@via-cli/ts-utils-temp`) **特性**: - ✅ TypeScript 5.x 配置 - ✅ tsup 构建工具 - ✅ Vitest 测试框架 - ✅ ESLint + Prettier - ✅ 完整的 npm 发布配置 **生成结构**: ``` my-utils/ ├── src/ │ └── index.ts ├── test/ │ └── index.test.ts ├── package.json ├── tsconfig.json ├── tsup.config.ts └── README.md ``` #### 2. Vue 组件库 (`@via-cli/vue-bu-temp`) **特性**: - ✅ Vue 3 Composition API - ✅ Vite 构建 - ✅ 组件库模式 - ✅ 类型声明自动生成 - ✅ 文档站点集成 **生成结构**: ``` my-components/ ├── src/ │ ├── components/ │ ├── index.ts │ └── styles/ ├── examples/ ├── package.json └── vite.config.ts ``` #### 3. Nuxt 3 项目 (`@via-cli/nuxt3-project-temp`) **特性**: - ✅ Nuxt 3.15+ 最新版本 - ✅ TypeScript 支持 - ✅ Tailwind CSS(可选) - ✅ ESLint + Prettier(可选) - ✅ 完整目录结构 - ✅ 内置 CLI 工具(add:component 等) **生成结构**: ``` my-nuxt-app/ ├── cli/ # 内置 CLI 工具 │ ├── add-component.js │ ├── add-page.js │ └── ... ├── components/ ├── pages/ ├── layouts/ ├── composables/ ├── middleware/ ├── server/ │ └── api/ ├── assets/ ├── public/ ├── nuxt.config.ts └── package.json ``` ### 模板调用流程 ``` 用户执行 │ ▼ via-cli init my-project │ ├─> Init Command │ ├─> 选择模板: Nuxt 3 项目 │ └─> packageName = "@via-cli/nuxt3-project-temp" │ ├─> Package Manager │ ├─> 检查缓存: ~/.via-cli/dependencies/ │ ├─> 下载/更新模板包 │ └─> 返回入口路径: /path/to/index.js │ ├─> Dynamic Executor │ ├─> 动态导入: import(rootFile) │ └─> 调用: module.default(options) │ └─> Template Generator ├─> 接收参数: { targetDir, projectName } ├─> 交互式询问: 项目配置 ├─> 渲染模板: EJS └─> 写入文件: targetDir ``` --- ## 开发指南 ### 环境准备 ```bash # 1. 克隆仓库 git clone cd via-cli-dev # 2. 安装依赖 pnpm install # 3. 构建所有包 pnpm build # 4. 链接到全局 pnpm link --global ``` ### 项目脚本 ```bash # 构建所有包 pnpm build # 监听模式(开发) pnpm dev # 清理构建产物 pnpm clean # 强制构建(忽略错误) pnpm build:force ``` ### 添加新命令 #### 1. 创建命令包 ```bash cd packages mkdir my-command cd my-command pnpm init ``` #### 2. 配置 package.json ```json { "name": "@via-cli/my-command", "version": "0.0.1", "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { "build": "tsup", "dev": "tsup --watch" }, "dependencies": { "@via-cli/common-tools": "workspace:*", "@via-cli/log-tools": "workspace:*" }, "devDependencies": { "tsup": "^8.0.0", "typescript": "^5.3.0" } } ``` #### 3. 创建命令类 **src/index.ts**: ```typescript import { Command } from "@via-cli/common-tools"; import { LogTools } from "@via-cli/log-tools"; interface MyCommandOptions { [key: string]: any; } class MyCommand extends Command { private action: string = ""; private options: MyCommandOptions = {}; init(): void { this.action = this._argv[0] || ""; this.options = this._cmd?.opts?.() || {}; LogTools.verbose("MY_COMMAND", "init", this.action, JSON.stringify(this.options)); } async exec(): Promise { try { if (!this.action) { await this.showHelp(); return; } // 实现命令逻辑 LogTools.info("MY_COMMAND", `执行 ${this.action} 操作`); // ... } catch (error) { LogTools.error("MY_COMMAND", "命令执行失败", String(error)); throw error; } } private async showHelp(): Promise { console.log(` 使用方法: via-cli my-command [options] 操作: action1 - 描述1 action2 - 描述2 选项: -o, --option 选项描述 `); } } export default MyCommand; ``` #### 4. 配置构建 **tsup.config.js**: ```javascript import { defineConfig } from 'tsup'; export default defineConfig({ entry: ['src/index.ts'], format: ['esm'], dts: true, clean: true, sourcemap: true, external: [/^@via-cli\//] }); ``` #### 5. 注册到 Core **packages/core/src/commands.ts**: ```typescript import init from "@via-cli/init-command"; import git from "@via-cli/git-command"; import cache from "@via-cli/cache-command"; import myCommand from "@via-cli/my-command"; // 导入新命令 const BUILTIN_COMMANDS = { init, git, cache, 'my-command': myCommand // 注册新命令 } as const; ``` **packages/core/src/index.ts**: ```typescript // 注册命令 program.command("my-command [action]") .option("-o --option ", "选项描述") .action(executeCommand); ``` #### 6. 更新 Core 依赖 **packages/core/package.json**: ```json { "dependencies": { "@via-cli/my-command": "workspace:*" } } ``` #### 7. 构建并测试 ```bash # 构建新命令 cd packages/my-command pnpm build # 构建 core cd ../core pnpm build # 测试 via-cli my-command action1 --option value ``` ### 添加新模板 #### 1. 创建模板包 ```bash cd templates mkdir my-template-temp cd my-template-temp pnpm init ``` #### 2. 配置 package.json ```json { "name": "@via-cli/my-template-temp", "version": "1.0.0", "type": "module", "main": "index.js", "dependencies": { "inquirer": "^9.0.0", "ejs": "^3.1.9", "@via-cli/utils-core": "workspace:*" } } ``` #### 3. 创建生成器 **index.js**: ```javascript #!/usr/bin/env node import inquirer from 'inquirer'; import ejs from 'ejs'; import { mkdir, writeFile, readFile, readdir, stat } from 'fs/promises'; import { join } from 'path'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); async function copyTemplate(templateDir, targetDir, data) { // 实现模板复制和渲染逻辑 // ... } async function main(options = {}) { console.log('🚀 创建新的 XXX 项目'); const targetDir = options.targetDir; const projectName = options.projectName; // 交互式询问 const answers = await inquirer.prompt([ // ... ]); // 准备数据 const projectData = { projectName: answers.projectName, // ... }; // 复制模板 const templateDir = join(__dirname, 'template'); await copyTemplate(templateDir, targetDir, projectData); console.log('✅ 项目创建成功!'); } export default main; ``` #### 4. 创建模板文件 ``` my-template-temp/ ├── template/ │ ├── package.json.ejs │ ├── README.md.ejs │ ├── src/ │ │ └── index.ts.ejs │ └── ... └── index.js ``` #### 5. 注册到 Init 命令 **packages/init-command/src/index.ts**: ```typescript private templates: TemplateConfig[] = [ // ... { name: "我的模板", packageName: "@via-cli/my-template-temp", description: "创建一个 XXX 项目" } ]; ``` #### 6. 测试 ```bash # 本地测试 via-cli init test-project --targetPath /path/to/my-template-temp/index.js # 发布后测试 via-cli init test-project # 选择 "我的模板" ``` ### 调试技巧 #### 1. 开启 Debug 模式 ```bash via-cli init my-project --debug # 或设置环境变量 LOG_LEVEL=verbose via-cli init my-project ``` #### 2. 查看详细日志 ```typescript import { LogTools } from "@via-cli/log-tools"; LogTools.verbose("TAG", "详细信息", data); LogTools.info("TAG", "普通信息"); LogTools.success("TAG", "成功信息"); LogTools.warn("TAG", "警告信息"); LogTools.error("TAG", "错误信息"); ``` #### 3. VS Code 调试配置 **.vscode/launch.json**: ```json { "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Debug CLI", "runtimeExecutable": "node", "runtimeArgs": ["--loader", "ts-node/esm"], "args": [ "${workspaceFolder}/packages/core/src/index.ts", "init", "test-project", "--debug" ], "console": "integratedTerminal", "env": { "LOG_LEVEL": "verbose" } } ] } ``` --- ## 设计决策 ### 为什么选择 Monorepo? **优势**: - ✅ **统一依赖管理**: 所有包共享相同的依赖版本 - ✅ **代码复用**: 工具库可以在多个包之间共享 - ✅ **原子化提交**: 跨包的修改可以在一次提交中完成 - ✅ **便于重构**: 修改公共接口时可以同时更新所有使用方 **劣势**: - ⚠️ **构建复杂度**: 需要管理包之间的构建顺序 - ⚠️ **仓库体积**: 随着包数量增加,仓库会变大 ### 为什么移除 add 命令? **原因**: 1. **职责分离**: 全局 CLI 专注于项目管理,代码生成交给项目内部 2. **降低耦合**: 避免全局 CLI 了解每个项目类型的细节 3. **提高灵活性**: 每个项目可以自定义生成器逻辑 4. **简化维护**: 生成器与模板一起维护,版本同步 **参考文档**: [docs/ARCHITECTURE_IMPROVEMENT.md](./docs/ARCHITECTURE_IMPROVEMENT.md) ### 为什么使用 TypeScript? - ✅ **类型安全**: 减少运行时错误 - ✅ **IDE 支持**: 更好的代码提示和自动补全 - ✅ **重构友好**: 修改接口时可以快速发现所有影响点 - ✅ **文档化**: 类型声明本身就是最好的文档 ### 为什么使用 tsup? - ✅ **零配置**: 开箱即用,配置简单 - ✅ **基于 esbuild**: 构建速度快 - ✅ **自动生成类型**: 支持 `.d.ts` 文件生成 - ✅ **ES Module**: 原生支持 ESM ### 为什么使用 Commander.js? - ✅ **成熟稳定**: Node.js 生态最流行的 CLI 框架 - ✅ **功能完整**: 支持子命令、选项解析、帮助生成等 - ✅ **易于扩展**: 简单的 API 设计 - ✅ **TypeScript 支持**: 提供完整的类型定义 ### 缓存策略 **双缓存系统**: 1. **旧版依赖缓存** (`~/.via-cli/dependencies/`) - 用途: 模板包专用 - 特点: 项目特定,每个包独立目录 2. **统一包缓存** (`~/.via-cli/npm-cache/`) - 用途: pkg-manager v1.0+ 使用 - 特点: 全局共享,节省空间 **为什么需要两种缓存?** - 向后兼容性:保留旧缓存方式 - 渐进式迁移:新功能使用统一缓存 - 灵活选择:用户可以根据需求选择 --- ## 常见问题 ### Q1: 如何更新 CLI 到最新版本? ```bash # 全局更新 npm install -g @via-cli/core@latest # 或使用 pnpm pnpm add -g @via-cli/core@latest # 检查版本 via-cli --version ``` ### Q2: 模板缓存在哪里? ``` Windows: C:\Users\\.via-cli\dependencies\ macOS/Linux: ~/.via-cli/dependencies/ ``` ### Q3: 如何清理缓存? ```bash # 查看缓存 via-cli cache list # 清理所有缓存 via-cli cache clear # 删除特定包 via-cli cache remove ``` ### Q4: 如何使用本地模板开发? ```bash # 使用 --targetPath 指定本地路径 via-cli init test-project --targetPath /absolute/path/to/template/index.js ``` ### Q5: Git 命令出错怎么办? ```bash # 检查是否在 Git 仓库中 git status # 开启 debug 模式查看详细信息 via-cli git dev --debug # 查看帮助 via-cli git --help ``` ### Q6: 如何贡献代码? 1. Fork 仓库 2. 创建功能分支: `git checkout -b feature/my-feature` 3. 提交修改: `git commit -m "feat: add new feature"` 4. 推送到分支: `git push origin feature/my-feature` 5. 提交 Pull Request --- ## 相关文档 - [架构改进说明](./docs/ARCHITECTURE_IMPROVEMENT.md) - [统一缓存管理方案](./docs/CACHE_MANAGEMENT.md) - [项目结构重组说明](./docs/PROJECT_RESTRUCTURE.md) - [Git 命令使用指南](./packages/git-command/USAGE.md) - [缓存管理文档](./packages/cache-command/README.md) - [包管理器文档](./packages/pkg-manager/README.md) - [项目重构记录](./docs/PROJECT_RESTRUCTURE.md) --- ## 许可证 ISC --- ## 作者 wangjinmeng <1164622507@qq.com> --- **最后更新**: 2025-10-30 **版本**: v0.0.4