import fs from 'node:fs' import { spawn } from 'node:child_process' import path from 'node:path' import { defineConfig } from 'vite' function generatedPages(directory) { const inputs = {} for (const entry of fs.readdirSync(directory, { withFileTypes: true })) { const filePath = path.join(directory, entry.name) if (entry.isDirectory()) Object.assign(inputs, generatedPages(filePath)) else if (entry.name === 'index.html') { const relativePath = path.relative('.generated', filePath) const key = relativePath.endsWith('/index.html') ? relativePath.slice(0, -'/index.html'.length) : relativePath inputs[key] = path.resolve(filePath) } else if (entry.name === '404.html' && directory === '.generated') { inputs['404'] = path.resolve(filePath) } } return inputs } function copyGeneratedFiles() { const files = ['feed.xml', 'sitemap.xml'] return { name: 'copy-generated-files', closeBundle() { const dist = path.resolve('dist') if (!fs.existsSync(dist)) return for (const file of files) { const source = path.resolve('.generated', file) if (fs.existsSync(source)) fs.copyFileSync(source, path.join(dist, file)) } }, } } function contentGenerator() { let timer let generator let rerun = false const generate = (server) => { if (generator) { rerun = true return } generator = spawn(process.execPath, ['generator.js', '--drafts'], { stdio: 'inherit' }) generator.on('error', (error) => { console.error(`Content generation failed: ${error.message}`) }) generator.on('close', (code) => { generator = undefined if (code === 0) server.ws.send({ type: 'full-reload' }) if (rerun) { rerun = false generate(server) } }) } return { name: 'content-generator', configureServer(server) { const sources = [ path.resolve('notes'), path.resolve('posts'), path.resolve('README.md'), path.resolve('THIRD_PARTY_LICENSES.md'), path.resolve('external.json'), path.resolve('generator.js'), path.resolve('html.js'), ] server.watcher.add(sources) server.watcher.on('all', (event, file) => { if (!['add', 'change', 'unlink'].includes(event)) return const isMarkdown = file.endsWith('.md') && (file.startsWith(`${path.resolve('notes')}/`) || file.startsWith(`${path.resolve('posts')}/`)) const isExternal = file === path.resolve('external.json') const isStandaloneMarkdown = file === path.resolve('README.md') || file === path.resolve('THIRD_PARTY_LICENSES.md') const isGenerator = file === path.resolve('generator.js') || file === path.resolve('html.js') if (!isMarkdown && !isStandaloneMarkdown && !isExternal && !isGenerator) return clearTimeout(timer) timer = setTimeout(() => { generate(server) }, 100) }) }, } } export default defineConfig({ plugins: [contentGenerator(), copyGeneratedFiles()], root: '.generated', publicDir: '../public', resolve: { alias: { '/src': path.resolve('src'), }, }, server: { watch: { ignored: ['**/.generated/**'], }, }, build: { outDir: path.resolve('dist'), emptyOutDir: true, rollupOptions: { input: generatedPages('.generated'), }, }, })