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

mixa

v0.7.0

Published

Fast and simple command line argument parsing with subcommands

Downloads

14

Readme

M.I.X.A.

MIXA logo

Table of Contents generated with DocToc

M.I.X.A.

  • M.I.X.A. is short for Meta—Internal/eXternal command—Arguments
  • this is the generalized command line structure
  • flags, a.k.a. 'options', 'settings', Boolean or not:
    • Boolean flags have no srgument and are true when given
    • flags of other types must be given a value as -f value, --flag value, optionally with an equals sign between the flag name and the value
  • Internal commands must parse their specific flags and other arguments.
  • External commands call a child process that is passed the remaing command line arguments, so those can be dealt with summarily.

M.I.X.A. for Command Line Argument Parsing

Command Line Structure

node cli.js │ --cd=some/other/place funge --verbose=true -gh 'foo'
│  │ │    │ │ │                   │ │   │ └────────────┘ └─┘ └───┘
│  │ │    │ │ │                   │ │   │   L             S   P
└──┘ └────┘ │ └───────────────────┘ └───┘ └──────────────────────┘
  E    S    │   M                    I/X    A
  • E—executable
  • S—script
  • M—meta flags (flags that pertain to MIXA)
  • I/X—internal or external command
  • A—arguments (flags that pertain to the command)
  • L—long flag verbose with value
  • S—short Boolean flags g, h
  • P—positional flag

A valid command line must either call for printing a application-specific help (using one of ... -h, ... --help, or ... help), or application version (using one of ... -v, ... --version, or ... version), or else spell out a configured application-specific command (including additional arguments where required).

Metaflags

Metaflags are command line arguments preceded by one (short form) or two (long form) dashes that are placed before the command line like so:

node cli.js --help                                  # show help and exit
node cli.js -h                                      # dto.
node cli.js --version                               # show version and exit
node cli.js -v                                      # dto.
node cli.js --cd some/other/place/somewhere foo 42  # change into directory given, then run `foo 42`
node cli.js -d some/other/place/somewhere foo 42    # dto.

The above are the preconfigured metaflags, but one can define additional ones in the Job Definition (<jobdef>).

Job Definition (<jobdef>)

A Job definition (an object of type <jobdef>) specifies declaratively what additional metaflags and commands are available for the application in question. A Job definition may contain the following fields (question mark indicates optional value, angle brackets specifiy types; the default is given behind the equals sign):

  • ?exit_on_error <boolean> = true—When calling MIXA.run jobdef, input, determines whether to exit with an exit code in case an error in the jobdef or the input was detected. exit_on_error does not affect the behavior of MIXA.parse jobdef, input.
  • ?meta <mixa_flagdefs>—An object that specifies (additional) metaflags to go before the command. See Flag Definitions (<flagdefs>, <flagdef>)
  • ?commands <mixa_cmddefs>—See Command Definitions (<cmddefs>, <cmddef>).

Example:

jobdef =
  exit_on_error: true
  commands:
    foo: { ... definition for command `foo` ... }
    bar: { ... definition for command `bar` ... }

This job definition declares that there two commands foo and bar in the application.

Command Definitions (<cmddefs>, <cmddef>)

The keys of a cmddefs object are interpreted as command names; its values are <cmddef> objects:

  • ?description <text>—A helpful one-liner to explain what the command does.
  • ?allow_extra <boolean> = false—Whether to allow unspecified arguments on the command line; these will be made available as verdict.extra.
  • ?flags <mixa_flagdefs>—An object detailing each command line argument to the command in question.
  • ?runner <_mixa_runnable>—A synchronous or asynchronous function to be called by MIXA.run jobdef, input provided no error occured during validation of jobdef and parsing the input
  • ?plus <any>—Any additional value or values that should be made accessible to the runner as verdict.plus.

Example (cont'd from above):

# file: cli.coffee

MIXA = require 'mixa'

jobdef =
  exit_on_error: true
  commands:
    foo:
      description:    "Do something awesome"
      allow_extra:    true                          # allow unconfigured extra arguments
      runner:         run_foo                       # function to be called
    listfiles:
      runner:         MIXA.runners.execSync         # call convenience function for sync sub-process
      plus:           { executable: 'ls', }         # `execSync` will use `plus.executable` as name of executable
      allow_extra:    true                          # `true` b/c we want to pass arguments to `ls`

console.log MIXA.run jobdef, process.argv

We can now call node cli.js --cd=somewhere listfiles -- -AlF from the command line to execute ls -AlF and get back whatever that outputs.

Flag Definitions (<flagdefs>, <flagdef>)

The keys of a flagdefs object are interpreted as long flag names; its values are <flagdef> objects:

  • multiple <_mixa_mutliple>false, 'greedy', 'lazy'; defaults to false; if 'greedy', multiple values may be set without repeating the flag name; if 'lazy', flag name must be repeated for each value. Ensuing named values are honored in either case.
  • fallback <any>—Used when flag is missing; note that when flag is mentioned without a value, then value none will be assigned
  • positional <boolean>true or false (translated to defaultOption), indicates whether unnamed argument id allowed; interacts with allow_extra; only at most one flag can be marked positional

Command Line Parsing: Example

  • parse jobdef, process.argv will return object with
    • jobdef—The jobdef that describes how to parse command line arguments into a command with flags (jobflags and metaflags)
    • input—The argv used as input, unchanged
    • verdict—The result of parsing the input argv against the jobdef; this is either a 'happy' result or, if an error was detected, a 'sad' result with indicators what the problem was.
      • A happy verdict will have the following attributes:
        • cmd—The matching command;
        • argv—Remaining arguments, if any;
        • parameters—Object with the named flags and their values;
        • plus—The plus attribute from the matching jobdef's command definition, if any;
        • runner—The runner attribute from the matching jobdef's command definition, if any.
      • A sad verdict will have the following attributes:
        • cmd—Invariably set to help, indicating that a helpful message should be displayed;
        • error:
          • tag—A short upper case textual code that identifies the error class
          • code—An integer error code in the range [ 1 .. 127 ]
          • message—An error message
    • output:—What the runner returned
      • ok:—The value of the computation, if any, depending on the runner called
      • error:—Details in case a runner encountered problems computing an ok value; as above, will have fields tag, code, message where present

upcoming

Passing Options to Other Programs

  • Sometimes one wants to pass options to another executable; e.g. say we want to use node x.js search -iname 'whatever' to call the Linux find command in a subprocess and we wish to not analyze any options but just pass them through to the subprocess
  • Note that the find utility uses long options with a single hyphen; problem is that the parser currently used bei MIXA will misinterpret iname as [ '-i', '-n', '-a', '-m', '-e', ]
  • As a workaround, use -- (a.k.a. 'the inhibitor') somewhere after the command (search in this example) but before before the first single-dash flag to be left as-is (-iname in this case), that is, call your program as node x.js search -- -iname 'whatever'.
  • A future version of MIXA might not require using --.
  • Observe that allow_extra should be set to true to allow extra arguments; otherwise, an EXTRA_FLAGS error will be generated.

Passing Options to Run Methods

  • set runner in the command definition to a synchronous or asynchronous function that will be called with the object that describes the parsing result; this will be called the 'run method' or the 'runner'
  • set plus in the command definition to any value to be attached to the result object
  • the value of plus is specific to the run method chosen

M.I.X.A. for TOML Configuration File Parsing

NOTE This functionality is pre-alpha; details are likely to change in upcoming releases

Configuration Sources

In the following list, later entries win over earlier ones; this is the same principle that CSS and Object.assign() use.

  • a file named .$app_name.toml in the user's home directory;
  • a file named .$app_name.toml in the directory of the parent project (i.e. the containing directory of the package.json file for the package that has mixa as direct dependency);
  • other file(s) identified by one or more paths passed to read_cfg() (from the command line);
  • other settings passed to read_cfg() (from the command line).

M.I.X.A. for Checking Pinned Versions of Dependencies

(forthcoming)

  • Motivation
    • Spec-ulation Keynote - Rich Hickey
    • HN Discussion
    • another HN Discussion
    • devs like to forget they chose a particular version because of a particular feature that got removed or changed in a later version of a given dependency
    • one example would be the now-popular move to ES Modules ('import') when your own package still used CJS Modules ('require')
    • no way in package.json to indicate reason for a particular version choice: was it the latest available at the time? would the software work with a newer version or is it known not to?
    • tools such as npm-check always suggest to install the latest version which may not always be the right version for the project at hand
  • How it works
    • can configure SemVer matchers in file pinned-package-versions.json which is not touched by tools
    • add require( 'mixa/lib/pinned-package-versions.json' ) close to your package entry point (index.js, main.js)

Also See

To Do

  • [–] consider whether to replace positional flag configuration with a single option in command configuration

  • [–] implement mandatory flags

  • [–] consider moving from command-line-args to sade

  • [–] consider to offer sub-contexts as done by https://github.com/substack/subarg

  • [+/–] implement 'default_command'

    • [+] must match existing named command
    • [–] cannot have positional flags
  • [–] implement parsing of TOML configuration files

  • [–] make checks for pinned dependency versions part of public API

  • [+] in check-package-versions.coffee, handle missing dependency gracefully

  • [–] rename MIXA.configurator -> MIXA.cfg

  • [–] implement defaults, overrides for MIXA.cfg

  • [–] update all dependencies

  • [–] update all dependency intertype, then integrate MIXA flag type declaration with intertype

  • [–] allow parameters use both hypen-minus - and underscores _ transparently

  • [–] unify { cmd, command, cmds, commands, } -> { cmd, cmds, }

  • [–] do not use runners, restrict functionality to extract (and normalize) command and parameters (i.e. 'flags')

  • [–] rename parameters -> flags

  • [–] accept an optional types (an Intertype instance); naming convention is to prefix flag names with cli_ (or flag_?); can then set the type declaration in flags section to any of these:

    • [–] nothing (or null): to be filled out using the conventional prefix, the flag name, and the types argument
    • [–] any value that is legal for an InterType type declaration, including:
      • [–] the name of a type that exists in types
      • [–] a test function
      • [–] a valid InterType type declaration object
    • [–] in any event, enforce at declaration time that types.create[ type ].create() can be meaningfully called; this may entail an update to intertype to avoid non-sensical auto-generated create methods

Is Done

  • [+] in case of error, should still deliver all flags with values in verdict.parameters an attribute verdict.extra_flags will contain a list of extraneous flags