经常使用create-react-app
作为命令行生成项目,总想着自己能配置一个属于自己的命令行工具,很酷炫的样子。最近有时间,终于来实现了
初始化项目 1 2 mkdir y-cli && cd y-cli npm init -y
核心步骤 npm link
新建一个文件 ./bin/yyt.js
,并写一点简单的东西
1 2 3 4 #!/usr/bin/env node console .log('hello,node cli!' )
再在 package.json
里添加 bin
配置,需要指定一个自定义命令,如 y-cli
1 2 3 "bin": { "y-cli": "./bin/yyt.js" }
在命令行里运行:
1 2 3 npm link npm install . -g
可以看到如下,npm将命令作为软链接连接至对应的js文件
1 2 3 4 5 6 7 8 9 xxx@xxxMBP y-cli % sudo npm link npm WARN [email protected] No description npm WARN [email protected] No repository field. up to date in 0.832s found 0 vulnerabilities /usr/local /bin/yyt -> /usr/local /lib/node_modules/y-cli/bin/yyt.js /usr/local /lib/node_modules/y-cli -> /Users/xxx/Documents/code/y-cli
所用到的库 cli需要获取用户的输入,选择并做相应的事情,就需要使用到一些库来帮忙我们更方便的写cli
核心:
commander - 处理核心命令 主要模块,在终端输出各种信息全靠这个模块
download-git-repo - 下载git模块
美化:
chalk — 美化终端字符显示
figlet — 在终端输出大型字符
inquirer — 命令行参数输入交互
shelljs — 在js文件写命令行
ora - 实行loading效果
让我们来把这些库全部安装一遍
commander 根据commander文档 的一些参数,我们来编写脚本 yyt.js
1 2 3 4 5 6 7 8 #!/usr/bin/env node const program = require ('commander' )program.version(require ("../package.json" ).version) program.parse(process.argv)
commander自带 -V
和 -h
两个命令,运行 yyt -h
可看到:
1 2 3 4 5 6 xxx@xxxdeMacBook-Pro y-cli % yyt -h Usage: yyt [options] Options: -V, --version output the version number -h, --help display help for command
.option()
加入自定义命令
1 program.option('-d, --description' , 'yyt 脚手架简介:。。。。' )
可以发现已经加上该命令:
1 2 3 4 5 6 7 xxx@xxxdeMacBook-Pro y-cli % yyt -h Usage: yyt [options] Options: -V, --version output the version number -d, --description yyt 脚手架简介:。。。。 -h, --help display help for command
.command()
自定义命令,
基础格式:
1 program.option('hello <param1> [param2]')
值得注意的是,尖括号<>
里的参数是必填参数,方括号[]
里的参数是可选参数
利用.command()
写一个简单的demo,弄清整个基础流程:
1 2 3 4 5 6 7 8 9 program .command('hello <param1> [param2]' ) .usage('<command> <param1> [param2]' ) .action((param1, param2 )=> { console .log(param1); console .log(param2); })
1 2 3 4 5 6 7 8 ziyouzhiyi@ziyouzhiyideMacBook-Pro y-cli % yyt hello error: missing required argument 'param1' ziyouzhiyi@ziyouzhiyideMacBook-Pro y-cli % yyt hello t t undefined ziyouzhiyi@ziyouzhiyideMacBook-Pro y-cli % yyt hello t y t y
上面这些参数也可通过.option()
来实现,但需要加上-
,如-react
,如以下的例子,利用参数创建项目:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 program.command('create <projectName>' ) .option('-react' ) .option('-vue' ) .action((projectName, option ) => { const type = Object .keys(option)[0 ] switch (type) { case "Vue" : console .log("创建vue项目" ) break ; case "React" : console .log("创建react项目" ) break ; default : console .log("未添加参数,不能下载" ) } })
执行命令后可以看到打印:
1 2 ziyouzhiyi@ziyouzhiyideMacBook-Pro y-cli % yyt create test -react 创建react项目
download-git-repo 下载的步骤把他替换成真的仓库,需要用到 download-git-repo
库
把console.log替换成真实的git仓库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ... case "React" : download( 'direct:[email protected] :ys558/yyt-template.git#main' , projectName, { clone : true }, (err, x) => { if (err) console .log(err) console .log(`${projectName} 项目创建成功` ) } ) break ; default : console .log("未添加参数,不能下载" ) ...
👆的步骤下载的仓库是在命令行下直接完成的,如果要像vue一样能让用户输入生成的,可以用该库,我们把代码改造一下,实际上就是在 .action()
的回调中进行处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 const questions = [ { type: 'input' , name: 'projectName' , message: '请输入项目名称' , }, { type: 'list' , name: 'frameWork' , message: '请选择框架' , default : 'React' , choices: ['React' ,'Vue' ] }, ] program .command('create' ) .action(() => { inquirer.prompt(questions) .then(({projectName, frameWork} ) => { switch (frameWork) { case 'React' : download( 'direct:[email protected] :ys558/yyt-template.git' , projectName, { clone : true }, (err) => { if (err) { console .log(err) return }else { console .log(`${projectName} 项目创建成功` ) } }) break ; case 'Vue' : download( 'direct:[email protected] :ys558/yyt-template.git' , projectName, { clone : true }, (err) => { if (err) { console .log(err) return }else { console .log(`${projectName} 项目创建成功` ) } }) break ; default : break ; } }) })
美化 美化终端字体,如 chalk
:
1 2 3 4 5 6 if (err) { console .log(chalk.bgYellow(err)) return }else { console .log(`${chalk.bgGrey(projectName)} ${chalk.greenBright('项目创建成功' )} ` ) }
figlet
:
1 2 3 4 figlet.defaults({font : 'Standard' }) function logo ( ) { console .log(figlet.textSync('hi yyt!' )) }
这是一个比较麻烦的模块,他是 es module
类型的import
导入模块,而 node 用的是commonjs
的 require
导入模块,而且须在 package.json
里添加 { "type" : "module" }
才能正常加载,下面贴上相关代码:
1 2 3 4 5 6 const spinner = ora("下载初始化模板中..." )spinner.start() spinner.succeed(`${chalk.bgGrey(projectName)} ${chalk.greenBright('项目创建成功' )} ` ) spinner.stop()