Files
gh-vanman2024-cli-builder-p…/skills/commander-patterns/examples/advanced-option-class.md
2025-11-30 09:04:14 +08:00

12 KiB

Advanced Option Class Usage

Comprehensive examples of the Option class for advanced option handling.

Basic Option Class

import { Command, Option } from 'commander';

const program = new Command();

program
  .command('deploy')
  .addOption(
    new Option('-e, --env <environment>', 'target environment')
      .choices(['dev', 'staging', 'prod'])
      .default('dev')
  )
  .action((options) => {
    console.log('Environment:', options.env);
  });

program.parse();

Option with Choices

import { Command, Option } from 'commander';

program
  .command('log')
  .addOption(
    new Option('-l, --level <level>', 'log level')
      .choices(['debug', 'info', 'warn', 'error'])
      .default('info')
  )
  .addOption(
    new Option('-f, --format <format>', 'output format')
      .choices(['json', 'yaml', 'table'])
      .default('table')
  )
  .action((options) => {
    console.log(`Logging at ${options.level} level in ${options.format} format`);
  });

Usage:

mycli log --level debug --format json
mycli log --level invalid    # Error: invalid choice

Mandatory Options

import { Command, Option } from 'commander';

program
  .command('deploy')
  .addOption(
    new Option('-t, --token <token>', 'API token')
      .makeOptionMandatory()
  )
  .addOption(
    new Option('-e, --env <environment>', 'environment')
      .choices(['dev', 'staging', 'prod'])
      .makeOptionMandatory()
  )
  .action((options) => {
    console.log('Deploying with token:', options.token);
    console.log('Environment:', options.env);
  });

Usage:

mycli deploy    # Error: required option missing
mycli deploy --token abc --env prod    # ✓ Works

Options from Environment Variables

import { Command, Option } from 'commander';

program
  .command('deploy')
  .addOption(
    new Option('-t, --token <token>', 'API token')
      .env('API_TOKEN')
      .makeOptionMandatory()
  )
  .addOption(
    new Option('-u, --api-url <url>', 'API URL')
      .env('API_URL')
      .default('https://api.example.com')
  )
  .action((options) => {
    console.log('Token:', options.token);
    console.log('API URL:', options.apiUrl);
  });

Usage:

export API_TOKEN=abc123
export API_URL=https://custom-api.com

mycli deploy    # Uses environment variables
mycli deploy --token xyz    # CLI arg overrides env var

Custom Argument Parsers

import { Command, Option } from 'commander';

program
  .command('scale')
  .addOption(
    new Option('-r, --replicas <count>', 'number of replicas')
      .argParser((value) => {
        const count = parseInt(value, 10);
        if (isNaN(count) || count < 1 || count > 100) {
          throw new Error('Replicas must be between 1 and 100');
        }
        return count;
      })
      .default(3)
  )
  .addOption(
    new Option('-m, --memory <size>', 'memory limit')
      .argParser((value) => {
        // Parse sizes like "512M", "2G"
        const match = value.match(/^(\d+)([MG])$/i);
        if (!match) {
          throw new Error('Invalid memory format (use 512M or 2G)');
        }
        const [, num, unit] = match;
        const mb = parseInt(num) * (unit.toUpperCase() === 'G' ? 1024 : 1);
        return mb;
      })
      .default(512)
  )
  .action((options) => {
    console.log('Replicas:', options.replicas);
    console.log('Memory:', options.memory, 'MB');
  });

Usage:

mycli scale --replicas 5 --memory 2G
# Replicas: 5
# Memory: 2048 MB

Conflicting Options

import { Command, Option } from 'commander';

program
  .command('build')
  .addOption(
    new Option('--cache', 'enable caching')
      .conflicts('noCache')
  )
  .addOption(
    new Option('--no-cache', 'disable caching')
      .conflicts('cache')
  )
  .addOption(
    new Option('--watch', 'watch mode')
      .conflicts('production')
  )
  .addOption(
    new Option('--production', 'production build')
      .conflicts('watch')
  )
  .action((options) => {
    console.log('Cache:', options.cache);
    console.log('Watch:', options.watch);
    console.log('Production:', options.production);
  });

Usage:

mycli build --cache    # ✓ Works
mycli build --no-cache    # ✓ Works
mycli build --cache --no-cache    # ✗ Error: conflicting options
mycli build --watch --production    # ✗ Error: conflicting options

Option Implies

import { Command, Option } from 'commander';

program
  .command('server')
  .addOption(
    new Option('--ssl', 'enable SSL')
      .implies({ sslCert: './cert.pem', sslKey: './key.pem' })
  )
  .addOption(
    new Option('--ssl-cert <path>', 'SSL certificate path')
  )
  .addOption(
    new Option('--ssl-key <path>', 'SSL key path')
  )
  .addOption(
    new Option('--secure', 'secure mode')
      .implies({ ssl: true, httpsOnly: true })
  )
  .addOption(
    new Option('--https-only', 'enforce HTTPS')
  )
  .action((options) => {
    console.log('SSL:', options.ssl);
    console.log('SSL Cert:', options.sslCert);
    console.log('SSL Key:', options.sslKey);
    console.log('HTTPS Only:', options.httpsOnly);
  });

Usage:

mycli server --ssl
# SSL: true
# SSL Cert: ./cert.pem
# SSL Key: ./key.pem

mycli server --secure
# SSL: true
# HTTPS Only: true

Preset Configurations

import { Command, Option } from 'commander';

program
  .command('deploy')
  .addOption(
    new Option('--preset <preset>', 'use preset configuration')
      .choices(['minimal', 'standard', 'enterprise'])
      .argParser((value) => {
        const presets = {
          minimal: {
            replicas: 1,
            memory: '256M',
            cpu: '0.25',
            autoScaling: false,
          },
          standard: {
            replicas: 3,
            memory: '512M',
            cpu: '0.5',
            autoScaling: true,
          },
          enterprise: {
            replicas: 10,
            memory: '2G',
            cpu: '2',
            autoScaling: true,
            loadBalancer: true,
            monitoring: true,
          },
        };
        return presets[value as keyof typeof presets];
      })
  )
  .action((options) => {
    if (options.preset) {
      console.log('Using preset configuration:');
      console.log(JSON.stringify(options.preset, null, 2));
    }
  });

Usage:

mycli deploy --preset enterprise
# Using preset configuration:
# {
#   "replicas": 10,
#   "memory": "2G",
#   "cpu": "2",
#   "autoScaling": true,
#   "loadBalancer": true,
#   "monitoring": true
# }

Hidden Options (Debug/Internal)

import { Command, Option } from 'commander';

program
  .command('build')
  .option('-p, --production', 'production build')
  .addOption(
    new Option('--debug', 'enable debug mode')
      .hideHelp()
  )
  .addOption(
    new Option('--internal-api', 'use internal API')
      .hideHelp()
      .default(false)
  )
  .action((options) => {
    if (options.debug) {
      console.log('Debug mode enabled');
      console.log('All options:', options);
    }
  });

Complex Validation

import { Command, Option } from 'commander';

program
  .command('backup')
  .addOption(
    new Option('-s, --schedule <cron>', 'backup schedule (cron format)')
      .argParser((value) => {
        // Validate cron expression
        const cronRegex = /^(\*|([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])|\*\/([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])) (\*|([0-9]|1[0-9]|2[0-3])|\*\/([0-9]|1[0-9]|2[0-3])) (\*|([1-9]|1[0-9]|2[0-9]|3[0-1])|\*\/([1-9]|1[0-9]|2[0-9]|3[0-1])) (\*|([1-9]|1[0-2])|\*\/([1-9]|1[0-2])) (\*|([0-6])|\*\/([0-6]))$/;

        if (!cronRegex.test(value)) {
          throw new Error('Invalid cron expression');
        }
        return value;
      })
  )
  .addOption(
    new Option('-r, --retention <days>', 'backup retention in days')
      .argParser((value) => {
        const days = parseInt(value, 10);
        if (isNaN(days) || days < 1 || days > 365) {
          throw new Error('Retention must be between 1 and 365 days');
        }
        return days;
      })
      .default(30)
  )
  .action((options) => {
    console.log('Schedule:', options.schedule);
    console.log('Retention:', options.retention, 'days');
  });

Usage:

mycli backup --schedule "0 2 * * *" --retention 90
# Schedule: 0 2 * * *
# Retention: 90 days

Cumulative Options

import { Command, Option } from 'commander';

program
  .command('log')
  .addOption(
    new Option('-v, --verbose', 'increase verbosity')
      .argParser((value, previous) => {
        return previous + 1;
      })
      .default(0)
  )
  .action((options) => {
    console.log('Verbosity level:', options.verbose);
    // 0 = normal
    // 1 = verbose (-v)
    // 2 = very verbose (-vv)
    // 3 = debug (-vvv)
  });

Usage:

mycli log         # verbosity: 0
mycli log -v      # verbosity: 1
mycli log -vv     # verbosity: 2
mycli log -vvv    # verbosity: 3

Complete Example

#!/usr/bin/env node
import { Command, Option } from 'commander';
import chalk from 'chalk';

const program = new Command();

program
  .name('deploy-cli')
  .version('1.0.0');

program
  .command('deploy')
  .description('Deploy application with advanced options')

  // Required with choices
  .addOption(
    new Option('-e, --env <environment>', 'deployment environment')
      .choices(['dev', 'staging', 'prod'])
      .makeOptionMandatory()
  )

  // Environment variable with validation
  .addOption(
    new Option('-t, --token <token>', 'API token')
      .env('DEPLOY_TOKEN')
      .makeOptionMandatory()
      .argParser((value) => {
        if (value.length < 32) {
          throw new Error('Token must be at least 32 characters');
        }
        return value;
      })
  )

  // Custom parser with unit conversion
  .addOption(
    new Option('-m, --memory <size>', 'memory allocation')
      .argParser((value) => {
        const match = value.match(/^(\d+)([MG])$/i);
        if (!match) {
          throw new Error('Memory must be in format: 512M or 2G');
        }
        const [, num, unit] = match;
        return parseInt(num) * (unit.toUpperCase() === 'G' ? 1024 : 1);
      })
      .default(512)
  )

  // Conflicting options
  .addOption(
    new Option('--fast', 'fast deployment (skip tests)')
      .conflicts('safe')
  )
  .addOption(
    new Option('--safe', 'safe deployment (full tests)')
      .conflicts('fast')
      .default(true)
  )

  // Implies relationship
  .addOption(
    new Option('--production-mode', 'enable production optimizations')
      .implies({ env: 'prod', safe: true, monitoring: true })
  )
  .addOption(
    new Option('--monitoring', 'enable monitoring')
  )

  // Hidden debug option
  .addOption(
    new Option('--debug', 'debug mode')
      .hideHelp()
  )

  .action((options) => {
    console.log(chalk.blue('Deployment Configuration:'));
    console.log('Environment:', chalk.yellow(options.env));
    console.log('Token:', options.token ? chalk.green('***set***') : chalk.red('missing'));
    console.log('Memory:', `${options.memory}MB`);
    console.log('Mode:', options.fast ? 'fast' : 'safe');
    console.log('Production Mode:', options.productionMode || false);
    console.log('Monitoring:', options.monitoring || false);

    if (options.debug) {
      console.log(chalk.gray('\nDebug - All options:'));
      console.log(chalk.gray(JSON.stringify(options, null, 2)));
    }

    console.log(chalk.green('\n✓ Deployment started'));
  });

program.parse();

Usage:

export DEPLOY_TOKEN=abc123xyz789abc123xyz789abc12345

# Basic deployment
deploy-cli deploy --env staging

# Custom memory
deploy-cli deploy --env prod --memory 4G

# Fast mode
deploy-cli deploy --env dev --fast

# Production mode (implies multiple settings)
deploy-cli deploy --production-mode

# Debug
deploy-cli deploy --env dev --debug