Files
massive-cloud/vite.config.ts

248 lines
7.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// import { jsxLocPlugin } from "@builder.io/vite-plugin-jsx-loc"; // 禁用:与 R3F 冲突
import tailwindcss from "@tailwindcss/vite";
import react from "@vitejs/plugin-react";
import fs from "node:fs";
import { createRequire } from "node:module";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { defineConfig, type Plugin, type ViteDevServer } from "vite";
import { vitePluginManusRuntime } from "vite-plugin-manus-runtime";
// =============================================================================
// Manus Debug Collector - Vite Plugin
// Writes browser logs directly to files, trimmed when exceeding size limit
// =============================================================================
const PROJECT_ROOT = path.dirname(fileURLToPath(import.meta.url));
/** 从仓库根解析依赖(Vite root 在 client/ 时,部分环境下需显式指向根目录 node_modules) */
const requireFromRoot = createRequire(import.meta.url);
const LOG_DIR = path.join(PROJECT_ROOT, ".manus-logs");
const MAX_LOG_SIZE_BYTES = 1 * 1024 * 1024; // 1MB per log file
const TRIM_TARGET_BYTES = Math.floor(MAX_LOG_SIZE_BYTES * 0.6); // Trim to 60% to avoid constant re-trimming
type LogSource = "browserConsole" | "networkRequests" | "sessionReplay";
function ensureLogDir() {
if (!fs.existsSync(LOG_DIR)) {
fs.mkdirSync(LOG_DIR, { recursive: true });
}
}
function trimLogFile(logPath: string, maxSize: number) {
try {
if (!fs.existsSync(logPath) || fs.statSync(logPath).size <= maxSize) {
return;
}
const lines = fs.readFileSync(logPath, "utf-8").split("\n");
const keptLines: string[] = [];
let keptBytes = 0;
// Keep newest lines (from end) that fit within 60% of maxSize
const targetSize = TRIM_TARGET_BYTES;
for (let i = lines.length - 1; i >= 0; i--) {
const lineBytes = Buffer.byteLength(`${lines[i]}\n`, "utf-8");
if (keptBytes + lineBytes > targetSize) break;
keptLines.unshift(lines[i]);
keptBytes += lineBytes;
}
fs.writeFileSync(logPath, keptLines.join("\n"), "utf-8");
} catch {
/* ignore trim errors */
}
}
function writeToLogFile(source: LogSource, entries: unknown[]) {
if (entries.length === 0) return;
ensureLogDir();
const logPath = path.join(LOG_DIR, `${source}.log`);
// Format entries with timestamps
const lines = entries.map((entry) => {
const ts = new Date().toISOString();
return `[${ts}] ${JSON.stringify(entry)}`;
});
// Append to log file
fs.appendFileSync(logPath, `${lines.join("\n")}\n`, "utf-8");
// Trim if exceeds max size
trimLogFile(logPath, MAX_LOG_SIZE_BYTES);
}
/**
* Vite plugin to collect browser debug logs
* - POST /__manus__/logs: Browser sends logs, written directly to files
* - Files: browserConsole.log, networkRequests.log, sessionReplay.log
* - Auto-trimmed when exceeding 1MB (keeps newest entries)
*/
function vitePluginManusDebugCollector(): Plugin {
return {
name: "manus-debug-collector",
transformIndexHtml(html) {
if (process.env.NODE_ENV === "production") {
return html;
}
return {
html,
tags: [
{
tag: "script",
attrs: {
src: "/__manus__/debug-collector.js",
defer: true,
},
injectTo: "head",
},
],
};
},
configureServer(server: ViteDevServer) {
// POST /__manus__/logs: Browser sends logs (written directly to files)
server.middlewares.use("/__manus__/logs", (req, res, next) => {
if (req.method !== "POST") {
return next();
}
const handlePayload = (payload: any) => {
// Write logs directly to files
if (payload.consoleLogs?.length > 0) {
writeToLogFile("browserConsole", payload.consoleLogs);
}
if (payload.networkRequests?.length > 0) {
writeToLogFile("networkRequests", payload.networkRequests);
}
if (payload.sessionEvents?.length > 0) {
writeToLogFile("sessionReplay", payload.sessionEvents);
}
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ success: true }));
};
const reqBody = (req as { body?: unknown }).body;
if (reqBody && typeof reqBody === "object") {
try {
handlePayload(reqBody);
} catch (e) {
res.writeHead(400, { "Content-Type": "application/json" });
res.end(JSON.stringify({ success: false, error: String(e) }));
}
return;
}
let body = "";
req.on("data", (chunk) => {
body += chunk.toString();
});
req.on("end", () => {
try {
const payload = JSON.parse(body);
handlePayload(payload);
} catch (e) {
res.writeHead(400, { "Content-Type": "application/json" });
res.end(JSON.stringify({ success: false, error: String(e) }));
}
});
});
},
};
}
/**
* 自定义插件:禁用完整页面刷新,保留 HMR
* 当模块无法热更新时,不会自动刷新页面
*/
function vitePluginNoFullReload(): Plugin {
return {
name: 'no-full-reload',
// 修改 HMR 客户端代码,阻止自动刷新
transformIndexHtml(html) {
if (process.env.NODE_ENV === 'production') {
return html;
}
return {
html,
tags: [
{
tag: 'script',
children: `
// 拦截并阻止 Vite HMR 的完整页面刷新
(function() {
const originalReload = window.location.reload;
let reloadBlocked = false;
window.location.reload = function() {
if (!reloadBlocked) {
console.warn('[HMR] Page reload blocked. Manual refresh required if needed.');
reloadBlocked = true;
setTimeout(() => { reloadBlocked = false; }, 1000);
}
// 不执行实际刷新
};
})();
`,
injectTo: 'head',
},
],
};
},
};
}
// 注意:jsxLocPlugin 与 React Three Fiber 不兼容,已禁用
const plugins = [
react(),
tailwindcss(),
// jsxLocPlugin(), // 禁用:与 R3F 冲突,会导致 "Cannot set data-loc" 错误
vitePluginManusRuntime(),
vitePluginManusDebugCollector(),
vitePluginNoFullReload(), // 禁用自动刷新
];
export default defineConfig({
plugins,
resolve: {
alias: {
"@": path.resolve(PROJECT_ROOT, "client", "src"),
"@shared": path.resolve(PROJECT_ROOT, "shared"),
"@assets": path.resolve(PROJECT_ROOT, "attached_assets"),
delaunator: requireFromRoot.resolve("delaunator"),
"robust-predicates": requireFromRoot.resolve("robust-predicates"),
},
},
envDir: path.resolve(PROJECT_ROOT),
root: path.resolve(PROJECT_ROOT, "client"),
publicDir: path.resolve(PROJECT_ROOT, "client", "public"),
build: {
outDir: path.resolve(PROJECT_ROOT, "dist/public"),
emptyOutDir: true,
},
server: {
host: true,
allowedHosts: [
".manuspre.computer",
".manus.computer",
".manus-asia.computer",
".manuscomputer.ai",
".manusvm.computer",
"localhost",
"127.0.0.1",
],
fs: {
strict: true,
deny: ["**/.*"],
allow: [PROJECT_ROOT, path.join(PROJECT_ROOT, "node_modules")],
},
hmr: false,
watch: null,
},
});