Organize into folders

This commit is contained in:
Endeavorance 2025-03-10 20:58:11 -04:00
parent 84b8f1c9d6
commit 2e0d4b45ea
12 changed files with 121 additions and 64 deletions

View file

@ -7,7 +7,8 @@
},
"files": {
"ignoreUnknown": false,
"ignore": ["*.d.ts", "*.json"]
"ignore": ["*.d.ts", "*.json"],
"include": ["./src/**/*.ts"]
},
"formatter": {
"enabled": true,

View file

@ -1,15 +1,32 @@
import { watch } from "node:fs/promises";
import {
serializePlaybill,
ValidatedPlaybillSchema,
serializePlaybill,
} from "@proscenium/playbill";
import chalk from "chalk";
import { watch } from "node:fs/promises";
import { loadFromBinding, resolveBindingPath } from "./filetypes/binding";
import { usage } from "./usage";
import { CLIError, MuseError } from "./errors";
import { renderAsHTML } from "./renderers/html";
import { parseCLIArguments, type CLIArguments } from "./args";
import { fullDirname } from "./util/files";
import { loadFromBinding, resolveBindingPath } from "#filetypes/binding";
import { CLIError, MuseError } from "#lib/errors";
import { renderAsHTML } from "#renderers/html";
import { type CLIArguments, parseCLIArguments } from "#util/args";
import { getAbsoluteDirname } from "#util/files";
import { version } from "../package.json" with { type: "json" };
const usage = `
${chalk.bold("muse")} - Compile and validate Playbills for Proscenium
${chalk.dim(`v${version}`)}
Usage:
muse [/path/to/binding.yaml] <options>
Options:
--check Only load and check the current binding and resources, but do not compile
--write Write the output to a file. If not specified, outputs to stdout
--outfile Specify the output file path [default: playbill.json]
--verbose, -v Verbose output
--minify, -m Minify the output JSON
--watch Watch the directory for changes and recompile
--help, -h Show this help message
`.trim();
enum ExitCode {
Success = 0,
@ -71,17 +88,14 @@ async function main(): Promise<number> {
return ExitCode.Success;
}
// -- ARG VALIDATION -- //
if (options.check && options.write) {
throw new CLIError("Cannot use --check and --write together");
}
console.log(`Processing ${cliArguments.inputFilePath}`);
let lastProcessResult = await processBinding(cliArguments);
if (options.watch) {
const watchDir = fullDirname(cliArguments.inputFilePath);
const watchDir = getAbsoluteDirname(cliArguments.inputFilePath);
console.log(`Watching ${watchDir} for changes`);
const watcher = watch(watchDir, {
recursive: true,
});

View file

@ -1,14 +1,14 @@
import path from "node:path";
import {
type Playbill,
type UnknownResource,
getEmptyPlaybill,
} from "@proscenium/playbill";
import { Glob } from "bun";
import z from "zod";
import {
getEmptyPlaybill,
type UnknownResource,
type Playbill,
} from "@proscenium/playbill";
import { loadYAMLFileOrFail } from "../util/files";
import { FileNotFoundError } from "#lib/errors";
import { loadYAMLFileOrFail } from "#util/files";
import { loadResourceFile } from "./resource";
import { FileNotFoundError } from "../errors";
const HTMLRenderOptionsSchema = z.object({
styles: z.array(z.string()).optional(),

View file

@ -19,7 +19,7 @@ export class FileError extends MuseError {
}
}
export class MalformedResourceFileError extends FileError { }
export class MalformedResourceFileError extends FileError {}
export class FileNotFoundError extends FileError {
constructor(filePath: string) {
@ -29,4 +29,4 @@ export class FileNotFoundError extends FileError {
}
}
export class CLIError extends MuseError { }
export class CLIError extends MuseError {}

View file

@ -27,6 +27,11 @@ export function extractFrontmatter(content: string) {
return {};
}
/**
* Given a string of a markdown document, extract the markdown content
* @param content The raw markdown content
* @returns The markdown content without frontmatter
*/
export function extractMarkdown(content: string): string {
if (content.trim().indexOf("---") !== 0) {
return content;
@ -54,6 +59,12 @@ ${markdown.trim()}
`;
}
/**
* Given a string of markdown, convert it to HTML
*
* @param markdown The markdown to convert
* @returns A promise resolving to the HTML representation of the markdown
*/
export async function markdownToHtml(markdown: string): Promise<string> {
const rendered = await unified()
.use(remarkParse)

View file

@ -1,13 +1,13 @@
import {
ResourceMap,
type UnknownResource,
UnknownResourceSchema,
ResourceMap,
} from "@proscenium/playbill";
import YAML, { YAMLParseError } from "yaml";
import { ZodError } from "zod";
import { MalformedResourceFileError } from "../errors";
import { MalformedResourceFileError } from "#lib/errors";
import { loadFileOrFail } from "#util/files";
import { extractFrontmatter, extractMarkdown } from "./markdown";
import { loadFileOrFail } from "../util/files";
type FileFormat = "yaml" | "markdown";

View file

@ -6,14 +6,14 @@ import type {
Rule,
ValidatedPlaybill,
} from "@proscenium/playbill";
import { markdownToHtml } from "../filetypes/markdown";
import type { PlaybillBinding } from "../filetypes/binding";
import rehypeParse from "rehype-parse";
import { unified } from "unified";
import rehypeFormat from "rehype-format";
import rehypeStringify from "rehype-stringify";
import type { PlaybillBinding } from "lib/binding";
import { markdownToHtml } from "lib/markdown";
import capitalize from "lodash-es/capitalize";
import { FileNotFoundError } from "../errors";
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);

View file

@ -1,5 +0,0 @@
import { z } from "zod";
const Renderers = ["json", "html"] as const;
export const RendererSchema = z.enum(Renderers);
export type Renderer = "json" | "html";

View file

@ -1,18 +0,0 @@
import chalk from "chalk";
import { version } from "../package.json" with { type: "json" };
export const usage = `
${chalk.bold("muse")} - Compile and validate Playbills for Proscenium
${chalk.dim(`v${version}`)}
Usage:
muse [/path/to/binding.yaml] <options>
Options:
--check Only load and check the current binding and resources, but do not compile
--write Write the output to a file. If not specified, outputs to stdout
--outfile Specify the output file path [default: playbill.json]
--verbose, -v Verbose output
--minify, -m Minify the output JSON
--help, -h Show this help message
`.trim();

View file

@ -1,7 +1,14 @@
import { parseArgs } from "node:util";
import { RendererSchema, type Renderer } from "./types";
import { MuseError } from "./errors";
import { z } from "zod";
import { CLIError, MuseError } from "#lib/errors";
const Renderers = ["json", "html"] as const;
const RendererSchema = z.enum(Renderers);
type Renderer = z.infer<typeof RendererSchema>;
/**
* A shape representing the arguments passed to the CLI
*/
export interface CLIArguments {
inputFilePath: string;
@ -16,6 +23,14 @@ export interface CLIArguments {
};
}
/**
* Given an array of CLI arguments, parse them into a structured object
*
* @param argv The arguments to parse
* @returns The parsed CLI arguments
*
* @throws {CLIError} if the arguments are invalid
*/
export function parseCLIArguments(argv: string[]): CLIArguments {
const { values: options, positionals: args } = parseArgs({
args: argv,
@ -59,6 +74,11 @@ export function parseCLIArguments(argv: string[]): CLIArguments {
allowPositionals: true,
});
// -- ARG VALIDATION -- //
if (options.check && options.write) {
throw new CLIError("Cannot use --check and --write together");
}
const parsedRenderer = RendererSchema.safeParse(options.renderer);
if (!parsedRenderer.success) {

View file

@ -1,7 +1,14 @@
import YAML from "yaml";
import { FileError, FileNotFoundError } from "../errors";
import path from "node:path";
import YAML from "yaml";
import { FileError, FileNotFoundError } from "../lib/errors";
/**
* Load a file from the filesystem or throw an error if it does not exist.
* @param filePath The path to the file to load
* @returns The contents of the file as a string
*
* @throws {FileNotFoundError} if the file does not exist
*/
export async function loadFileOrFail(filePath: string): Promise<string> {
const fileToLoad = Bun.file(filePath);
const fileExists = await fileToLoad.exists();
@ -13,6 +20,15 @@ export async function loadFileOrFail(filePath: string): Promise<string> {
return await fileToLoad.text();
}
/**
* Load and parse a YAML file or throw an error if the file doesnt exist
* or the yaml fails to parse
* @param filePath The path to the file to load
* @returns The parsed contents of the file
*
* @throws {FileNotFoundError} if the file does not exist
* @throws {FileError} if the file is not valid YAML
*/
export async function loadYAMLFileOrFail(filePath: string): Promise<unknown> {
try {
return YAML.parse(await loadFileOrFail(filePath));
@ -21,6 +37,12 @@ export async function loadYAMLFileOrFail(filePath: string): Promise<unknown> {
}
}
export function fullDirname(inputPath: string): string {
return path.resolve(path.dirname(inputPath));
/**
* Returns the absolute path of the directory containing the given file
*
* @param filePath The path to the file
* @returns The absolute path of the directory containing the file
*/
export function getAbsoluteDirname(filePath: string): string {
return path.resolve(path.dirname(filePath));
}

View file

@ -22,6 +22,18 @@
// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false
"noPropertyAccessFromIndexSignature": false,
"baseUrl": "./src",
"paths": {
"#lib/*": [
"./lib/*"
],
"#renderers/*": [
"./renderers/*"
],
"#util/*": [
"./util/*"
],
}
}
}