Files
ONE-OS/axhub-make/vite.config.ts
王冕 a27e3b8e43 feat: sync full workspace including web modules, docs, and configurations to Gitea
Optimized the root .gitignore to exclude virtual environments, node modules,
and temp folders to ensure clean and lightweight version tracking.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-09 18:12:25 +08:00

283 lines
9.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 { defineConfig } from 'vite';
import type { Plugin } from 'vite';
import fs from 'fs';
import path from 'path';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
// ── 构建模式也需要的插件(静态导入) ──
import { addAxhubMarker } from './vite-plugins/addAxhubMarker';
import { axhubComponentEnforcer } from './vite-plugins/axhubComponentEnforcer';
import { forceInlineDynamicImportsOff } from './vite-plugins/forceInlineDynamicImportsOff';
import { injectStablePageIds } from './vite-plugins/injectStablePageIds';
import {
MAKE_CONFIG_RELATIVE_PATH,
MAKE_ENTRIES_RELATIVE_PATH,
} from './vite-plugins/utils/makeConstants';
import { readEntriesManifest, scanProjectEntries, writeEntriesManifestAtomic } from './vite-plugins/utils/entriesManifest';
// ── 仅 dev server 模式需要的插件 ──
// 使用动态 import() 避免在 build 模式下加载这些模块。
// lowdbService 等单例会在模块求值时创建持久资源,导致 vite build 进程无法退出。
async function loadServePlugins(): Promise<Plugin[]> {
const [
{ aiCliPlugin },
{ autoDebugPlugin },
{ axureBridgeProxyPlugin },
{ canvasApiPlugin },
{ ccConnectApiPlugin },
{ codeReviewPlugin },
{ configApiPlugin },
{ dataManagementApiPlugin },
{ docsApiPlugin },
{ docsImportApiPlugin },
{ downloadDistPlugin },
{ exportHtmlApiPlugin },
{ exportImageProxyPlugin },
{ fileSystemApiPlugin },
{ gitVersionApiPlugin },
{ lanAccessControlPlugin },
{ mediaManagementApiPlugin },
{ serveAdminPlugin },
{ sourceApiPlugin },
{ specDocApiPlugin },
{ subPagesApiPlugin },
{ templatesApiPlugin },
{ themesApiPlugin },
{ unsetReferenceApiPlugin },
{ uploadDocsApiPlugin },
{ versionApiPlugin },
{ virtualHtmlPlugin },
{ websocketPlugin },
{ writeDevServerInfoPlugin },
] = await Promise.all([
import('./vite-plugins/aiCliPlugin'),
import('./vite-plugins/autoDebugPlugin'),
import('./vite-plugins/axureBridgeProxyPlugin'),
import('./vite-plugins/canvasApiPlugin'),
import('./vite-plugins/ccConnectApiPlugin'),
import('./vite-plugins/codeReviewPlugin'),
import('./vite-plugins/configApiPlugin'),
import('./vite-plugins/dataManagementApiPlugin'),
import('./vite-plugins/docsApiPlugin'),
import('./vite-plugins/docsImportApiPlugin'),
import('./vite-plugins/downloadDistPlugin'),
import('./vite-plugins/exportHtmlApiPlugin'),
import('./vite-plugins/exportImageProxyPlugin'),
import('./vite-plugins/fileSystemApiPlugin'),
import('./vite-plugins/gitVersionApiPlugin'),
import('./vite-plugins/lanAccessControlPlugin'),
import('./vite-plugins/mediaManagementApiPlugin'),
import('./vite-plugins/serveAdminPlugin'),
import('./vite-plugins/sourceApiPlugin'),
import('./vite-plugins/specDocApiPlugin'),
import('./vite-plugins/subPagesApiPlugin'),
import('./vite-plugins/templatesApiPlugin'),
import('./vite-plugins/themesApiPlugin'),
import('./vite-plugins/unsetReferenceApiPlugin'),
import('./vite-plugins/uploadDocsApiPlugin'),
import('./vite-plugins/versionApiPlugin'),
import('./vite-plugins/virtualHtml'),
import('./vite-plugins/websocketPlugin'),
import('./vite-plugins/writeDevServerInfoPlugin'),
]);
return [
lanAccessControlPlugin(),
writeDevServerInfoPlugin(),
serveAdminPlugin(),
axureBridgeProxyPlugin(),
exportImageProxyPlugin(),
virtualHtmlPlugin(),
websocketPlugin(),
versionApiPlugin(),
downloadDistPlugin(),
exportHtmlApiPlugin(),
docsImportApiPlugin(),
docsApiPlugin(),
canvasApiPlugin(),
templatesApiPlugin(),
uploadDocsApiPlugin(),
sourceApiPlugin(),
specDocApiPlugin(),
subPagesApiPlugin(),
unsetReferenceApiPlugin(),
themesApiPlugin(),
fileSystemApiPlugin(),
dataManagementApiPlugin(),
mediaManagementApiPlugin(),
codeReviewPlugin(),
autoDebugPlugin(),
configApiPlugin(),
aiCliPlugin(),
gitVersionApiPlugin(),
ccConnectApiPlugin(),
];
}
const projectRoot = process.cwd();
const configPath = path.resolve(projectRoot, MAKE_CONFIG_RELATIVE_PATH);
let axhubConfig: any = { server: { host: 'localhost', allowLAN: true } };
if (fs.existsSync(configPath)) {
try {
axhubConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
} catch (error) {
console.warn(`Failed to parse ${MAKE_CONFIG_RELATIVE_PATH}, using defaults:`, error);
}
}
writeEntriesManifestAtomic(
projectRoot,
scanProjectEntries(projectRoot, ['components', 'prototypes', 'themes']),
);
const entries = readEntriesManifest(projectRoot);
const entryKey = process.env.ENTRY_KEY;
const jsEntries = entries.js as Record<string, string>;
const htmlEntries = entries.html as Record<string, string>;
const hasSingleEntry = typeof entryKey === 'string' && entryKey.length > 0;
let rollupInput: Record<string, string> = htmlEntries;
if (hasSingleEntry) {
if (!jsEntries[entryKey as string]) {
throw new Error(`ENTRY_KEY=${entryKey} 未在 ${MAKE_ENTRIES_RELATIVE_PATH} 中找到对应入口文件。请确保目录 src/${entryKey} 存在且包含 index.tsx 文件。`);
}
rollupInput = { [entryKey as string]: jsEntries[entryKey as string] };
}
const isIifeBuild = hasSingleEntry;
export default defineConfig(async ({ command }) => {
const isServe = command === 'serve';
// 仅在 serve 模式才加载 server-only 插件,避免 build 进程被持久资源阻塞
const servePlugins = isServe ? await loadServePlugins() : [];
const config: any = {
plugins: [
tailwindcss(),
injectStablePageIds(),
...servePlugins,
forceInlineDynamicImportsOff(isIifeBuild),
react({
jsxRuntime: 'classic',
babel: { configFile: false, babelrc: false }
}),
isIifeBuild ? addAxhubMarker() : null,
isIifeBuild ? axhubComponentEnforcer(jsEntries[entryKey as string]) : null
].filter(Boolean) as Plugin[],
root: 'src',
optimizeDeps: {
// React Fast Refresh 在开发态会接管 react/react-dom 的依赖预处理。
// 这里不要再把它们排除掉,否则会和 plugin-react 的 include 冲突。
exclude: [],
include: [
'lucide-react'
]
},
resolve: {
alias: [
{ find: '@', replacement: path.resolve(projectRoot, 'src') },
!isIifeBuild && !isServe && {
find: /^react$/,
replacement: path.resolve(projectRoot, 'src/common/react-shim.js')
},
!isIifeBuild && !isServe && {
find: /^react-dom$/,
replacement: path.resolve(projectRoot, 'src/common/react-dom-shim.js')
},
!isIifeBuild && !isServe && {
find: /^react\/.*/,
replacement: path.resolve(projectRoot, 'src/common/react-shim.js')
},
!isIifeBuild && !isServe && {
find: /^react-dom\/.*/,
replacement: path.resolve(projectRoot, 'src/common/react-dom-shim.js')
}
].filter(Boolean) as { find: string | RegExp; replacement: string }[]
},
server: {
port: 51720, // 默认从 51720 开始,如果被占用会自动尝试 51721, 51722...
strictPort: false, // 端口被占用时自动尝试下一个端口
host: '0.0.0.0', // 统一使用 0.0.0.0 绑定,确保端口检测正确
open: false, // 开发态不要自动打开浏览器,避免端口回退时误打开 51721/51722 等页面
cors: true,
// HMR 配置
hmr: {
// 禁用 Vite 的错误覆盖层Error Overlay
// 原因项目使用多入口架构prototypes、components 等Vite 的 Error Overlay 会在所有打开的页面上显示错误
// 这导致用户在访问页面 A 时,如果页面 B 出现构建错误,错误会跨页面显示在页面 A 上,造成困扰
// 解决方案:禁用 Vite 的 Error Overlay使用 dev-template.html 中已实现的自定义错误捕获和显示系统
// 优点:避免跨页面错误显示,保持错误提示的页面隔离性,风险最小
overlay: false
},
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
}
},
build: {
outDir: path.resolve(projectRoot, 'dist'),
emptyOutDir: !isIifeBuild,
target: isIifeBuild ? 'es2015' : 'esnext',
assetsInlineLimit: 1024 * 1024, // 1MB - 小于此大小的图片会被内联为 Base64
rollupOptions: {
input: rollupInput,
external: isIifeBuild ? ['react', 'react-dom'] : [],
output: {
entryFileNames: (chunkInfo: { name: string }) => `${chunkInfo.name}.js`,
format: isIifeBuild ? 'iife' : 'es',
name: 'UserComponent',
...(isIifeBuild
? {
globals: {
react: 'React',
'react-dom': 'ReactDOM'
},
generatedCode: { constBindings: false }
}
: {})
}
},
minify: isIifeBuild ? 'esbuild' : false
},
esbuild: isIifeBuild
? {
target: 'es2015',
legalComments: 'none',
keepNames: true
}
: {
jsx: 'transform',
jsxFactory: 'React.createElement',
jsxFragment: 'React.Fragment'
},
test: {
globals: true,
environment: 'node',
include: [
'tests/**/*.test.ts',
'tests/**/*.test.tsx',
'vite-plugins/**/*.test.ts',
],
root: '.',
}
};
return config;
});