库 | 做用 |
---|---|
chalk-pipe | 使用更简单的样式字符串建立粉笔样式方案 |
chalk | 正确处理终端字符串样式 |
Commander.js | 完整的 node.js 命令行解决方案 |
Inquirer.js | 一组通用的交互式命令行用户界面。 |
slash | 系统路径符处理 |
minimist | 解析参数选项 |
dotenv | 将环境变量从 .env文件加载到process.env中 |
dotenv-expand | 扩展计算机上已经存在的环境变量 |
hash-sum | 很是快的惟一哈希生成器 |
deepmerge | 深度合并两个或多个对象的可枚举属性。 |
yaml-front-matter | 解析yaml或json |
resolve | 实现node的 require.resolve() 算法,这样就能够异步和同步地使用require.resolve()表明文件 |
semver | npm的语义版本器 |
leven | 测量两字符串之间的差别<br/>最快的JS实现之一 |
lru cache | 删除最近最少使用的项的缓存对象 |
portfinder | 自动寻找 8000 至65535 内可用端口号 |
ora | 优雅的终端转轮 |
envinfo | 生成故障排除软件问题(如操做系统、二进制版本、浏览器、已安装语言等)时所需的通用详细信息的报告 |
memfs | 内存文件系统与Node's fs API相同实现 |
execa | 针对人类的流程执行 |
webpack-merge | 用于链接数组和合并对象,从而建立一个新对象 |
webpack-chain | 使用链式API去生成简化webpack版本配置的修改 |
strip-ansi | 从字符串中去掉ANSI转义码 |
address | 获取当前机器的IP, MAC和DNS服务器。 |
default-gateway | 经过对OS路由接口的exec调用得到机器的默认网关 |
joi | JavaScript最强大的模式描述语言和数据验证器。 |
fs-extra | 添加了未包含在原生fs 模块中的文件系统方法,并向fs 方法添加了promise支持 |
Acorn | 一个小而快速的JavaScript解析器,彻底用JavaScript编写。 |
zlib.js | ZLIB.js是ZLIB(RFC1950), DEFLATE(RFC1951), GZIP(RFC1952)和PKZIP在JavaScript实现。 |
nodejs交互工具库 -- chalk-pipe和chalknode
nodejs交互工具库 -- commander和Inquirerreact
nodejs交互工具库 -- slash, minimist和dotenv, dotenv-expandwebpack
nodejs交互工具库 -- hash-sum, deepmerge和yaml-front-mattergit
nodejs交互工具库 -- resolve和semvergithub
nodejs交互工具库 -- leven, lru cache和portfinderweb
nodejs交互工具库 -- webpack-merge和webpack-chainjson
nodejs交互工具库 -- strip-ansi, address, default-gateway和joivim
nodejs交互工具库 -- fs-extra, Acorn和zlib
完整的 node.js 命令行解决方案
yarn add commander
为简化使用,Commander 提供了一个全局对象。
const { program } = require('commander'); program.version('0.0.1');
若是程序较为复杂,用户须要以多种方式来使用 Commander,如单元测试等。建立本地 Command 对象是一种更好的方式:
const { Command } = require('commander'); const program = new Command(); program.version('0.0.1');
Commander 使用.option()
方法来定义选项,同时能够附加选项的简介。每一个选项能够定义一个短选项名称(-后面接单个字符)和一个长选项名称(--后面接一个或多个单词),使用逗号、空格或|
分隔。
选项能够经过 Commander 对象的同名属性获取,对于多个单词的长选项,使用驼峰法获取,例如--template-engine
与属性program.templateEngine
关联。选项命名可参考避免选项命名冲突。
多个短选项能够合并简写,其中最后一个选项能够附加参数。 例如,-a -b -p 80
也能够写为 -ab -p80
,甚至进一步简化为 -abp80
。
--
能够标记选项的结束,后续的参数均不会被命令解释,能够正常使用。 若是后续命令也须要设置选项,则能够经过该方式实现,例如:do -- git --version
。
选项在命令行中的顺序不固定,一个选项能够在其余选项以前或以后指定。
有两种最经常使用的选项,一类是 boolean 型选项,选项无需配置参数,另外一类选项则能够设置参数(使用尖括号声明)。若是在命令行中不指定具体的选项及参数,则会被定义为undefined
。
const { program } = require('commander'); program .option('-d, --debug', 'output extra debugging') .option('-s, --small', 'small pizza size') .option('-p, --pizza-type <type>', 'flavour of pizza'); program.parse(process.argv); if (program.debug) console.log(program.opts()); if (program.small) console.log('- small pizza size'); if (program.pizzaType) console.log(`- ${program.pizzaType}`);
经过program.parse(arguments)
方法处理参数,没有被使用的选项会存放在program.args
数组中。
node commander -d -s -p 123
{ debug: true, small: true, pizzaType: '123' }
- small pizza size
- 123
const { program } = require('commander'); program .option('-c, --cheese <type>', 'add the specified type of cheese', 'blue'); program.parse(process.argv); console.log(`cheese: ${program.cheese}`);
node commander
cheese: blue
选项的参数使用方括号声明表示参数是可选参数,即传值不是必须的。
const { program } = require('commander'); program .option('-c, --cheese [type]', 'Add cheese with optional type'); program.parse(process.argv); if (program.cheese === undefined) console.log('no cheese'); else if (program.cheese === true) console.log('add cheese'); else console.log(`add cheese type ${program.cheese}`);
node commander
no cheesenode commander -c 123
add cheese type 123
经过.requiredOption
方法能够设置选项为必填。必填选项要么设有默认值,要么必须在命令行中输入,对应的属性字段在解析时一定会有赋值。该方法其他参数与.option
一致。
const { program } = require('commander'); program .requiredOption('-c, --cheese <type>', 'pizza must have cheese'); program.parse(process.argv);
node Commandererror: required option '-c, --cheese <type>' not specified
选项的参数能够经过自定义函数来处理,该函数接收两个参数:用户新输入的参数和当前已有的参数(即上一次调用自定义处理函数后的返回值),返回新的选项参数。
自定义函数适用场景包括参数类型转换,参数暂存,或者其余自定义处理的场景。
自定义函数能够设置选项参数的默认值或初始值(例如参数用list
暂存时须要设置一个初始空集合)。
const { program } = require('commander'); function myParseInt(value, dummyPrevious) { // parseInt takes a string and an optional radix return parseInt(value); } function increaseVerbosity(dummyValue, previous) { return previous + 1; } program .option('-f, --float <number>', 'float argument', parseFloat) .option('-i, --integer <number>', 'integer argument', myParseInt) .option('-v, --verbose', 'verbosity that can be increased', increaseVerbosity, 0) ; program.parse(process.argv); if (program.float !== undefined) console.log(`float: ${program.float}`); if (program.integer !== undefined) console.log(`integer: ${program.integer}`); if (program.verbose > 0) console.log(`verbosity: ${program.verbose}`);
node Commander -f 1e2
float: 100node Commander -i 123
integer: 123node Commander -vvv
verbosity: 3
定义选项时,能够经过使用...
来设置参数为可变长参数。在命令行中,用户能够输入多个参数,解析后会以数组形式存储在对应属性字段中。在输入下一个选项前(-或--开头),用户输入的指令均会被视做变长参数。与普通参数同样的是,能够经过--
标记当前命令的结束。
const { program } = require('commander'); program .option('-n, --number <numbers...>', 'specify numbers') .option('-l, --letter [letters...]', 'specify letters'); program.parse(); console.log('Options: ', program.opts()); console.log('Remaining arguments: ', program.args);
node Commander -n 1 2 3 --letter a b c
Options: { number: [ '1', '2', '3' ], letter: [ 'a', 'b', 'c' ] }
Remaining arguments: []node Commander --letter=A -n80 operand
Options: { number: [ '80' ], letter: [ 'A' ] }
Remaining arguments: [ 'operand' ]
经过.command()
或.addCommand()
能够配置命令,有两种实现方式:为命令绑定处理函数,或者将命令单独写成一个可执行文件(详述见后文)。子命令支持嵌套。
.command()
的第一个参数能够配置命令名称及参数,参数支持必选(尖括号表示)、可选(方括号表示)及变长参数(点号表示,若是使用,只能是最后一个参数)。
使用.addCommand()
向program
增长配置好的子命令。
const { program } = require('commander'); // 经过绑定处理函数实现命令(这里的指令描述为放在`.command`中) // 返回新生成的命令(即该子命令)以供继续配置 program .command('clone <source> [destination]') .description('clone a repository into a newly created directory') .action((source, destination) => { console.log('clone command called'); }); program.parse(process.argv);
node Commander clone ./123
clone command called
其余用法以下
// 经过独立的的可执行文件实现命令 (注意这里指令描述是做为`.command`的第二个参数) // 返回最顶层的命令以供继续添加子命令 program .command('start <service>', 'start named service') .command('stop [service]', 'stop named service, or all if no name supplied'); // 分别装配命令 // 返回最顶层的命令以供继续添加子命令 program .addCommand(build.makeBuildCommand());
使用.command()
和addCommand()
来传递配置的选项。当opts.noHelp
设置为true
时,该命令不会打印在帮助信息里。当opts.isDefault
设置为true
时,若没有指定其余子命令,则会默认执行这个命令
基本经常使用的方法场景就这些了,更完整的用法能够直接查阅文档
一组通用的交互式命令行用户界面。
努力为Node.js(也许还有“CLI Xanadu”)提供一个容易嵌入和漂亮的命令行界面。
注意:
Inquirer.js
提供用户界面和查询会话流。若是您正在搜索一个完整的命令行程序实用程序,那么请查看
commander,
vorpal or
args.
yarn add inquirer
var inquirer = require('inquirer'); 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 when wrong } });
inquirer.prompt(questions) -> promise
启动提示界面(查询会话)
Rx.Observable
实例)inquirer.registerPrompt(name, prompt)
在name下注册提示插件
inquirer.createPromptModule() -> prompt function
建立一个自包含的查询器模块。若是您不但愿在覆盖或添加新的提示类型时影响一样依赖于查询器的其余库。
var prompt = inquirer.createPromptModule(); prompt(questions).then(/* ... */);
question object是一个包含问题相关值的 hash
input
- 可能的值 input
, number
, confirm
, list
, rawlist
, expand
, checkbox
, password
, editor
name
(后跟冒号)。numbers
、strings
或包含name
的objects
(在列表中显示)、value
(保存在answers hash中)和short
属性(在选择后显示)的对象。选择数组还能够包含一个分隔符。true
,不然返回错误消息(String
)。若是返回 false
,则提供一个默认的错误消息。list、rawList、expand
或checkbox
时将呈现的行数。默认状况下,选择(若是定义为函数)、validate
, filter
and when
能够异步调用函数。要么返回一个promise,要么使用 this.async()
来得到一个回调,您将使用最终值调用它。
{ /* Preferred way: with promise */ filter() { return new Promise(/* etc... */); }, /* Legacy way: with this.async */ validate: function (input) { // Declare function as asynchronous, and save the done callback var done = this.async(); // Do async stuff setTimeout(function() { if (typeof input !== 'number') { // Pass the return value in the done callback done('You need to provide a number'); return; } // Pass the return value in the done callback done(null, true); }, 3000); } }
在每一个提示符中包含客户端答案的键/值散列。
name
属性Value (取决于提示)
confirm
: (Boolean)input
: 用户输入(若是定义了过滤器,则进行筛选) (String)number
: 用户输入(若是定义了过滤器,则进行筛选) (Number)rawlist
, list
: 选择的选择值(若是没有指定值,则为name)(String)一个分隔符能够添加到任何选择数组:
// In the question object choices: [ "Choice A", new inquirer.Separator(), "choice B" ] // Which'll be displayed this way [?] What do you want to do? > Order a pizza Make a reservation -------- Ask opening hours Talk to the receptionist
构造函数接受一个兼性字符串值,它将用做分隔符。若是省略,分隔符将是--------
。
Separator实例的属性类型等于separator
。这将容许使用查询器接口的工具检测列表中的分隔符类型。
注意: 容许在方括号([])中写入的选项是可选的。别人是必需的。
{type: 'list'}
使用 type
, name
, message
, choices
[, default
, filter
, loop
] 属性. (注意,默认值必须是数组中的选择索引或选择值)
var inquirer = require('inquirer'); inquirer .prompt([ { type: 'list', name: 'theme', message: 'What do you want to do?', choices: [ 'Order a pizza', // 分隔符 new inquirer.Separator(), // 禁止选择 { name: 'Contact support', disabled: 'Unavailable at this time', }, 'Talk to the receptionist', ], }, { type: 'list', name: 'size', message: 'What size do you need?', choices: ['Jumbo', 'Large', 'Standard', 'Medium', 'Small', 'Micro'], filter: function (val) { return val.toLowerCase(); }, }, ]) .then(answers => { console.log(JSON.stringify(answers, null, ' ')); })
{type: 'rawlist'}
使用type
, name
, message
, choices
[, default
, filter
, loop
] 属性. (注意,默认值必须是数组中的选择索引)
var inquirer = require('inquirer'); inquirer .prompt([ { type: 'rawlist', name: 'theme', message: 'What do you want to do?', choices: [ 'Order a pizza', new inquirer.Separator(), 'Ask opening hours', ], }, { type: 'rawlist', name: 'size', message: 'What size do you need', choices: ['Jumbo', 'Large', 'Standard', 'Medium', 'Small', 'Micro'], filter: function (val) { return val.toLowerCase(); }, }, ]) .then(answers => { console.log(JSON.stringify(answers, null, ' ')); })
{type: 'expand'}
使用type
, name
, message
, choices
[, default
] 属性. (注意,默认值必须是数组中的选择索引。若是没有提供默认键,那么帮助将被用做默认选项)
注意,选择对象将接受一个名为key的额外参数,用于展开提示符。该参数必须是单个(小写)字符。h选项是由提示符添加的,不该该由用户定义。
var inquirer = require('inquirer'); inquirer .prompt([ { type: 'expand', message: 'Conflict on `file.js`: ', name: 'overwrite', choices: [ { key: 'y', name: 'Overwrite', value: 'overwrite', }, { key: 'a', name: 'Overwrite this one and all next', value: 'overwrite_all', }, ], }, ]) .then(answers => { console.log(JSON.stringify(answers, null, ' ')); })
{type: 'checkbox'}
使用type
, name
, message
, choices
[, filter
, validate
, default
, loop
] 属性. default
指望为选中选项值的数组。
默认状况下会选中标记为{checked: true}
的选项。
属性disabled
为true的选项将没法选择。若是disabled
是一个字符串,那么这个字符串将被输出到disabled
选项旁边,不然它将默认为“Disabled”
。禁用的属性也能够是一个同步函数,接收当前的答案做为参数,并返回一个布尔值或字符串。
var inquirer = require('inquirer'); inquirer .prompt([ { type: 'checkbox', message: 'Select toppings', name: 'toppings', choices: [ new inquirer.Separator(' = The Meats = '), { name: 'Pepperoni', }, { name: 'Ham', }, new inquirer.Separator(' = The Cheeses = '), { name: 'Mozzarella', checked: true, }, { name: 'Cheddar', }, ], validate: function (answer) { if (answer.length < 1) { return 'You must choose at least one topping.'; } return true; }, }, ]) .then(answers => { console.log(JSON.stringify(answers, null, ' ')); })
{type: 'confirm'}
使用type
, name
, message
, [default
] 属性. default
指望是一个布尔值
var inquirer = require('inquirer'); inquirer .prompt([ { type: 'confirm', name: 'toBeDelivered', message: 'Is this for delivery?', default: false, } ]) .then(answers => { console.log(JSON.stringify(answers, null, ' ')); })
{type: 'input'}
使用type
, name
, message
[, default
, filter
, validate
, transformer
] 属性.
var inquirer = require('inquirer'); var chalkPipe = require('chalk-pipe'); const requireLetterAndNumber = (value) => { if (/\w/.test(value) && /\d/.test(value)) { return true; } return 'Password need to have at least a letter and a number'; }; inquirer .prompt([ { type: 'input', name: 'name', message: "What's your first name", }, { type: 'input', name: 'fav_color', message: "What's your favorite color", transformer: function (color, answers, flags) { const text = chalkPipe(color)(color); if (flags.isFinal) { return text + '!'; } return text; }, }, { type: 'input', name: 'phone', message: "What's your phone number", validate: function (value) { var pass = value.match( /^([01]{1})?[-.\s]?\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4})\s?((?:#|ext\.?\s?|x\.?\s?){1}(?:\d+)?)?$/i ); if (pass) { return true; } return 'Please enter a valid phone number'; }, }, { type: 'password', message: 'Enter a password', name: 'password1', validate: requireLetterAndNumber, }, { type: 'password', message: 'Enter a masked password', name: 'password2', mask: '*', validate: requireLetterAndNumber, }, ]) .then(answers => { console.log(JSON.stringify(answers, null, ' ')); })
请注意,须要mask
来隐藏实际的用户输入。
{type: 'editor'}
使用type
, name
, message
[, default
, filter
, validate
] 属性
在临时文件上启动用户首选编辑器的实例。一旦用户退出编辑器,就会将临时文件的内容做为结果读入。要使用的编辑器是经过读取$VISUAL
或$EDITOR
环境变量肯定的。若是二者都不存在,则使用notepad (Windows上的)或vim (Linux或Mac上的)。
var inquirer = require('inquirer'); var chalkPipe = require('chalk-pipe'); const requireLetterAndNumber = (value) => { if (/\w/.test(value) && /\d/.test(value)) { return true; } return 'Password need to have at least a letter and a number'; }; inquirer .prompt([ { type: 'editor', name: 'bio', message: 'Please write a short bio of at least 3 lines.', validate: function (text) { if (text.split('\n').length < 3) { return 'Must be at least 3 lines.'; } return true; }, }, ]) .then(answers => { console.log(JSON.stringify(answers, null, ' ')); })
显示用户类型的选项列表,与其余包兼容,好比fuzzy(用于搜索)
带有自动完成和其余添加的复选框列表
使用数字键盘和箭头键的可定制的日期/时间选择器
提示在数组中选择添加新元素的索引
简单的提示与命令历史和动态自动完成
提示选择模糊文件/目录。
输入表情符号提示。
Prompt for input chalk-pipe style strings
可搜索的询问者复选框
发出询盘搜索列表
询问者提示您缺少创造性的用户。
用于查询器的S3对象选择器。
自动提交基于您当前的输入,保存一个额外的输入
询问者提示选择文件树中的一个文件或目录
询问者的相似表格的提示。
基本经常使用的方法场景就这些了,更完整的用法能够直接查阅文档