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
105 lines
2.6 KiB
TypeScript
105 lines
2.6 KiB
TypeScript
import { defineConfig, loadEnv, type PluginOption } from 'vite';
|
|
import vue from '@vitejs/plugin-vue';
|
|
|
|
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
|
|
}
|
|
};
|
|
});
|