feat(seo): implement dynamic metadata, sitemap, and robots.txt
Add dynamic meta tags for routes and entity detail pages Generate sitemap.xml and robots.txt dynamically in Vite Change default frontend port from 3000 to 20015
This commit is contained in:
@@ -1,9 +1,104 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import { defineConfig, loadEnv, type PluginOption } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
server: {
|
||||
port: 3000
|
||||
}
|
||||
const fallbackSiteUrl = 'https://pokopiawiki.tootaio.com';
|
||||
const frontendPort = 20015;
|
||||
const sitemapPaths = ['/pokemon', '/habitats', '/items', '/recipes', '/checklist', '/life'];
|
||||
const robotsDisallowPaths = [
|
||||
'/admin',
|
||||
'/login',
|
||||
'/register',
|
||||
'/forgot-password',
|
||||
'/reset-password',
|
||||
'/verify-email',
|
||||
'/pokemon/new',
|
||||
'/pokemon/*/edit',
|
||||
'/habitats/new',
|
||||
'/habitats/*/edit',
|
||||
'/items/new',
|
||||
'/items/*/edit',
|
||||
'/recipes/new',
|
||||
'/recipes/*/edit',
|
||||
'/automation',
|
||||
'/dish',
|
||||
'/events',
|
||||
'/actions',
|
||||
'/dream-island',
|
||||
'/clothes'
|
||||
];
|
||||
|
||||
function normalizeSiteUrl(value: string | undefined): string {
|
||||
return (value?.trim() || fallbackSiteUrl).replace(/\/+$/, '');
|
||||
}
|
||||
|
||||
function robotsTxt(siteUrl: string): string {
|
||||
const disallowLines = robotsDisallowPaths.map((path) => `Disallow: ${path}`).join('\n');
|
||||
return `User-agent: *\nAllow: /\n${disallowLines}\nSitemap: ${siteUrl}/sitemap.xml\n`;
|
||||
}
|
||||
|
||||
function sitemapXml(siteUrl: string): string {
|
||||
const urls = sitemapPaths
|
||||
.map(
|
||||
(path) => ` <url>
|
||||
<loc>${siteUrl}${path}</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
</url>`
|
||||
)
|
||||
.join('\n');
|
||||
|
||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
${urls}
|
||||
</urlset>
|
||||
`;
|
||||
}
|
||||
|
||||
function seoFilesPlugin(siteUrl: string): PluginOption {
|
||||
return {
|
||||
name: 'pokopia-seo-files',
|
||||
transformIndexHtml(html) {
|
||||
return html.replaceAll('%POKOPIA_SITE_URL%', siteUrl);
|
||||
},
|
||||
configureServer(server) {
|
||||
server.middlewares.use((request, response, next) => {
|
||||
if (request.url === '/robots.txt') {
|
||||
response.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
||||
response.end(robotsTxt(siteUrl));
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.url === '/sitemap.xml') {
|
||||
response.setHeader('Content-Type', 'application/xml; charset=utf-8');
|
||||
response.end(sitemapXml(siteUrl));
|
||||
return;
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
},
|
||||
generateBundle() {
|
||||
this.emitFile({
|
||||
type: 'asset',
|
||||
fileName: 'robots.txt',
|
||||
source: robotsTxt(siteUrl)
|
||||
});
|
||||
this.emitFile({
|
||||
type: 'asset',
|
||||
fileName: 'sitemap.xml',
|
||||
source: sitemapXml(siteUrl)
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
const env = loadEnv(mode, process.cwd(), '');
|
||||
const siteUrl = normalizeSiteUrl(process.env.VITE_SITE_URL ?? env.VITE_SITE_URL);
|
||||
|
||||
return {
|
||||
plugins: [vue(), seoFilesPlugin(siteUrl)],
|
||||
server: {
|
||||
port: frontendPort
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user