feat: prerender public site pages
This commit is contained in:
34
frontend/scripts/prerender-public.mjs
Normal file
34
frontend/scripts/prerender-public.mjs
Normal file
@@ -0,0 +1,34 @@
|
||||
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
|
||||
const publicRoutes = ['/', '/product', '/pricing', '/blogs', '/guides'];
|
||||
|
||||
const rootDir = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const distDir = resolve(rootDir, 'dist');
|
||||
const ssrEntry = resolve(rootDir, 'dist-ssr/entry-public-ssr.js');
|
||||
const templatePath = resolve(distDir, 'index.html');
|
||||
|
||||
const template = await readFile(templatePath, 'utf8');
|
||||
const { render } = await import(pathToFileURL(ssrEntry));
|
||||
const baseTemplate = template.replace('<title>Socialize</title>', '');
|
||||
|
||||
function outputPathForRoute(route) {
|
||||
if (route === '/') {
|
||||
return resolve(distDir, 'index.html');
|
||||
}
|
||||
|
||||
return resolve(distDir, route.replace(/^\//, ''), 'index.html');
|
||||
}
|
||||
|
||||
for (const route of publicRoutes) {
|
||||
const { appHtml, headTags } = await render(route);
|
||||
const html = baseTemplate
|
||||
.replace('</head>', `${headTags.headTags}\n</head>`)
|
||||
.replace('<div id="app"></div>', `<div id="app">${appHtml}</div>`);
|
||||
|
||||
const outputPath = outputPathForRoute(route);
|
||||
await mkdir(dirname(outputPath), { recursive: true });
|
||||
await writeFile(outputPath, html);
|
||||
console.log(`Prerendered ${route}`);
|
||||
}
|
||||
61
frontend/scripts/write-public-seo.mjs
Normal file
61
frontend/scripts/write-public-seo.mjs
Normal file
@@ -0,0 +1,61 @@
|
||||
import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
const publicRoutes = [
|
||||
{ path: '/', changefreq: 'weekly', priority: '1.0' },
|
||||
{ path: '/product', changefreq: 'weekly', priority: '0.8' },
|
||||
{ path: '/pricing', changefreq: 'monthly', priority: '0.7' },
|
||||
{ path: '/blogs', changefreq: 'weekly', priority: '0.6' },
|
||||
{ path: '/guides', changefreq: 'weekly', priority: '0.6' },
|
||||
];
|
||||
|
||||
const disallowedRoutes = [
|
||||
'/app/',
|
||||
'/login',
|
||||
'/register',
|
||||
'/forgot-password',
|
||||
'/reset-password',
|
||||
'/verify-email',
|
||||
];
|
||||
|
||||
const siteUrl = (process.env.VITE_PUBLIC_SITE_URL ?? process.env.SITE_URL ?? 'http://localhost:5173')
|
||||
.replace(/\/$/, '');
|
||||
|
||||
const distDir = resolve(process.cwd(), 'dist');
|
||||
|
||||
function absoluteUrl(path) {
|
||||
return new URL(path, `${siteUrl}/`).toString();
|
||||
}
|
||||
|
||||
const robots = [
|
||||
'User-agent: *',
|
||||
'Allow: /',
|
||||
...disallowedRoutes.map(route => `Disallow: ${route}`),
|
||||
'',
|
||||
`Sitemap: ${absoluteUrl('/sitemap.xml')}`,
|
||||
'',
|
||||
].join('\n');
|
||||
|
||||
const sitemapEntries = publicRoutes
|
||||
.map(route => [
|
||||
' <url>',
|
||||
` <loc>${absoluteUrl(route.path)}</loc>`,
|
||||
` <changefreq>${route.changefreq}</changefreq>`,
|
||||
` <priority>${route.priority}</priority>`,
|
||||
' </url>',
|
||||
].join('\n'))
|
||||
.join('\n');
|
||||
|
||||
const sitemap = [
|
||||
'<?xml version="1.0" encoding="UTF-8"?>',
|
||||
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
|
||||
sitemapEntries,
|
||||
'</urlset>',
|
||||
'',
|
||||
].join('\n');
|
||||
|
||||
await mkdir(distDir, { recursive: true });
|
||||
await writeFile(resolve(distDir, 'robots.txt'), robots);
|
||||
await writeFile(resolve(distDir, 'sitemap.xml'), sitemap);
|
||||
|
||||
console.log(`Wrote public SEO files for ${siteUrl}`);
|
||||
Reference in New Issue
Block a user