@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
JavaScript
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
};