From d8ddff01a6ff3c7b0f7e15404a2352d959870efb Mon Sep 17 00:00:00 2001 From: Endeavorance Date: Sat, 8 Mar 2025 14:45:08 -0500 Subject: [PATCH] Support for updated shapes --- bun.lock | 4 +-- package.json | 2 +- playbill/binding.yaml | 2 +- src/binding.ts | 80 ++++++++++++++++++++----------------------- src/index.ts | 16 +++------ src/resource.ts | 19 +++++----- 6 files changed, 58 insertions(+), 65 deletions(-) diff --git a/bun.lock b/bun.lock index ee1e8fd..7298190 100644 --- a/bun.lock +++ b/bun.lock @@ -4,7 +4,7 @@ "": { "name": "@proscenium/muse", "dependencies": { - "@proscenium/playbill": "0.0.14", + "@proscenium/playbill": "0.0.1", "chalk": "^5.4.1", "yaml": "^2.7.0", "zod": "^3.24.1", @@ -37,7 +37,7 @@ "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="], - "@proscenium/playbill": ["@proscenium/playbill@0.0.14", "https://git.astral.camp/api/packages/proscenium/npm/%40proscenium%2Fplaybill/-/0.0.14/playbill-0.0.14.tgz", { "peerDependencies": { "typescript": "^5" } }, "sha512-Fbo7IHTeRBDLwh6olETphh2duhMRrUx7dclFGT3VRhbCeCIqp+64N93M3fWlvOUrEyIU75WgpxkOz7FzfecAzg=="], + "@proscenium/playbill": ["@proscenium/playbill@0.0.1", "https://git.astral.camp/api/packages/proscenium/npm/%40proscenium%2Fplaybill/-/0.0.1/playbill-0.0.1.tgz", { "peerDependencies": { "typescript": "^5" } }, "sha512-oKOqGMKeIrmHOmBvk65RFTBfbXH8KjxR+4PMtZQ2KLVGpYPexj60muf7SmG54K32Vd10xXpBqMAG5BvGZvhLeg=="], "@types/bun": ["@types/bun@1.2.3", "", { "dependencies": { "bun-types": "1.2.3" } }, "sha512-054h79ipETRfjtsCW9qJK8Ipof67Pw9bodFWmkfkaUaRiIQ1dIV2VTlheshlBx3mpKr0KeK8VqnMMCtgN9rQtw=="], diff --git a/package.json b/package.json index baa4e1e..eb735b7 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "chalk": "^5.4.1", "yaml": "^2.7.0", "zod": "^3.24.1", - "@proscenium/playbill": "0.0.14" + "@proscenium/playbill": "0.0.1" }, "scripts": { "fmt": "bunx --bun biome check --fix", diff --git a/playbill/binding.yaml b/playbill/binding.yaml index bff6837..a9fce34 100644 --- a/playbill/binding.yaml +++ b/playbill/binding.yaml @@ -1,5 +1,5 @@ $binding: playbill -extends: ../extend-me/binding.yaml +extends: ../extend-me id: the-great-spires name: The Great Spires author: "Endeavorance " diff --git a/src/binding.ts b/src/binding.ts index 2a2e8db..8c02ddd 100644 --- a/src/binding.ts +++ b/src/binding.ts @@ -3,11 +3,12 @@ import { Glob } from "bun"; import z from "zod"; import { getEmptyPlaybill, - type AnyResource, + type UnknownResource, type Playbill, } from "@proscenium/playbill"; import { loadYAMLFileOrFail } from "./files"; import { loadResourceFile } from "./resource"; +import { FileNotFoundError } from "./errors"; const BindingFileSchema = z.object({ $binding: z.literal("playbill"), @@ -39,21 +40,42 @@ export interface PlaybillBinding { playbill: Playbill; } +export async function resolveBindingPath(bindingPath: string): Promise { + // If the path does not specify a filename, use binding.yamk + const inputLocation = Bun.file(bindingPath); + + // If it is a directory, try again seeking a binding.yaml inside + const stat = await inputLocation.stat(); + if (stat.isDirectory()) { + return resolveBindingPath(path.resolve(bindingPath, "binding.yaml")); + } + + // If it doesnt exist, bail + if (!(await inputLocation.exists())) { + throw new FileNotFoundError(bindingPath); + } + + // Resolve the path + return path.resolve(bindingPath); +} + export async function loadFromBinding( - filePath: string, + bindingPath: string, ): Promise { - const yamlContent = await loadYAMLFileOrFail(filePath); + const resolvedBindingPath = await resolveBindingPath(bindingPath); + const yamlContent = await loadYAMLFileOrFail(resolvedBindingPath); const binding = BindingFileSchema.parse(yamlContent); const fileGlobs = binding.files; - const bindingFileDirname = filePath.split("/").slice(0, -1).join("/"); + const bindingFileDirname = bindingPath.split("/").slice(0, -1).join("/"); let basePlaybill: Playbill | null = null; // If this is extending another binding, load that first if (binding.extends) { - // TODO: Allow extending built playbills in addition to bindings - const extendBindingPath = path.resolve(bindingFileDirname, binding.extends); - basePlaybill = (await loadFromBinding(extendBindingPath)).playbill; + const pathFromHere = path.resolve(bindingFileDirname, binding.extends); + const extendBindingPath = await resolveBindingPath(pathFromHere); + const loadedBinding = await loadFromBinding(extendBindingPath); + basePlaybill = loadedBinding.playbill; } const allFilePaths: string[] = []; @@ -71,13 +93,13 @@ export async function loadFromBinding( allFilePaths.push(...results); } - const bindingFileAbsolutePath = path.resolve(filePath); + const bindingFileAbsolutePath = path.resolve(bindingPath); const filePathsWithoutBindingFile = allFilePaths.filter((filePath) => { return filePath !== bindingFileAbsolutePath; }); // -- LOAD ASSOCIATED RESOURCE FILES -- // - const loadedResources: AnyResource[] = []; + const loadedResources: UnknownResource[] = []; for (const filepath of filePathsWithoutBindingFile) { const loaded = await loadResourceFile(filepath); loadedResources.push(...loaded); @@ -93,43 +115,17 @@ export async function loadFromBinding( playbill.description = binding.description ?? ""; for (const resource of loadedResources) { - switch (resource.$define) { - case "ability": - playbill.abilities.push(resource); - break; - case "species": - playbill.species.push(resource); - break; - case "entity": - playbill.entities.push(resource); - break; - case "item": - playbill.items.push(resource); - break; - case "tag": - playbill.tags.push(resource); - break; - case "method": - playbill.methods.push(resource); - break; - case "lore": - playbill.lore.push(resource); - break; - case "rule": - playbill.rules.push(resource); - break; - case "guide": - playbill.guides.push(resource); - break; - case "glossary": - playbill.glossaries.push(resource); - break; - } + const resourceType = resource.$define; + const resourceMap = playbill[resourceType] as Record< + string, + typeof resource + >; + resourceMap[resource.id] = resource; } return { _raw: binding, - bindingFilePath: filePath, + bindingFilePath: bindingPath, includedFiles: filePathsWithoutBindingFile, id: binding.id, diff --git a/src/index.ts b/src/index.ts index 2cd7db7..d78349f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import { parseArgs } from "node:util"; -import { ValidatedPlaybillSchema, checkDirectives } from "@proscenium/playbill"; +import { ValidatedPlaybillSchema } from "@proscenium/playbill"; import chalk from "chalk"; -import { loadFromBinding } from "./binding"; +import { loadFromBinding, resolveBindingPath } from "./binding"; import { usage } from "./usage"; import { CLIError, MuseError } from "./errors"; @@ -74,7 +74,8 @@ async function main(): Promise { } // -- BINDING FILE -- // - const bindingPath = args[0] ?? "./binding.yaml"; + const bindingPathInput = args[0] ?? "./binding.yaml"; + const bindingPath = await resolveBindingPath(bindingPathInput); verboseLog(`Building Playbill with binding: ${bindingPath}`); @@ -94,19 +95,12 @@ async function main(): Promise { return ExitCode.Error; } - const directiveValidationResult = checkDirectives(validatedPlaybill.data); - - if (!directiveValidationResult) { - console.error("Error validating playbill directives"); - return ExitCode.Error; - } - verboseLog("Playbill validated"); // -- EXIT EARLY IF JUST CHECKING VALIDATION --// if (options.check) { console.log(chalk.green("Playbill validated successfully")); - return ExitCode.Error; + return ExitCode.Success; } // -- SERIALIZE TO JSON --// diff --git a/src/resource.ts b/src/resource.ts index 2ebe522..fd921f5 100644 --- a/src/resource.ts +++ b/src/resource.ts @@ -1,6 +1,6 @@ import { - type AnyResource, - AnyResourceSchema, + type UnknownResource, + UnknownResourceSchema, ResourceMap, } from "@proscenium/playbill"; import YAML, { YAMLParseError } from "yaml"; @@ -15,7 +15,10 @@ function determineFileFormat(filePath: string): FileFormat { return filePath.split(".").pop() === "md" ? "markdown" : "yaml"; } -function loadYamlResourceFile(filePath: string, text: string): AnyResource[] { +function loadYamlResourceFile( + filePath: string, + text: string, +): UnknownResource[] { const parsedDocs = YAML.parseAllDocuments(text); if (parsedDocs.some((doc) => doc.toJS() === null)) { @@ -32,11 +35,11 @@ function loadYamlResourceFile(filePath: string, text: string): AnyResource[] { ); } - const collection: AnyResource[] = []; + const collection: UnknownResource[] = []; for (const doc of parsedDocs) { const raw = doc.toJS(); - const parsed = AnyResourceSchema.safeParse(raw); + const parsed = UnknownResourceSchema.safeParse(raw); if (!parsed.success) { throw new MalformedResourceFileError( @@ -59,7 +62,7 @@ function loadYamlResourceFile(filePath: string, text: string): AnyResource[] { function loadMarkdownResourceFile( filePath: string, text: string, -): AnyResource[] { +): UnknownResource[] { try { const frontmatter = extractFrontmatter(text); const markdown = extractMarkdown(text); @@ -69,7 +72,7 @@ function loadMarkdownResourceFile( description: markdown, }; - const parsed = AnyResourceSchema.parse(together); + const parsed = UnknownResourceSchema.parse(together); return [parsed]; } catch (e) { if (e instanceof YAMLParseError) { @@ -102,7 +105,7 @@ function loadMarkdownResourceFile( */ export async function loadResourceFile( filePath: string, -): Promise { +): Promise { const text = await loadFileOrFail(filePath); const format = determineFileFormat(filePath);