mise a jour structure SEO

This commit is contained in:
sebvtl728
2025-11-05 01:08:39 +01:00
parent 5909871dec
commit 1890d830e1
12 changed files with 3157 additions and 205 deletions
+149
View File
@@ -0,0 +1,149 @@
import { createServer } from "node:http";
import { readFile, stat, writeFile, mkdir } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import puppeteer from "puppeteer";
import { PUBLIC_ROUTES, NOT_FOUND_ROUTE } from "./routes.mjs";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const distDir = path.resolve(__dirname, "../dist");
const routes = PUBLIC_ROUTES.filter((route) => route.prerender).map(
(route) => route.path
);
const mimeTypes = {
".html": "text/html",
".js": "application/javascript",
".css": "text/css",
".json": "application/json",
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".avif": "image/avif",
".webp": "image/webp",
".svg": "image/svg+xml",
".ico": "image/x-icon",
".txt": "text/plain",
".xml": "application/xml",
".map": "application/json",
".gz": "application/gzip",
".br": "application/brotli",
};
function serveStaticFile(res, filePath) {
const ext = path.extname(filePath).toLowerCase();
const contentType = mimeTypes[ext] ?? "application/octet-stream";
res.statusCode = 200;
res.setHeader("Content-Type", contentType);
return readFile(filePath).then((data) => res.end(data));
}
function createStaticServer() {
const server = createServer(async (req, res) => {
try {
const requestUrl = new URL(req.url ?? "/", "http://localhost");
let pathname = decodeURIComponent(requestUrl.pathname);
const hasExtension = path.extname(pathname) !== "";
if (pathname.endsWith("/")) {
pathname = pathname.slice(0, -1);
}
let filePath = path.join(distDir, pathname);
try {
const fileStat = await stat(filePath);
if (fileStat.isDirectory()) {
filePath = path.join(filePath, "index.html");
}
await serveStaticFile(res, filePath);
return;
} catch (error) {
if (hasExtension) {
res.statusCode = 404;
res.end("Not found");
return;
}
filePath = path.join(distDir, "index.html");
await serveStaticFile(res, filePath);
}
} catch (error) {
res.statusCode = 500;
res.end(`Server error: ${error instanceof Error ? error.message : error}`);
}
});
return new Promise((resolve) => {
server.listen(0, () => {
const address = server.address();
if (address && typeof address === "object") {
resolve({ server, port: address.port });
} else {
resolve({ server, port: 4173 });
}
});
});
}
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function renderRoute(browser, baseUrl, route, outputFile) {
const page = await browser.newPage();
const targetUrl = `${baseUrl}${route}`;
try {
await page.goto(targetUrl, {
waitUntil: ["networkidle0", "domcontentloaded"],
timeout: 60000,
});
} catch (error) {
console.warn(
`[prerender] Navigation to ${targetUrl} failed: ${
error instanceof Error ? error.message : error
}`
);
}
try {
// Donne un léger délai pour laisser React terminer le rendu asynchrone
await delay(1500);
const html = await page.content();
const outputPath =
outputFile ??
(route === "/"
? path.join(distDir, "index.html")
: path.join(distDir, route.replace(/^\//, ""), "index.html"));
await mkdir(path.dirname(outputPath), { recursive: true });
await writeFile(outputPath, html, "utf8");
console.log(
`✔ prerendered ${route} -> ${path.relative(distDir, outputPath) || "index.html"}`
);
} finally {
await page.close();
}
}
async function main() {
const { server, port } = await createStaticServer();
const baseUrl = `http://127.0.0.1:${port}`;
const browser = await puppeteer.launch({ headless: true });
try {
for (const route of routes) {
await renderRoute(browser, baseUrl, route);
}
await renderRoute(browser, baseUrl, NOT_FOUND_ROUTE, path.join(distDir, "404.html"));
} finally {
await browser.close();
server.close();
}
}
main().catch((error) => {
console.error("[prerender] Failed to prerender routes:", error);
process.exit(1);
});