Node.jsでサクッとCLIアプリ作るなら cacjs が便利だよ

こんにちは、Pちゃんです。
この記事は、[DeNA IPプラットフォーム事業部 Advent Calendar 2017](https://qiita.com/advent-calendar/2017/ippf) 18日目の記事です。
[@koooootake](https://qiita.com/koooootake) に勧誘されて、うっかりアドベントカレンダーに登録してしまいました。

---

さて、今日はNode.jsを使ってサクッとCUIアプリケーションを作る方法を紹介していきたいと思います。

## 技術選定

一見、CLIアプリケーションの一つや二つくらい、素のNode.jsでも書くことができそうだと考えがちです。
しかし、サブコマンドの処理やオプションの処理、ヘルプを出すことなどを考えると、意外と面倒だったりします。
そこで、何かしらのフレームワークに乗っかるのが合理的です。

Node.jsでCUIアプリケーションをつくるためのフレームワークは、わりと数年前からデファクトが決まっています。
自分の観測範囲では、以下の2つがよく使われている気がします。

- [minimist](https://github.com/substack/minimist)
- [commander.js](https://github.com/tj/commander.js/)

しかし、今回は敢えて [cacjs](https://github.com/cacjs/cac) を紹介しようと思います。
 [cacjs](https://github.com/cacjs/cac) は、[@egoist](https://github.com/egoist) がつくった、シンプルでパワフルなコマンドラインインターフェイスをつくるためのフレームワークです。

特徴としては、

- 一つのコマンドに関する処理を一箇所にまとめて書けるので、直感的に書きやすい
- プラグイン機構を備えているため、本気でカスタマイズしていくことも可能である

などがあります。

## cacjs でCLIアプリをつくる

### 1. cacjs をインストールする

```sh
$ mkdir cli-app-sample && cd cli-app-sample
$ npm init
$ npm install cac
```

### 2. コードを書く

`index.js` をつくって、以下を書きます。
以下、敬称略です。

```index.js
const cac = require('cac')
const cli = cac()

const managers = ['ラミレス', '中畑', '尾花', '大矢', '牛島']

cli.command('managers', {
  desc: '横浜DeNAベイスターズの今の監督を表示します'
}, () => {
  console.log(`今の監督は${managers[0]}です`)
})

cli.parse()
```

### 3. 実行してみる

実はもうさっき書いた十数行でCLIアプリができています。
試しに `managers` を実行してみます。

```
$ node index.js managers
今の監督はラミレスです
```

次に `--help` とかも試してみます。

```
$ node index.js --help

  index.js 1.0.0

  USAGE

    index.js <command> [options]

  COMMANDS

    managers  横浜DeNAベイスターズの今の監督を表示します

  GLOBAL OPTIONS

    -v, --version  Display version                     [Type: boolean]
    -h, --help     Display help (You're already here)  [Type: boolean]

$ node index.js --version
1.0.0
```

すげえ、簡単すぎる。

### 4. サブコマンドを試してみる

先ほどのサンプルを改造して、過去の監督も取得できるようにしてみたいと思います。
`cli.command` のコールバックで `input` が返ってくるので、それを利用します。

```index.js
const cac = require('cac')

const cli = cac()

const managers = ['ラミレス', '中畑', '尾花', '大矢', '牛島']

cli.command('managers', {
  desc: '横浜DeNAベイスターズの過去の監督を表示します'
}, (input) => {
  if (input[0] > 0) {
    console.log(`${input[0]}人前の監督は${managers[input[0]]}です`)
  } else {
    console.log(`今の監督は${managers[0]}です`)
  }
})

cli.parse()
```

```
$ node index.js managers 1
1人前の監督は中畑です
```

### 5. フラグを試してみる

さて、先ほどのサンプルをさらに改造して、監督のフルネームも取得できるようにしてみようと思います。
今回の場合、`$ node index.js managers 3 --fullname` などとすると、3人前の監督がフルネームで取得できるようにします。
`cli.command` のコールバックで `flag` が返ってくるので、それを利用します。

```index.js
const cac = require('cac')

const cli = cac()

const managers = [
  ['アレックス', 'ラミレス'],
  ['中畑', '清'],
  ['尾花', '高夫'],
  ['大矢', '明彦'],
  ['牛島', '和彦']
]

cli.command('managers', {
  desc: '横浜DeNAベイスターズの過去の監督を表示します'
}, (input, flag) => {

  let managerNum = input[0] || 0

  if (managerNum > managers.length - 1 ) {
    console.log(`${managers.length}人以上前の監督のデータはありません...`)
    process.exit(1)
  }

  let managerName = managers[managerNum][0]

  // ここで flag.fullname を見て true ならフルネームにする
  if (flag.fullname) {
    managerName += ' ' + managers[managerNum][1]
  }

  if (!managerNum) {
    console.log(`今の監督は${managerName}です`)
  } else if (managers.length > managerNum) {
    console.log(`${managerNum}人前の監督は${managerName}です`)
  }
})

cli.parse()
```

```
$ node index.js managers 4 --fullname
4人前の監督は牛島 和彦です
```

## まとめ

- Node.jsで、サクッとCLIアプリ作るなら「cacjs」が便利だ
- ラミちゃんの本名は「アレキサンダー・ラモン・ラミレス・キニョーネス」