Clitter
Power armor for command line Node.js applications.
Clitter wraps your application and provides it with a command based interface, TUI helpers, formatters, and a nifty, semi-automatic help screen.
Features
- command based app interface
- command line argument parser
- semi-auto help page
- terse API
- TUI helpers
- prompt, confirm, password
- progress bar, spinner
- colorizers and formatters
- shell exec handler
- and more
- single dependency (minimist)
Table of Contents
Install
npm i clitter
Basic Usage
Clitter will make your handlers runnable from command line via commands. Any method that starts with cmd_
will be accessible to command line via its name, with _
characters translated to -
. For example method cmd_add_user
will run with add-user
command. Methods can be sync or async.
const Clitter = require('clitter')
class MyApp extends Clitter {
cmd_mycommand(argv) {
console.log(argv)
}
}
(new MyApp()).run()
Terminal
$ myapp mycommand -abc=3 --flag --option=value
{
_: ['mycommand'],
a: true,
b: true,
c: 3,
flag: true,
option: 'value',
}
Argument parsing
Clitter uses minimist
module to handle argument parsing. Parser config is set via opts
class property, and is optional.
class MyApp extends Clitter {
opts = {
alias: {
x: 'expanded',
},
default: {
name: 'value',
}
}
}
Options:
opts.string
- (str|arr) argument names to always treat as stringsopts.boolean
- (str|arr) argument names to always treat as booleansopts.alias
- (obj) mapping of argument names to their alias names, string or arrayopts.default
- (obj) mapping of argument names to default valuesopts.stopEarly
- (bool) when true
, populate argv._
with everything after the first non-optionopts.unknown
- (fn) function which is invoked with an argument name not defined in the opts. If the function returns false, the unknown option is not added to argv.opts['--']
- (bool) when true
, populate argv._
with everything before the --
and argv['--']
with everything after the --
Lifecycle: Init & Shutdown
init
and shutdown
are optional methods that are called before and after command execution. This is your chance to setup environment, run checks and then cleanup afterwards. shutdown
is also called on termination with .terminate
.
class MyApp extends Clitter {
async init(cmdName) {}
async cmd_mycommand(args) {}
async shutdown(exitCode) {}
}
Help Screen
Clitter provides semi-automatic help screen that needs little configuration to look good. It will pick up all defined commands and display them in declared order. It's up to you to add command descriptions, examples, and other info. Help screen will be shown when app is invoked without arguments, or via help command, ie myapp help
. All description strings and code examples can be multiline, broken with \n
, and it should print nicely aligned.
All fields are optional.
class MyApp extends Clitter {
help = this.Help({
app: 'superapp',
about: 'SuperApp does magic and more',
version: require('./package.json').version,
commands: {
mycommand: 'show command line args',
mycommand: ['description', 'command example'],
mycommand: ['description', ['cmd desc', 'cmd example'] ],
mycommand: ['description', [
['cmd desc', 'cmd example'],
['cmd desc', 'cmd example'],
]],
},
group: {
mycommand: true,
mycommand: 'Admin',
},
flags: {
'-f --fast': 'make the app run faster',
'-m --magic': 'enable real magic',
},
sections: {
'Advanced Usage': '...',
Website: 'https://super.app',
Repo: 'https://github.com/void/superapp',
Author: 'Mr Nobody (hello@dev.null)',
License: 'MIT',
},
hide: ['secretcmd'],
config: {
cmdPrepend: true,
},
})
}
Helpers
All helpers are available from within handlers via this
, ex:
cmd_somecmd() {
const answer = await this.confirm('All good?')
}
Some methods are async, pay attention to that.
Most can be seen in action by running npm run demo
in the package root.
Input
Confirm
.confirm(message, default?)
Get answer to yes/no question. Default is suggested when provided, and can be chosen by a single press of the Enter
key.
const maybe = await this.confirm('Really?', true)
Prompt
.prompt(message, validator)
Prompt user for input. validator
param is a function that must evaluate input, and return value upon success. When undefined
(or nothing), is returned from the validator, the prompt is repeated. Input is always a string.
const year = await this.prompt('What year?', inpt => {
inpt = parseInt(inpt)
if(inpt >= 2000 && inpt <= 2050)
return inpt
})
Password
.password(message, validator, mask='')
Prompt user for password. validator
has the same characteristic as with prompt. mask
is a string that will be used in place of a typed letter, ie *
. Default empty string does not print anyting.
const pass = await this.password(`Password?`, pass => {
if(pass == 'pass')
return pass
else
console.log('Invalid password')
})
Display
Clear
.clear()
Clear entire terminal screen.
this.clear()
Progress
.progress(steps, updateRateMs=200)
Shows animated progress bar. Provides information on current/total steps, steps per second, and the remaining time. updateRateMs
param adjusts how often the bar is updated, with millisecond resolution. Note that nothing else can output to the screen when progress is running, or it will break the display.
const progress = await this.progress(10000)
while(...) {
porgress.tick()
porgress.tick(100)
}
[■■■■■■■■■■■■···················] 48% | 241/500 | 118/s | 3s
Progress bar can also be adjusted on the fly:
progress.setCurrent(int)
progress.setTotal(int)
progress.complete()
Spinner
.spinner(message)
Shows animated spinner until stopped. Can complete with success or failure, the icon will be set accordingly. Optionally both .done()
and .fail()
methods take string arg that will replace original message. As with .progress
, don't output anything when the spinner is running.
const spinner = this.spinner('Processing...')
spinner.done()
spinner.fail('Something went wrong!')
Control Flow
Pause
.pause(message?)
Pauses program execution until Enter
key is pressed. Takes custom message as a parameter.
await this.pause()
Sleep
.sleep(ms)
Sleeps for given amount of milliseconds.
await this.sleep(1000)
Terminate
.terminate(message, code=1)
Terminates program execution with error message. Takes optional exit code as a parameter.
Calls shutdown
with exit code, before quitting.
this.terminate('The computer said no.')
Blocks
.header(text)
Displays nicely padded header.
this.header('Boot')
msgOk
.msgOk(message)
Displays success message with an icon.
this.msgOk('All good')
msgErr
.msgErr(message)
Displays failure message with an icon.
this.msgErr('Not good')
Formatters
Indent
.indent(spacer, text, opts)
Formats multi-line strings with consistent indentation.
Parameters:
spacer
- (str) string used to indent each line withtext
- (str) text to transformopts.prefix?
- (str) string to prefix each line withopts.array?
(bool) return lines as array, otherwise string
Padder
.padder(align, width, padChar=' ')
Returns configured function that will pad and align given string.
Params:
align
- (str) left <
or right >
width
- (int) width of resulting stringpadChar
- (str) character to pad with, defaults to space
const pad = this.padder('>', 10, '.')
const txt = pad('hello')
> '.....hello'
Colors
Functions for string coloring.
Note: Color handling to be reworked in future version to be more flexible.
Basic ANSI 16 colors
this.color.red(str)
this.color.redDark(str)
this.color.redDim(str)
this.color.redBold(str)
this.color.green(str)
this.color.greenDark(str)
this.color.greenDim(str)
this.color.greenBold(str)
this.color.yellow(str)
this.color.yellowDark(str)
this.color.yellowDim(str)
this.color.yellowBold(str)
this.color.blue(str)
this.color.blueDark(str)
this.color.blueDim(str)
this.color.blueBold(str)
this.color.magenta(str)
this.color.magentaDark(str)
this.color.magentaDim(str)
this.color.magentaBold(str)
this.color.cyan(str)
this.color.cyanDark(str)
this.color.cyanDim(str)
this.color.cyanBold(str)
this.color.white(str)
this.color.whiteDark(str)
this.color.whiteDim(str)
this.color.whiteBold(str)
this.color.black(str)
this.color.blackDark(str)
this.color.blackDim(str)
this.color.blackBold(str)
this.color.gray(str)
this.color.grayBold(str)
xterm 256 colors
this.color.xterm(colorCode, str)
this.color.xterm(184, str)
RGB True Color
this.color.rgb(colorStr, text)
this.color.rgb('130;50;240', text)
Shell Commands
Exec
.exec(cmd, options?)
Execute shell command and let it print its output to the console immediately. Uses child_process.spawn
.
You can use strings for simple commands, but for anything more complicated than that, use array in the form: [cmd, [arg1, arg2]]
. For options, see Node docs.
const res = await this.exec(cmd)
res.code
res.error
Exec Capture
.execCapture(cmd, options?)
Execute shell command and capture its output. Uses child_process.exec
.
Use string for command, for options, see Node docs.
const res = await this.execCapture(cmd)
res.code
res.stdout
res.stderr
res.error
Note
This library was tested on Linux only.
License
MIT