共计 6120 个字符,预计需要花费 16 分钟才能阅读完成。
根据需求搭建属于自己的脚手架
1. 处理终端用户输入的命令
- 新建一个新文件夹,取名为
cm-cli
- 打开终端,输入命令
npm init -y
生成 package.json 文件 - 安装相应的第三方库
cnpm i commander download-git-repo inquirer handlebars ora chalk log-symbols -S
- commander.js,可以自动的解析命令和参数,用于处理用户输入的命令。
- download-git-repo,下载并提取 git 仓库,用于下载项目模板。
- Inquirer.js,通用的命令行用户界面集合,用于和用户进行交互。
- handlebars.js,模板引擎,将用户提交的信息动态填充到文件中。
- ora,下载过程久的话,可以用于显示下载中的动画效果。
- chalk,可以给终端的字体加上颜色。
- log-symbols,可以在终端上显示出 √ 或 × 等的图标。
- 在 cm-cli 文件夹下新建一个
bin
文件夹,然后bin
里面新建一个project.js
文件 -
在之前创建好的
package.json
文件里新增如下选项"bin": {"master-cli": "bin/project.js"},
在
/cm-cli
目录下打开终端,使用npm link
或者npm i . -g
进行全局安装我们自己的脚手架,这样我们就可以在终端任何地方使用master-cli create <app-name>
进行脚手架的安装 -
在
project.js
文件里面加入以下代码#!/usr/bin/env node const program = require('commander') const chalk = require('chalk') program .version('1.0.0', '-V, --version') .usage('<command> [options]') program .command('create <app-name>') .description('create a new project powered by project-cli-service') .option('-d, --default', 'Skip prompts and use default preset') .action((name , cmd) => {console.log(chalk.blue(`${name}项目正在创建中...`)) }) program.parse(process.argv)
2. 从远程仓库下载模板
- 前文已经安装了
download-git-repo
这个第三方库,接下来在project.js
引入它
- download-git-repo 能够是我们从特定的远程仓库,GitHub,Gitlab 等下载相应的项目模板
- Github 官方网址:https://github.com/flipxfx/download-git-repo#readme 相关的使用方法进行自行浏览
-
方法:download(repository, destination, options, callback)
repository:模板下载的仓库地址
destination: 讲仓库的模板文件下载到本地哪里,
options:其他选项 clone:使用 git clone 命令下载模板
All other options (
proxy
,headers
,filter
, etc.) will be passed down accordingly and may override defaults- 除 clone 的其他选项: https://github.com/kevva/down…
- clone 的其他选项: https://github.com/jaz303/git…
const download = reruire('download-git-repo')
const url = 'github.com:ZhengMaster2020/template-cli#master'
download(url, name, {clone:ture}, function (err) {if (err) {console.log(chalk.red(err))
} else {// 进行 package.json 文件的内容替换操作}
})
3. 使用 inquirer 进行命令行交互
-
在
project.js
引入 inquirer 第三方库const inquirer = require('inquirer'); inquirer .prompt([{ name: 'version', message: '请输入项目版本', default: '1.0.0' },{ name: 'description', message: '请输入项目描述信息', default: '这是一个自定义脚手架生成的项目' },{ type: 'input' name: 'author', message: '请输入作者名称', default: 'zhengmaster' }]) .then(function (answer){console.log(answer) // 进行下载模板 // 模板的渲染等操作 })
这些内容是在中终端提示用户自行输入相应的内容来生成相应的 package.json 文件的
prompt 方法:接受一个数组,数组放多个对象,每个对象表示一个含义,name 表示含义的名称,message 表示含义的描述,default 为描述提供了一个默认值,type 表示类型,值为 input 表示输入类型。
then 方法:prompt 的回调函数,prompt 返回一个 answer(就是用户在终端输入的内容)它接受一个 answer 作为参数
更多使用方法的详情:https://blog.csdn.net/qq_26733915/article/details/80461257
4. 渲染模板
这里面要使用到的是模板引擎,有handlebar(本次我们使用的是这个),ejs, jade 等等
还有使用到了 Node 的 fs 模块:const fs = require('fs')
当下载模板完成之后会将用户输入的答案渲染到 package.json 中
我的模板仓库:https://github.com/ZhengMaster2020/template-cli
模板项目里面的 package.json
已经修改成如下:
{"name": "{{name}}", // 使用用户输入的 name 内容替换掉
"version": "{{version}}", // 用用户填的 version 内容替换
"description": "{{description}}",
"main": "main.js",
"scripts": {"test": "echo \"Error: no test specified\"&& exit 1"},
"repository": {
"type": "git",
"url": "git+https://github.com/ZhengMaster2020/template-cli.git"
},
"keywords": [],
"author": "{{author}}",
"license": "ISC",
"bugs": {"url": "https://github.com/ZhengMaster2020/template-cli/issues"},
"homepage": "https://github.com/ZhengMaster2020/template-cli#readme"
}
5. 终端现实的美化
在用户输入答案之后,开始下载模板,这时候使用 ora 来提示用户正在下载中。
const ora = require('ora');
// 开始下载
const spinner = ora('正在下载模板...');
spinner.start();
// 下载失败调用
spinner.fail();
// 下载成功调用
spinner.succeed();
然后通过 chalk 来为打印信息加上样式,比如成功信息为绿色,失败信息为红色,这样子会让用户更加容易分辨,同时也让终端的显示更加的好看。
const chalk = require('chalk');
console.log(chalk.green('项目创建成功'));
console.log(chalk.red('项目创建失败'));
除了给打印信息加上颜色之外,还可以使用 log-symbols 在信息前面加上 √ 或 × 等的图标
const chalk = require('chalk');
const symbols = require('log-symbols');
console.log(symbols.success, chalk.green('项目创建成功'));
console.log(symbols.error, chalk.red('项目创建失败'));
6. 完整内容
#!/usr/bin/env node
const program = require('commander')
const chalk = require('chalk')
const download = require('download-git-repo')
const inquirer = require('inquirer')
const fs = require('fs')
const ora = require('ora')
const symbols = require('log-symbols')
const handlebar = require('handlebars')
program
.version('1.0.0', '-V --version')
.usage('<command> [options]')
program
.command('create <app-name>')
.description('create a new project powered by project-cli-service')
.option('-d, --default', 'Skip prompts and use default preset')
.action((name) => {console.log(chalk.blue(`${name}项目正在创建中...`))
if (!fs.existsSync(name)) {
inquirer
.prompt([{
name: 'version',
message: '请输入项目版本',
default: '1.0.0'
},{
name: 'description',
message: '请输入项目描述信息',
default: '这是一个自定义脚手架生成的项目'
},{
name: 'author',
message: '请输入作者名称',
default: ''
}])
.then(answer => {
const url ='github.com:ZhengMaster2020/template-cli#master'
const spinner = ora(` 正在下载模板,源地址:${url}`)
spinner.start()
download(url, name, {clone: true}, function (err) {if (err) {spinner.fail()
console.log(symbols.error, chalk.red(err))
} else {spinner.succeed()
const fileName = name+'/package.json'
const meta = {
name,
version: answer.version,
description: answer.description,
author: answer.author
}
// {{name}}/package.json 路径存在时
if (fs.existsSync(fileName)) {const content = fs.readFileSync(fileName).toString()
const resultContent = handlebar.compile(content)(meta)
fs.writeFileSync(fileName, resultContent)
}
console.log(symbols.success, chalk.green('项目初始化成功'))
console.log(symbols.info, chalk.yellow('cd'+' '+ name))
console.log(symbols.info, chalk.yellow('npm run serve'))
console.log(symbols.info, chalk.yellow('npm run build'))
}
})
})
} else {console.log(symbols.error, chalk.red('项目已存在'))
}
})
program.parse(process.argv)
最终现实
执行 master-cli create my-cli
会在当前打开的终端目录下创建一个 my-cli
文件夹,这个文件夹就是我们的脚手架生成的项目。
zhengmaster MINGW64 /f/Front-end/vue-project/cm-cli
$ master-cli create my-cli
my-cli 项目正在创建中...
? 请输入项目版本 1.0.0
? 请输入项目描述信息 这是一个自定义脚手架生成的项目
? 请输入作者名称
√ 正在下载模板,源地址:github.com:ZhengMaster2020/template-cli#master
√ 项目初始化成功
i cd my-cli
i npm run serve
i npm run build
项中遇到的坑
// 在顶部添加这句:
#!/usr/bin/env node -- 这种用法是为了防止操作系统用户没有将 node 装在默认的 /usr/bin 路径里。当系统看到这一行的时候,首先会到 env 设置里查找 node 的安装路径,再调用对应路径下的解释器程序完成操作。//download-git-repo 踩坑(路径错误导致下载模板失败 --git clone status 128)
// 从 github 上下载所需得 template 下载地址不是你复制得 https://github.com/xxx/xxx.git
// 正确写法:download('github.com:ZhengMaster2020/template-cli#master', name, {clone: true}, (err) => {console.log(err ? 'Fail' : 'Success')
})
// 还有一种简写:ZhengMaster2020/express-tpl#template-cli#master
//#master 为模板所在的分支
本脚手架只是简单完成了一个 create 命令来创建一个项目,更多的功能日后更新。。。
我的 GitHub:https://github.com/ZhengMaster2020
更多脚手架的内容请自行参考:
vue-cli 官方网站:https://cli.vuejs.org/zh/
vue-cli 源码与原理实现:https://kuangpf.com/vue-cli-analysis/