- vite-plugin-prerender yerine Playwright tabanlı post-build pre-render scripti (apps/web/scripts/prerender.mjs): 10 public route statik HTML'e dönüştürülüyor - usePageMeta hook: her public route'da özgün title/description/canonical/OG tags - index.html: favicon.svg, canonical, OG/Twitter Card, geliştirilmiş title+desc - apps/web/public/: robots.txt, sitemap.xml (10 URL), favicon.svg - nginx HSTS header eklendi (Strict-Transport-Security) - Blog post sayfaları: blog_/$slug.tsx (4 tam yazı, 600-800 kelime) - blog.tsx: post kartlarına Link eklendi - Homepage: Schema.org JSON-LD (Organization + SoftwareApplication) - Footer: placeholder href="#" sosyal linkler kaldırıldı - deploy.sh + deploy.yml: playwright install chromium + prerender adımı Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
101 lines
2.8 KiB
JavaScript
101 lines
2.8 KiB
JavaScript
/**
|
|
* Post-build pre-renderer for sase.tr public pages.
|
|
*
|
|
* Starts a local vite preview server, visits each public route with
|
|
* headless Chromium, and saves the fully-rendered HTML to dist/.
|
|
* Crawlers that don't execute JavaScript receive complete HTML.
|
|
*
|
|
* Usage: node scripts/prerender.mjs
|
|
* Requires: dist/ to already exist (run `vite build` first)
|
|
*/
|
|
|
|
import { chromium } from "playwright";
|
|
import { preview } from "vite";
|
|
import { writeFileSync, mkdirSync, existsSync } from "fs";
|
|
import { join, dirname } from "path";
|
|
import { fileURLToPath } from "url";
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const ROOT = join(__dirname, "..");
|
|
const DIST = join(ROOT, "dist");
|
|
const PORT = 4174;
|
|
|
|
/** Public routes to pre-render. Dashboard/auth routes are excluded — they require login. */
|
|
const ROUTES = [
|
|
"/",
|
|
"/pricing",
|
|
"/about",
|
|
"/blog",
|
|
"/blog/vin-nedir-nasil-okunur",
|
|
"/blog/orijinal-mi-muadil-mi",
|
|
"/blog/dijital-donusum",
|
|
"/blog/dogru-parcayi-bulun",
|
|
"/contact",
|
|
"/demo",
|
|
];
|
|
|
|
async function prerender() {
|
|
if (!existsSync(DIST)) {
|
|
console.error("❌ dist/ not found — run `vite build` first.");
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log("🔍 Starting pre-render...\n");
|
|
|
|
// Start vite preview server (serves from dist/)
|
|
const server = await preview({
|
|
root: ROOT,
|
|
preview: { port: PORT, strictPort: false },
|
|
logLevel: "silent",
|
|
});
|
|
|
|
const browser = await chromium.launch({ args: ["--no-sandbox"] });
|
|
|
|
try {
|
|
for (const route of ROUTES) {
|
|
const page = await browser.newPage();
|
|
|
|
// Block all /api/ requests so auth resolves to unauthenticated immediately.
|
|
// Without this the page hangs waiting for session checks.
|
|
await page.route("**/api/**", (r) =>
|
|
r.fulfill({
|
|
status: 401,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ success: false, error: { code: "UNAUTHORIZED" } }),
|
|
}),
|
|
);
|
|
|
|
await page.goto(`http://localhost:${PORT}${route}`, {
|
|
waitUntil: "networkidle",
|
|
timeout: 30_000,
|
|
});
|
|
|
|
// Extra settle time for any deferred renders (animations, etc.)
|
|
await page.waitForTimeout(300);
|
|
|
|
const html = await page.content();
|
|
|
|
if (route === "/") {
|
|
writeFileSync(join(DIST, "index.html"), html, "utf-8");
|
|
} else {
|
|
const dir = join(DIST, route);
|
|
mkdirSync(dir, { recursive: true });
|
|
writeFileSync(join(dir, "index.html"), html, "utf-8");
|
|
}
|
|
|
|
await page.close();
|
|
console.log(` ✓ ${route}`);
|
|
}
|
|
} finally {
|
|
await browser.close();
|
|
await server.close();
|
|
}
|
|
|
|
console.log("\n✅ Pre-render complete — all routes saved to dist/\n");
|
|
}
|
|
|
|
prerender().catch((err) => {
|
|
console.error("❌ Pre-render failed:", err.message);
|
|
process.exit(1);
|
|
});
|