interface Loaded { map: BuiltAssetMap; buf: Buffer; } let assets: Loaded | null = null; export type StaticPageId = string; export async function reload() { const [map, buf] = await Promise.all([ fs.readFile(path.join(import.meta.dirname, "static.json"), "utf8"), fs.readFile(path.join(import.meta.dirname, "static.blob")), ]); console.log("new buffer loaded"); assets = { map: JSON.parse(map), buf, }; } export async function reloadSync() { const map = fs.readFileSync( path.join(import.meta.dirname, "static.json"), "utf8", ); const buf = fs.readFileSync(path.join(import.meta.dirname, "static.blob")); assets = { map: JSON.parse(map), buf, }; } export async function middleware(c: Context, next: Next) { if (!assets) await reload(); const asset = assets!.map[c.req.path]; if (asset) { return assetInner(c, asset, 200); } return next(); } export async function notFound(c: Context) { if (!assets) await reload(); let pathname = c.req.path; do { const asset = assets!.map[pathname + "/404"]; if (asset) return assetInner(c, asset, 404); pathname = pathname.slice(0, pathname.lastIndexOf("/")); } while (pathname); const asset = assets!.map["/404"]; if (asset) return assetInner(c, asset, 404); return c.text("the 'Not Found' page was not found", 404); } export async function serveAsset( c: Context, id: StaticPageId, status: StatusCode, ) { assets ?? await reload(); return assetInner(c, assets!.map[id], status); } export function hasAsset(id: string) { if (!assets) reloadSync(); return assets!.map[id] !== undefined; } export function etagMatches(etag: string, ifNoneMatch: string) { return ifNoneMatch === etag || ifNoneMatch.split(/,\s*/).indexOf(etag) > -1; } function subarrayAsset([start, end]: View) { return assets!.buf.subarray(start, end); } function assetInner(c: Context, asset: BuiltAsset, status: StatusCode) { const ifnonematch = c.req.header("If-None-Match"); if (ifnonematch) { const etag = asset.headers.ETag; if (etagMatches(etag, ifnonematch)) { return c.res = new Response(null, { status: 304, statusText: "Not Modified", headers: { ETag: etag, }, }); } } const acceptEncoding = c.req.header("Accept-Encoding") ?? ""; let body; let headers = asset.headers; if (acceptEncoding.includes("zstd") && asset.zstd) { body = subarrayAsset(asset.zstd); headers = { ...asset.headers, "Content-Encoding": "zstd", }; } else if (acceptEncoding.includes("gzip") && asset.gzip) { body = subarrayAsset(asset.gzip); headers = { ...asset.headers, "Content-Encoding": "gzip", }; } else { body = subarrayAsset(asset.raw); } return c.res = new Response(body, { headers, status }); } process.on("message", (msg: any) => { console.log({ msg }); if (msg?.type === "clover.assets.reload") reload(); }); import * as fs from "#sitegen/fs"; import type { Context, Next } from "hono"; import type { StatusCode } from "hono/utils/http-status"; import type { BuiltAsset, BuiltAssetMap, View } from "../incremental.ts"; import { Buffer } from "node:buffer"; import * as path from "node:path";