storebound/cli/lib/args.ts

39 lines
1.7 KiB
TypeScript

// Tiny argv parser: positionals, --flag, --key value, --key=value.
export interface Args {
_: string[];
has(name: string): boolean;
get(name: string): string | undefined;
str(name: string, d: string): string;
num(name: string, d: number): number;
list(name: string): string[];
/** Every value of a flag given more than once (--param a=1 --param b=2), in order. */
many(name: string): string[];
}
export function parseArgs(argv: string[], booleans: string[] = []): Args {
const flags = new Map<string, string>();
const repeated = new Map<string, string[]>();
const set = (k: string, v: string) => { flags.set(k, v); repeated.set(k, [...(repeated.get(k) ?? []), v]); };
const _: string[] = [];
const bool = new Set(['dry', 'yes', 'allow-price-changes', 'help', 'json', 'verbose', 'all-pages', 'apply', 'resume', 'force', 'force-unlock', 'include-drafts', 'not-sent-for-review', 'review-sent', ...booleans]);
for (let i = 0; i < argv.length; i++) {
const a = argv[i]!;
if (!a.startsWith('--')) { _.push(a); continue; }
const eq = a.indexOf('=');
if (eq > 0) { set(a.slice(2, eq), a.slice(eq + 1)); continue; }
const name = a.slice(2);
const next = argv[i + 1];
if (bool.has(name) || name.startsWith('no-') || next === undefined || next.startsWith('--')) set(name, 'true');
else { set(name, next); i++; }
}
const get = (n: string) => flags.get(n);
return {
_,
has: n => flags.has(n),
get,
str: (n, d) => get(n) ?? d,
num: (n, d) => { const v = get(n); const x = v === undefined ? d : Number(v); if (Number.isNaN(x)) throw new Error(`--${n}: not a number`); return x; },
list: n => (get(n) ?? '').split(',').map(s => s.trim()).filter(Boolean),
many: n => repeated.get(n) ?? [],
};
}