import { jsxLocPlugin } from "@builder.io/vite-plugin-jsx-loc"; import tailwindcss from "@tailwindcss/vite"; import react from "@vitejs/plugin-react"; import fs from "node:fs"; import path from "node:path"; import { defineConfig, type Plugin } from "vite"; import { vitePluginManusRuntime } from "vite-plugin-manus-runtime"; const PROJECT_ROOT = import.meta.dirname; /** Dev: / → i.modern.html; /i.html → UA router entry */ function vitePluginModernDevEntry(): Plugin { return { name: "modern-dev-entry", apply: "serve", configureServer(server) { server.middlewares.use((req, _res, next) => { const url = (req.url ?? "").split("?")[0]; if (url === "/" || url === "/index.html") { req.url = "/i.modern.html"; } next(); }); }, }; } /** 构建后将统一入口 i.html 复制到 dist/ 根目录 */ function vitePluginCopyRouterEntry(): Plugin { const src = path.resolve(import.meta.dirname, "client", "i.html"); const dest = path.resolve(import.meta.dirname, "dist", "i.html"); return { name: "copy-router-entry", closeBundle() { if (process.env.NODE_ENV !== "production") return; const distRoot = path.resolve(import.meta.dirname, "dist"); if (!fs.existsSync(distRoot)) fs.mkdirSync(distRoot, { recursive: true }); fs.copyFileSync(src, dest); }, }; } const plugins = [ vitePluginModernDevEntry(), react(), tailwindcss(), jsxLocPlugin(), vitePluginManusRuntime(), vitePluginCopyRouterEntry(), ]; /* ── Deploy base path ── */ const DEPLOY_ENV = process.env.DEPLOY_ENV || "production"; const isProd = process.env.NODE_ENV === "production"; const deployBase = isProd ? (DEPLOY_ENV === "test" ? "/x9/test/modern/" : "/x9/v2/modern/") : "/"; export default defineConfig({ plugins, base: deployBase, resolve: { alias: { "@": path.resolve(import.meta.dirname, "client", "src"), "@assets": path.resolve(import.meta.dirname, "attached_assets"), }, dedupe: ["react", "react-dom"], }, envDir: path.resolve(import.meta.dirname), root: path.resolve(import.meta.dirname, "client"), publicDir: path.resolve(import.meta.dirname, "client/public"), // 性能优化 build: { target: 'esnext', cssTarget: 'esnext', outDir: path.resolve(import.meta.dirname, "dist/modern"), emptyOutDir: true, minify: 'terser', terserOptions: { compress: { drop_console: true, drop_debugger: true, }, }, rollupOptions: { input: { i: path.resolve(import.meta.dirname, "client", "i.modern.html"), }, output: { manualChunks(id) { if (!id.includes("node_modules")) return; if ( id.includes("/echarts/") || id.includes("/zrender/") || id.includes("\\echarts\\") || id.includes("\\zrender\\") ) { return "echarts-vendor"; } if (id.includes("/react-dom/") || id.includes("/react/")) { return "react-vendor"; } if (id.includes("/wouter/")) return "router-vendor"; if (id.includes("/@radix-ui/")) return "radix-vendor"; if (id.includes("/framer-motion/")) return "motion-vendor"; if ( id.includes("/lucide-react/") || id.includes("/recharts/") || id.includes("/class-variance-authority/") || id.includes("/clsx/") || id.includes("/tailwind-merge/") ) { return "ui-vendor"; } }, }, }, // echarts full build is ~1.1 MB; only loaded on /eq and /effects (lazy routes). chunkSizeWarningLimit: 1200, cssCodeSplit: true, }, server: { port: 3000, strictPort: false, // Will find next available port if 3000 is busy host: true, // Browser requests with Origin are rejected (403) by the cloud API; proxy in dev avoids CORS/WAF. proxy: { "/luxsin-audio-api": { target: "https://api.luxsin.com.cn", changeOrigin: true, rewrite: (p) => p.replace(/^\/luxsin-audio-api/, "/audio"), }, }, allowedHosts: [ ".manuspre.computer", ".manus.computer", ".manus-asia.computer", ".manuscomputer.ai", ".manusvm.computer", "localhost", "127.0.0.1", ], fs: { strict: true, deny: ["**/.*"], }, }, });