UNPKG

vite-pug-static-builder

Version:

Vite + Pugを使用した静的サイトビルダー - 複数のPugファイルを静的HTMLとしてビルドするViteプラグイン

132 lines (131 loc) 5.76 kB
import fs from 'node:fs'; import path from 'node:path'; import { URL } from 'node:url'; import { send } from 'vite'; import picomatch from 'picomatch'; import { compilePug } from './pug.js'; /** * 開発サーバー用ミドルウェア生成 * @param settings - サーブ設定 * @param server - Vite開発サーバー * @returns Connectミドルウェア */ const createMiddleware = (settings, server) => { const { options, locals, ignorePattern } = settings; const pugOptions = { pretty: true, ...options }; const ignoreMatcher = ignorePattern ? picomatch(ignorePattern) : null; return async (req, res, next) => { // 特殊なURLパスをスキップ if (!req.url || req.url.startsWith('/@') || // @fs @vite @react-refresh etc... req.url.startsWith('/__inspect/') // vite-plugin-inspect ) { return next(); } const url = new URL(req.url, 'relative:///').pathname; // 無視パターンにマッチする場合はスキップ if (ignoreMatcher?.call(null, url)) { return next(); } const reqAbsPath = path.posix.join(server.config.root, url, url.endsWith('/') ? 'index.html' : ''); const parsedReqAbsPath = path.posix.parse(reqAbsPath); // HTMLファイル以外はスキップ if (parsedReqAbsPath.ext !== '.html') { return next(); } // 既存のHTMLファイルがある場合はスキップ if (fs.existsSync(reqAbsPath)) { return next(); } // 対応するPugファイルのパスを生成 const pugAbsPath = path.posix.format({ dir: parsedReqAbsPath.dir, name: parsedReqAbsPath.name, ext: '.pug', }); // Pugファイルが存在しない場合は404 if (!fs.existsSync(pugAbsPath)) { return send(req, res, '404 Not Found', 'html', {}); } try { // Pugファイルをコンパイル const compileResult = await compilePug(server.moduleGraph, url, pugAbsPath, pugOptions, locals); // Pugコンパイルエラー if (compileResult instanceof Error) { return next(compileResult); } // HTMLの変換処理 const transformResult = await server.transformRequest(url); if (transformResult) { const html = await server.transformIndexHtml(url, transformResult.code); return send(req, res, html, 'html', {}); } // transformResultがnullまたは予期しないエラー return next(new Error('An unexpected error has occurred during HTML transformation.')); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return next(new Error(`Pug compilation failed: ${errorMessage}`)); } }; }; /** * Vite用Pug開発サーバープラグイン * @param settings - サーブ設定 * @returns Viteプラグイン */ export const vitePluginPugServe = (settings) => { const { reload } = settings ?? {}; let server; return { name: 'vite-plugin-pug-serve', enforce: 'pre', apply: 'serve', configureServer(_server) { server = _server; server.middlewares.use(createMiddleware(settings ?? {}, server)); }, // Vite 7の新しいhotUpdateフックを使用してより効率的な処理を実装 hotUpdate(context) { const { file, timestamp } = context; const { environment } = this; // Pugファイル自体が変更された場合 if (path.extname(file) === '.pug') { // 対応するHTMLモジュールのURLを生成 const parsedPath = path.parse(file); const htmlUrl = path.posix.format({ dir: parsedPath.dir.replace(server.config.root, '').replace(/\\/g, '/'), name: parsedPath.name, ext: '.html', }); // Environment APIを使用してモジュールを取得・無効化(非同期処理を修正) environment.moduleGraph.getModuleByUrl(htmlUrl).then((htmlModule) => { if (htmlModule) { // モジュールを適切に無効化(Vite 7の新しいAPI) environment.moduleGraph.invalidateModule(htmlModule, new Set(), timestamp, true // HMRフラグを明示的にtrue ); } }).catch(() => { // モジュールが見つからない場合は無視 }); } // 従来のファイルモジュール無効化処理 const fileModules = environment.moduleGraph.getModulesByFile(file); if (fileModules) { const invalidatedModules = new Set(); // 型を明示的に指定 for (const fileModule of fileModules) { for (const importer of fileModule.importers) { if (importer.file && path.extname(importer.file) === '.pug') { environment.moduleGraph.invalidateModule(importer, invalidatedModules, timestamp, true); } } } } // リロード設定に基づいてフルリロードを実行 if (reload !== false) { environment.hot.send({ type: 'full-reload' }); } }, // 古いhandleHotUpdateフックは削除(Vite 7では非推奨) }; };