Files
controller-v2/vite.config.ts
T

129 lines
3.8 KiB
TypeScript

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" ? "/x8/test/modern/" : "/x8/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;
// Only split echarts (lazy EQ page). Do not manually chunk react, radix, lucide,
// or ai-markdown — forced splits hoist shared helpers and break React.forwardRef.
if (id.includes("/echarts/")) return "echarts-vendor";
},
},
},
// streamdown pulls full @shikijs/langs (~7 MB) + themes + mermaid; lazy-loaded with AIPage.
chunkSizeWarningLimit: 15000,
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: ["**/.*"],
},
},
});