import { watch } from "node:fs/promises"; import chalk from "chalk"; import { CLIError, MuseError, compileMarkdownInPlaybill, loadFromBinding, resolveBindingPath, } from "#lib"; import { renderPlaybillToHTML } from "#render/html"; import { type CLIArguments, getAbsoluteDirname, parseCLIArguments, USAGE, } from "#util"; enum ExitCode { Success = 0, Error = 1, } async function processBinding({ inputFilePath, options }: CLIArguments) { // Load the binding const bindingPath = await resolveBindingPath(inputFilePath); const binding = await loadFromBinding(bindingPath); // If --check is specified, exit early if (options.check) { console.log(chalk.green("Playbill validated successfully")); return ExitCode.Success; } if (options.markdown) { await compileMarkdownInPlaybill(binding); } // Serialize (default: JSON) let serializedPlaybill = ""; switch (options.renderer) { case "json": serializedPlaybill = binding.playbill.serialize(); break; case "html": serializedPlaybill = await renderPlaybillToHTML(binding); break; default: throw new CLIError(`Unknown renderer: ${options.renderer}`); } // Write to disk if --outfile is specified if (options.outfile !== "") { await Bun.write(options.outfile, serializedPlaybill); return ExitCode.Success; } // Otherwise, write to stdout console.log(serializedPlaybill); return ExitCode.Success; } async function main(): Promise { const cliArguments = parseCLIArguments(Bun.argv.slice(2)); const { options } = cliArguments; // If --help is specified, print usage and exit if (options.help) { console.log(USAGE); return ExitCode.Success; } let lastProcessResult = await processBinding(cliArguments); if (options.watch) { const watchDir = getAbsoluteDirname(cliArguments.inputFilePath); console.log(`Watching ${watchDir} for changes...`); const watcher = watch(watchDir, { recursive: true, }); for await (const event of watcher) { console.log( `Detected ${event.eventType} on ${event.filename}. Reprocessing...`, ); try { lastProcessResult = await processBinding(cliArguments); if (lastProcessResult === ExitCode.Error) { console.error(`Error processing ${event.filename}`); } else { console.log("Reprocessed changes"); } } catch (error) { console.error(`Error processing ${event.filename}`); console.error(error); } } return ExitCode.Success; } return ExitCode.Success; } try { const exitCode = await main(); process.exit(exitCode); } catch (error) { if (error instanceof MuseError) { console.error(chalk.red(error.fmt())); } else { console.error("An unexpected error occurred"); console.error(error); } process.exit(1); }