当前位置: 首页 > 后端技术 > Node.js

烹饪美味的CLI

时间:2023-04-03 18:08:55 Node.js

先写了。其实我很想写个菜谱。苦于厨艺有限,所以标题是骗人的,哈哈^_~今天来说说命令行工具(即CLI:command-lineinterface,下面会用CLI代替冗长的命令行工具名)发展。阅读本文后,您将对从头到尾开发CLI有更全面的了解。您也可以将这篇文章加入书签。当你想开发CLI的时候,回来浏览一下,总能找到你想要的。丹尼尔:花生可乐准备好了,等它开始吧。好了,开始吧,走吧!<( ̄︶ ̄)↗[GO!]>第一步:初始化项目,新建一个空的项目目录(以下是cook-cli的例子,所以这里命名为cook-cli),然后输入命令在目录下进行初始化,过程如下:$mkdircook-cli$cdcook-cli$npminit--yes通过npminit命令将目录初始化为Node.js项目,会生成cook-cli目录下的package.json文件。添加--yes将自动回答初始化过程中提出的所有问题。您可以尝试去掉这个参数,自己回答每个问题。>开启主线:CLI骨架代码项目已经初始化。接下来,我们将添加框架代码并让CLI飞一会儿。作为实现者,我们创建了src/index.js文件,它负责实现CLI的功能逻辑并实际完成工作。代码如下:exportfunctioncli(args){console.log('Ilikecooking');}然后发言人创建bin/cook文件,这是CLI的可执行入口文件和CLI的发言人在可执行环境中。代码如下:#!/usr/bin/envnoderequire=require('esm')(module/*,options*/);require('../src').cli(process.argv);你会发现这里用到了esm模块。它的作用是让我们可以直接使用ECMAScript模块规范在js源码中加载模块,即直接使用import和export。得益于这个模块,上面src/index.js代码可以直接写export。(请在项目根目录下运行npmiesm来安装模块)正式宣布我们有代言人,但我们必须宣传。所以在package.json中加入bin语句,宣告代言人的存在。如下:{..."bin":{"cook":"./bin/cook"},...}>时间预演:本地运行调试在CLI出来之前,本地开发调试必不可少。所以一种方便的调试方式是非常必要的。大牛:开发web应用,我可以通过浏览器调试功能。昨天的CLI怎么样?CLI最终是在终端运行的,所以我们需要先将其注册为本地命令行。方法很简单,只需要在项目根目录运行如下命令:$npmlink这条命令会在本地环境注册一个cookCLI,并将其执行逻辑代码链接到你的项目目录,所以每次修改并保存,立即生效。尝试运行以下命令:$cookDaniel:Nice!但是我还是有问题,我想在vscode中设置断点来调试,这样有时候更容易排查问题你是对的。方法也很简单,在vscode中添加如下配置即可,路径为:Debug>AddConfiguration。根据实际需要调试的命令参数,修改args的值即可。{“配置”:[{“类型”:“节点”,“请求”:“启动”,“名称”:“库克”,“程序”:“${workspaceFolder}/bin/cook”,“args”:["hello"]//填写你要调试的参数}]}>Intentrecognition:inputparameteranalysis插入一个小插曲:虽然你在工作中可能经常会接触到各种CLI,但是了解它们还是很有必要的CLI涉及的一些术语的简单介绍:命令(command)和子命令(subcommand)#cook是命令cook#start是cook的子命令cook启动命令选项(options)#-V是简写模式(shortflag)选项(注意:只有一个字母,多个字母代表多个选项)cook-V#--version是全写模式下的一个选项(长名)cook--version命令参数(argument)#source.js和target.js是cp命令的参数cpsource.jstarget.js其实子命令也是命令的参数ok,从上面的介绍,我们要实现一个CLI,用于输入参数(包括子命令,选项,参数)解析是不可避免的,所以让我们面对它们吧。指挥官:嘿,兄弟,别怕,有我在!是的,兄弟,有你真好。接下来,我们使用模块指挥官来解析输入的参数。过程和例子如下:模块安装npmicommandersrc/index.jsexample...importprogramfrom'commander';exportfunctioncli(args){program.parse(args);}一句话搞定,就是这么简单利落。丹尼尔:录取怎么样?如何使用它?在下面的示例中,我们将使用这些已解析的输入对象对象。所以请耐心等待。>离不开你:版本和帮助版本和帮助信息是CLI必须提供的部分,否则看起来太不专业。让我们看看如何去做。修改src/index.js,代码如下:importprogramfrom'commander';从'../package.json'导入pkg;exportfunctioncli(args){program.version(pkg.version,'-V,--version').usage('<命令>[选项]');program.parse(args);}是通过program.version和usage的链式调用来完成的,还是这么冷门。尝试运行以下命令:$cook-V$cook-h>Addgeneral:Addsubcommand现在我们开始丰富CLI的功能,从添加子命令start开始。它有一个参数food和一个选项--fruit,代码如下:......exportfunctioncli(args){.....program.command('start').option('-f,--fruit','要添加的水果').description('开始烹饪食物').action(function(food,option){console.log(`runstartcommand`);console.log(`argument:${food}`);console.log(`option:fruit=${option.fruit}`);});program.parse(args);}上面的例子演示了如何获取解析后的输入参数,在实际操作中,你可以得到你想要的一切,你想做什么完全取决于你。试试运行子命令:$cookstartpizza-fapple>寻找外援:调用外部命令有时候,我们需要在CLI中调用外部命令,比如npm之类的。execa:轮到我表演了。┏(^ω^)=?moduleinstallnpmiexecasrc/index.jsexample......importexecafrom'execa';exportfunctioncli(args){.....program.command('npm-version').description('显示npm版本').action(asyncfunction(){const{stdout}=awaitexeca('npm-v');console.log('Npmversion:',stdout);});program.parse(args);}以上通过execa调用外部命令npm-v。来吧,打印npm的版本号:$cooknpm-version>方便交流:提供人机交互有时候我们希望CLI可以通过问答的方式与用户进行交互,用户向我们提供输入或者选择。想要的信息。一阵风吹过,Inquirer.js在彩云上驰骋。npmiinquirer模块安装最常见的场景是:文本输入、选项、勾选、单选。示例如下:src/index.jsexample......importinquirerfrom'inquirer';exportfunctioncli(args){......program.command('ask').description('问一些问题').action(asyncfunction(option){constanswers=awaitinquirer.prompt([{type:'input',name:'name',message:'Whatisyourname?'},{type:'confirm',name:'isAdult',message:'Areyouover18yearsold?'},{type:'checkbox',name:'favoriteFrameworks',choices:['Vue','React','Angular'],message:'你最喜欢的框架是什么?'},{type:'list',name:'favoriteLanguage',choices:['Chinese','English','Japanese'],message:'你最喜欢什么语言?'}]);console.log('你的答案:',answers);});program.parse(args);}代码简单,直接上效果图:>减少焦虑:等待提醒,人机交互体验很重要,如果不能马上完成工作,需要及时反馈用户当前的工作进度,减少用户的等待焦虑感。ora和listr并肩而立,步伐整齐,相互靠近。首先玩的是ora模块安装npmiorasrc/index.jsexample......importorafrom'ora';exportfunctioncli(args){......program.command('wait')。description('等待5秒').action(asyncfunction(option){constspinner=ora('等待5秒').start();letcount=5;awaitnewPromise(resolve=>{letinterval=setInterval(()=>{if(count<=0){clearInterval(interval);spinner.stop();resolve();}else{count--;spinner.text=`等待${count}秒`;}},1000);});});program.parse(args);}废话不多说,直接上图:listr紧随其后。moduleinstallnpmilistrsrc/index.jsexample......importListrfrom'listr';exportfunctioncli(args){......program.command('steps').description('一些步骤').action(asyncfunction(option){consttasks=newListr([{title:'Runstep1',task:()=>newPromise(resolve=>{setTimeout(()=>resolve('1完成'),1000);})},{title:'运行第2步',task:()=>newPromise((resolve)=>{setTimeout(()=>resolve('2Done'),1000);})},{title:'运行第3步',task:()=>newPromise((resolve,reject)=>{setTimeout(()=>reject(newError('Oh,mygod')),1000);})}]);awaittasks.run().catch(err=>{console.error(err);});});program.parse(args);}还是不多说了,还是直接上图:>加点颜色:让生活不再单调粉笔:我是Wen艺术青年,我为艺术而生,应该是我<( ̄ ̄ ̄)/模块安装npmichalksrc/index.jsExample.....importchalkfrom'chalk';exportfunctioncli(args){console.log(chalk.yellow('我喜欢做饭'));.....}花花绿绿的CLI,是不是让你心情更愉悦了:>门面装饰:加个边框boxen:这是我的强项,看我的!<( ̄^ ̄)>moduleinstallnpmiboxensrc/index.jsexample......importboxenfrom'boxen';exportfunctioncli(args){console.log(boxen(chalk.yellow('我喜欢烹饪'),{填充:1}));......}好吧,看起来更专业一点:>发布结果:可以发布如果你在范围内发布,比如@daniel-dx/cook-cli。然后在package.json中添加如下配置,让你顺利发布(当然如果你是npm付费会员,这个配置可以省){"publishConfig":{"access":"public"},}临门一脚,启动:npmpublishOK,你的CLI已经对外发布了,现在你可以去https://www.npmjs.com/查看你发布的CLI。>提醒:是时候升级update-notifier了:终于来了,我等花开再说。X﹏X模块安装npmiupdate-notifiersrc/index.js示例......importupdateNotifierfrom'update-notifier';importpkgfrom'../package.json';exportfunctioncli(args){checkVersion();......}functioncheckVersion(){constnotifier=updateNotifier({pkg,updateCheckInterval:0});如果(notifier.update){notifier.notify();}}本地调试,我们将本地CLI降级一个版本,修改package.json版本为0.0.9,然后运行cook查看效果:o( ̄︶ ̄)o完美!以上详细介绍了开发CLI的一些必要或常用的步骤。当然,如果你只是想快速开发一个CLI,就像有些领导常说的:别跟我说过程,我只要结果。然后你可以开箱即用地使用专门为开发CLI而设计的框架,例如oclif。作为程序员,我们还是需要投入一些时间和精力来研究解决方案的来龙去脉,古今中外的了解,这样才能走得更远。好了,今天就到这里,再见我的朋友们!差点忘了附上例子的源码:https://github.com/daniel-dx/...┏(¥0¥)┛ByeBye!