Home

Awesome

<div align="center"> <h1> <strong>&lt;neodoc&gt;</strong> </h1> <em>Beautiful, handcrafted command line interfaces</em> </div> <hr> <p align="center"> <a href="https://www.npmjs.com/package/neodoc"> <img src="https://badge.fury.io/js/neodoc.svg" alt="NPM package"> </a> <a href="https://travis-ci.org/felixSchl/neodoc"> <img src="https://travis-ci.org/felixSchl/neodoc.svg?branch=development" alt="Build Status"> </a> <a href="https://ci.appveyor.com/project/felixSchl/neodoc"> <img src="https://ci.appveyor.com/api/projects/status/hjchg7in2l74by1d/branch/development?svg=true" alt="Build Status (appveyor)"> </a> <a href="https://gitter.im/felixSchl/neodoc"> <img src="https://badges.gitter.im/felixSchl/neodoc.svg" alt="Join the chat at https://gitter.im/felixSchl/neodoc"> </a> <br/> <p align="center"> <b><a href="#about">About</a></b> | <b><a href="#features">Features</a></b> | <b><a href="#installation">Installation</a></b> | <b><a href="#usage">Usage</a></b> | <b><a href="#language-overview-and-terminology">Language overview</a></b> | <b><a href="#license">License</a></b> | <b><a href="https://felixschl.github.com/neodoc">Playground <sup>new</sup></a></b> </p> <p align="center"> <img src="https://raw.githubusercontent.com/felixSchl/felixSchl.github.io/master/neodoc/neodoc.png" alt="preview" /> </p> <hr> <br/>

About

<strong><neodoc></strong> 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:

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.

A note to potential adopters and contributors: Neodoc is divided into two distinct parts — parsing the specification and parsing the argv, given the specificiation. Theoretically, the origin of the specification does not matter and the argv parser could be used standalone as it offers a more "correct" parse than most cli parsers out there, since it parses the input guided by the specification, rather than parsing the input and then matching it to the specification. See the "Features" section below. If neodoc finds adoption, I would not be surprised to see projects mimicking a yargs-like interface that use the neodoc parser, even though it somewhat defies the original idea of docopt.

Features

Installation

npm install --save neodoc

Usage

neodoc.run(help | spec, opts)

Parse and apply the given docopt help text. Alternatively, pass the output of neodoc.parse. 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:

For example:

#!/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 [-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 { /* ... */ }

See the examples folder for a more sophisticated version of the above example.

neodoc.parse(help, opts)

Parse the docopt text and derive the specification along with some meta information. The specification is the canonical representation of the CLI as described by it's help text and can be used for building parsers etc. The output is a plain JS object and can be serialized. The output can further be passed to neodoc.run. This avoids neodoc having to parse and solve the original help text again, since parsing JSON is a order of magnitude faster to parse.

Language overview and terminology

This section gives an overview over the neodoc cli specification language. Keywords are highlighted.

The over-arching format could be described as follows:

Usage:  <program> [<argument>...] [| <argument> [<argument>...]]
[ [or:] <program> [<argument>...] [| <argument> [<argument>...]]
]*

[options:
    [<argument> [<description and meta tags>]
    ]*
]*

Where <argument> may be any of the arguments described in the following subsections.

A full example:

usage: git fetch [options] [<repository> [<refspec>...]]
   or: git fetch [options] <group>
   or: git fetch --multiple [options] [(<repository> | <group>)...]
   or: git fetch --all [options]

options:
    -v, --verbose         be more verbose
    -q, --quiet           be more quiet
    --all                 fetch from all remotes
    -a, --append          append to .git/FETCH_HEAD instead of overwriting
    --upload-pack <path>  path to upload pack on remote end
    -f, --force           force overwrite of local branch
    -m, --multiple        fetch from multiple remotes
    -t, --tags            fetch all tags and associated objects
    [...]

1. Arguments

At the heart of the language are command line arguments. There are three fundamental types of arguments: options, positional arguments and commands. Options are arguments that start with either a single or a double dash ('-'), commands are literal matches of a certain string and positionals constitute everything else. Read on below for more detail on each argument type.

1.1. Options

Options are those arguments that start with either one or two dashes. They are referred to as "short" and "long" options respectively throughout this document and the source code of neodoc.

Options may take an potentially optional option-argument. Options that do not are referred to as flags. Options that do specify an option-argument but declare it as being optional may behave as flags if an argument could not be consumed at runtime.

The following is true for all options:

1.1.1. Long options

Long options are lead by two dashes and may take an potentially optional option-argument.
For example:

Note that all of the above forms could be followed by a ..., indicating that this option may appear one or more times. The repeated occurrence does not necessarily need to be adjacent to the previous match. Repeated occurrences are collected into an array or into a count if the option qualifies as a flag.

Note that successive dashes are allowed: --very-long-option.

1.1.2. Short options

Short options are lead by one dash and may take an potentially optional option-argument. A short option is a one character identifier, but can be "stacked".

For example:

Note, however that only the last option in the "option stack" may actually bind an argument:

...essentially nothing changes when options are stacked. Key is that only the last option in the stack may bind and consume arguments.

Again, note that all of the above forms could be followed by a ..., indicating that this option may appear one or more times. It is important to note that the repeatability is assigned to all options in the stack! Repeated occurrences are collected into an array or into a count if the option qualifies as a flag (hence for all but the last options in the stack).

1.1.3. Option-arguments

Option-arguments are arguments bound to options. If an option is said to take an option argument that is not optional, any attempt to match an option without the argument will result in an immediate parse error. Should an option-argument be declared optional and not matched during parsing, it may be treated as a flag and be substituted.

1.1.3.1. Option-argument bindings
1.1.4 The option secion

The option section gives a chance to add more information about options, such as their default value, their alias or their backing environment variable. Furthermore, options appearing in the option section may also indicate if the option is supposed to be repeatable or not. There is more information on this topic in section "1.7 - References - [options]".

For example:

options:
    -f, --foo BAR  This is foo bar. [env: FOO_BAR] [default: 123]

The text is pretty flexible and can be arranged as the author pleases. For example:

options:
    -f, --foo BAR...
        This is foo bar.
        [env: FOO_BAR] [default: 123]

1.2. Positionals

Positionals are arguments that do not lead with any dashes. The position of their occurrence matters and options are "bounded" by them in that an option declared before an positional argument may not occur after that positional. <sub>(#24)</sub> Positional arguments are distinguished from commands by being either enclosed in angled brackets or being all upper case.

For example:

Positional arguments either yield a single value if not repeated or an array of values if repeated. Note that contrary to options, repetition must occur directly adjacent to the previous match. <sub>(#24)</sub>

1.3. Commands

Commands are a specialized form of positionals that require to be matched literally, including casing. All other rules that apply to positionals apply to commands. They yield a boolean indicating their presence or a count indicating the number of their occurrences if repeated.

For example:

1.4. EOA - end-of-arguments

The EOA (end-of-arguments) is understood as the separator between known and unknown arguments. The eoa is typically -- but any option can become one by using the 'stop-at' setting.\o

For example:

1.5. Stdin marker

The stdin flag is a special, unnamed short-option: -. It's presence indicates that the program should be reading from standard input.

1.6. Groups

Groups are the only recursive argument type. Groups describe one or more mutually exclusive sets of arguments called "branches". At least one branch needs to yield a successful parse for the group to succeed.

For example:

The following is true for all groups:

1.6.1. Matching branches

Branches describe multiple mutually exclusive ways to parse a valid program. Branches can appear at the top-level or in any group. Since branches are mutually exclusive, only one branch can ever succeed. If multiple branches succeed, the highest scoring winner is elected. Generally, the depth of the parse within the branch (that is how deep into the branch the parse succeeded) as well as the weighting of the matched arguments matters. Arguments that were substituted by values in environment variables, or by their defaults or empty values, will have a lower ranking score than those that were read from argv.

1.7. References - [options]

This is not a real argument and not part of the canonical specification. It is used to indicate that the entire "options" section should be expanded in it's place. Since this approach lacks information about the relation between options, options are all expanded as optional and are exchangeable with adjacent options <sub>(#57)</sub>. One exception to this rule is where an option that is defined in the option section also appears directly adjacent to the [options] reference tag.

For example:

usage: prog [options] (-f | -b)
options:
    -f foo
    -b bar

This program won't accept the input -f -b as -f and -b are declared mutually exclusive from one another.

Likewise:

usage: prog [options] --foo ARG
options:
    -f, --foo ARG

Here, --foo won't be expanded again and hence remain required.


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.

License

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