はじめに
社内ツールやビルドスクリプトをNode.jsで作るとき、「オプションの解析」で地味に時間を溶かした経験はないでしょうか。process.argvを自前でパースし始めると、--verboseと-vをどう対応させるか、必須引数が足りないときにどうエラーを出すか、--helpをどう自動生成するかなど、考えることが次々と出てきます。
Commander.jsは、この「CLIツールにおけるボイラープレート」をまるごと肩代わりしてくれるライブラリです。コマンド定義・オプション解析・ヘルプ表示・サブコマンド管理まで、宣言的な書き方だけで済ませられます。今回はその魅力を、実際のコード例とともに紹介します。
Commander.jsとは
Commander.jsは、Node.js向けのコマンドライン引数解析ライブラリです。tj/commander.jsとしてGitHub上で開発されており、npmの週間ダウンロード数が数千万件を超える、事実上のデファクトスタンダードといえる存在です。Vue CLIやCreate React Appをはじめ、数多くのCLIツールの内部で採用されています。
主な特徴
- 宣言的なAPI -
.option()や.argument()をメソッドチェーンで繋ぐだけで、複雑なオプション定義が完結します - 自動生成されるヘルプ -
--helpを実装しなくても、コマンド定義から自動でヘルプメッセージが生成されます - サブコマンド対応 -
git commitのような「コマンド内コマンド」構造を、ファイル分割も含めて自然に扱えます - TypeScript対応 - 型定義が同梱されており、型安全にオプションを扱えます
インストール
npm、yarn、pnpmのいずれでも導入できます。
npm install commander
yarn add commander
pnpm add commander
基本的な使い方
まずは最小構成で、オプション付きの挨拶コマンドを作ってみましょう。
#!/usr/bin/env node
// greet.js
import { Command } from 'commander';
const program = new Command();
program
.name('greet')
.description('簡単な挨拶を表示するCLIツール')
.version('1.0.0');
program
.argument('<name>', '挨拶する相手の名前')
.option('-l, --loud', '大文字で表示する')
.option('-t, --times <number>', '繰り返し回数', '1')
.action((name, options) => {
const message = `こんにちは、${name}さん!`;
const output = options.loud ? message.toUpperCase() : message;
const times = parseInt(options.times, 10);
for (let i = 0; i < times; i++) {
console.log(output);
}
});
program.parse();
実行してみると、オプションの組み合わせが直感的に扱えることがわかります。
node greet.js 太郎
# => こんにちは、太郎さん!
node greet.js 太郎 --loud --times 2
# => コンニチハ、タロウサン!(2回表示)
node greet.js --help
# => 自動生成されたヘルプが表示される
--helpを一切実装していないのに、コマンド定義だけでヘルプが表示される点がCommander.jsの便利なところです。
実践的なユースケース
実際のツールでは、gitやnpmのように複数のサブコマンドを持つCLIを作ることが多いはずです。Commander.jsでは、サブコマンドごとにファイルを分割して管理できます。
// cli.js
#!/usr/bin/env node
import { Command } from 'commander';
const program = new Command();
program
.name('taskctl')
.description('タスク管理用CLIツール')
.version('1.0.0');
program
.command('add <title>')
.description('新しいタスクを追加する')
.option('-p, --priority <level>', '優先度 (low/medium/high)', 'medium')
.action((title, options) => {
console.log(`タスクを追加しました: ${title} (優先度: ${options.priority})`);
});
program
.command('list')
.description('タスク一覧を表示する')
.option('--all', '完了済みタスクも表示する')
.action((options) => {
console.log(options.all ? '全タスクを表示します' : '未完了タスクを表示します');
});
program
.command('remove <id>')
.description('タスクを削除する')
.action((id) => {
console.log(`タスクID ${id} を削除しました`);
});
program.parse();
このように定義しておくと、以下のようなコマンド体系が自然に出来上がります。
taskctl add "資料作成" --priority high
taskctl list --all
taskctl remove 42
taskctl add --help # addコマンドだけのヘルプも自動生成される
さらに、必須オプションのバリデーションも簡単に組み込めます。
program
.command('deploy')
.description('アプリケーションをデプロイする')
.requiredOption('-e, --env <environment>', 'デプロイ先の環境 (staging/production)')
.action((options) => {
const allowed = ['staging', 'production'];
if (!allowed.includes(options.env)) {
console.error(`エラー: --env は ${allowed.join(' か ')} を指定してください`);
process.exit(1);
}
console.log(`${options.env} 環境へデプロイを開始します`);
});
--envが未指定の場合、Commander.jsが自動的にエラーメッセージを表示して処理を止めてくれるため、自前のバリデーションコードを最小限に抑えられます。
まとめ
Commander.jsを使うと、CLIツール開発でありがちな「引数解析」「ヘルプ表示」「サブコマンド管理」といった定型作業から解放され、本来書きたいロジックに集中できます。社内ツールの自動化スクリプトから、npm公開用のCLIパッケージまで幅広く使える、まさに枯れた安心感のあるライブラリです。
まだprocess.argvを手動でパースしているなら、次に作るCLIツールではぜひCommander.jsを試してみてください。
