102 lines
2.9 KiB
JavaScript
102 lines
2.9 KiB
JavaScript
/**
|
|
* Post-build prerendering script.
|
|
* Starts a local server from dist/, visits each route with Puppeteer,
|
|
* and saves the fully-rendered HTML so crawlers see real content.
|
|
*
|
|
* Usage: node prerender.js (run after `vite build`)
|
|
*/
|
|
|
|
import { createServer } from 'http';
|
|
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
import { join, dirname } from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import puppeteer from 'puppeteer';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const DIST = join(__dirname, 'dist');
|
|
const PORT = 4173;
|
|
|
|
const ROUTES = ['/', '/technical', '/imprint', '/privacy-policy'];
|
|
|
|
/** Minimal static file server that falls back to index.html (SPA). */
|
|
function startServer() {
|
|
const mimeTypes = {
|
|
'.html': 'text/html',
|
|
'.js': 'application/javascript',
|
|
'.css': 'text/css',
|
|
'.json': 'application/json',
|
|
'.png': 'image/png',
|
|
'.jpg': 'image/jpeg',
|
|
'.svg': 'image/svg+xml',
|
|
'.pdf': 'application/pdf',
|
|
'.woff2': 'font/woff2',
|
|
'.woff': 'font/woff',
|
|
};
|
|
|
|
const server = createServer((req, res) => {
|
|
let filePath = join(DIST, req.url === '/' ? '/index.html' : req.url);
|
|
|
|
if (!existsSync(filePath) || !filePath.includes('.')) {
|
|
filePath = join(DIST, 'index.html');
|
|
}
|
|
|
|
const ext = '.' + filePath.split('.').pop();
|
|
const contentType = mimeTypes[ext] || 'application/octet-stream';
|
|
|
|
try {
|
|
const content = readFileSync(filePath);
|
|
res.writeHead(200, { 'Content-Type': contentType });
|
|
res.end(content);
|
|
} catch {
|
|
res.writeHead(404);
|
|
res.end('Not found');
|
|
}
|
|
});
|
|
|
|
return new Promise((resolve) => {
|
|
server.listen(PORT, () => {
|
|
console.log(` Server running on http://localhost:${PORT}`);
|
|
resolve(server);
|
|
});
|
|
});
|
|
}
|
|
|
|
async function prerender() {
|
|
console.log('\n🔍 Prerendering routes...\n');
|
|
|
|
const server = await startServer();
|
|
const browser = await puppeteer.launch({ headless: true });
|
|
|
|
for (const route of ROUTES) {
|
|
const page = await browser.newPage();
|
|
const url = `http://localhost:${PORT}${route}`;
|
|
|
|
console.log(` Rendering ${route} ...`);
|
|
await page.goto(url, { waitUntil: 'networkidle0', timeout: 15000 });
|
|
|
|
// Wait a bit for React to finish any async rendering
|
|
await new Promise((r) => setTimeout(r, 1500));
|
|
|
|
const html = await page.content();
|
|
await page.close();
|
|
|
|
// Write to dist/<route>/index.html
|
|
const outDir = route === '/' ? DIST : join(DIST, route);
|
|
if (!existsSync(outDir)) {
|
|
mkdirSync(outDir, { recursive: true });
|
|
}
|
|
const outFile = join(outDir, 'index.html');
|
|
writeFileSync(outFile, html, 'utf-8');
|
|
console.log(` ✓ Saved ${outFile.replace(DIST, 'dist')}`);
|
|
}
|
|
|
|
await browser.close();
|
|
server.close();
|
|
console.log('\n✅ Prerendering complete!\n');
|
|
}
|
|
|
|
prerender().catch((err) => {
|
|
console.error('Prerendering failed:', err);
|
|
process.exit(1);
|
|
});
|