Multi-track drifting for your command line applications
Just-in-time dependency injection of command line options for executable contexts in Node.js.
More formally, MTD stands for managing terminal dependencies - but that's no fun.
MTD is a lightweight module that wraps around minimist, providing an intelligent way to define tracks that your command line application might travel down, and allowing you to specify the command line options that each track depends upon. Tracks are essentially different execution contexts that your singular command line application might want to run, and are defined as the loose arguments provided to a command line application.
Using npm, of course.
$ npm install mtd
Below is a basic example of a command line application with a single track, echo
, which in turn has a single, required command line option, input
or i
.
// basic.js
'use strict';
const Depot = require('mtd');
new Depot()
.track(
'echo',
[ { $: 'input', alias: 'i', info: 'Some input.' } ],
(input) => console.log(input)
)
.embark();
We can test out our example script as $ node basic.js echo
.
This however results in an error:
Track [ echo ] missing options:
--input (-i) Some input.
MTD has spotted that our echo
track is missing an option, and lets us know.
If we try again, $ node basic.js echo --input Hello!
, we'll get the appropriate response:
Hello!
To allow for optional options, you must specify a default, using the property _
. If we changed the second argument of track
to
[ { $: 'input', _: 'Yo.' alias: 'i', info: 'Some input.' } ]
and ran $ node basic.js echo
again, we'd see no issues, and Yo.
would be printed to our terminal display.
Below is an example showcasing multiple tracks, foo
and bar
, both of which take no arguments.
// multi.js
'use strict';
const Depot = require('mtd');
new Depot()
.track('foo', [], () => {
console.log('foo track');
})
.track('bar', [], () => {
console.log('bar track');
})
.embark();
By default, MTD allows for multiple tracks to be executed. The execution of these tracks is synchronous, and happens in the same order that they are specified at the command line.
If we ran $ node multi.js foo bar
we would see the following in our terminal:
foo track
bar track
Conversely, $ node multi.js bar foo
would result in:
bar track
foo track
By default, MTD restricts tracks to a single execution. $ node multi.js foo foo
would display:
foo track
See the configure
method below for how to change the multi
and reruns
settings.
Requiring the module works very much like most Node
modules do.
const Depot = require('mtd');
With the previous, Depot
now holds a reference to the chief export, a class of the same name. You can of course give this any identifier you'd like.
Note: The rest of this document uses TypeScript notation.
constructor (options?: TypedOption[], args?: string[])
When constructed, Depot
instances take two optional arguments: options
, and args
.
Option
and TypedOption
objects will be explained below.
args
is an array of strings to parse as command line arguments. It defaults to process.argv.slice(2)
if not provided (or if passed process.argv
).
Note the methods that return the instance (configure
, track
, default
, always
), and as such can be chained.
public configure (
config: Settings
): this
This method takes an object where the values of any keys also found in the settings
object property of the Depot
instance will be overwritten with the values from this provided object.
This example shows the configuring the instance with the default settings.
.configure({
multi: true, // Allow execution of more than one track
reportErrors: true, // Print missing arguments and issues
reruns: false // Allow multiple executions of the same track
})
The following three methods each create Track
objects from their arguments. They are used to register tracks within a Depot
instance, and specify the command line arguments that are required.
public track (
handle: string,
options: Option[],
block: Block
): this
track
creates a regular track.
public default (
handle: string,
options: Option[],
block: Block
): this
default
creates a regular track, and sets it as the default track to execute if no tracks are provided via the command line. There can only be one default track at any given time, and repeat invocations will override an existing default.
public always (
handle: string,
options: Option[],
block: Block
): this
always
creates a regular track, and pushes its handle into an array of tracks that will be executed after all other tracks have departed.
The arguments for all three methods are the same, and are as follows:
-
handle
is the name of the track, and must be unique. Remember that tracks are defined as the loose arguments provided to a command line application. -
options
is an array ofOption
-like objects (see below), whose order corresponds with the arguments specified byblock
. -
block
is a generic function, with the signature(...args: any[]): any;
, and is the executable context of the track.
.track(
'foo',
[ { $: 'bar', _: false, alias: 'b', info: 'A description.' } ],
bar => {
if (bar) { ... }
...
}
)
public embark (): void
embark
is the final method that sets your command line application in motion. Call it after all tracks have been registered.
Failure to invoke this method will result in no tracks being dispatched.
Option
objects are used by the instance constructor for initial parsing, and global option settings. They are also used on a per-track track basis for specifying the command line arguments to be injected into the execution context.
interface Option extends Object {
[index: string]: any;
/*
* The name of the command line option.
* The most important property, and the only one that is required.
* It is also the most used property, so we use $ for brevity.
*
* $: foo would correspond with the command line option --foo
*/
$: string;
/*
* A default value to use in the event that no value
* is specified via the command line.
*/
_?: any;
/*
* An alternative name ($) to use.
* Generally speaking, this should be a single character,
* but that's not strictly enforced.
*/
alias?: string;
/*
* A description of the command line option,
* used for self documentation.
*/
info?: string;
}
The instance constructor alternatively takes an optional array of objects matching an extended version of the Object
interface, TypedOption
. These objects have two additional type properties, to tell the initial argument parsing which type the inputs should be resolved to. See minimist for more information.
interface TypedOption extends Option {
bool?: boolean;
string?: boolean;
}
These two options are mutually exclusive (if you include both, bool
takes precedence).
string
is useful for forcing large integers to be parsed as strings, without losing information to 53-bit doubles, since numeric-like arguments are treated as numbers otherwise.
These are not available to per-track Option
objects, since the argument parsing is long done by the time those are reviewed. They must be shadowed from the instance constructor option pool if you need these type assurances.
mtd-help
- A helpfulTrack
.
MTD is MIT!
Read it here.
Enjoy!