import { existsSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import react from "@vitejs/plugin-react"; import { defineConfig, type Plugin } from "vite"; import sourceIdentifierPlugin from "vite-plugin-source-identifier"; const rootDir = fileURLToPath(new URL(".", import.meta.url)); // 依赖预构建缓存被重建后(pnpm install / 重复启动 vite / --force), // 残留的旧浏览器标签页仍会按旧版本号(?v=)请求已删除的 chunk, // 每次都在终端刷 "The file does not exist ... in the optimize deps directory"。 // 该插件拦截这类「文件确实不存在」的 deps 请求,返回一段触发 location.reload() // 的模块脚本,让旧页面自愈到新版本;sessionStorage 防止真正的坏页面无限刷新。 const staleDepsChunkSelfReload = (): Plugin => ({ name: "stale-deps-chunk-self-reload", configureServer(server) { const depsDir = path.join(rootDir, "node_modules", ".vite", "deps"); server.middlewares.use((req, res, next) => { const match = /^\/node_modules\/\.vite\/deps\/([^?]+)/.exec(req.url ?? ""); if (!match) { next(); return; } const filePath = path.join(depsDir, decodeURIComponent(match[1])); if (existsSync(filePath)) { next(); return; } res.setHeader("Content-Type", "application/javascript"); res.end( "if(!sessionStorage.getItem('__vite_stale_chunk_reloaded__')){" + "sessionStorage.setItem('__vite_stale_chunk_reloaded__','1');" + "location.reload();}", ); }); }, }); // Tailwind 4 emits CSS cascade layers, which require Chrome 99+. Chrome 100 // is therefore the practical lower bound for this Win7 legacy build. const LEGACY_CHROME = 100 << 16; export default defineConfig({ plugins: [staleDepsChunkSelfReload(), sourceIdentifierPlugin(), react()], css: { // Also used by the production CSS minifier. Development CSS is handled // by the matching PostCSS compatibility pass in postcss.config.js. lightningcss: { targets: { chrome: LEGACY_CHROME, }, }, }, resolve: { alias: [ { // react-syntax-highlighter@16.1.1 exposes an async `sqlMore` // loader, but its own highlight.js@10.x dependency has no // `sql_more` module. Reuse the bundled SQL grammar so Rollup can // resolve every generated language loader without leaving a broken // browser-time external import. find: /^highlight\.js\/lib\/languages\/sql_more$/, replacement: "highlight.js/lib/languages/sql", }, { find: "@/env", replacement: path.resolve(rootDir, "src/env.ts"), }, { find: "next/navigation", replacement: path.resolve(rootDir, "src/shims/next-navigation.ts"), }, { find: "next/link", replacement: path.resolve(rootDir, "src/shims/next-link.tsx"), }, { find: "@vite", replacement: path.resolve(rootDir, "src"), }, { find: "@", replacement: path.resolve(rootDir, "src"), }, ], }, // Keep the current portable-file default, while allowing container builds to // emit absolute asset URLs. The latter is required for BrowserRouter pages // behind Nginx: a request to /page/... must still load /assets/... rather // than /page/assets/.... base: process.env.VITE_ASSET_BASE ?? "./", build: { cssMinify: "lightningcss", target: "chrome100", }, server: { // 与 scripts/env.sh FRONTEND_PORT=5174、圆桌 wujie 嵌入文档一致。 port: 5173, // 无界宿主跨域 fetch 子应用静态资源时,Vite 需回显请求的 Origin。 cors: true, }, });