import type { Plugin } from "vite"; import { SITE_URL } from "../src/seo"; /** * Sitemap 条目(路由 + SEO 元信息) * * 由 vite.config.ts 传入,插件据此生成 sitemap.xml。 * 新增页面时在 vite.config.ts 的 sitemapRoutes 数组里追加一项即可。 */ export interface SitemapEntry { /** 路由路径,如 "/"、"/demo"、"/blog/hello" */ path: string; /** 更新频率 */ changefreq?: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never"; /** 优先级 0-1 */ priority?: number; } /** * Vite 插件:构建时自动生成 sitemap.xml 和 robots.txt 到输出目录 * * - sitemap.xml:从 sitemapRoutes 生成,搜索引擎据此发现所有可索引页面 * - robots.txt:声明允许抓取的范围 + 指向 sitemap * * 这两个文件是构建产物,不纳入 git(已在 .gitignore 的 dist 范围内)。 */ export function sitemapPlugin(routes: SitemapEntry[]): Plugin { return { name: "webfont-sitemap", /** 构建产物写入完成后生成 SEO 文件 */ writeBundle() { const entries = routes.length > 0 ? routes : [{ path: "/" }]; const sitemap = generateSitemap(entries); const robots = generateRobots(); // 直接写入磁盘 —— 此时 Vite 已完成 dist 目录写入 import("node:fs").then((fs) => { import("node:path").then((path) => { const distDir = path.resolve(process.cwd(), "dist"); fs.writeFileSync(path.join(distDir, "sitemap.xml"), sitemap, "utf8"); fs.writeFileSync(path.join(distDir, "robots.txt"), robots, "utf8"); console.log(`[sitemap] 已生成 sitemap.xml (${entries.length} 条路由) 和 robots.txt`); }); }); }, }; } /** 生成 sitemap.xml 内容 */ function generateSitemap(entries: SitemapEntry[]): string { const urls = entries .map((entry) => { const loc = `${SITE_URL}${entry.path === "/" ? "" : entry.path}`; const changefreq = entry.changefreq ?? "weekly"; const priority = entry.priority ?? 0.8; return ` ${loc} ${changefreq} ${priority} `; }) .join("\n"); return ` ${urls} `; } /** 生成 robots.txt 内容 */ function generateRobots(): string { return `# 允许所有爬虫抓取 User-agent: * Allow: / # API 接口不需要索引 Disallow: /api/ # 站点地图 Sitemap: ${SITE_URL}/sitemap.xml `; }