89 lines
2.6 KiB
TypeScript
89 lines
2.6 KiB
TypeScript
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("&", "&")
|
|
.replaceAll("<", "<")
|
|
.replaceAll(">", ">")
|
|
}
|
|
|
|
function escapeAttribute(value: string) {
|
|
return escapeText(value).replaceAll('"', """)
|
|
}
|