import { fileURLToPath, URL } from 'node:url' import { defineConfig, Terser } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import minipic from 'vite-plugin-minipic' import Markdown from 'vite-plugin-md' // @ts-ignore import MarkdownItKatex from 'markdown-it-katex' import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs' import path from 'node:path' import { getStaticRoutes } from './src/router/routes'; import matter from 'gray-matter'; import expandableMediaPlugin from './src/plugins/markdown-it-expandable-media' import blogInfoPlugin from './src/plugins/markdown-it-blog-info'; import mermaidSSR from './src/plugins/markdown-it-mermaid-ssr'; import hljs from 'highlight.js' // https://vite.dev/config/ export default defineConfig({ plugins: [ vue({ include: [/\.vue$/, /\.md$/], }), vueJsx(), { name: 'generate-sitemap-and-robots', closeBundle: async () => { // 获取静态路由 const staticRoutes = getStaticRoutes(); // 读取所有 Markdown 文件并解析 frontmatter const blogDir = path.resolve(__dirname, 'src/blogs'); const blogFiles = readdirSync(blogDir).filter(file => file.endsWith('.md')); const blogRoutes = blogFiles.map(file => { const content = readFileSync(path.join(blogDir, file), 'utf-8'); const { data } = matter(content); return { path: encodeURI('/blogs/' + data.title + '.md'), name: data.title, changefreq: 'weekly', priority: 0.7, }; }); // 合并所有路由 const allRoutes = [...staticRoutes, ...blogRoutes]; const WEBSITE_URL = 'https://xn--876a.net'; // 生成 sitemap.xml let sitemap = '\n'; sitemap += '\n'; allRoutes.forEach(route => { sitemap += ' \n'; sitemap += ` ${WEBSITE_URL}${route.path}\n`; sitemap += ` ${new Date().toISOString().split("T")[0]}\n`; sitemap += ` ${route.changefreq}\n`; sitemap += ` ${route.priority}\n`; sitemap += ' \n'; }); sitemap += ''; // 生成 robots.txt let robotsTxt = ''; robotsTxt += 'User-agent: *\n'; robotsTxt += 'Allow: /\n'; robotsTxt += '\n'; // 可以添加自定义的 Disallow 规则 // robotsTxt += 'Disallow: /admin/\n'; // robotsTxt += 'Disallow: /private/\n'; robotsTxt += '\n'; robotsTxt += `Sitemap: ${WEBSITE_URL}/sitemap.xml\n`; // 确保 dist 目录存在 const distDir = path.resolve(__dirname, 'dist'); if (!existsSync(distDir)) { mkdirSync(distDir); } // 写入 sitemap.xml writeFileSync(path.join(distDir, 'sitemap.xml'), sitemap); console.log('Sitemap 生成成功!'); // 写入 robots.txt writeFileSync(path.join(distDir, 'robots.txt'), robotsTxt); console.log('Robots.txt 生成成功!'); } }, Markdown({ wrapperComponent: 'article', markdownItOptions: { linkify: true, highlight: function (str, lang) { if (lang && hljs.getLanguage(lang)) { try { return hljs.highlight(str, { language: lang }).value; } catch (__) { } } return ''; // use external default escaping } }, markdownItUses: [MarkdownItKatex, mermaidSSR, expandableMediaPlugin, blogInfoPlugin] }), minipic({ sharpOptions: { webp: { quality: 90, }, png: { quality: 80, }, avif: { quality: 80, }, jpg: { quality: 80, }, jpeg: { quality: 80, }, gif: { } }, convert: [ { from: 'png', to: 'webp' }, ], cache: true }) ], resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) }, }, server: { host: '0.0.0.0', } })