muse/src/util/args.ts

104 lines
2.5 KiB
TypeScript

import { parseArgs } from "node:util";
import { z } from "zod";
import { CLIError, MuseError } from "#lib";
import chalk from "chalk";
import { version } from "../../package.json" with { type: "json" };
export const USAGE = `
${chalk.bold("muse")} - Compile and validate Playbills for Proscenium
${chalk.dim(`v${version}`)}
Usage:
muse [/path/to/binding.yaml] <options>
Options:
--check Only load and check the current binding and resources, but do not compile
--outfile, -o Specify the output file path. If not specified, output to stdout
--watch, -w Watch the directory for changes and recompile
--renderer, -r Specify the output renderer. Options: json, html
--help, -h Show this help message
`.trim();
const Renderers = ["json", "html"] as const;
const RendererSchema = z.enum(Renderers);
type Renderer = z.infer<typeof RendererSchema>;
/**
* A shape representing the arguments passed to the CLI
*/
export interface CLIArguments {
inputFilePath: string;
options: {
check: boolean;
outfile: string;
help: boolean;
renderer: Renderer;
watch: boolean;
};
}
/**
* Given an array of CLI arguments, parse them into a structured object
*
* @param argv The arguments to parse
* @returns The parsed CLI arguments
*
* @throws {CLIError} if the arguments are invalid
*/
export function parseCLIArguments(argv: string[]): CLIArguments {
const { values: options, positionals: args } = parseArgs({
args: argv,
options: {
check: {
short: "c",
type: "boolean",
default: false,
},
outfile: {
short: "o",
type: "string",
default: "",
},
help: {
short: "h",
default: false,
type: "boolean",
},
renderer: {
short: "r",
default: "json",
type: "string",
},
watch: {
default: false,
type: "boolean",
},
},
strict: true,
allowPositionals: true,
});
// -- ARG VALIDATION -- //
if (options.check && options.outfile !== "") {
throw new CLIError("Cannot use --check and --outfile together");
}
const parsedRenderer = RendererSchema.safeParse(options.renderer);
if (!parsedRenderer.success) {
throw new MuseError(`Invalid renderer: ${parsedRenderer.data}`);
}
return {
inputFilePath: args[0] ?? "./binding.yaml",
options: {
check: options.check,
outfile: options.outfile,
help: options.help,
renderer: parsedRenderer.data,
watch: options.watch,
},
};
}