150 lines
3 KiB
TypeScript
150 lines
3 KiB
TypeScript
import Path from "node:path";
|
|
import { parseArgs } from "util";
|
|
import { marked } from "marked";
|
|
|
|
const { values: flags, positionals } = parseArgs({
|
|
args: Bun.argv.slice(2),
|
|
options: {
|
|
outfile: {
|
|
type: 'string',
|
|
short: 'o',
|
|
default: 'index.html',
|
|
},
|
|
template: {
|
|
type: 'string',
|
|
short: 't',
|
|
default: 'template.html',
|
|
},
|
|
stylesheet: {
|
|
type: 'string',
|
|
short: 's',
|
|
default: 'style.css',
|
|
},
|
|
},
|
|
allowPositionals: true,
|
|
});
|
|
|
|
async function readOptionalFileContent(filePath: string, defaultContent: string): Promise<string> {
|
|
const file = Bun.file(filePath);
|
|
const exists = await file.exists();
|
|
|
|
if (!exists) {
|
|
return defaultContent;
|
|
}
|
|
|
|
return file.text();
|
|
}
|
|
|
|
const inputFileArg: string | null = positionals[0] ?? null;
|
|
const inputFileName = Path.basename(inputFileArg ?? 'Document', '.md');
|
|
const inputFile = inputFileArg ? Bun.file(inputFileArg) : Bun.stdin;
|
|
|
|
const inputFileExists = await inputFile.exists();
|
|
|
|
if (!inputFileExists) {
|
|
throw new Error(`Input file ${inputFileArg} does not exist.`);
|
|
}
|
|
|
|
const outputFile = Bun.file(flags.outfile);
|
|
const templateFile = await readOptionalFileContent(flags.template, `
|
|
<!doctype html>
|
|
<html lang="en-US">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width" />
|
|
<title>%title%</title>
|
|
<style>
|
|
%stylesheet%
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<article>
|
|
%content%
|
|
</article>
|
|
</body>
|
|
</html>
|
|
`.trim());
|
|
|
|
const stylesheetFile = await readOptionalFileContent(flags.stylesheet, `
|
|
:root {
|
|
--color-bg: #333;
|
|
--color-text: #DDD;
|
|
--color-link: #FFF;
|
|
--color-strong: #ff8f8f;
|
|
--color-emphasis: #FF8F00;
|
|
|
|
--font-size: 18px;
|
|
--font-family: Seravek, 'Gill Sans Nova', Ubuntu, Calibri, 'DejaVu Sans', source-sans-pro, sans-serif;
|
|
--font-weight: 400;
|
|
--font-weight-bold: 500;
|
|
--line-height: 1.4;
|
|
|
|
--max-width: 700px;
|
|
}
|
|
|
|
body {
|
|
background-color: var(--color-bg);
|
|
color: var(--color-text);
|
|
font-size: var(--font-size);
|
|
font-family: var(--font-family);
|
|
font-weight: var(--font-weight);
|
|
line-height: var(--line-height);
|
|
}
|
|
|
|
article {
|
|
max-width: var(--max-width);
|
|
margin: 0 auto;
|
|
}
|
|
|
|
h1, h2, h3, h4, h5, h6 {
|
|
font-family: var(--font-family);
|
|
font-weight: var(--font-weight-bold);
|
|
}
|
|
|
|
em, i {
|
|
color: var(--color-emphasis);
|
|
}
|
|
|
|
strong, b {
|
|
color: var(--color-strong);
|
|
font-weight: var(--font-weight-bold);
|
|
}
|
|
|
|
a {
|
|
color: var(--color-link);
|
|
}
|
|
|
|
table {
|
|
border-collapse: collapse;
|
|
width: 100%;
|
|
margin: 1em 0;
|
|
font-size: 1em;
|
|
background-color: var(--color-bg);
|
|
color: var(--color-text);
|
|
border: 1px solid var(--color-text);
|
|
}
|
|
|
|
th, td {
|
|
padding: 0.75em 1em;
|
|
text-align: left;
|
|
border-bottom: 1px solid var(--color-text);
|
|
}
|
|
|
|
th {
|
|
background: var(--color-bg);
|
|
font-weight: 500;
|
|
}
|
|
|
|
`.trim());
|
|
|
|
const markdown = await inputFile.text();
|
|
const html = await marked.parse(markdown, {
|
|
gfm: true,
|
|
});
|
|
|
|
const output = templateFile
|
|
.replace("%stylesheet%", stylesheetFile)
|
|
.replace("%content%", html)
|
|
.replace("%title%", inputFileName);
|
|
|
|
await outputFile.write(output);
|