Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

neodoc

felixschl31.4kLicenseRef-LICENSE2.0.2

Beautiful, handcrafted command line interfaces

docopt, neodoc, cli, parser, parsing, argument, args, option, command

readme

<neodoc>

Beautiful, handcrafted command line interfaces

NPM package Build Status Build Status (appveyor) Join the chat at https://gitter.im/felixSchl/neodoc

About | Features | Installation | Usage | Language overview | 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 - just write your help-text
  • 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.

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

  • Derive command line interface from help text
  • Helpful error messages for both developers and users
  • Options-first parsing to compose large programs (see git example)
  • Fallback to alternate values: Argv -> Environment -> Defaults -> Empty
  • 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
    • -- end of options separator
    • - stdin marker
    • 99% compatible with a typical git <command> --help output
    • Full overview of the language →
  • Stop parsing at any option and collect successive input as the argument to that option. Similar to -- but for named options (and their aliases).
  • Specification parsing (help-text parsing) is separated from argv parsing and can be used for other projects outside of neodoc. Work is underway to make the argv parser usable from JS as well.
  • Count repeated flags
  • Parses values into primitive JS types (bool, string, number)
  • Correct and smart argument parsing. For example, neodoc has absolutely no problem parsing this input: tar -xvzfsome-dir/some-file, given a specification of: usage: tar [-xvzf FILE] while other parses would not know that the option stack ends at -f and falsly parse this as -x -v -z -f -s -o -m -e=-dir/some-file at best.

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:

  • opts.dontExit - Do not exit upon error or when parsing --help or --version. Instead throw and error / return the value.
  • 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.
  • opts.requireFlags - Require flags be present in the input. In neodoc, flags are optional by default and can be omitted. This option forces the user to pass flags explicitly, failing the parse otherwise.
  • opts.laxPlacement - Relax placement rules. Positionals and commands are no longer solid anchors. The order amongs them, however, remains fixed. This implies that options can appear anywhere.
  • opts.versionFlags - An array of flags that trigger the special version behavior: Print the program version and exit with code 0.
  • opts.version - The version to print for the special version behavior. Defaults to finding the version of the nearest package.json file, relative to the executing main module. Note that disk IO is only performed if opts.versionFlags is non-empty and opts.version is not set.
  • opts.helpFlags - An array of flags that trigger the special help behavior: Print the full program help text and exit with code 0.
  • opts.repeatableOptions - Allow options to be repeated even if the spec does not explicitly allow this. This "loosens" up the parser to accept more input and makes for a more intuitive command line. Please note: repeatability is still subject to chunking (use opts.laxPlacement to relax this further).
  • opts.transforms.presolve - an array of functions to be called prior to "solving" the input. This function takes the spec as it's only parameter. At this point, the spec is mostly untouched by neodoc with the exception of smart-options which runs as a fixed transform prior to user-provided callbacks if smart-options is true. Transforms that need to be aware of option stacks and [...-options] references should run here as this information is lost during the solving transforms.
  • opts.transforms.postsolve - an array of functions to be called after "solving" the input, just prior to passing the spec to the arg-parser. This function takes the spec as it's only parameter. At this point, the spec has been fully solved, expanded and canonicalised.
  • opts.allowUnknown - Collect unknown options under a special key ? instead of failing. Useful to send an unknown subset of options to another program.

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:

  • Options may take an optional "option-argument"
  • Options may be repeated using ...
  • Adjacent options are not fixed in position: -a -b is equivalent to -b -a. Likewise, -ab is equivalent to -ba. This also holds true for options that take option-arguments.
  • Options that are repeated collect values into an array
  • Flags that are repeated count the number of occurrences
  • Flags that are not repeated simply yield true if present
  • Flags and options with an optional option-argument can always be omitted from the input. They simply won't yield anything in the output value mapping.
  • Options may alias a short (one-character) form with a long form, e.g.: -v, --verbose
  • Options that take an argument can specify a [default: <value>] in the option section as fallback.
  • Options that take an argument can specify a [env: <key>] in the option section as fallback.
1.1.1. Long options

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

  • --long <ARG> the option-argument is loosely bound
  • --long <ARG> the option-argument is loosely bound and optional. (#55)
  • --long=<ARG> the option-argument is explicitly bound
  • --long[=<ARG>] the option-argument is explicitly bound an optional
  • [--long <ARG>] the option-argument is explicitly bound via the smart-options setting
  • [--long [<ARG>]] the option-argument is loosely bound via the smart-options setting and optional

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:

  • -a <ARG> the option-argument is loosely bound to -a
  • -a=<ARG> the option-argument is explicitly bound to -a
  • -a<ARG> the option-argument is explicitly bound to -a
  • -aARG the option-argument is loosely bound to -a
  • -a [<ARG>] the option-argument is loosely bound to -a. (#55)
  • -a=<ARG> the option-argument is explicitly bound to -a
  • -a[=<ARG>] the option-argument is explicitly bound to -a an optional
  • [-a <ARG>] the option-argument is explicitly bound to -a via the smart-options setting
  • [-a [<ARG>]] the option-argument is loosely bound to -a via the smart-options setting and optional

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

  • -abc is equivalent to -a -b -c
  • -abc <ARG> is equivalent to -a -b -c <ARG>
  • -abc=<ARG> is equivalent to -a -b -c=<ARG>
  • -abc[=<ARG>] is equivalent to -a -b -c=<ARG>

...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
  • "loose" binding: the option-argument is in adjacent position, but needs to be confirmed in the 'options' section. Should confirmation not take place, the adjacent argument is treated as a positional.
  • "explicit" binding: the option-argument is explicitly bound due to a lack of whitespace, an equal sign or through 'smart-options'.
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]".

  • An alias is assigned via -v, --verbose
  • A default value is assigned via [default: value]
  • An environment variable is assigned via [env: MY_KEY]

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. (#24) Positional arguments are distinguished from commands by being either enclosed in angled brackets or being all upper case.

For example:

  • <ARG> is a positional element named <ARG>
  • ARG is a positional element named ARG
  • [<ARG>] is an optional positional element named <ARG>
  • [ARG] is an optional positional element named ARG
  • [<ARG>]... is an optional positional element named <ARG> that repeats
  • <ARG>... is a positional element named <ARG> that repeats

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. (#24)

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:

  • command must be matched with input "command" on argv
  • command... must be matched on ore more times with input "command" on argv

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:

  • --
  • -- ARGS
  • -- ARGS...
  • [-- ARGS...]
  • [-- [ARGS...]]
  • ...and so on — they all have the same meaning.

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:

  • (foo | bar qux) means either match command foo or command bar directly followed by command qux.
  • [foo | bar qux] means either match command foo or command bar directly followed by command qux, but backtrack on failure and ignore the group.
  • (foo | bar qux)... means either match command foo or command bar directly followed by command qux, repeatedly. During repetition another branch can be matched, so this is valid: foo bar qux bar qux foo. The output is: { "foo": 2, "bar": 2, "qux": 2 }.

The following is true for all groups:

  • Groups can be repeated: ...
  • Groups can be optional using brackets: [ foo ]
  • Groups can be required using parenthesis: ( foo )
  • Groups must not be empty
  • Groups must contain 1 or more branches
  • Groups succeed if at least one branch succeeds
  • Multiple successful branch matches are weighted and scored
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 (#57). 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.

  • Better Error reporting. Let the user of your utility know why input was rejected and how to fix it
  • 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).
  • Flags are optional by default. There's arguably no reason to force the user to explicitly pass an option that takes no argument as the absence of the flag speaks — the key will be omitted from the output. 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. To disable this behaviour, enable options.requireFlags (see neodoc.run).\ Please note that the default behaviour may change in a future version of neodoc — refer to #61.
  • All arguments in a group are always required. This is regardless of whether or not the group itself is required - once you start matching into the group, all elements that are indicated as required have to be matched, either by value or via fallbacks.\ For example:
    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>]]
  • 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.
  • 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)
  • Stricter document layout. Neodoc imposes more restrictions on the format of the help text in order to achieve certain goals, such as:

    • Neodoc allows associating option aliases over newlines:

      options:
       -f,
       --foo this is foo
    • Neodoc does not require 2 spaces between option and argument. Instead, only those arguments that visually "look like" arguments are considered for binding (i.e. all-caps: ARG and in-angles: <arg>):

      options:
       -f,
       --foo ARG

      Should there be any ambiguity, the option can also be explicitly bound:

      options:
       -f,
       --foo=ARG

      The same is true for optional arguments:

      options:
       -f,
       --foo [ARG]
       -b,
       --bar[=ARG]
    • Neodoc is more conservative on learning about options in order to prevent subtly introducing options:

      usage: prog [options]
      
      options:
      
      --foo this is foo, and it is similar to some-command's
              --bar in that it does qux.

      Here, the author is talking about --bar in another context, so it should not be considered an option to the program. Neodoc figures this out based on indentation of the previous description start.

    • Neodoc allows interspersing spaces in between usage layouts:

      usage:
        prog foo bar
      
        prog qux woo

      it is important to not that in this format (above), identation of the layout is required. or:

      usage: prog foo bar
      
         or: prog qux woo
      • Neodoc requires an Options: section title in order to parse any options (see #76 for discussion). note: I am interested in a proposal on how to lift this requirement.

License

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

changelog

Changelog

Please note that all these tags mark releases that are available on npm with the respective version number - unless otherwise noted.

2.0.0 - 2018-02-20

Changes

  • BREAKING CHANGE - Fix #94: Long options no longer bind implicit arguments through substring matches.\ Before this change, the program usage: foo --foo=<arg> allowed passing in --foobar, which would yield --foo => "bar", which was found to have undesirable side-effects as explored in #94. Since changing this behavior has not only follow on effects for users of neodoc, but also users of users, a major version bump was necessary.

1.4.0 - 2017-02-17

Changes

  • Fix #89: [options] are now expanded without a surrounding group which used to cause patterns being discarded to eagerly due to partial matches

1.3.0 - 2016-11-02

New features

  • It is now possible to capture any unknown options, while still validating known options and positional/command arguments. With opts.allowUnknown toggled on, any unknown options are collected into a special key "?" in the output mapping. Note that, unknown input is collected "verbatim", i.e. as it was passed on the command line. This closes #42.

1.2.0 - 2016-10-31

This release focused mostly on performance. While performance is drastically improved with this release, it won't be the end of things in a continous effort to get neodoc as snappy as possible, while retaining it's strong parsing capabilities and feature set.

Changes

  • Update to purescript 0.10.x
  • Improve overall performance #81
    • To give an idea, the uglify example now runs about 140ms faster than before!
    • Optimize large parts of the code-base (lexer, solver, arg-parser)
    • Pre-trim descriptions section to speed up descriptions lexing
    • Let go of purescript-parsing, use own parser everywhere
    • Avoid partial function application where feasible, especially if function takes many arguments.
    • Optimize many parser combinators to work especially well with chars
    • Run the compiled output through the closure compiler to bring down require times. This comes at the cost of readable output, but provides a rough 20ms boost requiring neodoc, which is now around 50ms on the machines I tested on. Still not ideal, maybe we can shed more bloat.

1.1.0 - 2016-10-22

Fix

  • Fix #72: Add special flags to spec implicitely

Changes

  • Improve special flags behavior with opts.dontExit. Instead of just returning a string, return the output and add a special key .help for opts.helpFlags and .version for opts.versionFlags, respectively.
  • Allow dots in option names
  • Improve OptionAlias JS representation. It is now just a string. This makes it easy to write, read and validate opts.helpFlags and opts.versionFlags.

1.0.2 - 2016-10-20

Changes

  • Fix #80: Allow slashes in command names

1.0.1 - 2016-10-20

Fix

  • Fix bug in foreign instance, where opts.repeatableOptions would not be read properly

1.0.0 - 2016-10-19 :tada:

The first major release of neodoc.

Where the project is at

As a run up to v1, I decided to spend a lot of my time refactoring the code-base for various reasons. Primarily the core data structures that were employed prior to the refactor were insufficiently flexible and, looking back, clearly the wrong choice. The new core data structure, which resembles a tree, is by far more elegant and flexible, allows transformations and traversals and is more obviously "correct". Secondly, I acknowledged that the code was hard to follow and hindered adoption / contributors from getting into the code base. The most important pieces of the codebase are now documented and hopefully approachable enough.

New features

  • Neodoc now sports a couple of hooks that allow devs to hook into the transformation of the spec. See opts.transforms in the README for more information.
  • --version will now simply return the version as a string if opts.dontExit is true.
  • --help will now simply return the help as a string if opts.dontExit is true.
  • A new option options.repeatableOptions is introduced that allows consuming excess options. Many command line utilities do not care about excess options, so now neodoc can be told to not care either. This means that after successfully parsing a bunch of options, any option may-reoccur again.
  • Better error reporting. The parser now tracks the deepest error across all branches, making for more intuitive error messages.
  • The spec is now being canonicalised internally which helps create (a) better error messages and (b) to speed up the parser.
  • considerably better performance under difficult configurations and edge cases, i.e. when combining opts.laxPlacement with opts.requiredFlags and so on.

Changes

  • The "specification" format has changed drastically. If you've been using neodoc.parse, it's output is now entirely different and likewise the input to run expects the new neodoc.parse output. If you used this method to change the spec on-the-fly, you should have a look at the new opts.transforms option which allows you to easily hook into the transformation of the neodoc spec.

Internals

  • Major overhaul of the internals. The core data structures have changed and are now more composable.
    • The arg parser has been rewritten from the ground up, with next to none code sharing. The goal was a more efficient, simpler and better documented solution that is easier to reason about and debug.
    • Instead of representing a group as an argument, we now have a Layout data type which looks more like a classical tree:
      data Layout a = Group IsOptional IsRepeatable Branches | Elem a
      It turns out that this structure holds true throught all the various stages of transformation.
    • There's finally a Spec datatype that formalises what a specification is:
      type Branch a = NonEmpty List a
      type Toplevel a = List (Branch a)
      newtype Spec a = Spec {
        program      :: String
      , layouts      :: NonEmpty List (Toplevel a)
      , descriptions :: List Description
      , helpText     :: String
      , shortHelp    :: String
      }
    • A custom parser monad for arg-parsing that does not use monad transformers. It enables neodoc to better handle errors and should generally run faster. It allows the parser to keep read-only state, state that isolated in alternatives and when backtracking, as well as state that is "mutable", i.e. state that survives failures. In terms of running faster, the worst case that previously required adding in a cache now runs fine and the cache was removed.
    • Branches in groupings as well as top-levels are now encoded at the type level as NonEmpty
    • Move away from records to ADTs for speed and ease of use in many scenarios
    • Use AsForeign and IsForeign for FFI needs
    • Improve test bed readability all over, make it more easy to add tests
    • Overall add more comments and document entire modules in an effort to make the code "contributable"

0.10.1 - 2016-08-18

Fixes

  • Fix #70 - Ignore ANSI escape codes in parser. This allows the neodoc to be colored. For example:
    neodoc.run(`
    ${chalk.blue('Usage:')}
        prog <command> [<args>...]
    `);
    Thanks @matthewmueller for reporting
  • Fix #71 - Do not trim help text. This allows the developer to keep some left padding on the help text. The leading and trailing newlines are still removed, however, in order to make working with JS template strings easy. Thanks @matthewmueller for reporting
  • Fix optional positionals in lax-placement mode. For example:
    usage: prog [foo] -a
    Would fail: -a foo before this patch because [foo] simply fails at -a and gets omitted. The added tests cover these cases.
  • Fix 'stop-at' not working in groups in certain cases

0.10.0 - 2016-08-12

New features

  • Fix #48 - Implement special support for --help and --version. If --help is encountered, print the original, full help text. The flags that trigger this behavior can be overridden using options.helpFlags (ex. { helpFlags: ["-h"] }). To disable this behavior, pass it the empty list. Likewise, if --version is met, print the version of the CLI. The version can be explicitly set via options.version, but neodoc will otherwise fall back to look for a package.json of the executing main module (see require.main). The flags that trigger this behavior can be overriden using options.versionFlags. Again, use the empty list to turn this behavior of entirely.

Changes

  • Only parse repeating option-arguments repeatedly if the first argument is NOT bound explicitly. Ex:

    usage: prog --foo=<bar>... qux
    
    $ prog --foo=10 qux => {"--foo": [10], "qux": true }
    $ prog --foo 10 qux => error! missing 'qux' (got consumed by --foo).

Fixes

  • Fix case where parsing became extremely slow when options.laxPlacement was enabled.

0.9.2 - 2016-07-26

Fixes

  • Fix repeating positionals / commands in options.laxPlacement mode.

0.9.1 - 2016-07-26

Fixes

  • Fix options.laxPlacement - Ensure the positioning among positionals and commands remains in-tact.

0.9.0 - 2016-07-23

New features

  • Fix #24 - Add an option options.laxPlacement that relaxes the rules around argument positioning. Options can now appear anywhere in the input.
  • Fix #64 - Options may now specify more than two aliases. Consider: -h, -?, --help

Changes

  • Drastically improve arg parser performance by caching previous parses. This turned out to make a big difference because of the sheer amount of permutations the parser has to deal with, parsing the same input many times over.

0.8.0 - 2016-07-17

New features

  • Fix #61 - It is now possible to require flags be present in the input via option.requireFlags
  • It is no possible to run from a spec, previously generated by neodoc.parse. This allows neodoc.run(neodoc.parse(help), options) style usage with the primary use-cases of caching and spec alteration in mind. Caching the neodoc.parse output as JSON, for example, would have a massive impact on the time it takes to parse user input. We are talking ~30x faster.

Fixes

  • Fix #62 - remove unnecessary newlines lines from help output

Changes

  • Error messages have further drastically improved
  • Neodoc now fails to parse the usage section if two usage rows use a different program name
  • Neodoc now prints the program name in front of the error message
  • Neodoc now prints the error message first, and then the usage. Does this need to be configurable?

0.7.0 - 2016-07-03

Fixes

  • Fix #55 - Bind adjacent singleton group (optional and required) to the option to the left of it, if the options description indicates so.

Changes

  • Parse optional adjacent arg in option description section. For example:
    usage: prog [options]
    options:
      -f [BAR]  ...
  • Neodoc now performs even a litte faster.
  • Improve error messages for unknown options. Check left-over arguments on argv to see if they appear anywhere in spec and if not, show a different error message indicating that the argument is unknown.
  • Parse option-arguments repeatedly — BREAKING CHANGE\ \ Parse option-arguments repeatedly if the actual option-argument is said to repeat (via ...). This way multiple arguments can be passed right after the option:
    usage: prog --foo=ARG...
    This would be able to match $prog --foo 1 2 3 and yield: {"--foo": [ 1, 2, 3 ]}. The problem, however, is detecting when to stop parsing since it will eagerly consume any positionals and arguments required for a successful parse. \ \ The way it's implemented in this commit, the user would have the option to opt out of this behavior by wrapping the option in a singleton group:
    usage: prog (--foo=ARG)...
    This way, in order to repeat the option, the user would have to bring enter multiple --foo: $prog --foo 1 --foo 2 --foo 3. \ \ Note, however that even with the former usage, the latter is possble: $prog --foo 1 2 --foo 3 would be equivalent to $prog --foo 1 2 3

0.6.1 - 2016-06-24

Changes

  • Neodoc now performs even a litte faster.

0.6.0 - 2016-06-23

Changes

  • Neodoc now performs a lot faster, especially around lexing and parsing.

0.5.0 - 2016-06-17

New features

  • options.stopAt has been expanded to also work on options. Note that if an option has an alias, stop-at will cause to stop parsing for any of it's aliases. So if stop-at is set to '["-n"]', and "-n" is defined as "-n, --never", the parser will stop both for "-n" and "--never" (whichever is met earlier).

Fixes

  • Fixes major bug where the positional/command foo would resolve the same internal key as --foo, yielding the wrong value in the output map!

Changes

  • Disallow --foo... BAR if --foo is said to take an option-argument

Internals

  • Rewrite the entire arg parser module!

0.4.0 - 2016-06-07

New features

  • Add neodoc.parse which effectively exposes the neodoc spec to JS.
  • Add support for a custom EOA (end of arguments separator). This allows to stop parsing at a given positional/command argument: options.stopAt. Does not require options.optionsFirst

Fixes

  • Fix #41 - Parse fails at non-empty option section that contains no options
  • Fix entire groups not repeating properly

Changes

  • Print back the original usage section (#10)
  • Dedent help output
  • Remove positional argument aliases from output map — BREAKING CHANGE The reasoning is to keep the output canonical and clean.
  • Improve the options parser:
    • Allow for (commas are optional)
      -f, --foo
      --foo, -f
      -f=ARG, --foo # no argument-mismatch error
      --foo, -f=ARG # no argument-mismatch error
    • Do not swallow bad parses and provide meaningful error messages
  • Allow -? as a valid flag

Internals

  • A big refactor took place. Things moved, got renamed, made into Records and so on. The codebase is a lot tidier now.

0.3.0 - 2016-05-25

Fixes

  • Fix #31 - Allow [--] to denote optional --
  • Fix #33 - Allow to omit option-argument in either option or usage section if it is denoted as optional in at least one section.
  • Fix #34 - Fix option parser tripping over -- in option an options description text in the options section.
  • Lift strict alignment rules of options in the description section. A new option or positionals description is assumed if it is less indented than the preceding text block of descriptions

0.2.1 - 2016-05-11

Changes

  • Update README to contain a link to the playground

0.2.0 - 2016-05-10

Changes

  • Inherit repeatability in nested groups
  • Populate values at parser level — BREAKING CHANGE \ \ This is a fairly large re-think of how argument values are fetched from the their various input sources. Before, values would be loaded during a step after parsing, where the matched usage branch would be completely unrolled, unified and then populated. This had the disadvantage that one could not tell which of the mutually exclusive branches produced a result, since one value might arrive from ARGV and another one from the [default: ...] tag, or the environment, etc. (See #8) \ \ Now, values are loaded at parse-time, removing these issues. Further, "empty" values are elided from the output, representing the user not trying to match these keys (as opposed to choosing to set them explicitly). For example, the user might pass the value 'false' to an option and that value will be retained. If the option however yields false because there was no user input and because 'false' is its' empty fall-back, the value will be omitted. The same goes for matching repeating elements into an array. At least one element needs to be matched before a value will be yielded.

0.1.0 - 2016-05-05

:tada: This marks the first minor release for neodoc. Things are stabilising.

Changes

  • Fail fatally if an option's required option-argument is missing from the input. The parser won't even attempt to try any other branches — it will fail immediately. The same applies for providing an argument to an option that does not take an argument.

Internals

  • Run the compat spec parser on a trampoline to avoid stack overflows

0.0.15 - 2016-05-03

Changes

  • Stricter lexing of options:
    • Disallow spaces between -f and [=OPTARG] (only allow -f[=OPTARG] without any spaces).
    • Disallow '-' after '--' and '-' to make them distinct
    • Disallow '-' after any option
    • Disallow '[' and '(' right after any option

Fixes

  • Fix number value parser for user input
  • Short options - parse option-argument values if the option-argument was assigned explicitly

0.0.14 - 2016-05-02

Changes

  • Do not interpret quoted options, e.g.: prog '-x = foo' would currently yield -x=foo. As of this version, it will simply yield the string -x = foo. Explanation: Since the shell does the argument splitting for us, we receive as an argument the string "-x = foo" as opposed to three arguments -x, = and foo (which would, rightfully, result in -x="=" foo).
  • Enforce EOF at end of usage section
  • Ensure --[no-tags] won't parse for now since flag negation behavior is not yet implemented and the semantics will change once it is.
  • Improve overall error reporting

0.0.13 - 2016-05-01

Fixes

  • Allow termination via options-first inside a group. Parsing will simply halt, no matter how deep in a group once a positional / command argument is encountered (#21)
  • Fix ambiguous fallbacks not resolved across groups (#22)

0.0.12 - 2016-04-27

Fixes

  • Fix repeating option not being required at least once.

Changes

  • Values parsed on argv that have commas in them are no longer parsed into a list of values. This behavior was too specific, since who decides what the delimiter should be? Developers are advised to either a) use repeating arguments or b) split the value themselves after parsing. This change does not, however, have an impact on the parsing of the [default] tag - those values will still be parsed into lists since it's value is in the developer's control.This could be revised, if necessary.
  • Select fallbacks in ambiguous parses based on a score, where environment values based fallbacks rank higher than provided [default] values, etc.

0.0.11 - 2016-04-26

New features

  • Implement smart-opts. This is a new behavior that allows explicitly (as opposed to loosely) binding an option-argument to an option, just by placing it into a singleton group. For example: usage: foo [-o FILE]. In this example, given that options.smartOpts is true, no additional option description section is required in order to bind FILE to -o.

Changes

  • Lift the constraint on positionals / commands qualifying as candidates for options-first. Before, it was required that this argument be indicated as optional. This requirement has now been lifted.

0.0.10 - 2016-04-25

New features

  • Implement proper [options] expansion. This means that [options] is now aware of it's surroundings and won't expand options already present. This allows to intermix [options] with explicitly mentioned options.

0.0.9 - 2016-04-16

New features

  • It is now possible to indicate the repeatability of an option in the option description section.

Changes

  • In case of ambigious parses across group branches, select the left-most match.

0.0.8 - 2016-04-18

New features

  • Add the options.dontExit option to neodoc.run (defaults to false). Turning this option on will cause neodoc to throw an error, rather than quitting the program.

Changes

  • Error messages now longer show squiggly error lines. The behavior was neither aesthetically pleasing nor very helpful as a tool. Render simple error messages instead
  • Improve error message for mutually exclusive branches in a group
  • Add examples to the codebase
  • Restrict angle names to not contain a opening angle (<). This means that the following name is now illegal: <<foo>. There is no technical reason to this limitation and was introduced merely to avoid subtle parse errors. This could be revised, if necessary.
  • Disallow specifying an option twice in the options section
  • Disallow associating an option-argument with a non-trailing stacked option
  • Strictly check the adjacent arguments for the correct argument

0.0.7 - 2016-04-13

Changes

  • Relax the parsing of "free" groups — remove the restriction that in order for a group to be considered "free" it must be a singleton group. Now any group whose branches are all "free" is considered "free" itself.

Internals

  • Add more tests - increase scenario coverage

0.0.6 - 2016-04-11

Only internal changes and fix ups. Notably, simplify the lexer module by removing all references to the word lexer which has been previously decomissioned. Also avoid parsing the Garbage token when lexing the usage section — let it fail at the lexing stage.

0.0.5 - 2016-04-08

New features

  • Add 'options-first' feature
  • Add support for optional arguments

Internals

  • Add more tests - increase scenario coverage
  • Overhaul the parser generator

0.0.4 - 2016-04-05

Changes

  • Print only usage section upon error as opposed to complete help text

0.0.3 - 2016-04-03

Changes

  • Lift the restriction on command and positional names. Names can now have dashes, underscores and dots

0.0.2 - 2016-04-02

:tada: This marks the first generally available release of neodoc