Yargs

機能豊富で高度に拡張可能なNode.js CLIライブラリ。複雑なコマンド構造、検証、カスタマイズに優れています。

javascriptnodejsclicommand-linetypescript

GitHub概要

yargs/yargs

yargs the modern, pirate-themed successor to optimist.

ホームページ:https://yargs.js.org/
スター11,323
ウォッチ80
フォーク1,006
作成日:2013年11月7日
言語:JavaScript
ライセンス:MIT License

トピックス

なし

スター履歴

yargs/yargs Star History
データ取得日時: 2025/7/25 02:05

フレームワーク

yargs

概要

yargsは、Node.jsエコシステムで最も人気のあるCLIライブラリの一つです。インタラクティブなコマンドラインツールを構築するために設計されており、引数を解析してエレガントなユーザーインターフェースを生成します。「海賊をテーマにしたoptimistの現代的な後継者」として開発され、動的なヘルプメニューの自動生成、Bash補完機能、豊富な機能セットにより、Node.jsコミュニティで広く採用されています。

なぜNode.jsで最も人気なのか:

  • 包括的な機能: 引数解析、コマンドサポート、バリデーション、自動ヘルプ生成を一つのライブラリで提供
  • 優れたDX(開発者体験): 直感的なAPIと豊富なドキュメント
  • 活発なコミュニティ: 12年以上の開発歴、282人の貢献者、月間数百万ダウンロード
  • クロスプラットフォーム対応: Node.js、Deno、ブラウザ環境で動作

詳細説明

歴史と発展

yargsは12年以上前に開発が開始され、Node.jsのCLIライブラリとして成熟してきました。最初は「optimist」の後継として設計されましたが、現在では独自の進歩的な機能セットを持つ、完全に独立したライブラリとなっています。最近のアップデートでは、非同期処理の完全サポート、TypeScript型推論の強化、パフォーマンス改善が行われています。

エコシステムでの位置づけ

Node.jsエコシステムにおいて、yargsは以下の理由で特別な地位を占めています:

  • 最も成熟したCLIライブラリ: 長年の開発により安定性と機能の豊富さを実現
  • 多くのツールで採用: Mocha、Webpack、ESLintなど多数の著名プロジェクトで使用
  • モジュラー設計: CommandInstance、UsageInstance、ValidationInstanceなど明確に分離された設計
  • プラットフォーム抽象化: PlatformShimにより異なるJavaScript実行環境に対応

最新動向(2024-2025年)

  • 非同期処理の完全サポート: parseAsync()メソッドによるPromiseベースの解析
  • 強化されたTypeScript支援: より正確な型推論と型安全性
  • 国際化の拡張: チェコ語、ウクライナ語などの新しいロケール対応
  • ミドルウェアシステムの改善: グローバルミドルウェアと非同期ミドルウェアの対応
  • 補完機能の強化: 位置引数の補完、エイリアス対応、Zsh改善

主な特徴

コア機能

  • 宣言的なコマンド構築: 直感的で読みやすい構文でコマンドを定義
  • 動的ヘルプ生成: 定義されたコマンドとオプションに基づく自動ヘルプメニュー
  • 位置引数とオプション: <required>[optional] による柔軟な引数定義
  • サブコマンドサポート: ネストされたコマンド構造と階層的なCLI設計
  • エイリアス機能: コマンドとオプションの短縮形やエイリアス対応

高度な機能

  • ミドルウェアシステム: 引数処理のパイプライン化と前後処理のカスタマイズ
  • バリデーション機能: 引数の型チェック、範囲検証、カスタムバリデーション
  • 設定ファイル統合: JSON、YAML、package.jsonからの設定読み込み
  • 環境変数統合: 環境変数からの引数解析(prefix対応)
  • 国際化(i18n): 多言語でのヘルプメッセージとエラーメッセージ
  • シェル補完: Bash、Zsh、Fishでの自動補完スクリプト生成

開発者体験

  • TypeScript完全サポート: 型定義による型安全な開発とIntelliSense対応
  • 豊富なAPIメソッド: .example(), .usage(), .epilog() などによる詳細なヘルプカスタマイズ
  • テスト支援: ヘッドレスモードでのテスト実行とモック対応
  • デバッグ機能: 詳細なエラーメッセージと解析プロセスの可視化

最新機能とアップデート

v17.xシリーズの主要機能

  • 完全な非同期サポート: parseAsync()メソッドによるPromiseベースの解析
  • 非同期ミドルウェア: async/awaitに対応したミドルウェア関数
  • 非同期コマンドハンドラー: Promiseを返すコマンドハンドラーの完全サポート
  • 非同期バリデーション: 外部サービスとの連携によるバリデーション

最近の改善点

  • ESモジュール対応: import/export構文でのモダンな使用方法
  • TypeScript型推論の強化: より正確な型チェックとIntelliSense
  • パフォーマンス最適化: 起動時間の短縮と解析速度の向上
  • 補完機能の拡張: 位置引数、エイリアス、Zsh対応の改善
  • 新しいロケール対応: チェコ語、ウクライナ語などの国際化拡張
  • ミドルウェアの改善: グローバルミドルウェアとコマンド固有ミドルウェアの対応

メリット・デメリット

メリット

  • 豊富な機能と高い柔軟性
  • 優れたTypeScriptサポート
  • 活発なコミュニティと豊富なドキュメント
  • モジュール化された設計
  • 直感的なAPI設計
  • 包括的なテスト機能

デメリット

  • シンプルなスクリプトには過剰な場合がある
  • 設定オプションが多く、初心者には複雑
  • バンドルサイズがやや大きい
  • 学習曲線がある

主要リンク

書き方の例

基本的な使用例

#!/usr/bin/env node
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';

const argv = yargs(hideBin(process.argv)).parse();

if (argv.ships > 3 && argv.distance < 53.5) {
  console.log('Plunder more riffiwobbles!');
} else {
  console.log('Retreat from the xupptumblers!');
}
# 実行例
./plunder.js --ships=4 --distance=22
# 出力: Plunder more riffiwobbles!

コマンドとオプションの定義

#!/usr/bin/env node
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';

yargs(hideBin(process.argv))
  .command('serve [port]', 'サーバーを起動します', (yargs) => {
    return yargs
      .positional('port', {
        describe: 'バインドするポート',
        default: 5000,
        type: 'number'
      })
  }, (argv) => {
    if (argv.verbose) console.info(`ポート${argv.port}でサーバーを起動しています`)
    startServer(argv.port)
  })
  .option('verbose', {
    alias: 'v',
    type: 'boolean',
    description: '詳細なログ出力'
  })
  .parse()

function startServer(port) {
  console.log(`サーバーがポート${port}で起動しました`);
}

複数コマンドとエイリアス

#!/usr/bin/env node
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';

yargs(hideBin(process.argv))
  .command(['start [app]', 'run', 'up'], 'アプリを起動', {}, (argv) => {
    console.log('starting up the', argv.app || 'default', 'app')
  })
  .command({
    command: 'configure <key> [value]',
    aliases: ['config', 'cfg'],
    describe: '設定変数を設定',
    builder: (yargs) => yargs.default('value', 'true'),
    handler: (argv) => {
      console.log(`${argv.key}${argv.value}に設定しています`)
    }
  })
  .demandCommand()
  .help()
  .wrap(72)
  .parse()

TypeScriptでの使用例

#!/usr/bin/env node
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';

interface Arguments {
  username: string;
  email?: string;
  age: number;
  verbose: boolean;
}

const argv = yargs(hideBin(process.argv))
  .options({
    username: { 
      type: 'string', 
      demandOption: true,
      describe: 'ユーザー名'
    },
    email: { 
      type: 'string',
      describe: 'メールアドレス'
    },
    age: { 
      type: 'number', 
      default: 25,
      describe: '年齢'
    },
    verbose: { 
      type: 'boolean', 
      default: false,
      alias: 'v',
      describe: '詳細出力'
    }
  })
  .parseSync() as Arguments;

console.log(`ユーザー: ${argv.username} (${argv.age}歳)`);
if (argv.email) {
  console.log(`メール: ${argv.email}`);
}
if (argv.verbose) {
  console.log('詳細モードが有効です');
}

高度な機能の例

#!/usr/bin/env node
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import fs from 'fs';

// ミドルウェア関数
const normalizeCredentials = (argv) => {
  if (!argv.username || !argv.password) {
    try {
      const credentials = JSON.parse(fs.readFileSync('~/.credentials', 'utf8'));
      return { ...argv, ...credentials };
    } catch (error) {
      console.warn('認証情報ファイルが読み込めません');
    }
  }
  return argv;
};

yargs(hideBin(process.argv))
  .usage('使用法: $0 <command> [options]')
  .command('login', 'ユーザー認証', (yargs) => {
    return yargs
      .option('username', {
        describe: 'ユーザー名',
        type: 'string'
      })
      .option('password', {
        describe: 'パスワード',
        type: 'string'
      })
  }, (argv) => {
    authenticateUser(argv.username, argv.password);
  }, [normalizeCredentials])
  .command('deploy [env]', 'アプリケーションをデプロイ', (yargs) => {
    return yargs
      .positional('env', {
        describe: 'デプロイ環境',
        choices: ['dev', 'staging', 'production'],
        default: 'dev'
      })
      .option('force', {
        type: 'boolean',
        describe: '強制デプロイ'
      })
  }, async (argv) => {
    console.log(`${argv.env}環境へデプロイ中...`);
    if (argv.force) {
      console.log('強制デプロイモード');
    }
    await deployApplication(argv.env, argv.force);
  })
  .middleware(normalizeCredentials)
  .demandCommand(1, 'コマンドを指定してください')
  .help('h')
  .alias('h', 'help')
  .version('1.0.0')
  .epilog('詳細な使用方法については、各コマンドで --help を使用してください')
  .parse();

function authenticateUser(username, password) {
  console.log(`ユーザー ${username} を認証中...`);
}

async function deployApplication(env, force) {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log(`${env}環境へのデプロイが完了しました`);
      resolve();
    }, 2000);
  });
}

コマンドモジュールの例

// commands/server.js
export const command = 'server [port]';
export const describe = 'Webサーバーを起動';
export const builder = {
  port: {
    default: 3000,
    describe: 'サーバーポート',
    type: 'number'
  },
  host: {
    default: 'localhost',
    describe: 'サーバーホスト',
    type: 'string'
  }
};
export const handler = (argv) => {
  console.log(`サーバーを http://${argv.host}:${argv.port} で起動中...`);
  // サーバー起動ロジック
};
// cli.js
#!/usr/bin/env node
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';

yargs(hideBin(process.argv))
  .commandDir('commands')
  .demandCommand()
  .help()
  .parse();

バリデーションと設定の例

#!/usr/bin/env node
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';

const argv = yargs(hideBin(process.argv))
  .usage('使用法: $0 -w [num] -h [num]')
  .option('width', {
    alias: 'w',
    describe: '幅',
    type: 'number',
    demandOption: true
  })
  .option('height', {
    alias: 'h',
    describe: '高さ',
    type: 'number',
    demandOption: true
  })
  .option('format', {
    alias: 'f',
    describe: '出力形式',
    choices: ['json', 'xml', 'yaml'],
    default: 'json'
  })
  .check((argv) => {
    if (argv.width <= 0 || argv.height <= 0) {
      throw new Error('幅と高さは正の数である必要があります');
    }
    return true;
  })
  .example('$0 -w 10 -h 20', '幅10、高さ20で実行')
  .example('$0 -w 5 -h 8 -f xml', 'XML形式で出力')
  .help()
  .parse();

console.log(`面積: ${argv.width * argv.height}`);
console.log(`形式: ${argv.format}`);