minimist

引数パーシングのための最小限のライブラリ。非常に軽量でシンプルな実装が特徴です。

javascriptnodejscliargument-parsinglightweight

フレームワーク

minimist

概要

minimistは、Node.jsのコマンドライン引数を解析するための最小限かつ軽量なライブラリです。process.argvを構造化されたオブジェクトに変換する基本的な機能を提供し、シンプルなAPIと最小限の依存関係で設計されています。多くの大規模なCLIツールの内部で使用されており、引数解析の基盤として広く採用されています。

なぜminimistが選ばれるのか:

  • 最小限の設計: 軽量で高速、依存関係なし
  • シンプルなAPI: 学習コストが低く、すぐに使い始められる
  • 広範な互換性: Node.jsのあらゆるバージョンで動作
  • 基盤ライブラリ: 多くのCLIツールの内部実装で採用
  • 柔軟性: 基本的な解析機能を提供し、上位レイヤーでカスタマイズ可能

詳細

歴史と発展

minimistは、James Halliday(substack)氏によって2013年に開発され、Node.jsエコシステムにおける引数解析の基盤ライブラリとして成長しました。optimistの軽量版として設計され、必要最小限の機能に絞り込むことで高いパフォーマンスと互換性を実現しています。

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

Node.jsエコシステムにおいて、基盤的な役割を果たしています:

  • 内部依存: yargsやcommander.jsなどの上位ライブラリで使用
  • シンプルなスクリプト: 軽量なCLIスクリプトの標準選択
  • 教育目的: 引数解析の仕組みを学ぶための教材
  • プロトタイピング: 迅速な開発とテストのためのツール

最新動向(2024-2025年)

  • 安定した成熟: 長期間にわたって安定したAPIを維持
  • セキュリティ強化: プロトタイプ汚染対策などのセキュリティ改善
  • ES Module対応: CommonJSとESMの両方をサポート
  • TypeScript型定義: @types/minimistによる型安全性の向上
  • 継続的なメンテナンス: セキュリティアップデートと互換性の維持

主な特徴

基本機能

  • 引数解析: process.argvを構造化されたオブジェクトに変換
  • ダッシュ解析: ---プレフィックスの処理
  • 位置引数: オプション以外の引数を_配列に収集
  • エイリアス: 短縮形と完全形の引数エイリアス
  • 型指定: 文字列、ブール値、数値の型変換

高度なオプション

  • デフォルト値: 未指定の引数に対するデフォルト値設定
  • stopEarly: 最初の非オプション引数で解析停止
  • --処理: --以降の引数を特別扱い
  • unknown関数: 未定義のオプションのハンドリング
  • 変換関数: カスタムな値変換処理

パフォーマンス特性

  • 軽量: 10KB未満の小さなフットプリント
  • 高速: 最小限の処理オーバーヘッド
  • メモリ効率: 不要なオブジェクト生成を回避
  • 依存関係なし: 外部ライブラリに依存しない

メリット・デメリット

メリット

  • 軽量性: 最小限のバンドルサイズ
  • 高速性: 高いパフォーマンス
  • シンプルさ: 学習コストが低い
  • 安定性: 長期間にわたって安定したAPI
  • 互換性: 幅広いNode.jsバージョンで動作
  • 依存関係なし: セキュリティリスクが低い

デメリット

  • 機能制限: 基本的な機能のみ提供
  • ヘルプ生成なし: 自動ヘルプメッセージ機能がない
  • バリデーションなし: 入力値の検証機能がない
  • コマンドサポートなし: サブコマンドの仕組みがない
  • インタラクティブ機能なし: プロンプトなどの対話機能がない

主要リンク

書き方の例

基本的な使用例

const minimist = require('minimist');

// コマンド例: node script.js -a beep --foo bar baz
const args = minimist(process.argv.slice(2));
console.log(args);

// 出力:
// {
//   _: ['baz'],
//   a: 'beep',
//   foo: 'bar'
// }

型指定の例

const minimist = require('minimist');

// コマンド例: node script.js --port 3000 --debug --name myapp
const args = minimist(process.argv.slice(2), {
  string: ['name'],        // nameを文字列として扱う
  boolean: ['debug'],      // debugをブール値として扱う
  alias: {                 // エイリアスの設定
    p: 'port',
    d: 'debug',
    n: 'name'
  }
});

console.log(args);
// 出力:
// {
//   _: [],
//   port: 3000,
//   debug: true,
//   d: true,
//   name: 'myapp',
//   n: 'myapp',
//   p: 3000
// }

デフォルト値の設定

const minimist = require('minimist');

const args = minimist(process.argv.slice(2), {
  default: {
    port: 8080,
    host: 'localhost',
    debug: false
  },
  string: ['host'],
  boolean: ['debug'],
  alias: {
    p: 'port',
    h: 'host',
    d: 'debug'
  }
});

console.log(`Server starting on ${args.host}:${args.port}`);
console.log(`Debug mode: ${args.debug ? 'ON' : 'OFF'}`);

// 実行例:
// node server.js
// => Server starting on localhost:8080, Debug mode: OFF
//
// node server.js --port 3000 --debug
// => Server starting on localhost:3000, Debug mode: ON

stopEarlyオプションの使用

const minimist = require('minimist');

// stopEarly: 最初の非オプション引数で解析を停止
const args = minimist(process.argv.slice(2), {
  stopEarly: true
});

console.log(args);

// コマンド例: node script.js --verbose command --arg value
// 出力:
// {
//   _: ['command', '--arg', 'value'],
//   verbose: true
// }

ダブルダッシュ(--)の処理

const minimist = require('minimist');

const args = minimist(process.argv.slice(2), {
  '--': true  // --以降の引数を argv['--'] に格納
});

console.log(args);

// コマンド例: node script.js --name test -- --other-flag value
// 出力:
// {
//   _: [],
//   name: 'test',
//   '--': ['--other-flag', 'value']
// }

unknown関数によるカスタムハンドリング

const minimist = require('minimist');

const args = minimist(process.argv.slice(2), {
  unknown: function (arg) {
    if (arg.startsWith('-') || arg.startsWith('--')) {
      console.error(`Unknown option: ${arg}`);
      return false; // この引数を無視
    }
    return true; // 位置引数として処理
  }
});

console.log(args);

// コマンド例: node script.js --known value --unknown test positional
// エラー出力: Unknown option: --unknown
// 出力:
// {
//   _: ['positional'],
//   known: 'value'
// }

実用的なCLIツールの例

#!/usr/bin/env node
const minimist = require('minimist');
const fs = require('fs');
const path = require('path');

// ヘルプメッセージ
function showHelp() {
  console.log(`
Usage: mytools [options] <command> [args...]

Options:
  -h, --help     Show this help message
  -v, --verbose  Enable verbose output
  -o, --output   Output file path
  --version      Show version number

Commands:
  build          Build the project
  test           Run tests
  clean          Clean build artifacts

Examples:
  mytools build --output dist/
  mytools test --verbose
  mytools clean
`);
}

// バージョン情報
function showVersion() {
  const packageJson = JSON.parse(
    fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8')
  );
  console.log(packageJson.version);
}

// 引数解析
const args = minimist(process.argv.slice(2), {
  string: ['output', 'o'],
  boolean: ['help', 'verbose', 'version', 'h', 'v'],
  alias: {
    h: 'help',
    v: 'verbose',
    o: 'output'
  },
  default: {
    verbose: false
  }
});

// ヘルプ表示
if (args.help) {
  showHelp();
  process.exit(0);
}

// バージョン表示
if (args.version) {
  showVersion();
  process.exit(0);
}

// コマンドの取得
const command = args._[0];

if (!command) {
  console.error('Error: No command specified');
  showHelp();
  process.exit(1);
}

// ログ関数
function log(message) {
  if (args.verbose) {
    console.log(`[INFO] ${message}`);
  }
}

// コマンド処理
switch (command) {
  case 'build':
    log('Starting build process...');
    const outputDir = args.output || 'dist';
    console.log(`Building project to ${outputDir}/`);
    log('Build completed successfully');
    break;

  case 'test':
    log('Running test suite...');
    console.log('All tests passed!');
    break;

  case 'clean':
    log('Cleaning build artifacts...');
    console.log('Clean completed');
    break;

  default:
    console.error(`Error: Unknown command '${command}'`);
    showHelp();
    process.exit(1);
}

TypeScriptでの使用例

import minimist from 'minimist';

interface CLIArgs {
  _: string[];
  port: number;
  host: string;
  debug: boolean;
  config?: string;
  help?: boolean;
}

const args = minimist(process.argv.slice(2), {
  string: ['host', 'config'],
  boolean: ['debug', 'help'],
  alias: {
    p: 'port',
    h: 'host',
    d: 'debug',
    c: 'config'
  },
  default: {
    port: 3000,
    host: 'localhost',
    debug: false
  }
}) as CLIArgs;

if (args.help) {
  console.log('Usage: server [options]');
  console.log('Options:');
  console.log('  -p, --port <number>     Server port (default: 3000)');
  console.log('  -h, --host <string>     Server host (default: localhost)');
  console.log('  -d, --debug             Enable debug mode');
  console.log('  -c, --config <path>     Configuration file path');
  console.log('  --help                  Show this help');
  process.exit(0);
}

console.log(`Starting server on ${args.host}:${args.port}`);
if (args.debug) {
  console.log('Debug mode enabled');
}
if (args.config) {
  console.log(`Using configuration file: ${args.config}`);
}

設定ファイルとの組み合わせ

const minimist = require('minimist');
const fs = require('fs');
const path = require('path');

function loadConfig(configPath) {
  try {
    const configFile = path.resolve(configPath);
    const config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
    return config;
  } catch (error) {
    console.error(`Failed to load config file: ${error.message}`);
    return {};
  }
}

// 引数解析
const args = minimist(process.argv.slice(2), {
  string: ['config'],
  boolean: ['watch', 'minify'],
  alias: {
    c: 'config',
    w: 'watch',
    m: 'minify'
  },
  default: {
    config: './build.config.json'
  }
});

// 設定ファイルの読み込み
const config = loadConfig(args.config);

// CLIオプションで設定を上書き
const finalConfig = {
  ...config,
  watch: args.watch ?? config.watch ?? false,
  minify: args.minify ?? config.minify ?? true,
  input: args._[0] || config.input || 'src/index.js',
  output: args._[1] || config.output || 'dist/bundle.js'
};

console.log('Build configuration:', finalConfig);

// ビルド処理(例)
if (finalConfig.watch) {
  console.log('Starting in watch mode...');
}

if (finalConfig.minify) {
  console.log('Minification enabled');
}

console.log(`Input: ${finalConfig.input}`);
console.log(`Output: ${finalConfig.output}`);