@keeex/argv
v1.1.1
Published
Parses argv arguments
Readme
CLI argv parser
Design goal
This library handles parsing arguments from the CLI, and putting them in a JavaScript object. It optionally provides some amount of type safety for TypeScript usage.
It handles basic single-characters and multi-characters options, options arguments, and unnamed arguments. It also support one level of "selector", making it possible to toggle between different set of parsing options.
It does not support positional arguments, and options ordering isn't enforced.
Some default options are handled automatically, such as displaying inline help, versions, or licensing informations.
Usage
An object which describe all the available options must be provided.
Global configuration
It's interface is:
export interface ArgvOptions<ArgvConfig = unknown> {
/** Don't stop if there are extra, unhandled arguments */
allowExtraArgs?: boolean;
/** Defaults to `path.basename(process.argv[1])` */
appName?: string;
/** Provide top-level branching options. */
branches?: ArgvOptionBranch;
description?: string;
/**
* Extra, unnamed arguments.
*
* Can be overriden by branch options.
*/
extraArgs?: ExtraArgs;
/** Full license terms */
license?: string;
/** Short license terms */
licenseShort?: string;
/** Available options (shared by all branches, if any) */
options?: Array<ArgvOption>;
/** Defaults to `console.info()` output */
outputFunction?: (value: string) => void;
/** Type predicate applied to the final object before returning it. */
predicate?: TypePredicate<ArgvConfig>;
/** Displayed as a header for all inline help */
title?: string;
/** Version (without "v" prefix) */
version?: string;
}The options are described using one of these interfaces:
export interface ArgvOptionBase<Type> {
/**
* How to cast the raw value to the config property value.
*
* If not provided, will use the raw value.
*/
cast?: (value: Type) => Awaitable<unknown>;
default?: Type;
description?: string;
mandatory?: boolean;
/** no prefix dash */
short?: string;
/** kebab-case, no prefix dashes */
long?: string;
/**
* Property in the result object.
*
* Defaults to the camelcase version of the long form if present, or the short form.
* Only handle top-level, no object properties.
*/
propertyName?: string;
}
/** Accept a single boolean value */
export interface ArgvOptionBoolean extends ArgvOptionBase<boolean> {
type: "boolean";
}
/** Accept multiple boolean values */
export interface ArgvOptionBooleanArray extends ArgvOptionBase<Array<boolean>> {
type: "booleanArray";
}
interface ArgvOptionBranchOptions {
extraArgs?: ExtraArgs | null;
options?: Array<ArgvOption>;
}
/** Accept a single choice argument */
export interface ArgvOptionChoice extends ArgvOptionBase<string> {
choices: Array<string>;
type: "choice";
}
/** Accept multiple choice arguments */
export interface ArgvOptionChoiceArray extends ArgvOptionBase<Array<string>> {
choices: Array<string>;
type: "choiceArray";
}
/** Accept a single number argument */
export interface ArgvOptionNumber extends ArgvOptionBase<number> {
/** Name of the argument. Defaults to `value`. */
argName?: string;
type: "number";
}
/** Accept multiple number arguments */
export interface ArgvOptionNumberArray extends ArgvOptionBase<Array<number>> {
/** Name of the argument. Defaults to `value`. */
argName?: string;
type: "numberArray";
}
/** Accept a single string argument */
export interface ArgvOptionString extends ArgvOptionBase<string> {
/** Name of the argument. Defaults to `value`. */
argName?: string;
type: "string";
}
/** Accept multiple string arguments */
export interface ArgvOptionStringArray extends ArgvOptionBase<Array<string>> {
/** Name of the argument. Defaults to `value`. */
argName?: string;
type: "stringArray";
}Each option will return the expected type by default.
That value can be altered before it is returned by defining a cast property.
Branching option
In addition to shared options, it is possible to define a "branching" option which takes a string and will process all options according to a different configuration object.
This can be set in the branches property of the top-level config object.
export interface ArgvOptionBranch extends ArgvOptionBase<string> {
cast?: never;
/** What is the branch option. Defaults to "Branches options". */
name?: string;
options: Record<string, ArgvOptionBranchOptions>;
type: "branch";
}
interface ArgvOptionBranchOptions {
extraArgs?: ExtraArgs | null;
options?: Array<ArgvOption>;
}If present, options in this object's options[<value>] will be parsed in addition to the shared ones.
Unnamed arguments
It is possible to provide extra text values after all options are processed.
These are queued at the end of the CLI call, and should not start with a -.
If an unnamed argument must start with a -, it must be placed after the delimiter --, which indicates that everything after it is an unnamed argument.
It is possible to specify how many of these arguments are expected by configuring the extraArgs property:
export interface ExtraArgs {
/** Name of the extra arguments, for the inline help */
argName?: string;
cast?: (value: string) => Awaitable<unknown>;
description?: string;
max?: number;
min?: number;
/** Defaults to `extraArgs` */
propertyName?: string;
}The propertyName is the name of the property that will receive the array of string in the returned object.
This value can be overriden by branches.
Parsing argv
Argv arguments are processed using a single function call:
import {getArgvConfig} from "@keeex/argv";
interface ArgvConfig {
name: string;
port: number;
}
const isArgvConfig = (obj: unknown): obj is ArgvConfig => {
if (!obj) return false;
if (typeof obj.name !== "string") return false;
if (typeof obj.port !== "number") return false;
return true;
};
const cliConfig = await getArgvConfig(process.argv, {
options: [
{
long: "name",
mandatory: true,
short: "n",
type: "string",
},
{
default: 3000,
long: "port",
short: "p",
type: "number",
},
],
predicate: isArgvConfig,
});
if (cliConfig) {
// Do something with it
}Low-level usage
If you don't care much about the extra tooling but just want to grab a handful of options from the CLI, you can use the consumeArgv() function instead.
import * as argv from "@keeex/argv";
const input = consumeArgv(process.argv, ["i", "input"], true);
const verbose = consumeArgv(process.argv, ["v", "verbose"], false);
const files = argv.consumeArgvArgs(process.argv);It handles both single-character options prefixed with one dash (-) and longer options prefixed with two dashes (--).
If false is passed as the third parameter, it returns either true (if the flag was present), false (if the --no-<long form> flag was present), or undefined.
If true is passed as the third parameter, an extra string is expected after the options, and if present, that string is returned.
In addition, this function handles two types of values for argv: if the first value of the argv array seems to be a node binary, it expects the real arguments to start at argv[2].
If that's not the case, it starts parsing arguments at argv[0].
