Cleanup, commenting

This commit is contained in:
Endeavorance 2025-06-04 19:07:01 -04:00
parent d39e7f5154
commit 515217e347

View file

@ -1,19 +1,26 @@
import Path from "node:path"; import Path from "node:path";
import { parseArgs } from "node:util"; import { parseArgs } from "node:util";
import EMDY from "@endeavorance/emdy"; import EMDY from "@endeavorance/emdy";
import defaultStylesheet from "./defaults/default-style.css" with { import type { BunFile } from "bun";
import DEFAULT_STYLESHEET from "./defaults/default-style.css" with {
type: "text", type: "text",
}; };
import defaultTemplate from "./defaults/default-template.html" with { import DEFAULT_TEMPLATE from "./defaults/default-template.html" with {
type: "text", type: "text",
}; };
import { CLIError } from "./error"; import { CLIError } from "./error";
import type { BunFile } from "bun";
import { parseMarkdown } from "./markdown"; import { parseMarkdown } from "./markdown";
const DEFAULT_TEMPLATE_FILE = "_template.html"; const DEFAULT_TEMPLATE_FILE = "_template.html";
const DEFAULT_STYLESHEET_FILE = "_style.css"; const DEFAULT_STYLESHEET_FILE = "_style.css";
/**
* Given a file path, attempt to read the text of the file
* @param filePath - The path to the file to read
* @returns The text content of the file
*
* @throws CLIError if the file does not exist
*/
async function readFile(filePath: string): Promise<string> { async function readFile(filePath: string): Promise<string> {
const file = Bun.file(filePath); const file = Bun.file(filePath);
const exists = await file.exists(); const exists = await file.exists();
@ -23,7 +30,13 @@ async function readFile(filePath: string): Promise<string> {
return file.text(); return file.text();
} }
function replacePlaceholders( /**
* Renders the provided template with the given data.
* @param template - The template string containing placeholders like %key%
* @param data - An object containing key-value pairs to replace in the template
* @returns The rendered string with all placeholders replaced by their corresponding values
*/
function renderTemplate(
template: string, template: string,
data: Record<string, unknown>, data: Record<string, unknown>,
): string { ): string {
@ -34,17 +47,39 @@ function replacePlaceholders(
return output; return output;
} }
/**
* A collection of options parsed from the command line arguments.
*/
interface CLIOptions { interface CLIOptions {
/** A path to the output file */
outfile: string | null; outfile: string | null;
/** A path to the output directory */
outdir: string | null; outdir: string | null;
/** If true, force output to stdout */
stdout: boolean; stdout: boolean;
/** The path to the template file */
templateFilePath: string | null; templateFilePath: string | null;
/** The path to the stylesheet file */
stylesheetFilePath: string | null; stylesheetFilePath: string | null;
/** If true, show help message */
help: boolean; help: boolean;
/** If provided, overrides the document title */
title: string | null; title: string | null;
/** If provided, overrides the current working directory (default: .) */
cwd: string; cwd: string;
} }
/**
* Parse the command line arguments and return the options and positional arguments.
* @returns An object containing the parsed options and positional arguments
*/
function parseCLIArgs(): { options: CLIOptions; args: string[] } { function parseCLIArgs(): { options: CLIOptions; args: string[] } {
const { values: flags, positionals } = parseArgs({ const { values: flags, positionals } = parseArgs({
args: Bun.argv.slice(2), args: Bun.argv.slice(2),
@ -109,57 +144,69 @@ function parseCLIArgs(): { options: CLIOptions; args: string[] } {
}; };
} }
/**
* Attempt to load a custom template, falling back to a default
* @param options - The CLI options containing the template file path
* @returns The template string
*/
async function loadTemplate(options: CLIOptions): Promise<string> { async function loadTemplate(options: CLIOptions): Promise<string> {
if (options.templateFilePath) { if (options.templateFilePath) {
return readFile(options.templateFilePath); return readFile(options.templateFilePath);
} }
const defaultTemplateFilePath = Path.join(options.cwd, DEFAULT_TEMPLATE_FILE); const checkTemplateFile = Bun.file(
const defaultTemplateFile = Bun.file(defaultTemplateFilePath); Path.join(options.cwd, DEFAULT_TEMPLATE_FILE),
);
if (await defaultTemplateFile.exists()) { if (await checkTemplateFile.exists()) {
return defaultTemplateFile.text(); return checkTemplateFile.text();
} }
return defaultTemplate; return DEFAULT_TEMPLATE;
} }
/**
* Attempt to load a custom stylesheet, falling back to a default
* @param options - The CLI options containing the stylesheet file path
* @returns The stylesheet string
*/
async function loadStylesheet(options: CLIOptions): Promise<string> { async function loadStylesheet(options: CLIOptions): Promise<string> {
if (options.stylesheetFilePath) { if (options.stylesheetFilePath) {
return readFile(options.stylesheetFilePath); return readFile(options.stylesheetFilePath);
} }
const defaultStylesheetFilePath = Path.join( const checkStylesheetFile = Bun.file(
options.cwd, Path.join(options.cwd, DEFAULT_STYLESHEET_FILE),
DEFAULT_STYLESHEET_FILE,
); );
const defaultStylesheetFile = Bun.file(defaultStylesheetFilePath);
if (await defaultStylesheetFile.exists()) { if (await checkStylesheetFile.exists()) {
return defaultStylesheetFile.text(); return checkStylesheetFile.text();
} }
return defaultStylesheet; return DEFAULT_STYLESHEET;
} }
/**
* Build and write a file
* @param infile - The input file to read from
* @param outfile - The output file to write to
* @param options - The CLI options containing template and stylesheet paths
*/
async function buildFile( async function buildFile(
infile: BunFile, infile: BunFile,
outfile: BunFile, outfile: BunFile,
options: CLIOptions, options: CLIOptions,
): Promise<string> { ): Promise<void> {
const input = await infile.text(); const input = await infile.text();
const template = await loadTemplate(options); const template = await loadTemplate(options);
const stylesheet = await loadStylesheet(options); const stylesheet = await loadStylesheet(options);
const { content, ...props } = EMDY.parse(input) as Record<string, unknown> & { const { content, ...props } = EMDY.parse(input) as Record<string, unknown> & {
content: string; content: string;
}; };
const html = await parseMarkdown(content); const html = await parseMarkdown(content);
const title = options.title ?? props.title ?? "Untitled";
const title = [options.title, props.title].find((t) => t) || "Untitled"; const templateData = {
const replacers = {
content: html, content: html,
title, title,
stylesheet: stylesheet, stylesheet: stylesheet,
@ -168,13 +215,15 @@ async function buildFile(
...props, ...props,
}; };
const output = replacePlaceholders(template, replacers); await Bun.write(outfile, renderTemplate(template, templateData));
Bun.write(outfile, output);
return output;
} }
/**
* Get the output path based on the input path and options
* @param inputPath - The path of the input file
* @param options - The CLI options containing output directory
* @returns The resolved output path
*/
async function getOutputPath(inputPath: string, options: CLIOptions) { async function getOutputPath(inputPath: string, options: CLIOptions) {
const inputDirname = Path.dirname(inputPath); const inputDirname = Path.dirname(inputPath);
const inputBasename = Path.basename(inputPath); const inputBasename = Path.basename(inputPath);
@ -189,6 +238,12 @@ async function getOutputPath(inputPath: string, options: CLIOptions) {
return Path.join(inputDirname, outputBasename); return Path.join(inputDirname, outputBasename);
} }
/**
* Get the output file based on the input path and options
* @param inputPath - The path of the input file
* @param options - The CLI options containing output file and directory
* @returns The BunFile object representing the output file
*/
async function getOutputFile( async function getOutputFile(
inputPath: string, inputPath: string,
options: CLIOptions, options: CLIOptions,