mirror of
https://git.hmsn.ink/kospo/svcm/dmz.git
synced 2026-03-20 02:12:34 +09:00
130 lines
3.3 KiB
TypeScript
130 lines
3.3 KiB
TypeScript
// Pre-render the app into static HTML.
|
|
// run `pnpm ssg:build` and then `dist` can be served as a static site.
|
|
|
|
import fs from 'node:fs'
|
|
import fsp from 'node:fs/promises'
|
|
import path from 'node:path'
|
|
import { type ResolvedConfig, type InlineConfig, resolveConfig } from 'vite'
|
|
import colors from 'picocolors'
|
|
import { env } from 'std-env'
|
|
|
|
import { createRenderer } from './generate/renderer'
|
|
import { buildApp } from './generate/builder'
|
|
import { scanRoutes } from './generate/scan'
|
|
import { populateRouteParams } from './generate/populate'
|
|
import { renderToFile } from './generate/render-to-file'
|
|
|
|
async function build() {
|
|
const mode = env.MODE || env.NODE_ENV || 'production'
|
|
|
|
const viteConfig: InlineConfig = {}
|
|
const config = await resolveConfig(viteConfig, 'build', mode)
|
|
|
|
const cwd = process.cwd()
|
|
const root = config.root || cwd
|
|
const outDir = config.build.outDir || 'dist'
|
|
const out = path.isAbsolute(outDir) ? outDir : path.join(root, outDir)
|
|
|
|
const outStatic = out
|
|
const outServer = path.join(out, '.server')
|
|
|
|
if (fs.existsSync(out)) {
|
|
await fsp.rm(out, { recursive: true })
|
|
}
|
|
|
|
// scan base routes from src/pages
|
|
const routes = await scanRoutes(cwd)
|
|
|
|
// build client and server vite apps
|
|
await buildApp({
|
|
config,
|
|
viteConfig,
|
|
outStatic,
|
|
outServer,
|
|
})
|
|
|
|
// load renderer from server build
|
|
const {
|
|
manifest,
|
|
template,
|
|
render,
|
|
} = await createRenderer({
|
|
outServer,
|
|
outStatic,
|
|
})
|
|
|
|
// generate urls for pre-rendering depending on static config
|
|
const pages = await populateRouteParams({
|
|
config,
|
|
routes,
|
|
})
|
|
|
|
// pre-render each page sequentially
|
|
for (const page of pages) {
|
|
const start = performance.now()
|
|
const file = await renderToFile(render, {
|
|
url: page.url,
|
|
outStatic,
|
|
manifest,
|
|
template,
|
|
})
|
|
const duration = performance.now() - start
|
|
|
|
const formattedDuration = duration.toFixed(2).padStart(5) + 'ms'
|
|
config.logger.info(
|
|
colors.dim(`[${page.logPrefix}] ${colors.green(page.url)} - ${colors.cyan(file)} - ${formattedDuration}`),
|
|
)
|
|
}
|
|
|
|
// delete server build
|
|
await fsp.rm(path.join(outServer), { recursive: true, force: true })
|
|
|
|
// regenerate PWA service worker with updated files
|
|
await generatePWA({
|
|
config,
|
|
outStatic,
|
|
})
|
|
|
|
config.logger.info(
|
|
[
|
|
`Pre-rendering done. You can now serve the ${colors.cyan(
|
|
out.replace(cwd, '.'),
|
|
)} directory with a static file server.`,
|
|
`Example:`,
|
|
` ${colors.green('npx serve dist -p 3000')}`,
|
|
].join('\n'),
|
|
)
|
|
process.exit(0)
|
|
}
|
|
|
|
(async () => {
|
|
try {
|
|
await build()
|
|
}
|
|
catch (e) {
|
|
console.error(e)
|
|
process.exit(1)
|
|
}
|
|
})()
|
|
|
|
async function generatePWA({
|
|
config,
|
|
outStatic,
|
|
}: {
|
|
config: ResolvedConfig
|
|
outStatic: string
|
|
}) {
|
|
const pwaPlugin = config.plugins.find(plugin => plugin.name === 'vite-plugin-pwa')
|
|
?.api
|
|
if (pwaPlugin && !pwaPlugin.disabled && pwaPlugin.generateSW) {
|
|
config.logger.info(colors.green('[SSG] Regenerate PWA...'))
|
|
await pwaPlugin.generateSW()
|
|
|
|
// update sw.js to replace /index.html with nothing so that it can be served from /
|
|
|
|
const swPath = path.join(outStatic, 'sw.js')
|
|
const swContent = await fsp.readFile(swPath, 'utf-8')
|
|
await fsp.writeFile(swPath, swContent.replace(/\/index\.html/g, ''), 'utf-8')
|
|
}
|
|
}
|