This commit is contained in:
Endeavorance 2025-02-24 14:03:04 -05:00
parent 45580288e1
commit 8b276cc226
5 changed files with 145 additions and 119 deletions

View file

@ -1,20 +1,34 @@
import { parseArgs } from "node:util";
import {
type AnyResource,
DirectiveError,
ValidatedPlaybillSchema,
compileScript,
getEmptyPlaybill,
} from "@proscenium/playbill";
import chalk from "chalk";
import { loadBindingFile } from "./binding";
import { parseBindingFile } from "./binding";
import { loadResourceFile } from "./resource";
import { usage } from "./usage";
class CLIError extends Error {}
enum ExitCode {
Success = 0,
Error = 1,
}
async function main(): Promise<boolean> {
// Parse command line arguments
class CLIError extends Error { }
async function loadFileOrFail(filePath: string): Promise<string> {
const fileToLoad = Bun.file(filePath);
const fileExists = await fileToLoad.exists();
if (!fileExists) {
throw new Error(`File not found: ${filePath}`);
}
return await fileToLoad.text();
}
async function main(): Promise<number> {
// -- ARGUMENT PARSING -- //
const { values: options, positionals: args } = parseArgs({
args: Bun.argv.slice(2),
options: {
@ -53,11 +67,13 @@ async function main(): Promise<boolean> {
allowPositionals: true,
});
// -- HELP TEXT -- //
if (options.help) {
console.log(usage);
return true;
return ExitCode.Success;
}
// -- ARG VALIDATION -- //
if (options.check && options.write) {
throw new CLIError("Cannot use --check and --write together");
}
@ -74,30 +90,22 @@ async function main(): Promise<boolean> {
}
}
// -- BINDING FILE -- //
const bindingPath = args[0] ?? "./binding.yaml";
verboseLog(`Building Playbill with binding: ${bindingPath}`);
// Check if the binding file exists
const bindingFileCheck = Bun.file(bindingPath);
const bindingFileExists = await bindingFileCheck.exists();
if (!bindingFileExists) {
throw new Error(`Binding file not found: ${bindingPath}`);
}
const binding = await loadBindingFile(bindingPath);
const bindingFileContent = await loadFileOrFail(bindingPath);
const binding = parseBindingFile(bindingFileContent, bindingPath);
verboseLog(
`↳ Binding loaded with ${binding.files.length} associated resources`,
`↳ Binding loaded with ${binding.includedFiles.length} associated resources`,
);
const loadedResources: AnyResource[] = [];
// -- RESOURCE FILES -- //
verboseLog("Loading resources");
// Load resources listed in the binding file
for (const filepath of binding.files) {
const loadedResources: AnyResource[] = [];
for (const filepath of binding.includedFiles) {
verboseLog(`↳ Loading ${filepath}`);
const loaded = await loadResourceFile(filepath);
verboseLog(` ↳ Loaded ${loaded.length} resources`);
@ -106,13 +114,14 @@ async function main(): Promise<boolean> {
verboseLog("Constructing playbill");
// Consjtruct the playbill object
// -- COMPILE RESOURCES --//
const playbill = getEmptyPlaybill();
playbill.id = binding.info.id;
playbill.name = binding.info.name;
playbill.author = binding.info.author ?? "Anonymous";
playbill.version = binding.info.version;
playbill.id = binding.id;
playbill.name = binding.name;
playbill.author = binding.author;
playbill.version = binding.version;
playbill.description = binding.description;
for (const resource of loadedResources) {
switch (resource.$define) {
@ -145,66 +154,49 @@ async function main(): Promise<boolean> {
}
}
// Evaluate directives in descriptions for all resources
verboseLog("Processing descriptions");
for (const resource of loadedResources) {
try {
resource.description = compileScript(resource.description, playbill);
} catch (error) {
if (error instanceof DirectiveError) {
console.error(
`Error processing directives in ${resource.$define}/${resource.id}`,
);
console.error(error.message);
} else {
console.error(error);
}
return false;
}
}
// Validate the playbill object
// -- VALDATE PLAYBILL -- //
verboseLog("Validating playbill");
const validatedPlaybill = ValidatedPlaybillSchema.safeParse(playbill);
if (!validatedPlaybill.success) {
console.error("Error validating playbill");
console.error(validatedPlaybill.error.errors);
return false;
return ExitCode.Error;
}
verboseLog("Playbill validated");
// If --check is set, exit here
// -- EXIT EARLY IF JUST CHECKING VALIDATION --//
if (options.check) {
console.log(chalk.green("Playbill validated successfully"));
return true;
return ExitCode.Error;
}
// -- SERIALIZE TO JSON --//
const finalizedPlaybill = JSON.stringify(
validatedPlaybill.data,
null,
options.minify ? undefined : 2,
);
// -- WRITE TO DISK OR STDOUT --//
if (options.write) {
await Bun.write(options.outfile, finalizedPlaybill);
return true;
return ExitCode.Success;
}
console.log(finalizedPlaybill);
return true;
return ExitCode.Success;
}
try {
const success = await main();
if (success) {
process.exit(0);
} else {
process.exit(1);
}
const exitCode = await main();
process.exit(exitCode);
} catch (error) {
if (error instanceof Error) {
console.error("Uncaught error:");
console.error(chalk.red(error.message));
console.error(error.stack);
} else {
console.error("An unknown error occurred");
console.error(error);