随着NodeJs的不断发展,前端的东西越来越多,Vue脚手架,React脚手架等等,一系列的东西脱颖而出,进入人们的视野在视野中,这些脚手架工具还只是处于应用阶段。一直没想过脚手架是怎么实现的?vueinitwebpack项目名是如何通过这样一条命令来创建项目的,它最重要的模块就是我们今天要说的Commander。Commander模块也是国外TJ大佬写的项目地址:CommanderCommanderbasicusageCommander文档写的很详细。跟着文章详细学习。Commander是一个Nodejs模块,需要在Node环境下运行。使用前请确认是否安装了Node环境。安装。安装依赖npminstallcommander--saveOptions解析Commander模块下有一个option方法定义commander的options选项,作为option文件使用。varprogram=require('commander');program.option('-g,--git[type]','Add[marble]','Angie').parse(process.argv);console.log("process.argv",process.argv)console.log("program.args",program.args)console.log('你点了一个披萨:');if(program.git)console.log('-git');console.log('-%sgit',program.git);上面的例子会从process.argv中解析出args和options,然后将剩下的参数(undefinedparameters)赋值给commander对象的args属性(program.args),program.args是一个数组。打印出process.argv和program.args,查看输出结果如下,使用如下命令运行文件:nodeindex-gtypeAaronprocess.argv['F:\\node\\installation\\node.exe','C:\\Users\\wo_99\\Desktop\\cli-dome\\index','-g','type','Aaron']program.args['Aaron']选项方法可以接收三个参数:自定义标识必须分为长标识和短标识,中间用逗号、竖线或空格隔开;标志后面可以跟强制参数或可选参数,前者包含在<>中,后者包含在[]中。省略选项说明,不报错:使用--help命令时,显示标志说明。默认值可以省略:当没有传入参数时,使用默认值。如果我们执行nodeindex-g,得到的结果是Angiegit的第三个参数,在相应的位置填入默认值。除了上面的,你还可以使用下面的命令://执行-g参数a//执行-b参数snode索引-ga-bs//执行-g和-b并传递一个参数给-g//-b参数暂时不知道怎么传入nodeindex-gbaversion选项。默认情况下,调用版本会将-V和--version选项添加到命令中。当存在这些选项中的任何一个时,该命令将打印版本号并退出。varprogram=require('指挥官');program.version('0.0.1').parse(process.argv);//执行命令//nodeindex-V//输出结果//0.0.1如果你想让程序响应-v选项而不是-V选项,只需使用与选项方法相同的语法将自定义标志传递给版本方法。版本标志可以命名为任何值,但需要长选项。varprogram=require('commander');program.version('0.0.1','-e,--version');command添加命令名这个方法可以让你使用命令行来执行一个命令,即,一段:varprogram=require('commander');program.version('0.0.1','-V,--version').command('rm
').action(function(dir,cmd){console.log('remove'+dir+(cmd.recursive?'recursively':''))});program.parse(process.argv);//执行命令//节点索引rm/aaa-r//输出结果//去掉/aaa递归的意思是:代码中consolecontentcommand函数接收三个参数:命令名必须:命令后面可以跟<>或[]中包含的参数;命令的最后一个参数可以是可变的,像上面的例子一样在数组后面加上...符号;命令后传入的参数将传递给动作的回调函数和program.args数组。命令说明可以省略:如果存在,且不显示callaction(fn),则启动子命令程序,否则报错。配置选项可以省略:可以配置noHelp、isDefault等,在执行命令时验证命令的选项。任何未知选项都会报错。但是,如果未定义操作,则不会针对基于操作的命令验证选项。varprogram=require('指挥官');程序.version('0.0.1','-V,--version').command('rm').option('-r,--recursive','递归删除').action(function(dir,cmd){console.log('remove'+dir+(cmd.recursive?'recursively':''))});program.parse(process.argv);console.log(program.args)//执行命令//节点索引rm/aaa-r/ddd//输出结果//递归删除/ddd//['/aaa']helpOptionHelp提供帮助信息varprogram=require('commander');program.version('0.1.0').helpOption('-h,--HELP').option('-f,--foo','启用一些foo').option('-b,--bar','enablesomebar').option('-B,--baz','enablesomebaz');program.parse(process.argv);//执行命令//nodeindex-hornodeindex--HELP/*output*Options:*-V,--version输出版本号*-f,--fooenablesomefoo*-b,--barenablesomebar*-B,--bazenablesomebaz*-h,--HELP输出使用信息*/输出帮助信息并立即退出。可选的回调cb允许在显示之前对帮助文本进行后处理。helpOption还提供了一个长名,-h,--HELP前面是一个短名,后面是一个长名,两者都可以。版本的使用类似。description命令描述用于描述命令,即对命令的描述。上面说了,命令的第二个参数也是命令的描述。当它与描述同时存在时,它将优先于第二个参数的描述,并且描述将作为全局描述显示在最顶部。此描述仅在使用-HELP时可见。varprogram=require('commander');program.version('0.0.1','-V,--version').command('rm',"argisdescription").description("this是描述").option('-r,--recursive','递归删除').action(function(dir,cmd){console.log('remove'+dir+(cmd.recursive?'递归':''))});program.parse(process.argv);//执行命令//节点索引-h//输出结果/*thisisdescriptionOptions:-V,--version输出版本号-r,--recursiveRemoverecursively-h,--helpoutputusageinformationCommands:rmargisdescriptionhelp[cmd]displayhelpfor[cmd]*/从上面的输出可以看出,rm命令的最终描述isargisdescription,如果删除第二个参数,则输出thisisdescription。自定义事件监听器用于捕获option和command,使用时会触发该函数。varprogram=require('commander');program.version('0.0.1','-V,--version').command('rm',"argisdescription").option('-r,--recursive','递归删除').option('-g,--git[type]','Add[marble]','Angie').option('-a,--am',"ampm").action(()=>{console.log(123)});program.on('option:am',function(){console.log("on:am")});program.on('option:recursive',function(){console.log("option:recursive")});program.on('command:rm',function(){console.log("command:rm")});program.on('option:git',function(){console.log("option:git")});program.on('command:*',function(){console.log(987)控制台.error('无效命令:%s\n请参阅--help以获取可用命令列表。',program.args.join(''));process.exit(1);});program.on('--help',function(){console.log('****************');console.log('Examples:');console.log('****************');console.log('$deployexecsequential');console.log('$de策略执行异步');});程序.parse(process.argv);分别执行command和option,会依次触发对应的函数,但是command:*是什么时候触发的呢?已经定义了command和option,但是没有事件捕获会触发指定的参数或者没有参数时,传入参数也会触发函数。如果上面没有这个命令,command对应的事件:*willbetriggered,option:closelyfollowed是捕获事件的option的长名。开发本地模块,创建工程文件如下:├─bin│└─init-project.js├─lib│└─install.js└─templates└─dome1创建工程目录后,安装如下依赖包:chalkcommanderfs-extrapaththrough2vinyl-fswhich命令:npminstall--save-devchalkcommanderfs-extrathrough2vinyl-fswhichpath首先在init-project.js的第一行添加#!/usr/bin/env节点,用于指定脚本Program的执行,这里的Node可以使用!/usr/bin/node,如果用户将Node安装在非默认路径下,将找不到Node。所以~最好选择env环境变量来寻找Node安装目录。初始化项目.js#!/usr/bin/envnode//导入依赖varprogram=require('commander');varvfs=require('vinyl-fs');varthrough=require('through2');constchalk=require('粉笔');constfs=require('fs-extra');constpath=require('path');//定义版本号和命令选项program.version('1.0.0').option('-i--init[name]','initaproject','myFirstProject')program.parse(process.argv);if(program.init){//获取待构建项目的根目录varprojectPath=path.resolve(program.init);//获取要构建的项目名称varprojectName=path.basename(projectPath);console.log(`开始在${chalk.green(projectPath)}中初始化一个项目`);//根据要构建的项目名称创建文件夹fs.ensureDirSync(projectName);//获取本地模块下的demo1目录varcwd=path.join(__dirname,'../templates/demo1');//从demo1目录中读取除node_modules目录外的所有文件并过滤处理vfs.src(['**/*','!node_modules/**/*'],{cwd:cwd,dot:true}).pipe(through.obj(function(file,enc,callback){if(!file.stat.isFile()){returncallback();}this.push(file);返回n回调();}))//将从demo1目录读取的文件流写入之前创建的folder.pipe(vfs.dest(projectPath)).on('end',function(){console.log('Installingpackages...')//将节点工作目录更改为构建项目的根目录process.chdir(projectPath);//执行安装命令require('../lib/install');}).resume();}install.js//引入依赖varwhich=require('which');constchalk=require('chalk');varchildProcess=require('child_process');//启动子进程执行npminstall命令函数runCmd(cmd,args,fn){args=args||[];varrunner=childProcess.spawn(cmd,args,{stdio:'inherit'});runner.on('close',function(code){if(fn){fn(code);}})}//在系统中查找用于安装依赖包的命令functionfindNpm(){varnpms=['tnpm','cnpm','npm'];for(vari=0;i