Skip to content

Latest commit

 

History

History
303 lines (223 loc) · 5.23 KB

File metadata and controls

303 lines (223 loc) · 5.23 KB

commander

Complete solution for Node.js command-line interfaces


Installation

bun add commander

Current version in commando: 14.0.2


Basic Usage

import { Command } from 'commander';

const program = new Command();

program
  .name('cmdo')
  .description('Modern CLI framework')
  .version('2.0.0');

program.parse();

Options

program
  .option('-d, --debug', 'Output extra debugging')
  .option('-s, --small', 'Small pizza size')
  .option('-p, --pizza-type <type>', 'Flavour of pizza');

program.parse();

const options = program.opts();
if (options.debug) console.log(options);

Commands

program
  .command('deploy')
  .description('Deploy to environment')
  .argument('<environment>', 'Environment name')
  .option('-s, --skip-tests', 'Skip tests')
  .action((environment, options) => {
    console.log(`Deploying to ${environment}`);
    if (options.skipTests) {
      console.log('Skipping tests');
    }
  });

Commando Integration Pattern

Commando uses Commander to build commands from config:

// lib/core.ts
export function buildCommanderCommand(
  name: string,
  cmdoCmd: CommandoCommand,
  context: CommandoContext
): Command {
  const cmd = new Command(name);
  cmd.description(cmdoCmd.description);

  // Let command customize if needed
  if (cmdoCmd.defineCommand) {
    cmdoCmd.defineCommand(cmd);
  }

  // Install action handler
  cmd.action(async (...args) => {
    const options = args[args.length - 2];
    const positionalArgs = args.slice(0, -2);

    await cmdoCmd.execute(options, positionalArgs, context);
  });

  return cmd;
}

Defining Commands in Commando

Simple (No Options)

export const version: CommandoCommand = {
  description: 'Show version',
  execute: async (options, args) => {
    console.log('v2.0.0');
  }
};

With Options

export const build: CommandoCommand = {
  description: 'Build website',

  defineCommand: (cmd) => {
    cmd
      .option('-c, --clean', 'Clean build directory first')
      .option('--no-optimize', 'Skip optimization');
  },

  execute: async (options, args) => {
    if (options.clean) {
      await $`rm -rf dist`;
    }
    // options.optimize is boolean (auto-negated from --no-optimize)
  }
};

With Arguments

export const deploy: CommandoCommand = {
  description: 'Deploy to environment',

  defineCommand: (cmd) => {
    cmd
      .argument('<environment>', 'Target environment')
      .option('-s, --skip-tests', 'Skip tests');
  },

  execute: async (options, args) => {
    const environment = args[0];  // Required positional arg
    console.log(`Deploying to ${environment}`);
  }
};

Automatic Help

Commander generates help automatically:

$ cmdo deploy --help
Usage: cmdo deploy [options] <environment>

Deploy to environment

Arguments:
  environment           Target environment

Options:
  -s, --skip-tests      Skip tests
  -h, --help           display help for command

Validation

program
  .command('deploy')
  .argument('<environment>', 'Environment', (value) => {
    const valid = ['staging', 'production'];
    if (!valid.includes(value)) {
      throw new Error(`Invalid environment. Use: ${valid.join(', ')}`);
    }
    return value;
  });

Default Values

cmd
  .option('-b, --bucket <name>', 'S3 bucket', 'default-bucket')
  .option('--region <region>', 'AWS region', 'us-east-1');

Choices

import { Option } from 'commander';

cmd.addOption(
  new Option('-r, --region <region>', 'AWS region')
    .choices(['us-east-1', 'us-west-2', 'eu-west-1'])
);

Boolean Negation

// --no-optimize sets options.optimize = false
cmd.option('--no-optimize', 'Skip optimization');

// --no-color sets options.color = false
cmd.option('--no-color', 'Disable colors');

Variadic Arguments

cmd
  .argument('<files...>', 'Files to process')
  .action((files) => {
    console.log(`Processing: ${files.join(', ')}`);
  });

// Usage: cmdo process file1.txt file2.txt file3.txt

Subcommands

const cache = program
  .command('cache')
  .description('Cache operations');

cache
  .command('clear')
  .description('Clear cache')
  .action(() => {
    console.log('Clearing cache...');
  });

cache
  .command('show')
  .description('Show cache status')
  .action(() => {
    console.log('Cache status...');
  });

// Usage: cmdo cache clear

Error Handling

program.exitOverride();  // Throw instead of process.exit()

try {
  program.parse();
} catch (error) {
  console.error('Command failed:', error.message);
  process.exit(1);
}

TypeScript Support

import { Command } from 'commander';

// Get typed options
interface DeployOptions {
  skipTests?: boolean;
  dryRun?: boolean;
}

const options = program.opts<DeployOptions>();

References