Skip to content

jayrdeaton/Termkit

Repository files navigation

Termkit

A fluent CLI framework for Node.js with nested subcommands, middleware, and TypeScript support.

Installation

npm install termkit

Usage

Basic program

import { command, option, parse } from 'termkit'

const program = command('my-app')
  .version('1.0.0')
  .description('My CLI application')
  .option('v', 'verbose', null, 'Enable verbose output')
  .option('o', 'output', '<file>', 'Output file path')
  .action((options) => {
    console.log(options.output)
  })

try {
  program.parse(process.argv)
} catch (err) {
  console.error(err)
}

Options

Options support four variable shapes:

command('app')
  .option('b', 'boolean', null,        'Flag with no value')
  .option('o', 'optional', '[val]',    'Optional value')
  .option('r', 'required', '<val>',    'Required value')
  .option('a', 'array',    '[val...]', 'Array of values')

Accessible in actions and middleware via the long name:

.action((options) => {
  options.boolean  // true | undefined
  options.optional // string | true
  options.required // string
  options.array    // string[]
})

Value coercion

Append :number or :boolean to a variable to coerce the parsed string:

command('app')
  .option('p', 'port',    '<port:number>',     'Port number')
  .option('v', 'verbose', '[verbose:boolean]', 'Verbose mode')
  .option('n', 'nums',    '[nums:number...]',  'List of numbers')
  .action((options) => {
    options.port    // number
    options.verbose // boolean
    options.nums    // number[]
  })

Subcommands

Commands nest to any depth. Each level can have its own options and middleware.

import { command, option } from 'termkit'

command('app')
  .commands([
    command('serve')
      .option('p', 'port', '<port:number>', 'Port to listen on')
      .action((options) => startServer(options.port)),

    command('build')
      .option('w', 'watch', null, 'Watch for changes')
      .action((options) => runBuild(options)),
  ])

Commands can also carry their own positional variables:

command('get', '<id>')        // required
command('list', '[filter]')   // optional
command('tag', '[tags...]')   // array

Middleware

Middleware runs before the action. It can mutate the options object and supports async.

command('app')
  .middleware(async (options) => {
    options.user = await getUser(options.token)
  })
  .action((options) => {
    console.log(options.user)
  })

Use .middlewares([...]) to register several at once. When navigating into a subcommand, parent middleware runs first.

Default middleware and options

Apply middleware or options to every command created after the call:

import { setDefaults } from 'termkit'

setDefaults({
  middlewares: [
    async (options) => { options.timestamp = Date.now() }
  ]
})

Built-in help

Passing help anywhere in argv prints a formatted usage table and stops execution:

my-app help
my-app serve help

API

Functions

Function Signature Description
command (name, variables?, info?) => Command Create a command
option (short, long, variables, info) => Option Create an option
middleware (fn) => fn Identity helper for typing middleware inline
parse (argv) => Promise<unknown> Parse using the root command
setDefaults (defaults) => void Set defaults applied to all new commands

Classes

Command, Option, Variable, Termkit

Types

ActionFn, MiddlewareFn, ParsedOptions, CommandDefaults, VariableType

Authors

Jay DeatonGitHub

License

MIT

About

Terminal input parser

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors