npm.io
0.1.0 • Published yesterday

super-commander

Licence
ISC
Version
0.1.0
Deps
1
Size
32 kB
Vulns
0
Weekly
0

super-commander

npm version license

A fluent, deferred-registration wrapper around Commander. Register subcommands and global options in any order; the library assembles everything at .parse() so each module can independently contribute its own commands and options.

Installation

npm install super-commander
# or
pnpm add super-commander

Requires Node.js >= 22.12.0.

Quick start

import { SuperCommander } from 'super-commander';

const cli = new SuperCommander('myapp', (cmd) =>
    cmd.description('My awesome CLI').version('1.0.0')
);

// Register in any order; everything is deferred until parse()
cli.addOption('--verbose', 'Enable verbose output');
cli.addSubCommand('upload', (cmd) =>
    cmd
        .description('Upload a file')
        .argument('<file>')
        .action((file) => console.log('Uploading:', file))
);
cli.addSubCommand('download', (cmd) =>
    cmd
        .description('Download a file')
        .argument('<url>')
        .action((url) => console.log('Downloading:', url))
);

cli.parse();

Why super-commander?

super-commander gives you a fluent, order-independent API for building Commander CLIs. Register options and subcommands as you go; the library defers everything until .parse(), so you're free to compose your CLI from independent modules without worrying about what got registered first.

import { SuperCommander } from 'super-commander';

const cli = new SuperCommander('myapp');

// Modules can register their own commands and options independently
function addVerboseOption(cli: SuperCommander) {
    cli.addOption('--verbose', 'Enable verbose output');
}
function addUploadCommand(cli: SuperCommander) {
    cli.addSubCommand('upload', (cmd) =>
        cmd.description('Upload a file').argument('<file>').action(handler)
    );
}

addVerboseOption(cli);
addUploadCommand(cli);
cli.parse(); // Everything assembled here; order doesn't matter

The nested command trees (addSuperSubCommand) let you build deep CLI structures with the same fluent style, each subtree managing its own options and subcommands.

API

new SuperCommander(programName, fn?)

Create a new CLI program. programName sets the root command name shown in help output. The optional fn receives the root Command for setting .description(), .version(), etc.

.addSubCommand(name, fn?)

Register a subcommand. Pass a config callback as fn to set up the command (.description(), .argument(), .action(), etc.), or omit it for a bare subcommand. Throws if name is already registered.

// With config callback
cli.addSubCommand('upload', (cmd) =>
    cmd.description('Upload a file').argument('<file>').action(handler)
);

// Bare subcommand
cli.addSubCommand('status');
.addSuperSubCommand(subCmd)

Register a pre-built SuperSubCommand instance. Throws if a subcommand with the same name is already registered.

.addSuperSubCommand(name, fn)

Register a nested command tree. fn receives a fresh SuperSubCommand (which has all the same methods as SuperCommander). Use it when a subcommand needs its own subcommands, global options, and help configuration. Throws if name is already registered.

cli.addSuperSubCommand('admin', (admin) => {
    admin.withGlobal((cmd) => cmd.description('Admin commands'));
    admin.addSubCommand('users', (cmd) =>
        cmd.description('Manage users').action(() => {})
    );
    admin.addSubCommand('roles', (cmd) =>
        cmd.description('Manage roles').action(() => {})
    );
});
.addOption(flags, description?, fn?)

Register a global option added to the root command at parse time. Throws if the option name is already registered.

// Flags + description
cli.addOption('--verbose', 'Enable verbose output');

// Flags + description + config callback (e.g. set a default)
cli.addOption('--port <number>', 'Port to listen on', (opt) =>
    opt.default(3000).env('PORT')
);

// Flags + config callback (skip description)
cli.addOption('--debug', (opt) => opt.env('DEBUG').default(false));
.withGlobal(fn)

Escape hatch: replace the root Command via a callback. Use for Commander APIs the wrapper doesn't expose (.hook(), .showSuggestionAfterError(), etc.).

cli.withGlobal((cmd) => cmd.showSuggestionAfterError(true));
.configureHelp(config)

Set HelpConfiguration applied to the root command and all subcommands at parse time.

.parse(argv?, parseOptions?)

Assemble the full command tree and parse argv synchronously (defaults to process.argv). Returns the parsed root Command.

.parseAsync(argv?, parseOptions?)

Like .parse() but returns a Promise<Command>. Use when a command action is async and you need to await the result.

.command

Access the underlying root Command. Useful for inspecting the assembled tree after parsing.

.getName()

Returns the program name.

Re-exports

The package re-exports Commander types from the main entry for convenience:

import { SuperCommander, SuperSubCommand, Command, Option } from 'super-commander';
import type { HelpConfiguration, ParseOptions } from 'super-commander';

A dedicated super-commander/commander entry point re-exports everything from Commander:

import { Command, Option, Argument, program } from 'super-commander/commander';
import type { HelpConfiguration, ParseOptions } from 'super-commander/commander';

## License

[ISC](LICENSE)

Keywords