function parseCliArgs
parseCliArgs(args: string[]): CliOptions

Parses command line arguments into the three positionals and the flags.

Positionals fill the package, coder and command slots in order, skipping any slot a flag already filled — so -p png -c pngFile decode and png pngFile decode mean the same thing. decode and encode are reserved in the coder slot: a second word that names a command is the command, and the coder is left to be inferred (ADR 0005).

Every slot is text: positionals are read as strings, so 007 stays 007 rather than becoming the number 7 on its way to the specifier resolver.

A blank word fills its slot, and is then refused on its own terms. It used to be dropped as if it had never been typed, which is not a smaller version of the same thing: binstruct "" decode slid decode into the package slot and answered confidently about jsr:@binstruct/decode, and -p "" did it too. That is the shift an unknown flag causes, arriving by another route, and it takes the same answer — the slots it landed in are named in CliOptions.blankSlots and nothing runs.

There is no fourth slot, and a word that reaches for one is refused. Extra positionals used to be dropped where they stood, so binstruct arp arpData decode input.bin — the < forgotten — waited on a terminal for input that was sitting in the file it had just discarded. They are collected in CliOptions.extraArgs instead.

A word starting with - is a flag, and -- is how you say it is not. Everything after the separator fills a slot whatever it starts with, so binstruct -- -dash/mod.ts decode names a module the shell tab-completed from a directory called -dash. Without it, -dash/ was read as the flag cluster -d -a -s -h, whose h set --help — and the CLI answered a decode with the whole help screen on stdout, at exit 0, which is precisely the redirect corruption ADR 0001 exists to prevent.

A flag that is not recognised is refused, never ignored. Only the five declared here exist; anything else consumes a word and shifts every positional behind it, so binstruct --format json png would have answered confidently about json. They are collected rather than thrown, since reporting them is planCli's job.

Examples

Positionals and flags are interchangeable

import { assertEquals } from "@std/assert";
import { parseCliArgs } from "./cli.ts";

assertEquals(parseCliArgs(["png", "pngFile", "decode"]), {
  package: "png",
  coder: "pngFile",
  command: "decode",
  help: false,
  version: false,
  docs: false,
  unknownFlags: [],
  blankSlots: [],
  extraArgs: [],
});

const flagged = parseCliArgs(["-p", "png", "-c", "pngFile", "decode"]);

assertEquals(flagged.package, "png");
assertEquals(flagged.coder, "pngFile");
assertEquals(flagged.command, "decode");

A command word in the coder slot leaves the coder to be inferred

import { assertEquals } from "@std/assert";
import { parseCliArgs } from "./cli.ts";

const options = parseCliArgs(["arp", "decode"]);

assertEquals(options.package, "arp");
assertEquals(options.coder, undefined);
assertEquals(options.command, "decode");

-- makes a leading dash ordinary, and a stray flag is reported

import { assertEquals } from "@std/assert";
import { parseCliArgs } from "./cli.ts";

const separated = parseCliArgs(["--", "-dash/mod.ts", "decode"]);

assertEquals(separated.package, "-dash/mod.ts");
assertEquals(separated.command, "decode");
assertEquals(separated.help, false);

assertEquals(parseCliArgs(["--format", "json", "png"]).unknownFlags, [
  "--format",
]);

A blank word keeps its slot, and a fourth word is kept as well

import { assertEquals } from "@std/assert";
import { parseCliArgs } from "./cli.ts";

const blank = parseCliArgs(["", "decode"]);

assertEquals(blank.package, "");
assertEquals(blank.command, "decode");
assertEquals(blank.blankSlots, ["<package>"]);

const extra = parseCliArgs(["arp", "arpData", "decode", "input.bin"]);

assertEquals(extra.command, "decode");
assertEquals(extra.extraArgs, ["input.bin"]);

Parameters

args: string[]

Command line arguments

Return Type

The parsed slots and flags, with absent and blank values left undefined