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

给大家介绍一个cli工具

时间:2023-04-03 18:26:37 Node.js

你有没有遇到过在没有vue-cli或者create-react-app这样的脚手架的情况下,需要一个文件一个文件复制一个老项目的配置文件?最近笔者在群里为框架做一套基础的cli工具。通过这篇文章,笔者希望大家可以轻松实现一个属于自己的脚手架工具。原文链接:https://juejin.im/user/57ac15...首先准备工作,我们需要新建一个项目,初始化package.jsonmkdirmy-cli&&cdmy-clinpminit然后我们需要在项目bin文件夹下新建一个项目,在package.json中提供一个bin字段指向我们的bin文件夹,这样我们就可以通过npm实现指令的软链接。"bin":{"mycli":"bin/mycli"},在mycli中,我们需要在头部添加这样一条注释,作用是“指定用哪个解释器执行脚本”。#!/usr/bin/envnodeconsole.log('你好世界');接下来,全局安装我们的包,这样我们就可以在本地直接使用mycli命令了。sudonpminstall-g提供了一个基础模板由于我们要创建一个用于初始化项目的cli,所以项目模板是必不可少的。笔者这里提前准备了一个demo工程目录模板,这里不再赘述。其实写逻辑的核心逻辑很简单。就是通过控制台获取一些用户自定义的选项,然后根据选项从本地或者远程仓库中获取我们事先准备好的模版,将配置写入模版中,最后将模版复制到本地即可.我们在src下添加一个creator.js文件,里面导出了一个Creator类。在这个类中只需要三个简单的方法:init用于初始化,ask用于与命令行交互获取用户选择的数据,write用于调用模板的构造方法执行复制文件和写入数据的任务。classCreator{constructor(){//存储从命令行获取的数据,这里作为demo只需要这两个;this.options={名称:'',描述:'',};}//初始化;init(){}//与命令行交互;ask(){}//复制和写入数据;write(){}}module.exports=Creator;首先对init方法进行改进,在该方法中我们只需要调用ask方法和命令行进行交互,进行一些提示即可(可以使用chalk库来丰富我们的命令行交互颜色)//...init(){console.log(chalk.green('我的cli启动'));控制台日志();this.ask();}//...接下来是ask方法。在这个方法中,我们需要引导用户输入问题,根据提示获取用户的输入。这里使用inquirer的库来与命令行进行交互。//...ask(){//问题constprompt=[];prompt.push({type:'input',name:'name',message:'请输入项目名称',validate(input){if(!input){return'请输入项目名称!';}if(fs.existsSync(input)){return'项目名称已重复!'}returntrue;}});prompt.push({type:'input',name:'description',message:'请输入项目描述',});//返回承诺returninquirer.prompt(prompt);}//...修改刚才的init方法,将ask方法改为Promise转账。init(){console.log(chalk.green('myclistart'));控制台日志();this.ask().then((answers)=>{this.options=Object.assign({},this.options,answers);console.log(this.options);});}现在让我们转到命令行试试,修改bin/mycli文件,然后运行mycli命令。#!/usr/bin/envnodeconstCreator=require('../src/creator.js');constproject=newCreator();项目.init();与用户交互并获取数据后,我们要做的就是调用write方法执行复制构造。考虑到以后可能会增加很多模板目录,我们不妨将每个类型的模板复制构建工作放到模板中的脚本中,增加扩展性,增加一个template/index.js文件。接下来首先按照项目目录结构创建文件夹(注意项目执行目录和项目目录的关系)。module.exports=function(creator,options,callback){const{name,description}=options;//获取当前命令的执行目录,注意与项目目录区分开来constcwd=process.cwd();//项目目录constprojectPath=path.join(cwd,name);constbuildPath=path.join(projectPath,'build');constpagePath=path.join(projectPath,'page');constsrcPath=path.join(projectPath,'src');//创建一个新的项目目录//同步创建目录以避免文件目录错位fs.mkdirSync(projectPath);fs.mkdirSync(buildPath);fs.mkdirSync(页面路径);fs.mkdirSync(srcPath);callback();}然后回到creator.js文件,在Creator中的write中调用这个方法。//...init(){console.log(chalk.green('myclistarted'));控制台日志();this.ask().then((answers)=>{this.options=Object.assign({},this.options,answers);this.write();});}//...write(){console.log(chalk.green('我的cli构建开始了'));consttplBuilder=require('../template/index.js');tplBuilder(this,this.options,()=>{console.log(chalk.green('myclibuildcompleted'));console.log();console.log(chalk.grey(`启动项目:cd${this.options.name}&&npminstall`));});}//...在打开文件复制写入数据之前,我们需要使用到mem-fs和mem-fs-editor这两个库,前者可以帮助我们在内存中创建一个临时文件存储,后者可以以ejs的形式编辑我们的文件。现在在构造函数中初始化商店。constructor(){//创建内存存储conststore=memFs.create();this.fs=memFsEditor.create(store);this.options={名称:'',描述:'',};this.rootPath=path.resolve(__dirname,'../');this.tplDirPath=path.join(this.rootPath,'template');}接下来在Creator中添加copy和copyTpl两个方法,用于直接复制文件和Copyfilesandinjectdata。getTplPath(file){returnpath.join(this.tplDirPath,file);}copyTpl(file,to,data={}){consttplPath=this.getTplPath(file);this.fs.copyTpl(tplPath,to,data);}copy(file,to){consttplPath=this.getTplPath(file);this.fs.copy(tplPath,to);}然后我们按照ejs的语法修改模板中的package.json文件,实现数据注入功能{"name":"<%=name%>","version":"1.0.0","description":"<%=description%>","main":"index.js","scripts":{},"author":"","license":"isc"}回到template/index.js,对模板中的文件进行相应的复制和数据注入操作,最后打印一些可视化信息。module.exports=function(creator,options,callback){const{name,description}=options;//获取当前命令的执行目录,注意与项目目录区分开来constcwd=process.cwd();//项目目录constprojectPath=path.join(cwd,name);constbuildPath=path.join(projectPath,'build');constpagePath=path.join(projectPath,'page');constsrcPath=path.join(projectPath,'src');//创建一个新的项目目录//同步创建目录以避免文件目录错位fs.mkdirSync(projectPath);fs.mkdirSync(buildPath);fs.mkdirSync(页面路径);fs.mkdirSync(srcPath);creator.copyTpl('packagejson',path.join(projectPath,'package.json'),{name,description,});creator.copy('build/build.js',path.join(buildPath,'build.js'));creator.copy('page/index.html',path.join(pagePath,'index.html'));creator.copy('src/index.js',path.join(srcPath,'index.js''));creator.fs.commit(()=>{console.log();console.log(`${chalk.grey(`Createproject:${name}`)}${chalk.green('?')}`);console.log(`${chalk.grey(`创建目录:${name}/build`)}${chalk.green('?')}`);console.log(`${chalk.grey(`创建目录:${name}/page`)}${chalk.green('?')}`);console.log(`${chalk.grey(`创建目录:${name}/src`)}${chalk.green('?')}`);console.log(`${chalk.gray(`创建文件:${name}/build/build.js`)}${chalk.green('?')}`);console.log(`${chalk.grey(`创建文件:${name}/page/index.html`)}${chalk.green('?')}`);console.log(`${chalk.grey(`创建文件:${name}/src/index.js`)}${chalk.green('?')}`);打回来();});}执行mycli命令创建项目,一个简单的cli就完成了。至此,一个简单的cli就完成了,大家可以参考vue-cli、create-react-app等优秀的cli来适当扩展自己的cli工具。