JSPM

  • Created
  • Published
  • Downloads 7622
  • Score
    100M100P100Q128424F
  • License LicenseRef-LICENSE

Docopt implementation for node

Package Exports

  • neodoc

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (neodoc) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

<neodoc>

Beautiful, handcrafted command line interfaces

Build Status Join the chat at https://gitter.im/felixSchl/neodoc

About | Installation | Usage | Goals | Status | Contributing | License | Playground new

preview



About

<neodoc> is a revised implementation of the docopt language for node. In brief, it offers a unique way to author command lines by writing the command line's help text first and then deriving a matching parser from it, which can then be applied to user input. The advantages are numerous:

  • No boilerplate
  • Full control over beautiful, hand-crafted help texts
  • Documentation comes first - hence your users come first
  • Documentation is always right - your help-text is necessarily correct
  • Version-controlled help-text - the help-text becomes a regular part of your codebase

This implementation features error reporting, both for users and developers, reading values from environment variables, type coercion and much more. For an (in-)comprehensive comparison to the original, click here. To take neodoc for a ride, click here.

Features

  • Derive command line interface from help text
  • Helpful error messages for developer and user
  • Options-first parsing to compose large programs (see example below)
  • Fallback to alternate values:
    • User input -> Environment -> Defaults
  • Convenient, concise and widely accepted POSIX-style syntax
    • -f[=ARG], --foo[=ARG] options
    • <arg>, ARG positionals
    • clone, pull, etc. commands
    • [<arg>] optional groupings
    • (<arg>]) required groupings
    • [-f ARG] POSIX-style flags
    • -f[=ARG]... repeating elements
    • 99% compatible with a typical git <command> --help output
    • A full overview of the language is still pending, but things should be intuitive enough to be figured out. To test your intuition, check out the playground. Please do submit an issue if you find something surprising or counter-intuitive.

Installation

npm install --save neodoc

Usage

neodoc.run(helpText, opts)

Parse and apply the given docopt help text. If no options are provided, apply it to process.argv and process.env. The result is a mapping of key -> value, where the key is the canonical form of the option and its alias, if available.

Options:

  • opts.env - Override process.env
  • opts.argv - Override process.argv
  • opts.optionsFirst - Parse until the first command or <positional> argument, then collect the rest into an array, given the help indicates another, repeatable, positional argument, e.g. : [options] <ommand> [<args>...]
  • opts.smartOptions - Enable parsing groups that "look like" options as options. For example: [-f ARG...] means [-f=ARG...]
  • opts.stopAt - Stop parsing at the given options, i.e. [ -n ]. It's value will be the rest of argv.

neodoc.parse(helpText, opts)

Parse the docopt specification. This is the canonical representation of the CLI as described by it's help text and can be used for building parsers etc.

Options:

  • opts.smartOptions - Enable parsing groups that "look like" options as options. For example: [-f ARG...] means [-f=ARG...]

Example

For more examples, check out the 'examples' folder. To see the git example in action, run: node ./examples/git

#!/usr/bin/env node

const neodoc = require('neodoc');

const args = neodoc.run(`
usage: git [--version] [--help] [-C <path>] [-c <name=value>]
           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
           [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]
           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
           <command> [<args>...]
`, { optionsFirst: true, smartOptions: true });

if (args['<command>'] === 'remote') {
    const remoteArgs = neodoc.run(`
    usage:
        git remote [-v | --verbose]
        git remote add [-t <branch>] [-m <master>] [-f] [--tags|--no-tags]
                        [--mirror=<fetch|push>] <name> <url>
        git remote rename <old> <new>
        git remote remove <name>
        git remote set-head <name> (-a | --auto | -d | --delete | <branch>)
        git remote set-branches [--add] <name> <branch>...
        git remote set-url [--push] <name> <newurl> [<oldurl>]
        git remote set-url --add [--push] <name> <newurl>
        git remote set-url --delete [--push] <name> <url>
        git remote [-v | --verbose] show [-n] <name>...
        git remote prune [-n | --dry-run] <name>...
        git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]
    `, { argv: ['remote'].concat(args['<args>']), smartOptions: true })

    // ...
} else { /* ... */ }

Project goals

Overview of the short- and long-term goals of this project

  • Provide a declarative way to author command lines. Rather than deriving the help text from some EDSL, derive the CLI from a human readable, prose-like help text, located e.g. in the project's README. This guarantees documentation and implementation never diverge, because they simply can't.
  • Provide very good error reporting for users of the CLI and at least decent error reporting for developers authoring the docopt text.
  • The manually crafted docopt text must be readable, standardised, yet flexible and powerful enough to handle a fair set of use-cases.
  • A solid interface for use in regular javascript. Purescript should merely be an implementation detail.
  • Solid test coverage
  • Sensible compatibility with original docopt.
  • POSIX compatibility

Deviations from the original

This implementation tries to be compatible where sensible, but does cut ties when it comes down to it. The universal docopt test suite has been adjusted accordingly.

  • Better Error reporting. Let the user of your utility know why his input was rejected.

  • Optional arguments. Neodoc understands --foo[=BAR] (or -f[=<bar>]) as an option that can be provided either with or without an argument.

  • Alias matches. If --verbose yields a value, so will -v (given that's the assigned alias). Likewise, FOO yields value <foo> as well as FOO, and <foo> yields FOO and <foo>.

  • Flags are optional, always. There's no reason to force the user to explicitly pass an option that takes no argument. The absence of the flag speaks - the flag will be set to false. This is also the case for flags inside required groups. E.g.: The group (-a -b) will match inputs -a -b, -ab, -ba, -b -a, -b, -a and the empty input.

  • All arguments in a group are always required. This is regardless of whether or not the group itself is required or not - once you start matching into the group, elements in the group become required for the match to succeed. Consider:

    Usage: prog [<name> <type>]

    will fail prog foo, but pass prog foo bar. The rationale being that this is more general, since if the opposite behaviour (any match) was desired, it could be expressed as such:

    Usage: prog [[<name>] [<type>]]

    note: this rule excludes flags/switches and options that have default values (or other fallback values).

  • No abbreviations: --ver does not match --verbose. (mis-feature in the original implementation)

  • There is no null in the resulting value map. null simply means not matched - so the key is omitted from the resulting value map. (this is still under consideration)

  • Smart-options. Options can be inferred from groups that "look like" options: Usage: foo [-f FILE] would then expand to Usage: foo [-f=FILE]

  • Environment variables. Options can fall back to environment variables, if they are not explicitly defined. The order of evaluation is:

    1. User input (per process.argv)
    2. Environment variables (per [env: ...] tag)
    3. Option defaults (per [default: ...] tag)

Project status

Neodoc is fully usable and well-tested. If you find there is something broken or unintuitive about neodoc, please do open an issue. The following gives on overview of where neodoc is headed.

Beta Target (complete)

  • Scan the docopt text for usage sections and 0 or more description sections
  • Lex and parse usage sections
  • Lex and parse description sections
  • Resolve ambiguities by combining the parsed usage and description sections
  • Generate parser from the solved program specification
  • Lex and parse user input on the CLI
  • Transform the parsed args into something more useful
  • Run against docopt test-suite 99% done
  • Provide developer and user error reporting
  • Provide seamless interface to be called from JS
  • Read arguments from env vars
  • Implement special arguments
    • -- (end of args)
    • - (stdin)
    • [options]

Post-Beta Roadmap

Overview of where neodoc is headed, ordered (somewhat) by estimated priority.

  • Implement "options-first"
  • Improve scanner speed
  • POSIX compatibility (opt-in/out)
    • "Options-first" on by default (guideline 9)
    • Required option arguments be separate from the option: -f BAR
      • Update notation in usage syntax (disallow no spaces)
    • Optional option arguments be the same argument as the option: -fBAR
      • Update notation in usage syntax (disallow spaces)
    • Denote options in singleton groups: [-f BAR] means [-f=BAR] (no option description required)
    • Allow passing negative numbers as option arguments
    • Implement guideline 8 (also see 11)
  • Improve error messages
    • Improve error messages when matching free groups / options (be more specific than "Trailing input")
  • Implement optional option arguments: -a [=foo].
  • Implement --help and --version. The developer will be able to specify the option that will trigger the --help and --version convenience functions, with fallbacks to --help and --version.
    • Implement --help. If matched, print the docopt text.
    • Implement --version. If matched, print the npm package version.
  • Read options from config file
  • Allow for --foo[=<bar>] syntax (git style).
  • Auto-infer types when not specified (e.g. numbers, strings, booleans)
  • Implement [-f BAR] flag syntax ("smart-options")
  • Fix all purescript (pscid) warnings

Wishlist

A list of things that are desired, but have not found a place on the roadmap.

  • Provide typescript typings
  • Read options from config file
  • Add ranges support: -[0-9] would expand to -0123456789 (or -1, -2 ...). For a usecase, see here
  • Read options from prompt (Add a [prompt] tag)
  • Syntax plugins (vim, ...)
  • Put the "source" of a parsed option's value into the output, e.g. "env", "default", "user"
  • Provide warnings

Contributing

Contributions are highly encouraged and welcome. Bug fixes and clean ups do no need much coordination, but if you're interested in contributing a feature, contact me at felixschlitter@gmail.com and we can get the ball rolling — There's plenty to do. To get up and running, run:

npm i && bower i && npm test

NB: Purescript is declared a devDependency in package.json, so no need to bring your own. Also, during development, npm run watch is extremely useful to get immediate feedback.

Implementation overview

A quick overview of the implementation for potential contributors

The project can roughly be broken up into several distinct areas of work:

  1. Scanning the docopt text:
    1. Derive at least 1 usage section
    2. Derive 0 or more description sections
  2. Parsing the Specification
    1. Lex and parse the usage section
    2. Lex and parse any description sections
  3. Solve the parsed Specification into its canonical, unambigious form
  4. Generate a parser from the Specification
  5. Lex and parse the user input
    1. Lex and parse the user input
    2. Transform into a usable form

License

<neodoc> is released under the MIT LICENSE. See file LICENSE for a more detailed description of its terms.