動かざることバグの如し

近づきたいよ 君の理想に

Node.jsでコマンドライン引数処理を行うならcommand-line-argsがよさげ

最近だとコマンドラインツールはGolangで書かれることが多くなっていたが、Nodejsでコマンドラインで処理を受け付けたい、ということも全然あると思う。

そのときに必ず当たる壁がコマンドライン引数の処理である。

引数のパース、必須項目の扱い、ヘルプの表示等々自前で実装するのは色々ツラい。

そこでargvを使ってる人も多いと思う。が、最終更新が6年前でそれ以降一切メンテがされていない。。。流石に不安になる。

ってことで代役を探していたら、「command-line-args」というよさげなライブラリを見つけた。

インストール

yarn add command-line-args

or

npm install command-line-args

サンプル

const commandLineArgs = require('command-line-args');

const optionDefinitions = [
  {
    name: 'verbose',
    alias: 'v',
    type: Boolean
  },
  {
    name: 'src',
    type: String,
  },
  {
    name: 'timeout',
    alias: 't',
    type: Number,
    defaultValue: 3
  }
];
const options = commandLineArgs(optionDefinitions);

console.log(options);

で、実行するとoptionsに値が入っている

$node index.js --src image.png -t 10
{ src: 'image.png', timeout: 10 }

あとは公式ドキュメントを見ればなんとかなる(投げやりのスタイル

デフォルトオプションを定義したい

defaultValueを使う。さっきの例だと、timeoutのdefaultValueを3にしたのでtimeoutを指定しないで実行すると、

node index.js --src image.png
{ timeout: 3, src: 'image.png' }

となる。

1つのオプションで複数受け付けられるようにしてほしい

multipleをつける。さっきの例でいうと

{
  name: 'src',
  type: String,
  multiple: true
},

にすると

node index.js --src image1.png --src image2.png
{ timeout: 3, src: [ 'image1.png', 'image2.png' ] }

とできる。逆にmultiple指定指定してない状態で複数指定すると、 ALREADY_SET: Singular option already set [src=image1.png] のように例外エラーになる。

ヘルプ(usage)を表示したい

command-line-argsの特徴的な点として、それ自体には表示機能は実装されていない点。

じゃあどうすんのって話だが同じ作者のcommand-line-usageを入れればおk

yarn add command-line-usage

さっきの例をベースに改良したバージョンが以下

const commandLineArgs = require('command-line-args');
const commandLineUsage = require('command-line-usage');

const optionDefinitions = [
  {
    name: 'verbose',
    alias: 'v',
    type: Boolean
  },
  {
    name: 'src',
    type: String,
    defaultOption: true,
    multiple: true,
    description: 'file path'
  },
  {
    name: 'timeout',
    alias: 't',
    type: Number,
    defaultValue: 3,
    description: 'convert timeout'
  },
  {
    name: 'help',
    alias: 'h',
    type: Boolean,
    description: 'show help'
  }
];

const sections = [
  {
    header: 'Sample app',
    content: 'this is sample app for command-line-args'
  },
  {
    header: 'Options',
    optionList: optionDefinitions
  }
];

const options = commandLineArgs(optionDefinitions);
if(options.help) {
  const usage = commandLineUsage(sections);
  console.log(usage);
  process.exit(0);
}

console.log(options);

すると以下のようなヘルプが出来上がる。

~/tmp/node_options $node index.js --help

Sample app

  this is sample app for command-line-args

Options

  -v, --verbose
  --src string[]         file path
  -t, --timeout number   convert timeout
  -h, --help             show help

こっちに関しても結構カスタマイズできるので詳しくは公式ドキュメントを見てほしい。

バリデーション機能をつけたい

自分で実装しろ

と、いうのもcommand-line-args自体がそもそも対応してないし、対応する気もない。その理由は公式ドキュメントに書かれており、

Because this module specialises in one task - finding and extracting values set against defined options in process.argv.

要は昔は対応してた時期もあったけど、対応し始めるとキリなくてメンテできへんしつけねぇって話らしい。