85 lines
2.8 KiB
TypeScript
85 lines
2.8 KiB
TypeScript
import type { Context } from "hono";
|
|
import { utcDateString } from "./util.ts";
|
|
import { looksLikePathWithExtension } from "@silverbulletmd/silverbullet/lib/page_ref";
|
|
import { parse } from "../web/markdown_parser/parse_tree.ts";
|
|
import { extendedMarkdownLanguage } from "../web/markdown_parser/parser.ts";
|
|
import { renderMarkdownToHtml } from "../web/markdown/markdown_render.ts";
|
|
import type { ServerOptions } from "./http_server.ts";
|
|
import type { AssetBundle } from "../lib/asset_bundle/bundle.ts";
|
|
import { htmlEscape } from "../web/markdown/html_render.ts";
|
|
import type { SpacePrimitives } from "../lib/spaces/space_primitives.ts";
|
|
|
|
// Server-side renders a markdown file to HTML
|
|
export async function renderHtmlPage(
|
|
spacePrimitives: SpacePrimitives,
|
|
pageName: string,
|
|
c: Context,
|
|
options: ServerOptions,
|
|
clientAssetBundle: AssetBundle,
|
|
): Promise<Response> {
|
|
let html = "";
|
|
let lastModified = utcDateString(Date.now());
|
|
if (!options.auth && options.readOnly) {
|
|
// Only attempt server-side rendering when this site is not protected by auth and running in read-only mode
|
|
if (!looksLikePathWithExtension(pageName)) {
|
|
try {
|
|
const { data, meta } = await spacePrimitives.readFile(
|
|
`${pageName}.md`,
|
|
);
|
|
lastModified = utcDateString(meta.lastModified);
|
|
|
|
if (c.req.header("If-Modified-Since") === lastModified) {
|
|
// Not modified, empty body status 304
|
|
return c.body(null, 304);
|
|
}
|
|
const text = new TextDecoder().decode(data);
|
|
const tree = parse(extendedMarkdownLanguage, text);
|
|
html = renderMarkdownToHtml(tree);
|
|
} catch (e: any) {
|
|
if (e.message !== "Not found") {
|
|
console.error("Error server-side rendering page", e);
|
|
}
|
|
}
|
|
} else {
|
|
// If it it's a file with an extension and it doesn't exist we can't really create a new one/recover
|
|
try {
|
|
await spacePrimitives.getFileMeta(`${pageName}`);
|
|
} catch (e: any) {
|
|
if (e.message !== "Not found") {
|
|
return c.notFound();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const templateData = {
|
|
TITLE: pageName,
|
|
DESCRIPTION: stripHtml(html).substring(0, 255),
|
|
CONTENT: html,
|
|
HOST_URL_PREFIX: options.hostUrlPrefix ?? "",
|
|
};
|
|
|
|
html = clientAssetBundle.readTextFileSync(".client/index.html");
|
|
|
|
// Replace each template variable with its corresponding value
|
|
for (const [key, value] of Object.entries(templateData)) {
|
|
const placeholder = `{{${key}}}`;
|
|
const stringValue = typeof value === "boolean"
|
|
? (value ? "true" : "false")
|
|
: (key === "DESCRIPTION" ? htmlEscape(value) : String(value));
|
|
html = html.replace(placeholder, stringValue);
|
|
}
|
|
return c.html(
|
|
html,
|
|
200,
|
|
{
|
|
"Last-Modified": lastModified,
|
|
},
|
|
);
|
|
}
|
|
|
|
function stripHtml(html: string): string {
|
|
const regex = /<[^>]*>/g;
|
|
return html.replace(regex, "");
|
|
}
|