Files

89 lines
2.6 KiB
TypeScript
Raw Permalink Normal View History

import { mkdir, readFile, rm, writeFile } from "node:fs/promises"
import { dirname, join } from "node:path"
import { pathToFileURL } from "node:url"
import { docLocales } from "../src/content/types.ts"
type StaticPage = {
html: string
locale: (typeof docLocales)[number]
metadata: { description: string; title: string }
}
type ServerEntry = {
getStaticDocRoutes: () => readonly string[]
getStaticSearchIndex: (locale: (typeof docLocales)[number]) => unknown
renderStaticPage: (pathname: string) => Promise<StaticPage>
}
const docsDirectory = dirname(dirname(import.meta.filename))
const outputDirectory = join(docsDirectory, "dist")
const serverDirectory = join(docsDirectory, ".ssr")
const serverEntryUrl = pathToFileURL(
join(serverDirectory, "entry-server.js")
).href
const server = (await import(serverEntryUrl)) as ServerEntry
const template = await readFile(join(outputDirectory, "index.html"), "utf8")
await Promise.all(
server.getStaticDocRoutes().map(async (route) => {
const page = await server.renderStaticPage(route)
const filename = getRouteFilename(route)
await mkdir(dirname(filename), { recursive: true })
await writeFile(filename, createHtml(template, page), "utf8")
})
)
const notFoundPage = await server.renderStaticPage("/404")
await writeFile(
join(outputDirectory, "404.html"),
createHtml(template, notFoundPage),
"utf8"
)
const searchDirectory = join(outputDirectory, "search")
await mkdir(searchDirectory, { recursive: true })
await Promise.all(
docLocales.map((locale) =>
writeFile(
join(searchDirectory, `${locale}.json`),
JSON.stringify(server.getStaticSearchIndex(locale)),
"utf8"
)
)
)
await rm(serverDirectory, { recursive: true })
function getRouteFilename(route: string) {
return route === "/"
? join(outputDirectory, "index.html")
: join(outputDirectory, route.slice(1), "index.html")
}
function createHtml(
source: string,
page: Awaited<ReturnType<typeof server.renderStaticPage>>
) {
const description = `<meta name="description" content="${escapeAttribute(page.metadata.description)}" />`
return source
.replace(/<html lang="[^"]*">/, `<html lang="${page.locale}">`)
.replace(
/<title>[^<]*<\/title>/,
`<title>${escapeText(page.metadata.title)}</title>\n ${description}`
)
.replace('<div id="root"></div>', `<div id="root">${page.html}</div>`)
}
function escapeText(value: string) {
return value
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
}
function escapeAttribute(value: string) {
return escapeText(value).replaceAll('"', "&quot;")
}