实现总结
通过 package.json 的 bin 字段 注册全局命令,
并且 index.js 设置为可执行 node 脚本,
通过
npm link与本地npm创建链接,供本地全局使用当用户运行
vue3-zhb-cli时,自动执行index.js,它复制模板并生成项目。
实现步骤
1. 创建以下文件结构
vue3-zhb-cli/
├─ templates/
│ │ └─ vue-basic/ ← 模板项目文件(你想你的初始化项目是什么样的都在这里面写)
│ │ │ ├─ src ← 黄色区域内都是项目模板文件
│ │ │ │ ├─ App.vue
│ │ │ │ └─ main.js
│ │ │ ├─ index.html
│ │ │ ├─ package.json ← 里面你要用的依赖都自己配置好,不然模板构建完成无法运行
│ │ │ └─ vite.config.js ← 里面你要用的依赖都自己配置好,不然模板构建完成无法运行
├─ index.js ← CLI 主入口(命令逻辑)
└─ package.json ← 声明 CLI 命令名(bin 字段)
2. 编写index.js 文件内容( #!/usr/bin/env node 这行代码是关键!在index顶部声明为可执行命令)
// vue3-zhb-cli下的index.js文件(使用时将此行删除)
#!/usr/bin/env node
const inquirer = require('inquirer')
const fs = require('fs')
const path = require('path')
const chalk = require('chalk')
const { execSync } = require('child_process')
// 模板路径
const templatePath = path.join(__dirname, 'templates', 'vue-basic')
console.log(chalk.cyan('Welcome to Vue3 ZHB CLI! 🚀'))
async function run() {
try {
// 交互式输入项目名
const { projectName } = await inquirer.prompt([
{
type: 'input',
name: 'projectName',
message: 'Enter the project name:',
validate: input => (input ? true : 'Project name is required!'),
},
])
const projectDir = path.join(process.cwd(), projectName)
if (fs.existsSync(projectDir)) {
console.log(chalk.red(`The directory ${projectName} already exists.`))
return
}
// 创建目录
fs.mkdirSync(projectDir)
// 拷贝模板文件
const copyRecursiveSync = (src, dest) => {
const stats = fs.statSync(src)
if (stats.isDirectory()) {
// 不在目标目录再次创建顶层文件夹
if (!fs.existsSync(dest)) fs.mkdirSync(dest)
fs.readdirSync(src).forEach(child => {
copyRecursiveSync(path.join(src, child), path.join(dest, child))
})
} else {
fs.copyFileSync(src, dest)
}
}
copyRecursiveSync(templatePath, projectDir)
console.log(chalk.green(`Project ${projectName} created successfully!`))
//检测用户用的是npm\pnpm\yarn
const detectPackageManager = () => {
const candidates = [
{ cmd: 'pnpm --version', pm: 'pnpm' },
{ cmd: 'npx pnpm --version', pm: 'pnpm-npx' },
{ cmd: 'yarn --version', pm: 'yarn' },
{ cmd: 'npx yarn --version', pm: 'yarn-npx' },
]
for (const { cmd, pm } of candidates) {
try {
execSync(cmd, { stdio: 'ignore' })
return pm
} catch {}
}
return 'npm'
}
const packageManager = detectPackageManager()
const installCommand = {
pnpm: 'pnpm install',
'pnpm-npx': 'npx pnpm install',
yarn: 'yarn install',
'yarn-npx': 'npx yarn install',
npm: 'npm install',
}[packageManager]
// 安装依赖
console.log(`Installing dependencies with ${packageManager}...`)
execSync(installCommand, { cwd: projectDir, stdio: 'inherit' })
//不同依赖包生成不同提示
let runCommand
switch (packageManager) {
case 'yarn':
runCommand = 'yarn dev'
break
case 'pnpm':
runCommand = 'pnpm dev'
break
default:
runCommand = 'npm run dev'
}
console.log(chalk.green(`🎉 Project ${projectName} created successfully!`))
console.log(
chalk.blue(`To get started:\n cd ${projectName}\n ${runCommand}`)
)
} catch (err) {
console.error(chalk.red(err.message))
}
}
run()
// vue3-zhb-cli下的package.json文件
{
"name": "vue3-zhb-cli",
"version": "1.0.0",
"bin": {
"vue3-zhb-cli": "index.js"
//1. 这里是重点,通过cmd将执行的命令与vue3-zhb-cli下的index.js进行
// 关联即可将声明为可执行命令的index.js执行
//2. 这里vue3-zhb-cli你想执行什么命令构建项目,就改为什么
// 如:"a-b": "index.js","x-y-z": "index.js"
},
"dependencies": {
"inquirer": "^8.2.0",
"chalk": "^4.1.2"
},
"devDependencies": {}
}
3. 执行 npm link 可以在全局使用,将vue3-zhb-cli构建命令与全局的npm包进行关联
npm link 是 把本地开发的 npm 包“全局安装”并创建符号链接,让你可以像使用正式 npm 包一样使用自己写的 CLI 或库,而不需要每次都发布到 npm。
简单说就是:
- 你的本地包(CLI 或库) → 在全局创建一个 软链接
- 你可以在任何地方直接执行命令或
require()你的包
npm link工作原理
- CLI 项目的
package.json中有bin字段,例如:
"bin": {
"vue3-zhb-cli": "index.js"
}
npm link会在全局node_modules/.bin下创建一个 可执行符号链接,指向你的index.js- 命令行执行
vue3-zhb-cli时,就会调用你本地项目的index.js