105 lines
2.5 KiB
TypeScript
105 lines
2.5 KiB
TypeScript
import {
|
|
type AnyResource,
|
|
AnyResourceSchema,
|
|
ResourceMap,
|
|
} from "@proscenium/playbill";
|
|
import YAML, { YAMLParseError } from "yaml";
|
|
import { ZodError } from "zod";
|
|
import { ResourceFileError } from "./errors";
|
|
import { extractFrontmatter, extractMarkdown } from "./markdown";
|
|
|
|
type FileFormat = "yaml" | "markdown";
|
|
|
|
function determineFileFormat(filePath: string): FileFormat {
|
|
return filePath.split(".").pop() === "md" ? "markdown" : "yaml";
|
|
}
|
|
|
|
function loadYamlResourceFile(filePath: string, text: string): AnyResource[] {
|
|
const parsedDocs = YAML.parseAllDocuments(text);
|
|
|
|
if (parsedDocs.some((doc) => doc.toJS() === null)) {
|
|
throw new ResourceFileError("Encountered NULL resource", filePath);
|
|
}
|
|
|
|
const errors = parsedDocs.flatMap((doc) => doc.errors);
|
|
|
|
if (errors.length > 0) {
|
|
throw new ResourceFileError(
|
|
"Error parsing YAML resource",
|
|
filePath,
|
|
errors.map((e) => e.message).join(", "),
|
|
);
|
|
}
|
|
|
|
const collection: AnyResource[] = [];
|
|
|
|
for (const doc of parsedDocs) {
|
|
const raw = doc.toJS();
|
|
const parsed = AnyResourceSchema.parse(raw);
|
|
const type = parsed.$define;
|
|
const schemaToUse = ResourceMap[type];
|
|
const validated = schemaToUse.parse(parsed);
|
|
collection.push(validated);
|
|
}
|
|
|
|
return collection;
|
|
}
|
|
|
|
function loadMarkdownResourceFile(
|
|
filePath: string,
|
|
text: string,
|
|
): AnyResource[] {
|
|
try {
|
|
const frontmatter = extractFrontmatter(text);
|
|
const markdown = extractMarkdown(text);
|
|
|
|
const together = {
|
|
...frontmatter,
|
|
description: markdown,
|
|
};
|
|
|
|
const parsed = AnyResourceSchema.parse(together);
|
|
return [parsed];
|
|
} catch (e) {
|
|
if (e instanceof YAMLParseError) {
|
|
throw new ResourceFileError(
|
|
"Error parsing Markdown frontmatter",
|
|
filePath,
|
|
e.message,
|
|
);
|
|
}
|
|
|
|
if (e instanceof ZodError) {
|
|
throw new ResourceFileError(
|
|
"Error parsing resource file",
|
|
filePath,
|
|
e.message,
|
|
);
|
|
}
|
|
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load and process all documents in a yaml file at the given path
|
|
* @param filePath - The path to the yaml file
|
|
* @returns An array of validated resources
|
|
*
|
|
* @throws ResourceFileError
|
|
* @throws NullResourceError
|
|
*/
|
|
export async function loadResourceFile(
|
|
filePath: string,
|
|
): Promise<AnyResource[]> {
|
|
const file = Bun.file(filePath);
|
|
const text = await file.text();
|
|
const format = determineFileFormat(filePath);
|
|
|
|
switch (format) {
|
|
case "yaml":
|
|
return loadYamlResourceFile(filePath, text);
|
|
case "markdown":
|
|
return loadMarkdownResourceFile(filePath, text);
|
|
}
|
|
}
|