本番ビルドのSSGをesbuildからViteに変更
v2のSSGは
- esbuildで
.tsxを.mjsに変換(一時ファイル生成) - nodeで
.mjs読み込む -
.html作成
だったが、v3はViteのSSRを使うことで
-
.tsxを読み込む -
.html作成
を直接行う。
https://ja.vitejs.dev/guide/ssr.html#pre-rendering-ssg
- 一時ファイル生成がなくなることでパスの問題を軽減できる
- 処理をViteに統一することでViteプラグインとの互換性 #89 を高められる
this.emitFile で追加したHTMLはそこからバンドルを行わないので普通にoutputした場合と同じになる。
- https://rollupjs.org/guide/en/#thisemitfile
HTMLを仮想モジュール化してバンドルに含めることで理想的な処理になりそうだったが this.emitFile() されたHTMLのfileName のパスがおかしくなりビルドが通らない。
- https://github.com/vitejs/vite/issues/9662
- 仮想モジュールの規約 | Vite
Vite及びrollupにもemitした後のリストを修正する機能はないようなので厳しい。
- https://github.com/rollup/rollup/issues/3778
- this.emitFile | rollup.js
src/pages に一度HTMLファイルを出力して一時ファイルとして使えば通りそうだけど、あまり綺麗ではない...。
仮想モジュール化する場合の実装↓
import type { Plugin } from "vite"
import path from "node:path"
import { fileURLToPath } from "node:url"
import { createServer as createViteServer } from "vite"
import type { ResolvedConfig } from "../config/index.js"
import type { GetSources } from "../server/sources.js"
import type { ResolvedViteEntry } from "../config/vite.js"
import { compileApp } from "../compile/app.js"
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
type HtmlPages = {
fileId: string
moduleId: string
path: string
html: string
}[]
export function ssgv(config: ResolvedConfig): Plugin {
const PREFIX = `\0virtual:`
let htmlPages: HtmlPages = []
return {
name: "minista-vite-plugin:ssgv",
async config(viteConfig) {
const viteServer = await createViteServer(config.vite)
await viteServer.listen()
const { getSources } = (await viteServer.ssrLoadModule(
__dirname + "/../server/sources.js"
)) as { getSources: GetSources }
const { resolvedGlobal, resolvedPages } = await getSources()
await viteServer.close()
if (resolvedPages.length === 0) {
return
}
htmlPages = resolvedPages.map((page, index) => {
const fileId = "__minista_html" + index
const fileName = page.path.endsWith("/")
? path.join(page.path, "index.html")
: page.path + ".html"
const moduleId = path.join("src/pages", fileName)
return {
fileId,
moduleId,
path: page.path,
html: compileApp({
url: page.path,
resolvedGlobal,
resolvedPages: [page],
useDevelopBundle: false,
}),
}
})
const htmlArrayInputs = htmlPages.map((page) => [
page.fileId,
page.moduleId,
])
const htmlObjectInputs = Object.fromEntries(htmlArrayInputs)
const assetInputs = viteConfig.build?.rollupOptions?.input || {}
const mergedInputs = Object.assign(
assetInputs,
htmlObjectInputs
) as ResolvedViteEntry
return {
build: {
rollupOptions: {
input: mergedInputs,
},
},
}
},
resolveId(id) {
const target = htmlPages.find((page) => id === page.moduleId)
if (target) {
return PREFIX + id
}
},
load(id) {
if (id.startsWith(PREFIX)) {
id = id.slice(PREFIX.length)
const target = htmlPages.find((page) => id === page.moduleId)
if (target) {
return target.html
}
}
},
}
}
とりあえず、srcへの一時ファイル出力でなんとかした。バグが消えたら作り直したい。
参考:仮想モジュールを追加するViteプラグイン
- https://github.com/patak-dev/vite-plugin-virtual
参考:HTMLを追加するViteプラグインは仮想モジュールを使っておらず、どれも既存のHTMLを使うか生成した後に動かすか消すかを行なっている。
- https://github.com/vbenjs/vite-plugin-html
- https://github.com/windsonR/vite-plugin-virtual-html
- https://github.com/IndexXuan/vite-plugin-html-template
- https://github.com/Djaler/vite-plugin-asciidoc
HTMLをエントリーするのではなく、一度SSR用にビルドしてプラグインをページのtsxに反映。nodeでHTMLを生成する。