前言
咱们平时我的项目开发中,常常会有很多相似的代码文件,而咱们在应用的时候也会常常的去复制粘贴。为此我之前也写过一篇文章,探讨过进步开发效率的办法,然而说实话,也并不是很好用。
看现在炽热的前端框架,都有本人的CLI工具,例如Vue CLI,creat-react-app等等,搭建我的项目非常的不便。所以我也在想,要不也实现一个CLI工具,不肯定要和后面几个那样高大上,但只有能进步工作的效率,就值得试一试。
初始化我的项目
首先,咱们要关上CLI界面,用npm初始化一个我的项目:
npm init
package.json
{ "name": "mycli", "version": "1.0.0", "description": "A cli-demo", "main": "index.js", "author": "xmanlin", "license": "MIT"}
外面一些用不上都曾经去掉。
自定义命令
置信很多小伙伴在应用 npm 初始化我的项目的时候,或者在用 Vue CLI 搭建我的项目的时候,会发现它们有一个共同点——都有本人个性化的命令,感觉有点酷炫。那么咱们怎么能力实现本人的命令呢,很简略,在 package.json 增加 bin
:
{ "name": "mycli", "version": "1.0.0", "description": "A cli-demo", "main": "index.js", "bin": { "mycli": "./index.js" }, "author": "xmanlin", "license": "MIT"}
package.json中的 bin
的作用就是能够让设置的 mycli
成为一个可执行命令,而命令所执行的文件就是前面的 index.js
,这些都能够依据本人的想法来定。接着咱们要持续对 index.js
进行批改:
index.js
#!/usr/bin/env nodeconsole.log("执行胜利")
这里的第一行很重要,这里表明 index.js
是 node 可执行文件。
设置实现后,就能够全局装置咱们的CLI工具了:
npm install -g
+ mycli@1.0.0added 1 package from 1 contributor in 0.12s
能够看见全局装置胜利,最初咱们试试咱们的命令:
mycli
输入:
执行胜利
交互式命令行
刚刚小伙伴们在进行命令行操作的时候,应用了不同选项的命令,例如 npm init
、 npm install -g
,其中蕴含一些交互式的,比方在初始化我的项目时, npm init
提供一些输出问答模式的命令。接下来,咱们就来为本人的CLI工具增加这些相似的命令。
咱们能够依赖两个库进行咱们的开发:commander.js和Inquirer.js
- commander.js:残缺的 node.js 命令行解决方案。咱们能够利用它,疾速的编写咱们的命令行,自定义化操作。
- Inquirer.js:是惯例交互式命令行用户接口的汇合,提供给 Node.js 一个不便嵌入,丑陋的命令行接口。咱们能够用来疾速进行交互式命令行的编写。
这两个库的具体用法,这里就过多的介绍,小伙伴们能够点击下面名字的链接去相熟一下,花不了太多工夫,理论用起来也不难,前面那个没有中文Readme,然而不障碍大家会搜寻呀~对不对。
定义选项
咱们首先来实现相似于 npm -v
、node -v
这样相似的命令选项。
首先装置commander.js:
npm install commander
而后引入 commander.js 并对 index.js
进行批改
#!/usr/bin/env nodeconst { program } = require('commander');// 字符串宰割为数组的办法function strToArr(value, preValue){ return value.split(',')}// cli版本program.version(require('./package').version, '-v, --version', 'cli的最新版本');// 设置选项program .option('-d, --debug', '调试一下') .option('-l, --list <value>', '把字符串宰割为数组', strToArr) .action((options, command) => { // 进行逻辑解决 if(options.debug) { console.log("调试胜利") } if(options.list !== undefined) { console.log(options.list) } });// 解决命令行输出的参数program.parse(process.argv);
咱们来试试刚刚设置的命令选项:
mycli -d
输入:
调试胜利
输出:
mycli -l 1,2,3
输入:
[ '1', '2', '3' ]
在commander.js外面曾经给咱们定义好了 --help
选项:
mycli -h
输入:
Usage: index [options]Options: -v, --version cli的最新版本 -d, --debug 调试一下 -l, --list <value> 把字符串宰割为数组 -h, --help display help for command
利用 --help
选项,咱们能够很分明的理解到 mycli 中已有多少命令。
设置子命令
在我的项目开发中,咱们有时会用到相似于 npm run xxx
这样的命令,其中run
就相当于npm的子命令。这里咱们也能够给mycli设置相似的子命令:
const { program } = require('commander');...// 创立文件命令行program .command('create <filename>') .description('创立一个文件') .action((filename) => { console.log(filename) }) ...// 解决命令行输出的参数program.parse(process.argv);
command('create <filename>')
就是创立了一个 mycli 的 create
子命令,前面跟了一个必填参数。
输出:
mycli create file
输入
file
子命令创立胜利。
利用命令行创立我的项目文件
咱们当初可能定义选项,设置命令了。接下来咱们就能够理论做点货色,利用命令行来创立我的项目的文件。
简略的设计一个流程:
创立模板文件
咱们先来创立一个templates文件夹,而后在外面写几个常见的模板文件,这里的模板文件使用到了模板字符串,构造如下:
reactClass.js
module.exports = function (className) { return `import * as React from 'react';export class ${className} extends React.Component{ constructor(props){ super(props); this.state = {} } componentDidMount(){ } render() { return ( <div></div> ) }} ` }
vueTemplate.js
module.exports = function () { return ` <template> <div></div> </template> <script> export default { data() { return {} } methods: { } } </sctipt> <style lang="scss" scoped> </style> `}
index.js
const reactClass = require('./reactClass');const vueTemplate = require('./vueTemplate');module.exports = [ { name: 'reactClass', src: reactClass }, { name: 'vueTemplate', src: vueTemplate }]
模板文件创建实现后,咱们先把它放在一边,待会儿才用的上。
创立交互命令行以及调用模板
当咱们输出 mycli create file
命令后,须要失去下图中的成果,咱们能够手动高低进行抉择,也就是能够被称为交互式的命令。
这里就要用到咱们上文中提到的另外一个库——Inquirer.js
首先必定须要进行装置
npm install inquirer
引入并批改咱们根目录下的index.js:
#!/usr/bin/env nodeconst { program } = require('commander');const inquirer = require('inquirer');// 引入模板文件const templates = require('./templates/index');// 命令行抉择列表let prompList = [ { type:'list', name: 'template', message: '请抉择你想要生成的模板?', choices: templates, default: templates[0] }]...// 创立文件命令行program .command('create <filename>') .description('创立一个文件') .action(async (filename) => { const res = await inquirer.prompt(prompList) console.log(res) })// 解决命令行输出的参数program.parse(process.argv);
接下来咱们在命令行中输出:
mycli create file
就能够失去下面所展现的成果
而后抉择第一个后回车:
能够看到输入了咱们所抉择模板的名字。接下来就是进行理论文件的创立。
创立我的项目文件
创立文件则须要调用node.js的fs相干api,而后批改index.js:
// 解决文件const fs = require("fs");...// 创立文件命令行program .command('create <filename>') .description('创立一个文件') .action(async (filename) => { const res = await inquirer.prompt(prompList) if(res.template === 'reactClass') { templates.forEach((item) => { if(item.name === 'reactClass') { fs.writeFile(`./${filename}.jsx`, item.src(filename), function(err) { if(err) { console.log('创立失败:', err) } else { console.log(`创立文件胜利!${filename}.jsx`); } }) } }) } if(res.template === 'vueTemplate') { templates.forEach((item) => { if(item.name === 'vueTemplate') { fs.writeFile(`./${filename}.vue`, item.src(), function(err) { if(err) { console.log('创立失败:', err) } else { console.log(`文件创建胜利!${filename}`); } }) } }) } }) ...
咱们再次在命令行中输出mycli create file
,而后抉择一个模板。
输入:
创立文件胜利!file.jsx
同时咱们能够看见我的项目根目录上面新增了一个file.jsx文件:
关上file.jsx能够看见文件的类名也进行了相应的填写。不仅如此,因为咱们的mycli是全局装置的,所以能够说在电脑中任何地位,只有咱们输出 mycli create file
,咱们在当前目录下都能获取到file.jsx文件。也就意味着,咱们在开发一些我的项目的时候,不必在复制粘贴,删删改改。
间接一行命令就能搞定了~
更多功能
既然能创立文件,那么是否创立文件夹?答案是必定的,只须要持续增加命令就行:
...// 创立文件夹命令行program .command('create-f <folder>') .description('创立一个文件夹') .action((folder) => { if(fs.existsSync(folder)) { console.log('文件夹已存在') } else { fs.mkdirSync(folder); console.log('文件夹创立胜利') } });...
而后在命令行中输出mycli create-f xxx
,同样能够创立想要命名文件夹。
最初
到此咱们的 mycli 曾经能进行创立文件,创立文件夹等等操作了。跟着敲一遍,肯定会有播种的。后续咱们能够依据我的项目的理论状况编写模板文件,自定义想要的命令,进行必要的拓展。这个就留给大家纵情的施展啦~
有趣味的小伙伴能够关注我的公众号-前端车站,新文章会第一工夫在公众号发。