

[!NOTE]
This is one of 210 standalone projects, maintained as part
of the @thi.ng/umbrella monorepo
and anti-framework.
π Please help me to work full-time on these projects by sponsoring me on
GitHub. Thank you! β€οΈ
About
Declarative, functional CLI argument/options parser, app framework, arg value coercions, multi/sub-commands, usage generation, error handling etc..
Built-in argument types
The parser includes built-in support for the following argument types (of course
custom arg types are supported too):
| Flag | | --force, -f | force: true |
| String | β
| --foo bar | foo: "bar" |
| Float/int/hex | β
| --bg ff997f | bg: 16750975 |
| Enum | β
| --type png | type: "png" |
| KV pairs | β
| --define foo=bar | define: { foo: "bar" } |
| KV multi pairs | β
| -D foo=bar -D foo=baz | define: { foo: ["bar", "baz"] } |
| JSON | | --config '{"foo": [23]}' | config: { foo: [23] } |
| Fixed size tuple | | --size 640x480 | size: { value: [640, 480] } |
If multiple values/repetitions are allowed for an argument, the values will be
collected into an array (apart from KV pairs, which will yield an object).
Furthermore, for multi-args and tuples, an optional delimiter can be specified
to extract individual values, e.g. -a 1,2,3 equals -a 1 -a 2 -a 3
Re-usable argument presets
The following commonly used arguments are available as predefined presets:
Higher order, configurable preset specs:
To use these presets, simply import and splice them into your own arg
definitions (see code examples below).
CLI app framework
The package provides a simple framework to conveniently define single and
multi-command applications in a declarative and modular manner. Such apps are
defined via command specs and other configuration options. The framework then
handles all argument parsing, validation, usage display and delegation to
sub-commands.
The wrapper defines a user-customizable command
context with all
important information which is passed to the commands and also includes a logger
(writing to stderr). Other help/usage and error output also respects the
NO_COLOR convention.
A fully documented code example is
further below.
For some publicly available production uses, please see the related packages
section in this readme.
Status
STABLE - used in production
Search or submit any issues for this package
Breaking changes in 3.0.0
- Required arguments are now to be specified using either
required: true or
given a default value
- All factory functions now only accept a single arg spec, with any type-specific
options moved into the spec, for example:
- old:
oneOf(["a","b","c"], {...})
- new:
oneOf({ opts: ["a","b","c"], ...})
- old:
tuple(identity, 3, {...})
- new:
tuple({ size: 3, coerce: identity, ...})
- Where applicable,
delimiters are now to be included in the arg spec (rather
than given as separate function arg)
Installation
yarn add @thi.ng/args
ESM import:
import * as args from "@thi.ng/args";
For Node.js REPL:
const args = await import("@thi.ng/args");
Package sizes (brotli'd, pre-treeshake): ESM: 3.47 KB
Dependencies
Note: @thi.ng/api is in most cases a type-only import (not used at runtime)
Projects using this package
API
Generated API docs
Basic usage
import {
ARG_VERBOSE,
flag,
hex,
json,
kvPairs,
oneOf,
parse,
size,
string,
vec,
type Args,
type KVDict,
type Tuple,
} from "@thi.ng/args";
type ImgType = "png" | "jpg" | "gif" | "tiff";
interface TestArgs {
configPath?: string;
force?: boolean;
bg: number;
type: ImgType;
size?: Tuple<number>;
pos?: Tuple<number>;
xtra?: { a: number; b: string };
define?: KVDict;
verbose: boolean;
}
const specs: Args<TestArgs> = {
...ARG_VERBOSE,
configPath: string({
alias: "c",
hint: "PATH",
desc: "Config file path (CLI args always take precedence over those settings)",
}),
force: flag({
alias: "f",
desc: "Force operation",
fn: (_) => (console.log("force mode enabled"), true),
}),
bg: hex({
desc: "Background color",
default: 0xffffff,
defaultHint: "ffffff",
}),
type: oneOf({
alias: "t",
desc: "Image type",
opts: ["png", "jpg", "gif", "tiff"],
required: true,
}),
size: size({ size: 2, hint: "WxH", desc: "Target size", delim: "x" }),
pos: vec({ size: 2, desc: "Lat/Lon coordinates", hint: "LAT,LON" }),
xtra: json({
alias: "x",
desc: "Extra options",
group: "extra",
}),
define: kvPairs({
alias: "D",
desc: "Define dict entry",
group: "extra"
}),
};
try {
const args = parse(specs, process.argv, {
usageOpts: {
prefix: `
β β β β
ββ β β
β β β β β β β β β @thi.ng/args demo app
β β β β β β β β β β v1.0.0
β β
β β β\n\n`,
showGroupNames: true,
groups: ["flags", "main", "extra"],
lineWidth: 72,
},
});
console.log(args);
} catch (_) {}
Invoking this as CLI script without arguments will generate an error about a
missing --type arg and output the generated usage info (by default with ANSI
color highlights):
illegal argument(s): missing arg: --type
β β β β
ββ β β
β β β β β β β β β @thi.ng/args demo app
β β β β β β β β β β v1.0.0
β β
β β β
Flags:
-f, --force Force operation
-v, --verbose Display extra information
Main:
--bg HEX Background color (default: "ffffff")
-c PATH, --config-path PATH Config file path (CLI args always take
precedence over those settings)
--pos N,N Lat/Lon coordinates
--size WxH Target size
-t ID, --type ID [required] Image type: "png", "jpg",
"gif", "tiff"
Extra:
-D key=val, --define key=val [multiple] Define dict entry
-x JSON, --xtra JSON Extra options
Generate & display help
Usage information can be generated via usage() and is automatically triggered
via the special --help option (configurable, see
ParseOpts).
Each arg can be associated with arbitrary group IDs, which are then used to
segment usage output. By default, flag() args are assigned to a "flags"
group, all others to "main". The default output order too is ["flags", "main"], but can be configured via a group option given an arg spec or
factory function.
By default, ANSI colors are used to format the result string of usage(), but
can be disabled (see
UsageOpts).
Parsing, value coercions & side effects
The below invocation demonstrates how the various argument types are handled &
represented in the result. Parsing stops with the first non-argument value (here
sourcefile.png) and the remaining values are made available via rest in the
result object.
bun index.ts \
-f -t png --bg ff00ff --size 640x480 \
-D author=toxi -D date=2018-03-24 \
--xtra '{"foo": [23]}' \
sourcefile.png
Declarative, multi-command CLI application
The following example defines a CLI app with two sub-commands: hello and
list. Each command has its own options, in addition to common/shared ones.
Each command is defined in a modular manner (usually in its own source file).
All aspects like arg parsing, validation, and command selection/delegation is
handled by the cliApp() wrapper.
import {
ARG_VERBOSE,
cliApp,
configureLogLevel,
int,
string,
type Command,
type CommandCtx,
} from "@thi.ng/args";
import { files } from "@thi.ng/file-io";
interface CommonOpts {
verbose: boolean;
}
interface AppCtx<T extends CommonOpts> extends CommandCtx<T, CommonOpts> {
}
interface HelloOpts extends CommonOpts {
name: string;
}
const HELLO: Command<HelloOpts, CommonOpts> = {
desc: "Print out a greeting",
opts: {
name: string({
alias: "n",
desc: "Name for greeting",
required: true,
}),
},
inputs: 0,
fn: async (ctx) => {
ctx.logger.debug("opts", ctx.opts);
console.log(`Hello, ${ctx.opts.name}!`);
},
};
interface ListFilesOpts extends CommonOpts {
depth: number;
filter?: string;
}
const LIST_FILES: Command<ListFilesOpts, CommonOpts> = {
desc: "List files in given dir",
opts: {
filter: string({
alias: "f",
desc: "Filter regexp",
}),
depth: int({
alias: "d",
desc: "Recursion depth (directory levels)",
default: Infinity,
}),
},
inputs: 1,
fn: async (ctx) => {
for (let f of files(ctx.inputs[0], ctx.opts.filter, ctx.opts.depth)) {
console.log(f);
}
},
};
cliApp<CommonOpts, AppCtx<any>>({
name: "example",
start: 2,
opts: {
...ARG_VERBOSE,
},
commands: {
hello: HELLO,
list: LIST_FILES,
},
usage: {
prefix: `Example app
===================================
Usage: example [opts] [inputs]\n`,
paramWidth: 24,
lineWidth: 80,
},
ctx: async (ctx) => {
configureLogLevel(ctx.logger, ctx.opts.verbose);
return ctx;
},
});
Example usage (here using bun to launch the above CLI app, though the usage
info is written to assume an example launcher/wrapper):
bun readme-cliapp.ts
bun readme-cliapp.ts hello --help
bun readme-cliapp.ts hello --name thi.ng -v
bun readme-cliapp.ts list -d 2 -f '.js' .
bun readme-cliapp.ts hello
Authors
If this project contributes to an academic publication, please cite it as:
@misc{thing-args,
title = "@thi.ng/args",
author = "Karsten Schmidt",
note = "https://thi.ng/args",
year = 2018
}
License
Β© 2018 - 2025 Karsten Schmidt // Apache License 2.0