nodejs开发命令行工具,过程比较简单,但是一套完整的命令行程序开发过程还是需要功夫的,网上的资料大多比较零散,这篇文章本教程旨在整合完整的开发过程。 npm有很多命令行开发相关的包,比如minimist、optimist、nopt、commander.js、yargs等,用法和效果都差不多。其中TJ的commander和yargs用的比较多。本文基于指挥官。你可以参考这个教程。yargs教程可以参考阮大神或者这篇文章。 另外,一个完整的命令行工具开发还需要了解process、shelljs、path、linebyline等模块,这些都是node基础模块或者一些简单的模块,很简单,就不说了更多的。使用回调函数处理异步也需要对Promise和Generator函数有一点了解。这是教程:i5ting大师《深入浅出js(Node.js)异步流程控制》和阮大神异步编程教程和诺言小人书。另外,如果想尝试ES7stage3的async/await异步解决方案,可以参考这篇教程。async/await解决方案需要babel转码,这里是教程。我喜欢async/await(哪个节点开发者不喜欢它?)但我不喜欢它。另外,async/await本身就是Promise的语法糖,所以我没有选择使用它。据江湖消息,nodejs将于今年晚些时候(10月份?)支持async/await,非常期待。 以下是文末示例中用到的一些依赖。"dependencies":{"bluebird":"^3.4.1","co":"^4.6.0","colors":"^1.1.2","commander":"^2.9.0","dox":"^0.9.0","handlebars":"^4.0.5","linebyline":"^1.3.0","mkdirp":"^0.5.1"} 这里使用bluebirdPromise,TJ大神的co用于执行Generator功能,handlebars为模板,linebyline用于逐行读取文件,colors用于美化输出,mkdirp用于创建目录,教程中的例子是一个工具,可以自动生成数据库和API接口的markdown文档,并修改githooks为项目的每次commit自动更新文档,借助TJ大神的dox模块。 所有推荐教程/教材仅供参考,自行选择阅读。安装Node 各个操作系统下的安装见Nodejs官网。安装完成后,使用node-v或者whichnode等命令测试是否安装成功。这是命令行开发中非常有用的命令。使用which命令保证你的系统中没有同名的命令行工具,比如whichcommandName。例如,whichtestdev命令返回空白,这意味着testdev命令名还没有被使用过。初始化并新建一个.js文件,这是你的命令要执行的主要程序入口文件,比如testdev.js。在文件的第一行添加#!/usr/bin/envnode表示系统在运行该文件时使用node作为解释器,相当于nodetestdev.js命令。初始化package.json文件,根据提示信息使用npminit命令创建,或者使用npminit-y默认设置创建。创建完成后,需要修改package.json文件的内容,添加"bin":{"testdev":"./testdev.js"}这个信息是用来告诉npm的路径和名称你的命令(testdev)要执行的脚本文件,这里我们指定testdev命令的执行文件为当前目录下的testdev.js文件。为了方便测试,添加代码console.log('helloworld');到testdev.js文件。这个只是用来测试环境是否搭建成功。更复杂的程序逻辑和流程需要根据实际情况编写和测试。 使用npmlink命令可以在本地安装新建的包,然后可以使用testdev运行命令。正常的话控制台会打印helloworldcommander TJ的commander很简洁,README.md里面已经把使用方法写的很清楚了。这是示例中的代码:constprogram=require('commander'),co=require('co');constappInfo=require('./../package.json'),asyncFunc=require('./../common/asyncfunc.js');program.allowUnknownOption();program.version(appInfo.version);program.command('init').description('初始化当前目录下的doc.json文件').action(()=>co(asyncFunc.initAction));program.command('show').description('Show配置文件状态').action(()=>co(asyncFunc.showAction));program.command('run').description('启动程序').action(()=>co(asyncFunc.runAction));program.command('modifyhook').description('修改项目下的hook文件').action(()=>co(asyncFunc.modifyhookAction));program.command('*').action((env)=>{console.error('Nocommand"%s"',env);});program.on('--help',()=>{console.log('例子:');console.log('');console.log('$createDOC--help');console.log('$createDOC-h');console.log('$createDOCshow');console.log('');});program.parse(process.argv); definite定义了四个命令和个性化帮助,说明交互式命令行流程 commander只实现了命令行参数和响应的一对一固定功能,即一个命令必须对应一个回复,那么如何实现人机交互像npminit或eslint--init这样的命令行与用户进行交互。交互后,根据用户的不同需求,反馈不同的结果。这里需要node内置的进程模块。 这是我实现的一个init命令函数代码:exports.initAction=function*(){try{vardocPath=yieldexists(process.cwd()+'/doc.json');if(docPath){func.initRepl(config.coverInit,arr=>{co(newDoc(arr));})}else{func.initRepl(config.newInit,arr=>{co(newDoc(arr));})}}catch(err){console.warn(err);} 首先检查doc.json文件是否存在,如果有叠加交互,如果没有执行生成交互,try...catch捕获错误。 互动内容配置如下:newInit:[{title:'initConfirm',description:'初始化createDOC,生成doc.json。确认?(y/n)',defaults:'y'},{title:'defaultConfirm',description:'是否使用默认配置。(y/n)',defaults:'y'},{title:'showConfig',description:'是否要显示doc.json的当前配置?(y/n)',defaults:'y'}],coverInit:[{title:'modifyConfirm',description:'doc.json已经存在,初始化会覆盖文件。确认?(y/n)',defaults:'y'},{title:'defaultConfirm',description:'是否使用默认配置。(y/n)',defaults:'y'},{title:'showConfig',description:'是否要显示doc.json的当前配置?(y/n)',defaults:'y'}], 人机交互部分代码为initRepl函数内容如下://初始化命令,人机交互控制exports.initRepl=function(init,func){变量i=1;varinputArr=[];变量len=init.length;process.stdout.write(init[0].description);process.stdin.resume();process.stdin.setEncoding('utf-8');process.stdin.on('data',(chunk)=>{chunk=chunk.replace(/[\s\n]/,'');if(chunk!=='y'&&chunk!=='Y'&&chunk!=='n'&&chunk!=='N'){console.log(config.colors.red('你输入的命令是:'+chunk));console.warn(config.colors.red('请输入正确的命令:y/n'));process.exit();}if((init[i-1].title==='modifyConfirm'||init[i-1].title==='initConfirm')&&(chunk==='n'||chunk==='N')){process.exit();}varinputJson={title:init[i-1].title,值:chunk,};inputArr.push(inputJson);if((len--)>1){process.stdout.write(init[i++].description)}else{process.stdin.pause();func(inputArr);}});} HMI以向用户提问的形式进行交互,根据不同的用户输入产生不同的结果。顺序读取问题列表并记录用户输入结果。如果用户输入n/N,则终止交互,用户输入非法字符(除y/Y/n/N以外)提示输入命令错误文档自动化 文档自动化,数据库文档自动化,靠sequelize手写(根据不同需求自己写逻辑),API文档使用TJ的dox也很简单。由于此处代码与命令行功能关系不大,请读者查看示例网址中的代码。示例地址github地址npm地址