npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

clitter

v0.5.0

Published

Power armor for command line applications

Downloads

11

Readme

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: {
            // map argument short forms to long ones
            // mypp cmd -x  =>  argv = {_:['cmd'], x:true, expanded:true}
            x: 'expanded',
        },
        default: {
            // default argunment values
            // myapp cmd  =>  argv = {_:['cmd'], name:'value'}
            name: 'value',
        }
    }
}

Options:

  • opts.string - (str|arr) argument names to always treat as strings
  • opts.boolean - (str|arr) argument names to always treat as booleans
  • opts.alias - (obj) mapping of argument names to their alias names, string or array
  • opts.default - (obj) mapping of argument names to default values
  • opts.stopEarly - (bool) when true, populate argv._ with everything after the first non-option
  • opts.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 {

    // init receives command name
    async init(cmdName) {}

    // handler receives command args
    async cmd_mycommand(args) {}

    // shutdown receives exit code
    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({
        // Name of the app
        app: 'superapp',
        // Short information about the app
        about: 'SuperApp does magic and more',
        // Version of the app
        version: require('./package.json').version,
        // Command descriptions
        commands: { // showing several formats
            // most basic, description only
            mycommand: 'show command line args',
            // with single example
            mycommand: ['description', 'command example'],
            // single example with own description
            mycommand: ['description', ['cmd desc', 'cmd example'] ],
            // multiple examples with descriptions
            mycommand: ['description', [
                        ['cmd desc', 'cmd example'],
                        ['cmd desc', 'cmd example'],
            ]],
        },
        // Group commands, separate them by empty line, or a header
        group: {
            // blank line
            mycommand: true,
            // header
            mycommand: 'Admin',
        },
        // Describe flags used by the app
        flags: {
            '-f --fast': 'make the app run faster',
            '-m --magic': 'enable real magic',
        },
        // Sections provide additional information.
        // Keys are arbitrary and will serve as section header.
        // Multiline text is supported, naturally.
        sections: {
            'Advanced Usage': '...',
            Website: 'https://super.app',
            Repo:    'https://github.com/void/superapp',
            Author:  'Mr Nobody ([email protected])',
            License: 'MIT',
        },
        // Commands to hide from the help screen.
        hide: ['secretcmd'],
        // Help page config options
        config: {
            // automatically prepend app and command names to command examples
            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)
// Really? [Y/n]

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(...) {
    // tick by one
    porgress.tick()
    // tick by any
    porgress.tick(100)
}

[■■■■■■■■■■■■···················] 48% | 241/500 | 118/s | 3s

Progress bar can also be adjusted on the fly:

progress.setCurrent(int) // adjust current step and update progress
progress.setTotal(int)   // adjust total steps required to complete
progress.complete()      // complete immediately, fill bar to 100%

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...')
// ...something time taking...
// then complete with success
spinner.done()
// or failure
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) // one second

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

.header(text)

Displays nicely padded header.

this.header('Boot')

msgOk

.msgOk(message)

Displays success message with an icon.

this.msgOk('All good')
// ✔ All good

msgErr

.msgErr(message)

Displays failure message with an icon.

this.msgErr('Not good')
// ✘ Not good

Formatters

Indent

.indent(spacer, text, opts)

Formats multi-line strings with consistent indentation.

Parameters:

  • spacer- (str) string used to indent each line with
  • text - (str) text to transform
  • opts.prefix? - (str) string to prefix each line with
  • opts.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 string
  • padChar - (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  // exit code
res.error // on 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   // exit code
res.stdout
res.stderr
res.error  // on error

Note

This library was tested on Linux only.

License

MIT