You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

132 lines
2.9 KiB
TypeScript

import { readdir, mkdir, stat, readFile, writeFile } from "node:fs/promises";
import { HTMLElement, parse, NodeType } from "node-html-parser";
import { render as renderNow } from "./now";
import { render as renderHomeQuote } from "./quote";
import { render as renderLogs } from "./logs";
import { render as renderWork, renderResume } from "./work";
const walkdir = async (path: string) => {
const paths = await readdir(path);
const out = [] as string[];
for (const p of paths) {
const p_ = `${path}/${p}`;
const st = await stat(p_);
if (st.isDirectory()) {
// TODO: do something special
continue;
}
out.push(p_);
}
return out;
};
const RENDERERS = {
"x-meta": () => "",
"x-now": renderNow,
"x-logs": renderLogs,
"x-home-quote": renderHomeQuote,
"x-work": renderWork,
};
const renderElement = (el: HTMLElement) => {
if (el.nodeType == NodeType.TEXT_NODE) {
return el.rawText;
}
const renderer = RENDERERS[el.tagName.toLowerCase()] ?? renderBasic;
return renderer(el);
};
const renderBasic = (el: HTMLElement) => {
const tag = el.tagName.toLowerCase();
let out = `<${tag}`;
for (const attr in el.attributes) {
out += ` ${attr}="${el.attributes[attr]}"`;
}
out += ">";
const children = el.childNodes.map(renderElement).join("");
out += children;
out += `</${tag}>`;
return out;
};
const buildPage = async (pagePath: string) => {
const contents = (await readFile(pagePath)).toString();
const parsed = parse(contents);
const pageOpts = (() => {
const el = parsed.querySelector("x-meta");
if (!el) {
return {
title: null,
};
}
const title = el.querySelector("title");
return {
title: title?.innerText ?? null,
};
})();
const els = parsed.childNodes.map(renderElement).join("");
const rendered = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charset="utf-8" />
<link rel="stylesheet" href="/style.css" />
<script src="/bundle.js"></script>
${
pageOpts.title
? `<title>${pageOpts.title} - idylls.net</title>`
: "<title>idylls.net</title>"
}
</head>
<body>
<nav>
<ul>
<li><a href="/index.html">Home</a></li>
<li><a href="/about.html">About</a></li>
<li><a href="/about.html#contact">Contact</a></li>
<li><a href="/work.html">Work</a></li>
<li><a href="https://git.idylls.net">Git</a></li>
<li><a href="https://status.idylls.net">Services</a></li>
<li><a href="/nav.html">More</a></li>
</ul>
</nav>
<main>
${els}
</main>
</body>
</html>
`;
const [, p] = pagePath.split("content/pages/");
const outPath = `build/${p}`;
return writeFile(outPath, rendered);
};
export const buildPages = async () => {
await mkdir("build/", { recursive: true });
const paths = await walkdir("content/pages");
for (const p of paths) {
await buildPage(p);
}
const resumeContent = renderResume();
await writeFile("build/resume.html", resumeContent);
};