制作 node 命令行!

这篇文章是为了记录使用commander模块制作node命令行工具的经验以作备忘,请善用ctrl + f进行搜索。

基本

下面是几个commander模块最基本的 概念 或者 方法,最简单的命令行可以参考 github - 基本示例。

概念

  • commander 推荐链式调用写法。
  • 命令行工具有关的一般都写在项目根目录的bin目录下
  • 在命令行入口文件开头要加上#!/usr/bin/env node 来指定使用node运行
  • 可以直接以node 文件名.js [子命令] [参数]的形式使用命令行,下文会给出如何全局调用命令行

基本方法

  • version('1.0.0') 设置版本: 接受一个字符串,设置当前工具的版本。
  • description('介绍') 设置介绍: 接受一个字符串,设置为当前命令的介绍。
  • parse(process.argv) 解析参数: 参数固定为process.argv,一般放在末尾。

添加参数

可以使用option()指定一个参数,如下:为命令行指定一个参数-t

program
    .version('1.0.0')
    .description('这是一个命令行工具')
    .option('-t, --test [测试参数]', '输入测试参数')

option()的第二个参数是参数的介绍,而第一个参数很重要,它指定了参数的两种写法、是否必填 以及 参数的名字。下面对第一个参数-t, --test [测试参数]进行下分析:

  • 参数写法 -t, --test
  • 是否必填 <>必填 或者 []选填,
  • 参数的名字 测试参数,这个填什么都行,用于让用户更加了解。

参数使用

当用户输入了一个参数之后该如何使用它呢?这就要用到action()方法了,这个方法指定了用户在调用了命令之后应执行的逻辑。而 用户输入的参数就包含在action回调的第一个参数里

//app.js
program
    .command('config')
    .description('配置')
    .option('-t, --test [测试参数]', '输入测试参数')
    .action(cmd => {
        console.log(cmd)
    })
// 用户输入
> node app.js config -t 123 
// 输出
> {
    ...
    test: 123,
  }

要注意的是,如果这里不指定参数名-t直接追加参数值的话,action回调的参数cmd的值为就会被替换成追加的参数值,如下:

// 用户输入
> node app.js config helloworld 
// 输出
> helloworld

添加子命令

可以通过command()来快速添加一个子命令,该方法接受一个字符串参数,内容为该子命令的名字,并且可以通过链式调用快速完善一个子命令。

program
    .command('config')
    .description('配置')
    .action(cmd => {
        console.log('配置')
    })

全局命令行

配置全局安装非常简单,首先确保命令行的入口文件的第一行是#!/usr/bin/env node,这个很关键。

然后在package.json里添加bin字段,如下:这个auto-git就是全局安装之后你的命令行工具的名字,而后面的"bin/index.js"就是命令行工具的入口了,这个不一定是index.js,自己可以随便指定,但是第一行都要有#!/usr/bin/env node

"bin": {
  "auto-git": "bin/index.js"
},

本地调试

想要本地调试也很简单,直接在 项目根目录 下执行如下命令就好了:

npm install . -g

然后就可以在任意位置使用bin中指定的工具名字来使用命令行了,并且你修改代码后也无需重新安装即可使用。

发布后的安装

和本地调试的安装基本一样,不过.换成了你的package包名,然后就可以正常使用了。

npm install <你的包名> -g

这里有一点需要注意的是,package.json里的name字段指定的是你的包名,而 package.json里的bin字段才是命令行的名字

子命令模式

commander有一个模式叫做 子命令模式,可以把不同的子命令拆分在多个文件里来方便维护。这里写一下用法,可以参考例子 github - 子命令模式。

首先是启用子命令模式,在一般的写法中,使用command()注册子命令之后都会链式调用一个action()来添加调用子命令时触发的逻辑。只要 在使用command()之后没有调用action(),就可以启用子命令模式,如下:

//app.js
#!/usr/bin/env node
const program = require('commander')

program
    .version('1.0.0')
    .description('这是一个命令行工具')
    .option('-t, --test [测试参数]', '输入测试参数')
    .command('add', '添加')
    .parse(process.argv)

可以看到,这里只使用command()注册了子命令而并没有设置其参数和逻辑,那这些内容应该写在哪里呢?子命令的内容应写在同级目录下的<主命令文件名>-<子命令名>.js。例如,上面例子中add子命令的内容就应该写在app-add.js中,如下:

var program = require('commander')

program
    .option('-t, --test [测试]', '测试参数')
    .action(cmd => cmdAction(cmd))
    .parse(process.argv)

function cmdAction(cmd) {
    console.log('添加')
}

这样就可以正常使用子命令了,但是要注意以下两点:

  • 子命令文件中,program链式调用的最后一定要加上parse(process.argv),不然子命令会无法执行且不报任何错误
  • 子命令的文件只能放在同级目录下,没有这个文件的话会报下面这个错误:
    error: app-add(1) does not exist, try --help
    

使用 json 保存配置项

使用json来保存命令行的配置项还是比较常用的,但是类似的文章并没有找到太多,所以这里也简单写一下,可以参考例子 github - 保存配置文件。

这里用fspath模块来读写json文件,并且封装出了下面三个方法供其他文件使用:

  • init(): 放在最开始的地方,用于检测本地有没有设置文件setting.json,没有的话就新建该文件
  • load(): 读取setting.json中的数据并转化为js对象
  • save(): 将接受的参数保存到setting.json
const fs = require('fs')
const path = require('path')

// 设置json保存的位置
const settingFile = path.join(__dirname, 'setting.json')
// 设置的初始模板
const settingTempalte = {
    default: 'default value'
}

module.exports = {
    init: () => {
        try {
            fs.statSync(settingFile)
        }
        catch (e) {
            console.log('设置文件不存在, 已新建')
            save(settingTempalte)
        }
    }, 
    load: () => {
        return JSON.parse(fs.readFileSync(settingFile, 'utf8'))
    }, 
    save: (data) => {
        fs.writeFileSync(settingFile, JSON.stringify(data, null, 4))
    }
}

具体的调用方法可以在 例子 中找到。

跟随 package.json 文件中的版本号

因为commander需要通过version()来设置版本,这样在加上package.json里的版本就要修改两个地方,如果我们想让命令行工具的版本跟随package.json中的版本应该怎么做呢。很简单,也是使用fs模块就可以了。

const fs = require('fs')
const packageData = fs.readFileSync('./package.json', 'utf8')
const packageVersion = JSON.parse(packageData).version

program
    .version(packageVersion)
    ...

参考

  • npm - commander
  • 使用commander.js做一个Nodejs命令行程序

你可能感兴趣的:(制作 node 命令行!)