Move rendering to playbill
This commit is contained in:
parent
2e0d4b45ea
commit
d550f057c5
5 changed files with 42 additions and 414 deletions
31
src/index.ts
31
src/index.ts
|
@ -1,15 +1,16 @@
|
|||
import { watch } from "node:fs/promises";
|
||||
import {
|
||||
ValidatedPlaybillSchema,
|
||||
serializePlaybill,
|
||||
renderPlaybillToHTML,
|
||||
renderPlaybillToJSON,
|
||||
} from "@proscenium/playbill";
|
||||
import chalk from "chalk";
|
||||
import { loadFromBinding, resolveBindingPath } from "#filetypes/binding";
|
||||
import { CLIError, MuseError } from "#lib/errors";
|
||||
import { renderAsHTML } from "#renderers/html";
|
||||
import { loadFromBinding, resolveBindingPath } from "#lib/binding";
|
||||
import { CLIError, FileNotFoundError, MuseError } from "#lib/errors";
|
||||
import { type CLIArguments, parseCLIArguments } from "#util/args";
|
||||
import { getAbsoluteDirname } from "#util/files";
|
||||
import { version } from "../package.json" with { type: "json" };
|
||||
import path from "node:path";
|
||||
|
||||
const usage = `
|
||||
${chalk.bold("muse")} - Compile and validate Playbills for Proscenium
|
||||
|
@ -53,15 +54,33 @@ async function processBinding({ inputFilePath, options }: CLIArguments) {
|
|||
return ExitCode.Success;
|
||||
}
|
||||
|
||||
let css = "";
|
||||
|
||||
if (binding.html?.styles) {
|
||||
for (const style of binding.html.styles) {
|
||||
const cssFilePath = path.resolve(binding.bindingFileDirname, style);
|
||||
const cssFile = Bun.file(cssFilePath);
|
||||
const cssFileExists = await cssFile.exists();
|
||||
|
||||
if (cssFileExists) {
|
||||
css += `${await cssFile.text()}\n`;
|
||||
} else {
|
||||
throw new FileNotFoundError(cssFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- SERIALIZE USING RENDERER --//
|
||||
let serializedPlaybill = "";
|
||||
|
||||
switch (options.renderer) {
|
||||
case "json":
|
||||
serializedPlaybill = serializePlaybill(validatedPlaybill.data);
|
||||
serializedPlaybill = await renderPlaybillToJSON(validatedPlaybill.data);
|
||||
break;
|
||||
case "html":
|
||||
serializedPlaybill = await renderAsHTML(validatedPlaybill.data, binding);
|
||||
serializedPlaybill = await renderPlaybillToHTML(validatedPlaybill.data, {
|
||||
styles: css,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw new CLIError(`Unknown renderer: ${options.renderer}`);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import path from "node:path";
|
||||
import {
|
||||
PlaybillSchema,
|
||||
type Playbill,
|
||||
type UnknownResource,
|
||||
getEmptyPlaybill,
|
||||
} from "@proscenium/playbill";
|
||||
import { Glob } from "bun";
|
||||
import z from "zod";
|
||||
|
@ -115,7 +115,15 @@ export async function loadFromBinding(
|
|||
}
|
||||
|
||||
// -- COMPILE PLAYBILL FROM RESOURCES --//
|
||||
const playbill = basePlaybill ?? getEmptyPlaybill();
|
||||
const playbill =
|
||||
basePlaybill ??
|
||||
PlaybillSchema.parse({
|
||||
$define: "playbill",
|
||||
id: "blank",
|
||||
name: "Unnamed playbill",
|
||||
description: "Unnamed Playbill",
|
||||
version: "0.0.1",
|
||||
});
|
||||
|
||||
playbill.id = binding.id;
|
||||
playbill.name = binding.name;
|
||||
|
|
|
@ -1,183 +0,0 @@
|
|||
import path from "node:path";
|
||||
import type {
|
||||
Ability,
|
||||
Guide,
|
||||
Method,
|
||||
Rule,
|
||||
ValidatedPlaybill,
|
||||
} from "@proscenium/playbill";
|
||||
import type { PlaybillBinding } from "lib/binding";
|
||||
import { markdownToHtml } from "lib/markdown";
|
||||
import capitalize from "lodash-es/capitalize";
|
||||
import rehypeFormat from "rehype-format";
|
||||
import rehypeParse from "rehype-parse";
|
||||
import rehypeStringify from "rehype-stringify";
|
||||
import { unified } from "unified";
|
||||
import { FileNotFoundError } from "#lib/errors";
|
||||
|
||||
async function renderGuide(guide: Guide): Promise<string> {
|
||||
const html = await markdownToHtml(guide.description);
|
||||
|
||||
return `<section class="guide flow">
|
||||
<h2>${guide.name}</h2>
|
||||
${html}
|
||||
</section>`;
|
||||
}
|
||||
|
||||
async function renderRule(rule: Rule): Promise<string> {
|
||||
const html = await markdownToHtml(rule.description);
|
||||
|
||||
return `<section class="rule flow">
|
||||
<h2>${rule.name}</h2>
|
||||
${html}
|
||||
</section>`;
|
||||
}
|
||||
|
||||
async function renderAbility(ability: Ability): Promise<string> {
|
||||
const html = await markdownToHtml(ability.description);
|
||||
|
||||
const costs: string[] = [];
|
||||
|
||||
if (ability.ap > 0) {
|
||||
costs.push(`<li>AP Cost: ${ability.ap}</li>`);
|
||||
}
|
||||
|
||||
if (ability.hp > 0) {
|
||||
costs.push(`<li>HP Cost: ${ability.hp}</li>`);
|
||||
}
|
||||
|
||||
if (ability.ep > 0) {
|
||||
costs.push(`<li>EP Cost: ${ability.ep}</li>`);
|
||||
}
|
||||
|
||||
const costList = costs.length > 0 ? `<ul>${costs.join("\n")}</ul>` : "";
|
||||
|
||||
return `
|
||||
<div class="ability flow">
|
||||
<h4>${ability.name}</h4>
|
||||
<small>${ability.type} / ${ability.xp} XP</small>
|
||||
${costList}
|
||||
${html}
|
||||
</div>`.trim();
|
||||
}
|
||||
|
||||
async function renderMethod(method: Method, playbill: ValidatedPlaybill) {
|
||||
const descriptionHTML = await markdownToHtml(method.description);
|
||||
let ranksHTML = "";
|
||||
let rankNumber = 1;
|
||||
|
||||
for (const rank of method.abilities) {
|
||||
ranksHTML += `<h3>Rank ${rankNumber}</h3><div class="method-rank flow">`;
|
||||
|
||||
for (const ability of rank) {
|
||||
const html = await renderAbility(playbill.ability[ability]);
|
||||
ranksHTML += html;
|
||||
}
|
||||
|
||||
ranksHTML += "</div>";
|
||||
|
||||
rankNumber++;
|
||||
}
|
||||
|
||||
return `<section class="method flow">
|
||||
<h2>${method.curator}'s Method of ${method.name}</h2>
|
||||
${descriptionHTML}
|
||||
${ranksHTML}
|
||||
</section>`;
|
||||
}
|
||||
|
||||
async function renderItemTags(playbill: ValidatedPlaybill) {
|
||||
let tagsTableHTML = `
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Tag</th>
|
||||
<th>Item Types</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`.trim();
|
||||
|
||||
for (const tag of Object.values(playbill.tag)) {
|
||||
tagsTableHTML += `
|
||||
<tr>
|
||||
<td>${tag.name}</td>
|
||||
<td>${tag.types.map(capitalize).join(", ")}</td>
|
||||
<td>${await markdownToHtml(tag.description)}</td>
|
||||
</tr>
|
||||
`.trim();
|
||||
}
|
||||
|
||||
tagsTableHTML += "</tbody></table>";
|
||||
|
||||
return `<section class="tags flow"><h2>Item Tags</h2>${tagsTableHTML}</section>`;
|
||||
}
|
||||
|
||||
async function renderDocument(
|
||||
playbill: ValidatedPlaybill,
|
||||
binding: PlaybillBinding,
|
||||
): Promise<string> {
|
||||
const guides = await Promise.all(
|
||||
Object.values(playbill.guide).map(renderGuide),
|
||||
);
|
||||
|
||||
const methods = await Promise.all(
|
||||
Object.values(playbill.method).map((method) => {
|
||||
return renderMethod(method, playbill);
|
||||
}),
|
||||
);
|
||||
|
||||
const rules = await Promise.all(Object.values(playbill.rule).map(renderRule));
|
||||
|
||||
let css = "";
|
||||
|
||||
if (binding.html?.styles) {
|
||||
for (const style of binding.html.styles) {
|
||||
const cssFilePath = path.resolve(binding.bindingFileDirname, style);
|
||||
const cssFile = Bun.file(cssFilePath);
|
||||
const cssFileExists = await cssFile.exists();
|
||||
|
||||
if (cssFileExists) {
|
||||
css += `<style>
|
||||
${await cssFile.text()}
|
||||
</style>`;
|
||||
} else {
|
||||
throw new FileNotFoundError(cssFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return `
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
${css}
|
||||
</head>
|
||||
<body>
|
||||
<article>
|
||||
<h1>${playbill.name}</h1>
|
||||
<small>By ${playbill.author}</small>
|
||||
<blockquote>${playbill.description}</blockquote>
|
||||
${guides.join("\n")}
|
||||
${methods.join("\n")}
|
||||
${await renderItemTags(playbill)}
|
||||
${rules.join("\n")}
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
`.trim();
|
||||
}
|
||||
|
||||
export async function renderAsHTML(
|
||||
playbill: ValidatedPlaybill,
|
||||
binding: PlaybillBinding,
|
||||
): Promise<string> {
|
||||
const doc = await renderDocument(playbill, binding);
|
||||
const formatted = await unified()
|
||||
.use(rehypeParse)
|
||||
.use(rehypeFormat)
|
||||
.use(rehypeStringify)
|
||||
.process(doc);
|
||||
return String(formatted);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue