CLI ツール
ルール
- Commander.js で引数解析 —
.command()、.option()、.argument() に説明文を付ける
- 対話型プロンプト:
@clack/prompts (モダンで美しい) または inquirer — 引数が指定されていない場合のユーザー入力に使用
- スピナー:
ora で長時間の処理を表示 — 開始、メッセージ更新、成功/失敗を表示
- カラー:
chalk でスタイル付き出力 — 控えめに使用: エラーは赤、成功は緑、警告は黄色、補足情報は薄色
- 設定ファイル:
cosmiconfig — .toolrc、.toolrc.json、tool.config.js、package.json のキーを自動検索
- 終了コード: 成功は
0、一般エラーは 1、使用方法エラーは 2 — 常に process.exit(code) を使用
--help および --version フラグ: Commander が自動追加 — すべてのオプションに明確な説明を記述
- エラーハンドリング: 最上位でエラーをキャッチし、ユーザーフレンドリーなメッセージを stderr に出力し、コード 1 で終了
- 公開:
package.json の bin フィールド、#!/usr/bin/env node シバン、"type": "module" で ESM 対応
パターン
#!/usr/bin/env node
import { Command } from "commander";
import * as p from "@clack/prompts";
import ora from "ora";
import chalk from "chalk";
const program = new Command()
.name("my-tool")
.description("便利なことを行う CLI")
.version("1.0.0");
program
.command("init")
.description("新しいプロジェクトを初期化")
.option("-t, --template <name>", "使用するテンプレート", "default")
.action(async (options) => {
p.intro(chalk.bold("プロジェクト設定"));
const name = await p.text({ message: "プロジェクト名?", placeholder: "my-app" });
if (p.isCancel(name)) { p.cancel("キャンセルされました。"); process.exit(0); }
const spinner = ora("プロジェクトを作成中...").start();
await createProject(name, options.template);
spinner.succeed(chalk.green("プロジェクトを作成しました!"));
p.outro(`次のコマンドを実行: ${chalk.cyan(`cd ${name} && npm install`)}`);
});
program.parse();
避けるべきこと
- エラーを stdout に出力しない — エラーには
console.error() または process.stderr.write() を使用
- 失敗時にコードを指定せずに終了しない — 常に
process.exit(1) を実行し、スクリプトや CI が失敗を検出できるようにする
- フォーマットされていないテキストの壁を作らない — chalk の色、セクション、余白を使って可読性を向上
- フラグの代替がない対話型入力を必須にしない — CI/スクリプト用に常に
--flag 上書きをサポート
- シバン
#!/usr/bin/env node を忘れない — これがないと、npx やグローバルインストールが機能しない