从零开发一个node命令行工具

什么是命令行工具?

命令行工具(Cmmand Line Interface)简称cli,顾名思义就是在命令行终端中使用的工具。咱们经常使用的 gitnpmvim 等都是 cli 工具,好比咱们能够经过 git clone 等命令简单把远程代码复制到本地。前端

为何要用cli工具?

和 cli 相对的是图形用户界面(gui),windows 环境中几乎都是 gui 工具,而 linux 环境中则几乎都是 cli 工具,由于二者用户不一样,gui 侧重于易用,cli 则侧重于效率。对于熟悉 gui 和集成开发环境(IDE)的程序员,这彷佛很难理解。毕竟用鼠标点点拽拽,不是更方便么?vue

很遗憾,答案是否认的。gui对于某些简单操做,可能更快、更方便。好比移动文件、阅读邮件或写word文档。但若是你依赖 gui 完成所有工做,你将会错过环境的某些能力,好比使常见任务自动化,或是利用各类工具的所有功能。而且,你也没法将工具组合,建立出定制的宏工具。gui 的好处是所见即所得(what you see is what you get)。缺点是所见即所有所得(what you see is all you get)node

做为注重实效的程序员,你不断的想要执行特别的操做(gui 可能不支持的操做)。当你想要快速地组合一些命令,以完成一次查询或某种其余的任务时,cli 要更为合适。好比:查看上周哪些js文件没有改动过:linux

# cli:
find . -name '*.js' -mtime +7 -print
# gui:
1.点击并转到"查找文件",点击"文件名"字段,敲入"*.js",选择"修改日期"选项卡;
2.而后选择"介于".点击"开始日期",敲入项目开始的日期。
3.点击"结束日期",敲入1周之前的日期(确保手边有日历),点击"开始查找"复制代码

如何开发一个 cli 工具?

基本上,使用任何成熟的语言均可以开发 cli 工具,做为一个前端小白,仍是 JavaScript 比较顺手,所以咱们选用 node 做为开发语言。git

建立一个项目

# 1.建立一个目录:
mkdir kid-cli && cd kid-cli
# 2.由于最终咱们要把cli发布到npm上,因此须要初始化一个程序包: 
npm init
# 3.建立一个index.js文件
touch index.js
# 4.打开编辑器(vscode)
code  .
复制代码

安装 code 命令,运行 VS code 并打开命令面板( ⇧⌘P),而后输入 shell command 找到: Install 'code' command in PATH 就好了。程序员

打开index.js文件,添加一段测试代码:github

#!/usr/bin/env node

console.log('hello world!’) 复制代码

终端运行 node 程序,须要先输入 node 命令,好比vue-cli

node index.js
复制代码

能够正确输出 hello world!,代码顶部的 #!/usr/bin/env node是告诉终端,这个文件要使用 node 去执行。shell

建立一个命令

通常 cli都有一个特定的命令,好比 git,刚才使用的 code 等,咱们也须要设置一个命令,就叫 kid 吧!如何让终端识别这个命令呢?很简单,打开 package.json 文件,添加一个字段 bin,而且声明一个命令关键字和对应执行的文件:npm

# package.json
......
  "bin": {
    "kid": "index.js"
  },
......
复制代码

若是想声明多个命令,修改这个字段就行了。

而后咱们测试一下,在终端中输入 kid,会提示:

zsh: command not found: kid
复制代码

为何会这样呢?回想一下,一般咱们在使用一个 cli 工具时,都须要先安装它,好比 vue-cli,使用前须要全局安装:

npm i vue-cli -g
复制代码

而咱们的 kid-cli 并无发布到 npm 上,固然也没有安装过了,因此终端如今还不认识这个命令。一般咱们想本地测试一个 npm 包,可使用:npm link 这个命令,本地安装这个包,咱们执行一下:

npm link
复制代码

而后再执行

kid
复制代码

命令,看正确输出 hello world! 了。

到此,一个简单的命令行工具就完成了,可是这个工具并无任何卵用,别着急,咱们来一点一点加强它的功能。

查看版本信息

首先是查看 cli 的版本信息,但愿经过以下命令来查看版本信息:

kid -v
复制代码

这里有两个问题

  1. 如何获取 -v 这参数?
  2. 如何获取版本信息?

在 node 程序中,经过 process.argv 可获取到命令的参数,以数组返回,修改 index.js,输出这个数组:

console.log(process.argv)
复制代码

而后输入任意命令,好比:

kid -v -h -lalala
复制代码

控制台会输出

[ '/Users/shaolong/.nvm/versions/node/v8.9.0/bin/node',
  '/Users/shaolong/.nvm/versions/node/v8.9.0/bin/kid',
  '-v',
  '-h',
  '-lalala' ]
复制代码

这个数组的第三个参数就是咱们想要的 -v

第二个问题,版本信息通常是放在package.json 文件的 version 字段中, require 进来就行了,改造后的 index.js 代码以下:

#!/usr/bin/env node
const pkg = require('./package.json')
const command = process.argv[2]

switch (command) {
    case '-v':
    console.log(pkg.version)
    break
    default:
    break
}
复制代码

而后咱们再执行kid -v,就能够输出版本号了。

初始化一个项目

接下来咱们来实现一个最多见的功能,利用 cli 初始化一个项目。

整个流程大概是这样的:

  1. cd 到一个你想新建项目的目录;
  2. 执行 kid init 命令,根据提示输入项目名称;
  3. cli 经过 git 拉取模版项目代码,并拷贝到项目名称所在目录中;

为了实现这个流程,咱们须要解决下面几个问题:

执行复杂的命令

上面的例子中,咱们经过 process.argv 获取到了命令的参数,可是当一个命令有多个参数,或者像新建项目这种须要用户输入项目名称(咱们称做“问答”)的命令时,一个简单的swith case 就显得捉襟见肘了。这里咱们引用一个专门处理命令行交互的包:commander

npm i commander --save
复制代码

而后改造index.js

#!/usr/bin/env node

const program = require('commander')

program.version(require('./package.json').version)
program.parse(process.argv)
复制代码

运行

kid -h
复制代码

会输出

Usage: kid [options] [command]

Options:

  -V, --version  output the version number
  -h, --help     output usage information
复制代码

commander已经为咱们建立好了帮助信息,以及两个参数 -V-h,上面代码中的program.version 就是返回版本号,和以前的功能一致,program.parse 是将命令参数传入commander 管道中,通常放在最后执行。

添加问答操做

接下来咱们添加 kid init 的问答操做,这里有须要引入一个新的包:inquirer, 这个包能够经过简单配置让 cli 支持问答交互。

npm i inquirer --save
复制代码

index.js:

#!/usr/bin/env node

const program = require('commander')
var inquirer = require('inquirer')

const initAction = () => {
    inquirer.prompt([{
        type: 'input',
        message: '请输入项目名称:',
        name: 'name'
    }]).then(answers => {
        console.log('项目名为:', answers.name)
        console.log('正在拷贝项目,请稍等')
    })
}

program.version(require('./package.json').version)

program
    .command('init')
    .description('建立项目')
    .action(initAction)

program.parse(process.argv)
复制代码

program.command 能够定义一个命令,description 添加一个描述,在 --help 中展现,action 指定一个回调函数执行命令。inquirer.prompt 能够接收一组问答对象,type字段表示问答类型,name 指定答案的key,能够在 answers 里经过 name 拿到用户的输入,问答的类型有不少种,这里咱们使用 input,让用户输入项目名称。

运行 kid init,而后会提示输入项目名称,输入后会打印出来。

运行 shell 脚本

熟悉 git 和 linux 的同窗几句话即可以初始化一个项目:

git clone xxxxx.git --depth=1
mv xxxxx my-project
rm -rf ./my-project/.git
cd my-project
npm i
复制代码

那么如何在 node 中执行 shell 脚本呢?只须要安装 shelljs 这个包就能够轻松搞定。

npm i shelljs --save
复制代码

假定咱们想克隆 github 上 vue-admin-template 这个项目的代码,并自动安装依赖,改造index.js,在 initAction 函数中加上执行shell脚本的逻辑:

#!/usr/bin/env node

const program = require('commander')
const inquirer = require('inquirer')
const shell = require('shelljs')

const initAction = () => {
    inquirer.prompt([{
        type: 'input',
        message: '请输入项目名称:',
        name: 'name'
    }]).then(answers => {
        console.log('项目名为:', answers.name)
        console.log('正在拷贝项目,请稍等')
        
        const remote = 'https://github.com/PanJiaChen/vue-admin-template.git'
        const curName = 'vue-admin-template'
        const tarName = answers.name

        shell.exec(`
                git clone ${remote} --depth=1
                mv ${curName} ${tarName}
                rm -rf ./${tarName}/.git
                cd ${tarName}
                npm i
              `, (error, stdout, stderr) => {
            if (error) {
                console.error(`exec error: ${error}`)
                return
            }
            console.log(`${stdout}`)
            console.log(`${stderr}`)
        });
    })
}

program.version(require('./package.json').version)

program
    .command('init')
    .description('建立项目')
    .action(initAction)

program.parse(process.argv)
复制代码

shell.exec 能够帮助咱们执行一段脚本,在回调函数中能够输出脚本执行的结果。

测试一下咱们初始化功能:

cd ..
kid init
# 输入一个项目名称
复制代码

能够看到,cli已经自动从github上拉取vue-admin-template的代码,放在指定目录,并帮咱们自动安装了依赖。

尾声

最后别忘了将你的 cli 工具发布到 npm 上,给更多的同窗使用。

npm publish

复制代码

怎么样,是否是感受看似神秘的命令行开发其实也没有什么技术含量,上文列举的只是 cli 开发的冰山一角,想要开发出强大的 cli 工具,除了须要熟悉 node 和经常使用工具包,更重要的是了解 linux 经常使用命令和文件系统,但愿各位同窗能够受到启发,开发出属于本身的 cli 工具。

安利时间

前端的技术点众多,其中不乏抽象且晦涩的知识点,它们用文字没法很直观的表述出来,因此众多开发者对这些知识点的理解都是是而非,若是咱们经过图画来展现,就会很容易理解。所以Diagram项目但愿开发者能经过这种方式吃透前端技术领域的知识点。 咱们每周会定时更新,每期更新5张技术图解或者思惟导图。

每周对着这些图解来温习,相信用不了多久,你也能成为前端大神!

项目github地址: github.com/Tnfe/TNFE-D…

相关文章
相关标签/搜索