import legacy from '@vitejs/plugin-legacy'; import react from '@vitejs/plugin-react'; import { defineConfig, loadEnv } from 'vite'; import removeConsole from 'vite-plugin-remove-console'; import topLevelAwait from 'vite-plugin-top-level-await'; import wasm from 'vite-plugin-wasm'; import { viteBuildInfo } from './build/buildInfo'; import { configCompressPlugin } from './build/compress'; import svgConvert from './build/svgConvert'; import { root, alias, wrapperEnv } from './build/utils'; // https://vitejs.dev/config/ export default defineConfig(({ mode }) => { const env = wrapperEnv(loadEnv(mode, root)); const isProd = mode === 'production'; // @vitejs/plugin-legacy 在 config 阶段会无条件把 build.target 覆盖成含具体浏览器版本(如 safari13)的 // esbuild target。而 vite-plugin-top-level-await(brotli-wasm 依赖顶层 await)在转换 TLA 产物时会读取 // 这个 target,esbuild 0.28 无法把 TLA 生成的解构赋值降级到任何具体 safari 目标(safari13/14 均报 // "Transforming destructuring ... not supported yet",仅纯 ES 版本如 es2022 可通过)。 // // 此内联插件把现代 chunk 的 build.target 钉为 es2022: // - enforce: 'post' 且排在 topLevelAwait() 之前 → 两个 post 插件间按数组顺序执行, // 故本插件的 config hook 先于 TLA 运行,TLA 备份到的即 es2022,转换得以通过。 // - 现代浏览器(safari13+/chrome87+)原生支持解构,es2022 产物运行无碍; // 真正的旧浏览器仍由 plugin-legacy 的 legacy chunk(babel 全量转译)兜底。 const pinModernTargetPlugin = { name: 'pin-modern-target-es2022', enforce: 'post' as const, config(config: { build?: { target?: unknown } }) { config.build = config.build || {}; config.build.target = 'es2022'; }, }; return { base: env.VITE_BUILD_PUBLIC_PATH, plugins: [ react(), wasm(), pinModernTargetPlugin, topLevelAwait(), legacy({ targets: [ 'chrome >= 87', 'safari >= 13', 'firefox >= 78', 'edge >= 88', 'not IE 11', ], }), isProd && removeConsole({ includes: ['log', 'warn'], external: ['error'], }), svgConvert(), viteBuildInfo(), configCompressPlugin(env.VITE_BUILD_COMPRESSION!), ].filter(Boolean), resolve: { alias }, optimizeDeps: { exclude: ['brotli-wasm'], }, css: { preprocessorOptions: { less: { javascriptEnabled: true, }, scss: { additionalData: `@use "@/styles/variables" as *;`, }, }, }, server: { port: env.VITE_DEV_PORT, host: '0.0.0.0', open: true, proxy: { '/dev/api/v1': { target: env.VITE_DEV_PROXY_TARGET_API_BASE_URL, changeOrigin: true, rewrite: (path) => path.replace(/^\/dev\/api\/v1/, ''), secure: false, configure: (proxy) => { proxy.on('error', (err) => { console.log('proxy error', err); }); proxy.on('proxyReq', (_, req) => { console.log( 'Sending Request to the Target:', req.method, req.url, env.VITE_DEV_PROXY_TARGET_API_BASE_URL ); }); proxy.on('proxyRes', (proxyRes, req) => { console.log( 'Received Response from the Target:', proxyRes.statusCode, req.url ); }); }, }, }, warmup: { clientFiles: ['./index.html', './src/{pages,components}/*'], }, }, build: { outDir: 'dist', sourcemap: !isProd, // build.target 由上方 pinModernTargetPlugin 在 config 阶段统一钉为 'es2022'(详见该插件注释), // 此处不再设置,避免与之冲突。 // 与 plugin-legacy targets 对齐,使 CSS 压缩/转换兼容同一批浏览器(plugin-legacy 不处理 CSS) cssTarget: ['chrome87', 'safari13', 'firefox78', 'edge88'], rollupOptions: { output: { // 产物文件名:入口/代码块用 [name]-[hash:8].js,静态资源用 [name]-[hash:8][extname] entryFileNames: 'assets/[name]-[hash].js', chunkFileNames: 'assets/[name]-[hash].js', assetFileNames: 'assets/[name]-[hash][extname]', manualChunks: { 'react-vendor': ['react', 'react-dom', 'react-router-dom'], 'antd-vendor': ['antd', '@ant-design/icons'], 'utils-vendor': ['lodash-es', 'ramda'], 'i18n-vendor': [ 'i18next', 'react-i18next', 'i18next-browser-languagedetector', ], }, }, }, chunkSizeWarningLimit: 1500, }, }; });