npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@thi.ng/args

v3.2.9

Published

Declarative, functional CLI argument/options parser, app framework, arg value coercions, multi/sub-commands, usage generation, error handling etc.

Readme

@thi.ng/args

npm version npm downloads Mastodon Follow

[!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

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):

| 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

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.48 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";

// CLI args will be validated against this interface
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;
}

// arg specifications
const specs: Args<TestArgs> = {
    // 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):

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

# force mode enabled
# {
#   result: {
#     force: true,
#     type: 'png',
#     bg: 16711935,
#     size: Tuple { value: [640, 480] }
#     define: { author: 'toxi', date: '2018-03-24' },
#     xtra: { foo: [23] },
#     verbose: false,
#   },
#   index: 15,
#   rest: [ 'sourcefile.png' ],
#   done: false
# }

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";

// common command opts
interface CommonOpts {
    verbose: boolean;
}

// custom command context
interface AppCtx<T extends CommonOpts> extends CommandCtx<T, CommonOpts> {
    // plus any custom additions here...
}

// command-specific options
interface HelloOpts extends CommonOpts {
    name: string;
}

// command definition
const HELLO: Command<HelloOpts, CommonOpts> = {
    // 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<ListFilesOpts, CommonOpts> = {
    // 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<CommonOpts, AppCtx<any>>({
    // 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
===================================
Usage: example [opts] [inputs]\n`,
        // 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):

bun readme-cliapp.ts

# Example app
# ===================================
# Usage: example [opts] [inputs]
#
# Available commands:
#
# hello : Print out a greeting
# list  : List files in given dir
#
# -v, --verbose           Display extra information
# displaying help for a sub-command
bun readme-cliapp.ts hello --help

# Example 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
# 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!
# 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
# 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:

@misc{thing-args,
  title = "@thi.ng/args",
  author = "Karsten Schmidt",
  note = "https://thi.ng/args",
  year = 2018
}

License

© 2018 - 2026 Karsten Schmidt // Apache License 2.0