Declarative, functional CLI argument/options parser, app framework, arg value coercions, multi/sub-commands, usage generation, error handling etc.
npm install @thi.ng/args
!npm downloads

> [!NOTE]
> This is one of 214 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
- Built-in argument types
- Re-usable argument presets
- CLI app framework
- Status
- Breaking changes in 3.0.0
- Installation
- Dependencies
- Projects using this package
- API
- Basic usage
- Generate & display help
- Parsing, value coercions & side effects
- Declarative, multi-command CLI application
- Authors
- License
Declarative, functional CLI argument/options parser, app framework, arg value coercions, multi/sub-commands, usage generation, error handling etc..
The parser includes built-in support for the following argument types (of course
custom arg types are supported too):
| Argument type | Multiple | Example | Result |
|----------------------|--------------|----------------------------|-----------------------------------|
| 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
The following commonly used arguments are available as predefined presets:
- ARG_DRY_RUN
- ARG_QUIET
- ARG_VERBOSE
Higher order, configurable preset specs:
To use these presets, simply import and splice them into your own arg
definitions (see code examples below).
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 theNO_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.
STABLE - used in production
Search or submit any issues for this package
- 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)
``bash`
yarn add @thi.ng/args
ESM import:
`ts`
import * as args from "@thi.ng/args";
For Node.js REPL:
`js`
const args = await import("@thi.ng/args");
Package sizes (brotli'd, pre-treeshake): ESM: 3.48 KB
- @thi.ng/api
- @thi.ng/checks
- @thi.ng/errors
- @thi.ng/logger
- @thi.ng/strings
- @thi.ng/text-format
Note: @thi.ng/api is in _most_ cases a type-only import (not used at runtime)
- @thi.ng/block-fs: Customizable block-based storage,
adapters & file system layer
- @thi.ng/meta-css: Data-driven CSS framework
codegen, transpiler & bundler
- @thi.ng/pointfree-lang: Forth style syntax
layer/compiler & CLI for the @thi.ng/pointfree DSL
- @thi.ng/tangle: Literate programming code block
tangling / codegen utility, inspired by org-mode & noweb
- @thi.ng/wasm-api-bindgen: Polyglot bindings
code generators (TS/JS, Zig, C11) for hybrid WebAssembly projects
- thing-tools
`ts tangle:export/readme.ts
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";
// CLI args will be validated against this interface
interface TestArgs {
configPath?: string;
force?: boolean;
bg: number;
type: ImgType;
size?: Tuple
pos?: Tuple
xtra?: { a: number; b: string };
define?: KVDict;
verbose: boolean;
}
// arg specifications
const specs: Args
// re-use predefined preset (see readme section above)
...ARG_VERBOSE,
// string arg
configPath: string({
alias: "c",
hint: "PATH",
desc: "Config file path (CLI args always take precedence over those settings)",
}),
// boolean flag (default: false)
force: flag({
alias: "f",
desc: "Force operation",
// side effect and/or validation
// parsing only continues if function returns true
fn: (_) => (console.log("force mode enabled"), true),
}),
// hex int value
bg: hex({
desc: "Background color",
// mandatory args require a default value and/or required: true
default: 0xffffff,
defaultHint: "ffffff",
}),
// enum value (mandatory)
type: oneOf({
alias: "t",
desc: "Image type",
opts: ["png", "jpg", "gif", "tiff"],
// mandatory args require a default value and/or required: true
required: true,
}),
// fixed size numeric tuple w/ x as delimiter
size: size({ size: 2, hint: "WxH", desc: "Target size", delim: "x" }),
// syntax sugar for:
// size: tuple(2, coerceInt, { hint: "WxH", desc: "Target size" }, "x"),
// another version for tuples of floating point values
pos: vec({ size: 2, desc: "Lat/Lon coordinates", hint: "LAT,LON" }),
// syntax sugar for:
// pos: tuple(2, coerceFloat, { desc: "Lat/Lon" }),
// JSON string arg
xtra: json({
alias: "x",
desc: "Extra options",
group: "extra",
}),
// key-value pairs parsed into an object (multiple allowed)
define: kvPairs({
alias: "D",
desc: "Define dict entry",
group: "extra"
}),
};
try {
// parse argv w/ above argument specs & default options
// (by default usage is shown if error occurs)
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):
`text
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
`
Usage information can be generated via usage() and is automatically triggered--help
via the special 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""main"
group, all others to . 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(), butUsageOpts
can be disabled (see).
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.
`bash
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.`ts tangle:export/readme-cliapp.ts
import {
ARG_VERBOSE,
cliApp,
configureLogLevel,
int,
string,
type Command,
type CommandCtx,
} from "@thi.ng/args";
import { files } from "@thi.ng/file-io";// common command opts
interface CommonOpts {
verbose: boolean;
}
// custom command context
interface AppCtx extends CommandCtx {
// plus any custom additions here...
}
// command-specific options
interface HelloOpts extends CommonOpts {
name: string;
}
// command definition
const HELLO: Command = {
// brief description (for
--help usage)
desc: "Print out a greeting",
// command specific options (arguments)
// (will be combined with common opts)
opts: {
name: string({
alias: "n",
desc: "Name for greeting",
required: true,
}),
},
// this command does not accept any inputs
inputs: 0,
// command implementation
fn: async (ctx) => {
// log message only shown if --verbose/-v given
ctx.logger.debug("opts", ctx.opts);
console.log(Hello, ${ctx.opts.name}!);
},
};// command-specific options
interface ListFilesOpts extends CommonOpts {
depth: number;
filter?: string;
}
// command definition
const LIST_FILES: Command = {
// brief description (for
--help usage)
desc: "List files in given dir",
// command specific options
opts: {
filter: string({
alias: "f",
desc: "Filter regexp",
}),
depth: int({
alias: "d",
desc: "Recursion depth (directory levels)",
default: Infinity,
}),
},
// this command requires exactly 1 input
// (if supporting a range, use [min, max])
inputs: 1,
// command implementation
fn: async (ctx) => {
for (let f of files(ctx.inputs[0], ctx.opts.filter, ctx.opts.depth)) {
console.log(f);
}
},
};// define & start CLI app
cliApp>({
// app name
name: "example",
// process.argv index from which to start parsing from
start: 2,
// list common command opts here
opts: {
// re-use verbose flag arg spec preset
...ARG_VERBOSE,
},
// list of commands
commands: {
hello: HELLO,
list: LIST_FILES,
},
// set to true if only a single command
// in this case the command name would NOT be required/expected
// single: true,
// usage opts
usage: {
// prefix/header string
prefix:
Example app,
// configure column width for param usage info
paramWidth: 24,
lineWidth: 80,
}, // context initialization/augmentation
// (called before arg parsing commences)
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):`bash
bun readme-cliapp.tsExample app
===================================
Usage: example [opts] [inputs]
#
Available commands:
#
hello : Print out a greeting
list : List files in given dir
#
-v, --verbose Display extra information
``bash
displaying help for a sub-command
bun readme-cliapp.ts hello --helpExample app
===================================
Usage: example [opts] [inputs]
#
Current command:
#
hello : Print out a greeting
#
-v, --verbose Display extra information
#
-n STR, --name STR [required] Name for greeting
``bash
invoking
hello sub-command (with verbose flag)
bun readme-cliapp.ts hello --name thi.ng -v
[DEBUG] example: opts {"name":"thi.ng","verbose":true}
Hello, thi.ng!
``bash
invoking
list sub-command
bun readme-cliapp.ts list -d 2 -f '.js' .
./dev/api.js
./dev/runtime.js
./dev/test/main.js
./index.js
``bash
missing arg error
bun readme-cliapp.ts hello
illegal argument(s): missing arg: --name
#
(...additional usage output omitted for brevity)
`Authors
If this project contributes to an academic publication, please cite it as:
`bibtex
@misc{thing-args,
title = "@thi.ng/args",
author = "Karsten Schmidt",
note = "https://thi.ng/args",
year = 2018
}
``© 2018 - 2026 Karsten Schmidt // Apache License 2.0