# parser _(no submodules)_ ## Purpose The `parser` module turns a raw `argv` token array into a structured `ParsedArgs` object with typed options, named positional arguments, and a `rest` passthrough channel. It is the low-level argument-parsing engine that higher-level command definitions build on, designed to model the behaviour CLI users expect from mature tools like `git`, `npm`, and `cargo`. The module is deliberately imperative and framework-free: no reflection, no decorators, no hidden state. Every behaviour — type coercion, `--no-*` negation, short-flag resolution, choice validation, required-option checking — is enforced in a single linear pass over `argv`, and every failure mode raises a `CliError` with a stable code drawn from `CLI_CODES.PARSE.*` so callers can branch on machine-readable codes instead of message strings. ## Features - Boolean flags (`--verbose`) and negation (`--no-verbose`) - String/number/array options via `--key value`, `--key=value`, or short-flag (`-v`) forms - Short-flag mapping via `short: "v"` on the option config - Passthrough `rest` channel after `--` - Variadic positionals (``) collected as arrays - Required positionals and required options with dedicated error codes - Choice validation with `INVALID_VALUE` - `allowUnknown: true` to silently ignore unrecognised flags - Defaults for options and positionals - Helper parsers for flag specs (`parseOptionFlags`) and positional specs (`parseArgumentSpec`) ## Usage: Basic Example ```ts import { parseArgs } from "@suaveplan/cli/parser"; const result = parseArgs(["--verbose", "src/file.ts"], { options: { verbose: { type: "boolean" } }, arguments: [{ name: "file", required: true, variadic: false }], }); console.log(result.options.verbose); // true console.log(result.positionals.file); // "src/file.ts" ``` ## Usage: Advanced Example ```ts import { parseArgs, parseOptionFlags, parseArgumentSpec } from "@suaveplan/cli/parser"; const flag = parseOptionFlags("-o, --output ", "Output file"); const posArg = parseArgumentSpec("", "Source files"); const result = parseArgs( ["-o", "out.txt", "--format=json", "a.ts", "b.ts", "--", "--not-a-flag"], { options: { [flag.name]: flag.config, format: { type: "string", choices: ["json", "text"], default: "text" }, retries: { type: "number", default: 3 }, }, arguments: [posArg], } ); // result.options.output === "out.txt" // result.options.format === "json" // result.positionals.sources === ["a.ts", "b.ts"] // result.rest === ["--not-a-flag"] ``` ## API Reference ### `parseArgs(argv, config?)` - `argv: string[]` — raw CLI tokens (without the node/bun prefix) - `config?: ParserConfig` — optional options/arguments definitions, `allowUnknown`, `stopEarly` - Returns `ParsedArgs` with `options`, `positionals`, and `rest` - Throws `CliError` with codes `UNKNOWN_OPTION`, `MISSING_VALUE`, `INVALID_VALUE`, `MISSING_ARGUMENT`, `MISSING_REQUIRED_OPTION` ### `parseOptionFlags(flags, description?, defaultValue?)` - `flags: string` — commander-style spec, e.g. `"-v, --verbose"` or `"--output "` - Returns `{ name, config }` where `name` is the camelCased long-flag name - Throws `CliError(UNKNOWN_OPTION)` if no long flag is provided ### `parseArgumentSpec(spec, description?)` - `spec: string` — `""`, `"[name]"`, `""`, or `"[name...]"` - Returns an `ArgumentConfig` with `required`, `variadic`, `name`, and optional `description` ## Implementation Notes - Single-pass state machine: tokens are consumed left-to-right with a small `restMode` flag that flips when `--` is encountered. There are no lookaheads beyond peeking at `argv[i + 1]` to decide whether a flag's value is the next token. - `--no-*` handling strips the literal `--no-` prefix (not the camelCased form) and then camelCases the remainder, so `--no-dry-run` correctly maps to the `dryRun` option. - Values are only consumed as the next token when that token does not start with `-`, preventing `--foo --bar` from stealing `--bar` as the value of `--foo`. - `number` type uses `Number(raw)` and rejects `NaN` so strings like `"abc"` raise `INVALID_VALUE` rather than silently becoming `NaN`. - All errors are `CliError` instances carrying `context` metadata (option name, offending value, allowed choices) so UIs and logs can render actionable diagnostics without re-parsing message strings. - Schemas mirror the TypeScript types exactly and are exported separately so callers can validate untrusted `ParserConfig` inputs at the boundary without pulling Zod into hot paths.