From f7f861a1cb4db1d8ab32a7c2a0d2489544088d08 Mon Sep 17 00:00:00 2001 From: Endeavorance Date: Fri, 21 Mar 2025 16:40:47 -0400 Subject: [PATCH] Use new playbill updates --- src/define/index.ts | 109 +++++++------------------ src/index.ts | 18 ++-- src/lib/binding.ts | 106 +++--------------------- src/lib/component-file.ts | 11 +-- src/lib/pfm.ts | 18 ++-- src/render/html/component/glossary.tsx | 6 +- src/render/html/component/method.tsx | 7 +- src/render/html/index.tsx | 16 ++-- 8 files changed, 81 insertions(+), 210 deletions(-) diff --git a/src/define/index.ts b/src/define/index.ts index 4e683b3..68b0aa6 100644 --- a/src/define/index.ts +++ b/src/define/index.ts @@ -1,5 +1,6 @@ import { type Ability, + type AnyPlaybillComponent, type Blueprint, type Item, type Method, @@ -161,37 +162,7 @@ const parseComponentType = (val: unknown): ComponentType => { return val as ComponentType; }; -export type ParsedComponent = - | { - type: "ability"; - component: Ability; - } - | { - type: "blueprint"; - component: Blueprint; - } - | { - type: "item"; - component: Item; - } - | { - type: "method"; - component: Method; - } - | { - type: "resource"; - component: Resource; - } - | { - type: "rule"; - component: Rule; - } - | { - type: "species"; - component: Species; - }; - -function parseAbilityDefinition(obj: unknown): ParsedComponent { +function parseAbilityDefinition(obj: unknown): Ability { const parsed = AbilitySchema.parse(obj); const ability = parseAbility({ @@ -214,13 +185,10 @@ function parseAbilityDefinition(obj: unknown): ParsedComponent { roll: parsed.roll, }); - return { - type: "ability", - component: ability, - }; + return ability; } -function parseBlueprintDefinition(obj: unknown): ParsedComponent { +function parseBlueprintDefinition(obj: unknown): Blueprint { const parsed = BlueprintSchema.parse(obj); const blueprint = parseBlueprint({ @@ -244,34 +212,28 @@ function parseBlueprintDefinition(obj: unknown): ParsedComponent { spark: parsed.spark, }); - return { - type: "blueprint", - component: blueprint, - }; + return blueprint; } -function parseItemDefinition(obj: unknown): ParsedComponent { +function parseItemDefinition(obj: unknown): Item { const parsed = ItemSchema.parse(obj); - return { - type: "item", - component: parseItem({ - ...parsed, - damage: - parsed.damage > 0 - ? { - amount: parsed.damage, - type: parsed.damageType, - } - : null, - abilities: parsed.abilities, - tweak: "", - temper: "", - }), - }; + return parseItem({ + ...parsed, + damage: + parsed.damage > 0 + ? { + amount: parsed.damage, + type: parsed.damageType, + } + : null, + abilities: parsed.abilities, + tweak: "", + temper: "", + }); } -function parseMethodDefinition(obj: unknown): ParsedComponent { +function parseMethodDefinition(obj: unknown): Method { const parsed = MethodSchema.parse(obj); // Prefer `abilities` if defined, otherwise @@ -310,40 +272,29 @@ function parseMethodDefinition(obj: unknown): ParsedComponent { abilities: abilities, }; - return { - type: "method", - component: parseMethod(method), - }; + return parseMethod(method); } -function parseResourceDefinition(obj: unknown): ParsedComponent { +function parseResourceDefinition(obj: unknown): Resource { const parsed = ResourceSchema.parse(obj); - - return { - type: "resource", - component: parseResource(parsed), - }; + return parseResource(parsed); } -function parseRuleDefinition(obj: unknown): ParsedComponent { +function parseRuleDefinition(obj: unknown): Rule { const parsed = RuleSchema.parse(obj); - return { - type: "rule", - component: parseRule(parsed), - }; + return parseRule(parsed); } -function parseSpeciesDefinition(obj: unknown): ParsedComponent { +function parseSpeciesDefinition(obj: unknown): Species { const parsed = SpeciesSchema.parse(obj); - return { - type: "species", - component: parseSpecies(parsed), - }; + return parseSpecies(parsed); } -export function parsePlaybillComponent(obj: unknown): ParsedComponent | null { +export function parsePlaybillComponent( + obj: unknown, +): AnyPlaybillComponent | null { const baseParse = Base.parse(obj); if (baseParse.$hidden) { diff --git a/src/index.ts b/src/index.ts index 72166e2..4f0ca63 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,17 +21,17 @@ enum ExitCode { } async function processBinding({ inputFilePath, options }: CLIArguments) { - // -- BINDING FILE -- // + // Load the binding const bindingPath = await resolveBindingPath(inputFilePath); const binding = await loadFromBinding(bindingPath); - // -- EXIT EARLY IF JUST CHECKING VALIDATION --// + // If --check is specified, exit early if (options.check) { console.log(chalk.green("Playbill validated successfully")); return ExitCode.Success; } - // -- SERIALIZE USING RENDERER --// + // Serialize (default: JSON) let serializedPlaybill = ""; switch (options.renderer) { @@ -48,12 +48,13 @@ async function processBinding({ inputFilePath, options }: CLIArguments) { throw new CLIError(`Unknown renderer: ${options.renderer}`); } - // Write to disk if an outfile is specified + // 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; } @@ -62,26 +63,27 @@ async function main(): Promise { const cliArguments = parseCLIArguments(Bun.argv.slice(2)); const { options } = cliArguments; - // -- HELP TEXT -- // + // If --help is specified, print usage and exit if (options.help) { console.log(USAGE); return ExitCode.Success; } - console.log(`Processing ${cliArguments.inputFilePath}`); let lastProcessResult = await processBinding(cliArguments); if (options.watch) { const watchDir = getAbsoluteDirname(cliArguments.inputFilePath); - console.log(`Watching ${watchDir} for changes`); + 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}`); + console.log( + `Detected ${event.eventType} on ${event.filename}. Reprocessing...`, + ); try { lastProcessResult = await processBinding(cliArguments); diff --git a/src/lib/binding.ts b/src/lib/binding.ts index 1a945b9..9e75fc8 100644 --- a/src/lib/binding.ts +++ b/src/lib/binding.ts @@ -1,14 +1,5 @@ import path from "node:path"; -import { - type Ability, - type Blueprint, - type Item, - type Method, - Playbill, - type Resource, - type Rule, - type Species, -} from "@proscenium/playbill"; +import { type AnyPlaybillComponent, Playbill } from "@proscenium/playbill"; import { Glob } from "bun"; import z from "zod"; import { loadYAMLFileOrFail } from "#util"; @@ -33,11 +24,7 @@ const BindingSchema = z.object({ terms: z.record(z.string(), z.string()).default({}), }); -type Binding = z.infer; - export interface BoundPlaybill { - _raw: Binding; - // File information bindingFilePath: string; bindingFileDirname: string; @@ -93,7 +80,12 @@ export async function loadFromBinding( const fileGlobs = binding.files; const bindingFileDirname = path.dirname(resolvedBindingPath); - const playbill = new Playbill(); + const playbill = new Playbill( + binding.name, + binding.author, + binding.description, + binding.version, + ); // If this is extending another binding, load that first for (const includePath of binding.include) { @@ -151,86 +143,15 @@ export async function loadFromBinding( await Promise.all(fileLoadPromises); - // Decorate the playbill with the binding metadata - playbill.name = binding.name; - playbill.author = binding.author ?? "Anonymous"; - playbill.version = binding.version; - playbill.description = binding.description ?? ""; - - const abilities: Ability[] = []; - const bluprints: Blueprint[] = []; - const methods: Method[] = []; - const species: Species[] = []; - const items: Item[] = []; - const rules: Rule[] = []; - const resources: Resource[] = []; - - // Aggregate all components before adding to playbill to allow for - // ensuring components are added in a valid order + // Aggregate all loaded components + const loadedComponents: AnyPlaybillComponent[] = []; for (const file of componentFiles) { - // Load components from the file into the playbill - for (const component of file.components) { - switch (component.type) { - case "ability": - abilities.push(component.component); - break; - case "blueprint": - bluprints.push(component.component); - break; - case "method": - methods.push(component.component); - break; - case "species": - species.push(component.component); - break; - case "item": - items.push(component.component); - break; - case "rule": - rules.push(component.component); - break; - case "resource": - resources.push(component.component); - break; - default: - throw new Error("Unknown component type"); - } - } + loadedComponents.push(...file.components); } - // Add resources first - for (const resource of resources) { - playbill.addResource(resource); - } - // Add abilities before methods, species, and blueprints - for (const ability of abilities) { - playbill.addAbility(ability); - } - - // Add species before blueprints - for (const specie of species) { - playbill.addSpecies(specie); - } - - // Add methods after abilities - for (const method of methods) { - playbill.addMethod(method); - } - - // Add items - for (const item of items) { - playbill.addItem(item); - } - - // Add blueprints after methods, species, items, and abilities - for (const blueprint of bluprints) { - playbill.addBlueprint(blueprint); - } - - // Add rules - for (const rule of rules) { - playbill.addRule(rule); - } + // Add all components to the playbill + // (This method ensures proper addition order maintain correctness) + playbill.addManyComponents(loadedComponents); // Add all definitions for (const [term, definition] of Object.entries(binding.terms)) { @@ -238,7 +159,6 @@ export async function loadFromBinding( } return { - _raw: binding, bindingFilePath: bindingPath, bindingFileDirname, files: filePathsWithoutBindingFile, diff --git a/src/lib/component-file.ts b/src/lib/component-file.ts index dd272af..0dd6ea4 100644 --- a/src/lib/component-file.ts +++ b/src/lib/component-file.ts @@ -1,10 +1,11 @@ import path from "node:path"; -import { type ParsedComponent, parsePlaybillComponent } from "define"; +import { parsePlaybillComponent } from "define"; import YAML, { YAMLParseError } from "yaml"; import { ZodError } from "zod"; import { loadFileOrFail } from "#util"; import { MalformedResourceFileError } from "./errors"; import { toSlug } from "./slug"; +import type { AnyPlaybillComponent } from "@proscenium/playbill"; type FileFormat = "yaml" | "markdown"; @@ -45,7 +46,7 @@ function extractMarkdown(content: string): string { function parseYAMLResourceFile( filePath: string, text: string, -): ParsedComponent[] { +): AnyPlaybillComponent[] { const parsedDocs = YAML.parseAllDocuments(text); if (parsedDocs.some((doc) => doc.toJS() === null)) { @@ -62,7 +63,7 @@ function parseYAMLResourceFile( ); } - const collection: ParsedComponent[] = []; + const collection: AnyPlaybillComponent[] = []; for (const doc of parsedDocs) { const raw = doc.toJS(); @@ -78,7 +79,7 @@ function parseYAMLResourceFile( function parseMarkdownResourceFile( filePath: string, text: string, -): ParsedComponent[] { +): AnyPlaybillComponent[] { try { const defaultName = path.basename(filePath, ".md"); const defaultId = toSlug(defaultName); @@ -124,7 +125,7 @@ export class ComponentFile { private _filePath: string; private _raw = ""; private _format: FileFormat; - private _components: ParsedComponent[] = []; + private _components: AnyPlaybillComponent[] = []; private _basename: string; constructor(filePath: string) { diff --git a/src/lib/pfm.ts b/src/lib/pfm.ts index 29b9e60..97043e6 100644 --- a/src/lib/pfm.ts +++ b/src/lib/pfm.ts @@ -8,7 +8,7 @@ import { toSlug } from "./slug"; import rehypeRaw from "rehype-raw"; import remarkGfm from "remark-gfm"; import rehypeShiftHeading from "rehype-shift-heading"; -import type { TaggedComponent } from "@proscenium/playbill"; +import type { AnyPlaybillComponent } from "@proscenium/playbill"; export type MarkdownParserFunction = (input: string) => Promise; @@ -26,9 +26,7 @@ export function createMarkdownRenderer( .use(remarkGfm) .use(remarkWikiLink, { aliasDivider: "|", - permalinks: binding.playbill - .allComponents() - .map((c) => `${c.component.id}`), + permalinks: binding.playbill.allComponents().map((c) => c.id), pageResolver: (permalink: string) => { return [toSlug(permalink)]; }, @@ -54,21 +52,19 @@ export async function compileMarkdownInPlaybill( const playbill = boundPlaybill.playbill; // Define a processor function to iterate over all components and process their markdown - const processMarkdownInComponent = async (entry: TaggedComponent) => { - entry.component.description = await renderMarkdown( - entry.component.description, - ); + const processMarkdownInComponent = async (entry: AnyPlaybillComponent) => { + entry.description = await renderMarkdown(entry.description); - if (entry.type === "resource" && entry.component.type === "table") { + if (entry._component === "resource" && entry.type === "table") { const newData: string[][] = []; - for (const row of entry.component.data) { + for (const row of entry.data) { const newRow: string[] = []; for (const cell of row) { newRow.push(await renderMarkdown(cell)); } newData.push(newRow); } - entry.component.data = newData; + entry.data = newData; } }; diff --git a/src/render/html/component/glossary.tsx b/src/render/html/component/glossary.tsx index 784f43d..a139dc4 100644 --- a/src/render/html/component/glossary.tsx +++ b/src/render/html/component/glossary.tsx @@ -1,15 +1,15 @@ import { Section } from "./base/section"; interface GlossaryTableProps { - glossary: Record; + terms: [string, string[]][]; } -export function GlossaryTable({ glossary }: GlossaryTableProps) { +export function GlossaryTable({ terms }: GlossaryTableProps) { return (
- {Object.entries(glossary).map(([term, defs]) => { + {terms.map(([term, defs]) => { return (
{term}
diff --git a/src/render/html/component/method.tsx b/src/render/html/component/method.tsx index 96b7258..e39106b 100644 --- a/src/render/html/component/method.tsx +++ b/src/render/html/component/method.tsx @@ -17,7 +17,12 @@ export function MethodSection({ method, playbill }: MethodSectionProps) {

Rank {i + 1}

{rank.map((abilityId) => { - const ability = playbill.abilities[abilityId]; + const ability = playbill.getAbility(abilityId); + + if (ability === null) { + throw new Error(`Ability not found: ${abilityId}`); + } + return ; })}
diff --git a/src/render/html/index.tsx b/src/render/html/index.tsx index a710903..bb26ddd 100644 --- a/src/render/html/index.tsx +++ b/src/render/html/index.tsx @@ -1,5 +1,4 @@ import { Playbill } from "@proscenium/playbill"; -import sortBy from "lodash-es/sortBy"; import { compileMarkdownInPlaybill, type BoundPlaybill } from "#lib"; import { GlossaryTable } from "./component/glossary"; import { PageHeader } from "./component/header"; @@ -19,9 +18,6 @@ export async function renderPlaybillToHTML( const playbill = Playbill.fromSerialized(boundPlaybill.playbill.serialize()); await compileMarkdownInPlaybill(boundPlaybill); - // Soprt rules by their order prop - const rulesByOrder = sortBy(Object.values(playbill.rules), "order"); - // Prepare stylesheet const cssParts: string[] = []; @@ -38,7 +34,7 @@ export async function renderPlaybillToHTML( const body = renderToString( <>
- {Object.values(playbill.species).map((species) => ( + {playbill.allSpecies.map((species) => (
- {Object.values(playbill.methods).map((method) => ( + {playbill.allMethods.map((method) => ( ))}
- {Object.values(playbill.items).map((item) => ( + {playbill.allItems.map((item) => ( ))}
- {rulesByOrder.map((rule) => ( + {playbill.allRules.map((rule) => ( ))}
- {} + {}
- {Object.values(playbill.resources).map((resource) => ( + {playbill.allResources.map((resource) => ( ))}