📡

Commander.jsでサブコマンドを設定するとprogram.opts()は使えない

2024/01/17に公開

はじめに

Commander.jsでCLIツールを作成するとき、オプションの扱いでつまづきがあったので備忘録です。
https://github.com/tj/commander.js

ドキュメントではprogram.opts()でコマンドのオプション引数の値を取得していますが、どうもこのメソッドはcommand()でサブコマンドを追加した場合はオプションの値を取得できないようでした。

検証環境

  • Ubuntu 20.04.6 LTS (WSL)
  • Node.js v18.17.1
  • Commander.js v11.1.0

コード例

まずはprogram.opts()が使える場合のコード例です。

index.js
const { program } = require("commander");

program
  .option("-t, --test [hoge]", "optional args", "hoge")
  .action((options)=>{
    console.log("program.opts() (action内): %o", program.opts());
    console.log("options (action内): %o", options);
  });

program.parse();

console.log("program.opts() (action外): %o", program.opts());
実行結果
$ node index.js --test hoge
program.opts() (action内): { test: 'hoge' }
options (action内): { test: 'hoge' }
program.opts() (action外): { test: 'hoge' }

コード中のどこでもprogram.opts()でオプションの値が取得できていることが分かります。

一方、program.command()でサブコマンドを追加してからオプションを追加すると以下のようになります。

index.js
const { program } = require("commander");

program
  .command("sample")    // サブコマンドを追加
  .option("-t, --test [hoge]", "optional args", "hoge")
  .action((options)=>{
    console.log("program.opts() (action内): %o", program.opts());
    console.log("options (action内): %o", options);
  });

program.parse();

console.log("program.opts() (action外): %o", program.opts());
実行結果
$ node index.js sample --test hoge
program.opts() (action内): {}
options (action内): { test: 'hoge' }
program.opts() (action外): {}

サブコマンドを追加した際はaction()メソッドに渡すコールバック関数の第一引数でのみオプションの値が参照できていることがわかります。なお、actionに渡すコールバック関数にasync修飾子をつけて非同期にした場合も同様です。

この挙動、私がざっと探した限りではドキュメントに記載が見つからず、仕様なのかバグなのかもよく分かりません。一応action()の解説はドキュメントにありますが、thank.jsの例ではサブコマンドが無いし、optionsは第二引数だし、色々微妙に違います。
issueを立てるかドキュメント修正のプルリクを出すかも検討しましたが、そもそもサブコマンドを追加したときだけなのか?とか諸々の検証が面倒をやる時間がなく、ドキュメントにしてもサブコマンドを追加しなければドキュメント通りではあるわけで、どこに追加修正すればいいのかも正直よく分かりません。ちょっと仕様が複雑すぎて手が付けられなかったので、代わりにissueなりプルリクなり出しておいてくれる方いらっしゃいましたら是非お願いします。また、この記事の内容について「ドキュメントのここに書いてあるよ!」という方いらっしゃいましたらコメントで教えてくださると非常に助かります。

Discussion