Tom MacWright

tom@macwright.org

zeroarg

zeroarg logo: zero-config argument parser

zeroarg is a weird new kind of argument parser.

Argument parsers, like yargs, commander, and meow, are utilities that make command-line interfaces possible, by parsing arguments that you give to a program in a shell like bash or zsh into variables in a language, like JavaScript.

I’ve written a bunch of command-line utilities, and argument parsing feels increasingly weird, foreign - kind of wrong. Documentation.js, for instance, has a lot of argument parsing code that mirrors the code that handles its Node API. I want consistent behavior between the two - for the same defaults to come into play regardless of whether you’re plugging in with the Node API or the CLI. But I end up restating a lot of stuff, wiffle-waffling over whether to express something in yargs, or keep argument parsing simple and do validation and such in the application level.

Especially when programs express more and more with type systems like Flow, it feels weird restating those types whenever we parse arguments.

meow is interesting, in that, instead of a fancy chaining JavaScript API, it parses documentation and generates a parser that aligns to that documentation.

zeroarg, instead, generates a CLI argument parser based on code. It uses the magic of documentation.js to infer parameter types & defaults, and read examples and a description for a function, and then configures yargs, under the hood, to generate a CLI utility for that function.

For example, here’s a command-line utility that takes numbers and returns their sum:

#!/usr/bin/env node

var zeroarg = require('zeroarg');

zeroarg(function () {
  /**
   * Add numbers together
   * @param {Array<number>} numbers
   */
  return function add(numbers) {
    console.log(numbers.reduce((sum, num) => sum + num, 0));
  }
});

And using it:

$ add run 1 2 3
6

It’s smart enough to handle options, too:

#!/usr/bin/env node

var zeroarg = require('zeroarg');

zeroarg(function () {
  /**
   * Add numbers together
   * @param {number} numbers
   * @param {Object} options
   * @param {string} options.a
   * @param {number} options.b
   * @param {wiffles|waffles} [options.c=wiffles]
   */
  return function(hello, { a, b, c }) {
    console.log(hello, a, b, c);
  }
});

And it reads JSDoc types and enforces those types in yargs: in this example, the option c (--c) becomes a choice between wiffles & waffles.

Under the hood:


So: the thesis is:

  1. Argument parsing right now is weird in a world of type systems.
  2. What if we used existing types to generate argument parsers.
  3. And CLI utilities didn’t need to configure - or really ‘know about’ their use as CLI utilities. They could be plain functions.

zeroarg is bleeding-edge, experimental technology: if you feel the same way about traditional argument parsing as I do, I really invite you to try it out and contribute if you see ways for it to advance. Some things that I’ve had on the top of my head these few days:

May 04, 2017  @tmcw