[READ-ONLY] Mirror of https://github.com/bombshell-dev/args. <1kB CLI flag parser
args args-parser cli command-line command-line-parser node
5

Configure Feed

Select the types of activity you want to include in your feed.

50 3 9

Clone this repository

https://tangled.org/bomb.sh/args https://tangled.org/did:plc:fpp3tb3sxtikwyfxt3pi7gdl
git@tangled.org:bomb.sh/args git@tangled.org:did:plc:fpp3tb3sxtikwyfxt3pi7gdl

For self-hosted knots, clone URLs may differ based on your setup.



README.md

@bomb.sh/args#

A <1kB library for parsing CLI flags with first-class schema validation.

Features#

🤏 very small

🍃 very simple

🏃 very fast (beats node:util)

🔏 strongly typed via @standard-schema/spec

🧩 works with any schema library (Zod, Valibot, Arktype, or built-ins)


Usage#

Basic (no schema)#

import { parseSync } from "@bomb.sh/args";

// my-cli build --bundle -rf --a value --b=value --c 1
const args = parseSync(process.argv.slice(2));
// { _: ['build'], bundle: true, r: true, f: true, a: "value", b: "value", c: 1 }

With schema (async)#

Pass any @standard-schema/spec-compliant schema to parse() for validation and strong type inference. Works with Zod, Valibot, Arktype, and more.

import { parse } from "@bomb.sh/args";
import { z } from "zod";

const args = await parse(process.argv.slice(2), {
  schema: z.object({
    port: z.coerce.number().default(3000),
    verbose: z.boolean().default(false),
  }),
  alias: { v: "verbose" },
});

// args.port → number
// args.verbose → boolean

Built-in schema primitives#

No Zod? No problem. Import from @bomb.sh/args/schema for zero-dependency validation.

import { parse } from "@bomb.sh/args";
import { object, number, boolean, string, array } from "@bomb.sh/args/schema";

const args = await parse(process.argv.slice(2), {
  schema: object({
    port: number(),
    verbose: boolean(),
    tags: array(string()),
  }),
});

Per-field aliases and docs#

Built-in primitives support chainable .alias() and .docs() methods. Aliases registered on fields are automatically wired into parse() — no manual alias map needed.

import { parse } from "@bomb.sh/args";
import { object, number, boolean, string } from "@bomb.sh/args/schema";

const args = await parse(process.argv.slice(2), {
  schema: object({
    port: number().alias("p").docs("Port to listen on"),
    verbose: boolean().alias("v").docs("Enable verbose output"),
  }),
});

// parse(["-p", "3000"]) → { port: 3000 }
// parse(["-v"])         → { verbose: true }

camelCase ↔ kebab-case auto-normalization#

parse() automatically resolves camelCase ↔ kebab-case flag variants when using an object() schema. No aliases required — --moduleTypes and --module-types both resolve to the same field.

const args = await parse(["--module-types", "esm"], {
  schema: object({ moduleTypes: string() }),
});
// args.moduleTypes → "esm"

const args2 = await parse(["--moduleTypes", "esm"], {
  schema: object({ moduleTypes: string() }),
});
// args2.moduleTypes → "esm"

Boolean negation#

Flags prefixed with --no- are automatically treated as false for the base flag name.

// my-cli --no-verbose
// args.verbose → false

Nested flags#

Dotted flag names create nested output objects.

// my-cli --output.dir dist --output.format esm
// args.output → { dir: "dist", format: "esm" }

= syntax#

Both --flag value and --flag=value forms are supported.

// my-cli --port=3000
// my-cli --port 3000
// Both → args.port → 3000

ParseOptions#

interface ParseOptions {
  /** Standard Schema-compliant schema. Drives return type and validation. */
  schema?: StandardSchemaV1;

  /**
   * Map short flags to long names. When using built-in schema primitives,
   * prefer per-field .alias() instead. Manual aliases take precedence on conflict.
   */
  alias?: Record<string, string>;

  /**
   * Auto-inject environment variables as flag defaults before validation.
   * --foo-bar maps to PREFIX_FOO_BAR (uppercased, hyphens → underscores).
   */
  env?: false | { prefix: string };
}

Positional arguments#

Positionals land in _ on the raw parsed object. Define _ in your schema for full type safety.

import { z } from "zod";

const args = await parse(process.argv.slice(2), {
  schema: z.object({
    _: z.tuple([z.string()]),  // first positional is a required string
    port: z.coerce.number().default(3000),
  }),
});

// args._[0] → string (typed!)

Use z.array() for variadic positionals:

schema: z.object({
  _: z.array(z.string()),
  // ...
})

-- terminator#

Everything after -- is collected into _ verbatim, not parsed as flags.

my-cli --verbose -- --not-a-flag file.txt
// args.verbose → true
// args._ → ["--not-a-flag", "file.txt"]

Environment variable fallback#

When env: { prefix } is set, any PREFIX_FLAG_NAME env var is injected as a default for missing flags before schema validation.

// APP_PORT=9000 my-cli
const args = await parse([], {
  schema: z.object({ port: z.coerce.number() }),
  env: { prefix: "APP" },
});
// args.port → 9000

Argv always takes precedence over env vars.


Error handling#

import { parse, ParseError } from "@bomb.sh/args";

try {
  const args = await parse(argv, { schema });
} catch (e) {
  if (e instanceof ParseError) {
    for (const issue of e.issues) {
      console.error(issue.message);
    }
  }
}

@bomb.sh/args/schema#

Built-in schema primitives that implement @standard-schema/spec. Composable with other schema libraries or usable standalone.

Primitives#

import { string, number, boolean, array, object } from "@bomb.sh/args/schema";
Primitive Input Output
string() string | number | boolean string
number() number | string (coerces numeric strings) number
boolean() boolean | "true" | "false" | "1" | "0" boolean
array(item?) unknown[] T[]
object(shape) object { [K]: OutputType }

All primitives support .docs(description) and .alias(...names) for metadata and per-field alias registration.


parseSync()#

Synchronous parsing with no schema validation. Returns RawArgs.

import { parseSync } from "@bomb.sh/args";

const args = parseSync(process.argv.slice(2), {
  alias: { v: "verbose" },
  env: { prefix: "APP" },
});
// args.verbose → string | number | boolean | undefined

Benchmarks#

mri               x 1,650,986 ops/sec ±0.32% (97 runs sampled)
@bomb.sh/args     x 1,407,191 ops/sec ±0.38% (99 runs sampled)
minimist          x 383,506 ops/sec ±0.28% (99 runs sampled)
node:util         x 320,953 ops/sec ±0.35% (98 runs sampled)
yargs-parser      x 31,874 ops/sec ±1.32% (92 runs sampled)

Acknowledgements#

This package was previously published as ultraflag up until v0.3.0, when it was renamed to @bomb.sh/args.