开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 11 天,点击查看活动详情
前言
最近在做自己的脚手架,其中一定少不了命令行交互,commander(指挥官)作为一款优秀的命令行交互的库,是命令行交互首选的库。但是里面的api众多,只有我们理解透彻,才能用起来得心应手。今天我们就来一起探讨commander。
但我觉得写一篇文章应该想想怎么写才能让别人记得住,而不是只是当做字典查阅,你不觉得你翻字典的时候浪费了你很多摸鱼的时间么,有这时间打一局王者他不香么?
commander
安装commander
npm install commander
声明program变量
首先我们要知道program是什么?
program就是一个全局对象,对就是一个对象,一会儿我们会往这个对象上添加属性和方法。你可以把program想成你的秘书
不知道这个图片是不是你心中的理想秘书的样子,哈哈。你在命令行敲的命令都是对你的秘书下命令,现在需要的就是你要告诉你的秘书,你下什么命令她需要去做什么
const { program } = require('commander');
解析命令行参数
代码如下
program.parse(process.argv)
可能有的朋友不太熟悉process是什么?
process是node的全局模块,作用比较直观。可以通过它来获得node进程相关的信息,比如运行node程序时的命令行参数。或者设置进程相关信息,比如设置环境变量。
process.argv 返回一个数组,数组元素分别如下:
- 元素1:node
- 元素2:可执行文件的绝对路径
- 元素x:其他,比如参数等
// print process.argv
process.argv.forEach(function(val, index, array) {
console.log('参数' + index + ': ' + val);
});
运行命令 NODE_ENV=dev node argv.js --env production,输出如下。(不包含环境变量)
参数0: /Users/a/.nvm/versions/node/v6.1.0/bin/node
参数1: /Users/a/Documents/git-code/nodejs-learning-guide/examples/2016.11.22-node-process/argv.js
参数2: --env
参数3: production
所以我们可以理解program.parse(process.argv)就是你的秘书在聆听你的命令
环境准备
新建一个空文件夹->StudyCommander
npm init -y //创建一个package.json
再新建一个bin文件夹,里面新建一个index.js文件
#! /usr/bin/env node
const { program } = require('commander');
const pkg = require('../package.json'); //获取本地的package.json文件
program.version(pkg.version);//指定秘书的版本
program.parse(process.argv);
在package.json文件中用创建bin属性
"bin": {
"secretary": "./bin/index.js"
},
现在在命令行输入npm link命令,把secretary命令注册到全局环境,这时候你只要在命令行输入secretary命令,他都会执行本文件中index.js脚本
npm link
现在咱们在命令行演示一下
secretary -V
-> 1.0.0 // 输入内容为当前秘书的版本
选项
什么是选项?
你可以理解为就是在全局program上定义属性,属性值可以是boolean类型或者字符串
常见boolean类型或者带参数类型选项
program.option('-d, --debug', 'whether to enable debug mode?', false);
program.option('-e, --envName <envName>', 'get environment variable name');
选项参数分析
- 参数一:是选项的名称,一个
-是简写,两个--是全称,如果是带参数的话,那么尖括号参数名 - 参数二:选项描述
- 参数三:选项默认值
如何获取属性
program.opts() // 就能获取全部选项
现在我们测试一下
good和预期一样
命令注册
命令就很简单了,你就可以理解为让你的秘书帮你去做一些事情,我们可以看以下简单的例子
program
.command('clone [source] [destination]')
.description('clone a repository into a newly created directory')
.option('-d,--debug', 'whether to enable debug mode?', false)
.action((source, destination, options) => {
console.log('source', source)
console.log('destination', destination)
console.log('options', options)
})
【注意】 `<>`表示必填,`[]`表示可选
当然我们也可以分开写,我们建议分开写
program
.command('split')
.description('分割字符串')
// 用<>表示是一个必输的字符,[]表示可输
.argument('<string>', 'str to split')
.argument('<array>', 'str to split')
.option('-s, --separator <char>', 'separator character', ',')
.action((str, array, options) => {
console.log('str', str)
console.log('array', array)
console.log('options', options)
})
选项和命令的监听
对options的监听
现在我要对--debug进行监听,如果有输入--debug就执行回调函数,这个回调函数要早于命令的回调函数。
program.on('option:debug', () => {
console.log('开启了debug模式');
})
对command的监听
如果用户输入一个我们没有定义的命令,那么需要给用户提供一个纠错信息,那么就可以使用监听功能。
program.on('command:*', obj => {
console.error('未知的命令:' + obj[0])
})
帮助信息
此时我们在命令行输入
secretary
会显示所有的选项和命令
如果要修改首行提示也就是
Usage: secretary [options] [command]
可以使用usage属性
program.usage("<command> [options]");
此时首行提示就会变成
Usage: secretary <command> [options]
inquirer
但是你有没有感觉这个秘书很生硬呀,必须一次输入全部命令,而且也没有跟人对话的那种功能
这个时候就要引出我们的inquire了
安装
npm i inquirer@8 //因为我们要使用require引入得9版本以下的
prompt
prompt是什么意思
接下来让我们的chatGPT解读一下
其实inquirer的主要方法就是prompt,接下来看一下prompt的用法,prompt方法接受question作为参数,返回一个promise
inquirer
.prompt([
/* Pass your questions in here */
])
.then((answers) => {
// Use user feedback for... whatever!!
})
.catch((error) => {
if (error.isTtyError) {
// Prompt couldn't be rendered in the current environment
} else {
// Something else went wrong
}
});
question参数
- question
Array<Object>参数是一个数组,可以接受多个问题
其中 Object 里面可以塞:
type:【String】提示的类型,默认input,包含input、number、confirm、list、rawlist、expand、checkbox、password、editorname:【String】存储当前问题回答的变量message:【String|Function】提问的问题内容default:【String|Number|Boolean|Array|Function】默认值choices:【Array|Function】列表选项validate:【Function】验证方法,校验输入值是否可行,有效返回true,否则返回字符串表示错误信息(返回false则为默认的错误信息)filter:【Function】对答案进行过滤处理,返回处理后的值transformer:【Function】操作答案的显示效果when:【Function|Boolean】接受答案,根据前面的内容判断是否需要展示该问题pageSize:【Number】在list、rawlist、expand、checkbox这种多选项中,进行分页拆分prefix:【String】修改默认前缀suffix:【String】修改默认后缀askAnswered:【Boolean】已有答案是否强制提问loop:【Boolean】list是否能循环滚动选择,默认true
单选框
program
.command('service [serviceName]')
.description('what would you like some services?')
.action((serviceName) => {
if (!serviceName) {
// 如果serviceName为空,要inquire boss
inquirer.prompt([
{
type: 'list', // 很奇怪,为啥不是radio
name: 'services', //存储当前问题回答的变量
message: 'boss, 你想要什么服务',
choices: ["陪客人吃饭", "订机票出差", "通知经理开会"]
}
]).then((answer) => {
if (answer.services === "通知经理开会") {
console.log("好的, 我这就通知所有部门经理到会议室开会");
}
})
}
})
输入框
现在让我们的秘书询问我们需要什么服务
inquirer.prompt([
{
type: 'input', //
name: 'otherService',
message: 'boss, 还有什么其他吩咐么?',
}
]).then((answer) => {
// 此时不管你的老板说啥,你都说好的
if (answer.otherService) {
console.log("好的");
}
})
通过上面两个例子,我相信聪明的小伙伴你已经学的差不多了
源码
#! /usr/bin/env node
const { program } = require('commander');
const inquirer = require('inquirer');
const pkg = require('../package.json');
program.option('-d,--debug', 'whether to enable debug mode?', false);
program.option('-e, --envName <envName>', 'get environment variable name');
program
.command('clone [source] [destination]')
.description('clone a repository into a newly created directory')
.option('-d,--debug', 'whether to enable debug mode?', false)
.action((source, destination, options) => {
console.log('source', source)
console.log('destination', destination)
console.log('options', options)
})
program
.command('service [serviceName]')
.description('what would you like some services?')
.action((serviceName) => {
if (!serviceName) {
// 如果serviceName为空,要inquire boss
inquirer.prompt([
{
type: 'list', // 很奇怪,为啥不是radio
name: 'services', //存储当前问题回答的变量
message: 'boss, 你想要什么服务',
choices: ["陪客人吃饭", "订机票出差", "通知经理开会"]
}
]).then((answer) => {
if (answer.services === "通知经理开会") {
console.log("好的, 我这就通知所有部门经理到会议室开会");
}
inquirer.prompt([
{
type: 'input', //
name: 'otherService',
message: 'boss, 还有什么其他吩咐么?',
}
]).then((answer) => {
// 此时不管你的老板说啥,你都说好的
if (answer.otherService) {
console.log("好的");
}
})
})
}
})
program.on('option:debug', () => {
console.log('开启了debug模式');
})
program.on('command:*', obj => {
console.error('未知的命令:' + obj[0])
})
program.version(pkg.version);
program.parse(process.argv);