commander.jsを利用したCLIコマンドの作成
node の実行コマンドを自作できる commander.js を初めて使ったため、その備忘録として纏めてみました。
※基本は、公式 Github の README を自分なりに解釈したものになります。
Install
まずは、JavaScript or TypeScript の環境を作り、yarn run でファイル実行できる様にしておきましょう。(yarn init
で簡単に作れます)
次に、commander.js をインストールします。
# npm
$ npm install commander
# yarn
$ yarn add commander
使い方
commander.js には program 変数があるので、それを使って作成していくことが可能です。
しかし、複数の方法で commander を使用する可能性がある大規模なプログラムでは、ローカルに Commander オブジェクトを作成して使用する方が良いです。
// CommonJS (.cjs)
const { Command } = require('commander');
const program = new Command();
// ECMAScript (.mjs)
import { Command } from 'commander';
const program = new Command();
// TypeScript (.ts)
import { Command } from 'commander';
const program = new Command();
オプションの設定
コマンドにオプションを設定する場合は、.option()
メソッドを利用します。オプションは複数設定でき、オプション名は1文字と長い文字のオプション名を持たせることができます。
オプションには2種類存在し、オプションに引数を渡す方法とオプションを指定することで boolean 判定をさせる方法があります。
解析されたオプションは、Command オブジェクトに対して.opts()
をコールすることでアクセスでき、アクションハンドラに渡されます。
-template-engine
のような複数語のオプションはキャメルケースになり、program.opts().templateEngine などになります。
例えば、下記の様にコードを設定した場合のコードを示します。
import { Command } from "commander";
const program = new Command();
program.option("-p, --port <port>", "server port setting", "80");
program.parse();
console.log(program.opts());
この場合、yarn run ts-node index.ts
を実行すると port の指定はありませんが、port=80 で指定することになります。
yarn run v1.22.19
{ port: '80' }
✨ Done in 0.50s.
今度は port の指定方法ですが、指定方法は複数あります。
いずれも port=3000 で指定されます。
yarn run ts-node index.ts -p 3000
yarn run ts-node index.ts -p3000
yarn run ts-node index.ts --port 3000
yarn run ts-node index.ts --port=3000
オプションの設定は柔軟性があり、下記の様な特性を持っています。
- オプションは複数指定可能
- フラグとして boolean を指定できる
- 否定フラグも作成できる
import { Command } from "commander";
const program = new Command();
program
.option("-p, --port <port>", "server port setting", "80")
.option("-d, --debug", "development debug")
.option("-e, --server-environment <env>", "env mode");
program.parse();
console.log(program.opts());
# 全て指定した場合
$ yarn run test-command --port=3000 -d -e local
{ port: '3000', debug: true, serverEnvironment: 'local' }
✨ Done in 0.41s.
# 指定しない場合、デフォルト値があるものだけになる
$ yarn run test-command
{ port: '80' }
✨ Done in 0.76s.
# 引数が必要なオプションに引数無しで実行するとエラーとなる
$ yarn run test-command --port=3000 -d -e
error: option '-e, --server-environment <env>' argument missing
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
# boolean引数はまとめることが可能
$ yarn run test-command --port=3000 -de local
{ port: '3000', debug: true, serverEnvironment: 'local' }
✨ Done in 0.74s.
否定オプションbooleanの設定
基本オプションは指定するとtrue
を示しますが、指定するとfalse
を示す方法もあります。
それは、オプションにno-
をつけることです。
import { Command } from "commander";
const program = new Command();
program
.option("-p, --port <port>", "server port setting", "80")
.option("-d, --debug", "development debug")
.option("-e, --server-environment <env>", "env mode")
.option("-l, --no-error-log", "error log output");
program.parse();
console.log(program.opts());
$ yarn run test-command --port=3000 -de local -l
{
port: '3000',
errorLog: false,
debug: true,
serverEnvironment: 'local'
}
✨ Done in 0.51s.
# 否定に関しては、指定しなければtrueとなる。
$ yarn run test-command --port=3000 -de local
{
port: '3000',
errorLog: true,
debug: true,
serverEnvironment: 'local'
}
✨ Done in 0.80s.
必須オプション
オプションに必須設定可能で、その必須オプションの指定がなければエラーとなります。
program
.requiredOption('-c, --cheese <type>', 'pizza must have cheese');
program.parse();
$ yarn run test-command
error: required option '-c, --cheese <type>' not specified
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
オプションの配列指定
import { Command } from "commander";
const program = new Command();
program
.requiredOption("-c, --cheese <type>", "pizza must have cheese")
.option("-n, --number <numbers...>", "specify numbers");
program.parse();
console.log(program.opts());
$ yarn run test-command -c cheeze -n 1 2 3
{ cheese: 'cheeze', number: [ '1,2,3' ] }
✨ Done in 0.43s.
細かなオプション設定
さらに細かいオプションを指定する場合は、.addOption()
メソッドを使い、その中でOption
オブジェクトを作成することでオプションの設定が可能です。
例えば、下記の様に option に .choices([])
を使うことで、指定した値しか使えなくなります。
import { Command, Option } from "commander";
const program = new Command();
program.addOption(
new Option("-d, --drink <size>", "drink size").choices([
"small",
"medium",
"large",
])
);
program.parse();
console.log(program.opts());
$ yarn run test-command -d small
{ drink: 'small' }
✨ Done in 0.58s.
# choiceにない値を指定するとエラーとなる
$ yarn run test-command -d llll
error: option '-d, --drink <size>' argument 'llll' is invalid. Allowed choices are small, medium, large.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
オプションのカスタム設定
カスタム・オプション処理
オプション引数のカスタム処理を行う関数を指定できます。コールバック関数は、ユーザーが指定したオプション引数と、オプションの前回値の 2 つのパラメータを受け取ります。コールバック関数は、オプションの新しい値を返します。
これにより、オプション引数を希望の型に強制したり、値を累積したり、完全にカスタムな処理を行うことができます。
オプションのデフォルト値/開始値を、関数パラメータの後に指定可能です。
import { Command, InvalidArgumentError } from "commander";
const program = new Command();
function increaseVerbosity(value: string, previous: number) {
const parsedValue = parseInt(value, 10);
if (isNaN(parsedValue)) {
throw new InvalidArgumentError("Not a number.");
}
return parsedValue;
}
program
.option("-p, --port <port>", "server port setting", "80")
.option("-i --increase <number>", "increase number", increaseVerbosity);
program.parse();
console.log(program.opts());
$ yarn run test-command -p 5000 --increase 5
{ port: '5000', increase: 5 }
✨ Done in 0.56s.
コマンド作成
例えば、yarn run test-command hoge
のように hoge を指定することで、コマンドとして扱うことが可能です。
import { Command } from "commander";
const program = new Command();
// バージョン情報
program.version("0.0.1", "-v, --version");
// hogeコマンド
program
.command("hoge")
.description("my first example")
.action(() => console.log("run hoge command"));
program.parse();
console.log(program.args);
$ yarn run test-command hoge
run hoge command
[ 'hoge' ]
alias も設定できます、詳細は下記を参考にしてください。
引数指定
.argument()
を使い、コマンドに対して引数を指定します。
argument では、引数の順番がそのままコードに適用されます。
下記の場合、server が第一引数、user が第二引数となります。
import { Command } from "commander";
const program = new Command();
program
.name("connect")
.argument("<server>", "connect to the specified server")
.argument("[user]", "user account for connection", "guest")
.description("Example program with argument descriptions")
.action((server, user) => {
console.log("server:", server);
console.log("user:", user);
});
program.parse();
console.log(program.args);
$ yarn run test-command ec2 mattu
server: ec2
user: mattu
[ 'ec2', 'mattu' ]
引数をスプレッド演算子で指定できます。
import { Command } from "commander";
const program = new Command();
program
.version("0.1.0")
.command("rmdir")
.argument("<dirs...>")
.action(function (dirs: string[]) {
dirs.forEach((dir) => {
console.log("rmdir %s", dir);
});
});
program.parse();
console.log(program.args);
$ yarn run test-command rmdir test add
rmdir test
rmdir add
[ 'rmdir', 'test', 'add' ]
✨ Done in 0.43s.
Discussion