UNPKG

@thi.ng/args

Version:

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

177 lines (176 loc) 3.72 kB
import { identity } from "@thi.ng/api/fn"; import { repeat } from "@thi.ng/strings/repeat"; import { coerceFloat, coerceHexInt, coerceInt, coerceJson, coerceKV, coerceOneOf, coerceTuple } from "./coerce.js"; const __desc = (opts, prefix) => `${prefix ? prefix + ": " : ""}${opts.map((x) => `"${x}"`).join(", ")}`; const __hint = (hint, delim) => hint + (delim ? `[${delim}..]` : ""); const defSingle = (type, coerce, hint) => (spec) => ({ type, coerce, hint, group: "main", ...spec }); const defMulti = (type, coerce, hint, delim = ",") => (spec) => ({ type, delim, hint: __hint(hint, spec.delim ?? delim), coerce: (x) => x.map(coerce), group: "main", multi: true, ...spec }); const flag = (spec) => ({ type: "flag", group: "flags", default: false, ...spec }); const string = defSingle("string", identity, "STR"); const strings = defMulti("strings", identity, "STR"); const float = defSingle("float", coerceFloat, "NUM"); const floats = defMulti("floats", coerceFloat, "NUM"); const int = defSingle("int", coerceInt, "INT"); const ints = defMulti("ints", coerceInt, "INT"); const hex = defSingle("hex", coerceHexInt, "HEX"); const hexes = defMulti("hexes", coerceHexInt, "HEX"); const oneOf = (spec) => ({ ...spec, type: "oneOf", coerce: coerceOneOf(spec.opts), hint: spec.hint ?? "ID", group: spec.group ?? "main", desc: __desc(spec.opts, spec.desc) }); const oneOfMulti = (spec) => ({ ...spec, type: "oneOfMulti", coerce: coerceOneOf(spec.opts), hint: spec.hint ?? __hint("ID", spec.delim), group: spec.group ?? "main", desc: __desc(spec.opts, spec.desc), multi: true }); const kvPairs = (spec) => { if (!spec.delim) spec.delim = "="; return { type: "kvPairs", coerce: coerceKV(spec.delim, spec.strict, false), hint: `key${spec.delim}val`, group: "main", multi: true, split: false, ...spec }; }; const kvPairsMulti = (spec) => ({ type: "kvPairsMulti", coerce: coerceKV(spec.delim, spec.strict, true), hint: `key${spec.delim}val`, group: "main", multi: true, ...spec }); const tuple = (spec) => { if (!spec.delim) spec.delim = ","; return { ...spec, type: "tuple", coerce: coerceTuple(spec.coerce, spec.size, spec.delim), hint: spec.hint ?? [...repeat("N", spec.size)].join(spec.delim), group: spec.group ?? "main" }; }; const size = (spec) => tuple({ ...spec, coerce: coerceInt }); const vec = (spec) => tuple({ ...spec, coerce: coerceFloat }); const json = (spec) => ({ type: "json", coerce: coerceJson, hint: "JSON", group: "main", ...spec }); const ARG_TYPES = { flag, float, floats, hex, hexes, int, ints, json, kvPairs, kvPairsMulti, oneOf, oneOfMulti, string, strings, tuple }; const ARG_DRY_RUN = { dryRun: flag({ desc: "Dry run (no changes applied)" }) }; const ARG_QUIET = { quiet: flag({ alias: "q", desc: "Disable all logging" }) }; const ARG_VERBOSE = { verbose: flag({ alias: "v", desc: "Display extra information" }) }; const ARG_OUT_DIR = (defaultVal, desc) => ({ outDir: string({ alias: "O", desc: "Output directory" + (desc ?? ""), hint: "PATH", default: defaultVal, required: !!defaultVal }) }); const ARG_OUT_FILE = (defaultVal, desc) => ({ outFile: string({ alias: "o", desc: "Output file" + (desc ?? ""), hint: "PATH", default: defaultVal, required: !!defaultVal }) }); export { ARG_DRY_RUN, ARG_OUT_DIR, ARG_OUT_FILE, ARG_QUIET, ARG_TYPES, ARG_VERBOSE, flag, float, floats, hex, hexes, int, ints, json, kvPairs, kvPairsMulti, oneOf, oneOfMulti, size, string, strings, tuple, vec };