๐Ÿ“ฆ justjake / ts-cli

Straightforward and type-safe command-line argument parsing with subcommands.

โ˜… 4 stars โ‘‚ 0 forks ๐Ÿ‘ 4 watching
๐Ÿ“ฅ Clone https://github.com/justjake/ts-cli.git
HTTPS git clone https://github.com/justjake/ts-cli.git
SSH git clone git@github.com:justjake/ts-cli.git
CLI gh repo clone justjake/ts-cli
Jake Teton-Landis Jake Teton-Landis More massaging for README 3e1dd0c 6 years ago ๐Ÿ“ History
๐Ÿ“‚ master View all commits โ†’
๐Ÿ“ examples
๐Ÿ“ src
๐Ÿ“„ .gitignore
๐Ÿ“„ package.json
๐Ÿ“„ README.md
๐Ÿ“„ tsconfig.json
๐Ÿ“„ yarn.lock
๐Ÿ“„ README.md

@jitl/cli

Straightforward and type-safe command-line argument parsing with subcommands.

Example

import { createCLICommand, validate, logger, runThenExitIfMain, runCommand } from '@jitl/cli'

export const command = createCLICommand({
  description: `Say hello`,
  flags: {
    name: {
      description: `What name should we greet?`,
      validate: validate.string(),
    },
    exuberant: {
      description: `Are we excited?`,
      validate: validate.boolean(),
      default: () => false,
    },
  },
  async run({ name, exuberant }) {
    logger.log(`Hello, ${name}. Nice to see you today${exuberant ? '!' : '.'}`)
  },
})

runThenExitIfMain(module, (name, argv) => runCommand(name, command, argv))

Motivation

  • Type-safe without stuttering.
While existing libraries offer typings via @types/... packages, few infer the arguments of the run() function of a command from the parameters.

The parameters of the run(flags, args, rest) function are completely inferred from the runtime type specification, so your CLI command declarations are succinct and readable.

  • Library, not a framework.
Existing Typescript frameworks like oclif force a specific setup, and contain many layers of abstraction between invocation and your run() function. This package offers flexibility through composition, rather than a complex "plug-ins" or "hooks" interface.

  • Usable conventions.
Many command-line frameworks follow typical Unix conventions, which are suitable for experts, but provide a poor user experience for casual CLI users.

  • Prefer long (GNU-style) named flags like --bool | --value given-value.
Removes mental burden of argument order, and makes reading your shell history more clear.

Positional arguments are still available for glob-related use-cases.

  • Prompt users for missing required arguments.
Don't kick the user and force them to read the help.

  • Bike-shedding.
There are many command-line frameworks out there. It's easy to be opinionated about the "mistakes" (choices) a CLI framework makes.

Because this package is short, has a flat dependency graph, and favors composition over hooks/plugins/inheritance, it's easy to replace, wrap, extend, smoosh, and make it your own.

Dependencies

This package only imports its dependencies when they are needed for a specific purpose.

  • chalk: imported when displaying help output. Used to bold text, which has a nice
appearance.
  • table: imported when displaying help output. Used to format the table of
flags, etc.

  • fs-extra: imported when loading a directory of subcommands. Used to scan a
directory for subcommand files. For better performance and type-safety, but also more boilerplate, consider manually constructing a CommandLoaders object.

  • inquirer: imported when a user fails to provide a required argument. Used
to interactively prompt the user for the needed value.

In short, this package never imports a dependency on the happy path to run a fully-specified command.

Documentation

For now, you'll need to read the code.

Known Deficiencies

  • Design modifications for the Command interface to allow nesting subcommands
easily without custom code. Currently, because we throw if an unknown flag is given, a standard CLI command cannot dispatch to subcommands unless they both share the same flags.

An elegant design would allow for arbitrary nesting of commands and subcommand loaders.

  • The current validate: Validator<T> interface is not introspectable at runtime,
which limits the usefulness and accuracy of help messages for an argument. All we can show to guide the user is the default value.

A usable system should be able to infer specific syntax based on the type of the flag.

  • Documentation.
It should be auto-generated from TSDoc comments in the source.

  • Examples for subcommands.
This should go in the ./examples directory.

  • Tests.
Ah, tests.