UNPKG

vite-plugin-seo-files

Version:

Vite plugin to generate static SEO files like sitemap.xml and robots.txt after build.

82 lines (78 loc) 3.42 kB
import fs from 'fs'; import path from 'path'; import { globSync } from 'glob'; import { pathToFileURL } from 'url'; export default function seoFilesPlugin(options) { const { siteUrl, generateSitemap = true, generateRobots = true, exclude = [], additionalUrls = [], disallow = [] } = options; if (!siteUrl || !/^https?:\/\//.test(siteUrl)) { throw new Error('[vite-plugin-seo-files] You must provide a valid siteUrl.'); } return { name: 'vite-plugin-seo-files', async closeBundle() { const distDir = path.resolve(process.cwd(), 'dist'); const srcRoutesPath = path.resolve(process.cwd(), 'src/routes-list.js'); if (!fs.existsSync(distDir)) { fs.mkdirSync(distDir, { recursive: true }); } // === Sitemap === if (generateSitemap) { let urls = ''; if (fs.existsSync(srcRoutesPath)) { const routesModule = await import(pathToFileURL(srcRoutesPath).href); const routes = routesModule.default || []; urls = routes.map(route => ` <url> <loc>${siteUrl.replace(/\/$/, '')}${route}</loc> <lastmod>${new Date().toISOString().split('T')[0]}</lastmod> <priority>${route === '/' ? '1.00' : '0.50'}</priority> </url>`).join(''); } else { const files = globSync('**/*.html', { cwd: distDir, ignore: ['404.html', '403.html', ...exclude], }); urls = files.map((file) => { const loc = `${siteUrl.replace(/\/$/, '')}/${file.replace(/index\.html$/, '').replace(/\\/g, '/')}`; const stats = fs.statSync(path.join(distDir, file)); const lastmod = stats.mtime.toISOString().split('T')[0]; const priority = file === 'index.html' ? '1.00' : '0.50'; return ` <url> <loc>${loc}</loc> <lastmod>${lastmod}</lastmod> <priority>${priority}</priority> </url>`; }).join(''); } const manualUrls = additionalUrls.map(route => ` <url> <loc>${siteUrl.replace(/\/$/, '')}${route}</loc> <lastmod>${new Date().toISOString().split('T')[0]}</lastmod> <priority>0.50</priority> </url>`).join(''); const sitemap = `<?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">${urls}${manualUrls} </urlset>`; fs.writeFileSync(path.join(distDir, 'sitemap.xml'), sitemap.trim()); console.log('✅ sitemap.xml generated'); } // === robots.txt === if (generateRobots) { const disallowLines = disallow.length ? disallow.map(p => `Disallow: ${p}`).join('\n') : 'Disallow:'; const robots = `# https://www.robotstxt.org/ # Allow all crawlers full access User-agent: * # Prevent indexing of sensitive or non-public areas ${disallowLines} # Sitemap file Sitemap: ${siteUrl.replace(/\/$/, '')}/sitemap.xml`; fs.writeFileSync(path.join(distDir, 'robots.txt'), robots); console.log('✅ robots.txt generated'); } }, }; }