refactor: reorganize package structure and decouple framework packages (#338)
* refactor: reorganize package structure and decouple framework packages ## Package Structure Reorganization - Reorganized 55 packages into categorized subdirectories: - packages/framework/ - Generic framework (Laya/Cocos compatible) - packages/engine/ - ESEngine core modules - packages/rendering/ - Rendering modules (WASM dependent) - packages/physics/ - Physics modules - packages/streaming/ - World streaming - packages/network-ext/ - Network extensions - packages/editor/ - Editor framework and plugins - packages/rust/ - Rust WASM engine - packages/tools/ - Build tools and SDK ## Framework Package Decoupling - Decoupled behavior-tree and blueprint packages from ESEngine dependencies - Created abstracted interfaces (IBTAssetManager, IBehaviorTreeAssetContent) - ESEngine-specific code moved to esengine/ subpath exports - Framework packages now usable with Cocos/Laya without ESEngine ## CI Configuration - Updated CI to only type-check and lint framework packages - Added type-check:framework and lint:framework scripts ## Breaking Changes - Package import paths changed due to directory reorganization - ESEngine integrations now use subpath imports (e.g., '@esengine/behavior-tree/esengine') * fix: update es-engine file path after directory reorganization * docs: update README to focus on framework over engine * ci: only build framework packages, remove Rust/WASM dependencies * fix: remove esengine subpath from behavior-tree and blueprint builds ESEngine integration code will only be available in full engine builds. Framework packages are now purely engine-agnostic. * fix: move network-protocols to framework, build both in CI * fix: update workflow paths from packages/core to packages/framework/core * fix: exclude esengine folder from type-check in behavior-tree and blueprint * fix: update network tsconfig references to new paths * fix: add test:ci:framework to only test framework packages in CI * fix: only build core and math npm packages in CI * fix: exclude test files from CodeQL and fix string escaping security issue
This commit is contained in:
542
packages/editor/editor-app/vite.config.ts
Normal file
542
packages/editor/editor-app/vite.config.ts
Normal file
@@ -0,0 +1,542 @@
|
||||
import { defineConfig, Plugin } from 'vite';
|
||||
import react from '@vitejs/plugin-react-swc';
|
||||
import wasm from 'vite-plugin-wasm';
|
||||
import topLevelAwait from 'vite-plugin-top-level-await';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
function copyPluginModulesPlugin(): Plugin {
|
||||
const modulePaths = [
|
||||
{ name: 'editor-runtime', path: path.resolve(__dirname, '../editor-runtime/dist') },
|
||||
{ name: 'behavior-tree', path: path.resolve(__dirname, '../behavior-tree/dist') },
|
||||
];
|
||||
|
||||
/**
|
||||
* 递归复制目录中的 JS 文件
|
||||
*/
|
||||
function copyJsFilesRecursively(srcDir: string, destDir: string, relativePath: string = '') {
|
||||
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
const srcPath = path.join(srcDir, entry.name);
|
||||
const relPath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
// 递归复制子目录
|
||||
const subDestDir = path.join(destDir, entry.name);
|
||||
if (!fs.existsSync(subDestDir)) {
|
||||
fs.mkdirSync(subDestDir, { recursive: true });
|
||||
}
|
||||
copyJsFilesRecursively(srcPath, subDestDir, relPath);
|
||||
} else if (entry.name.endsWith('.js')) {
|
||||
// 复制 JS 文件
|
||||
const destPath = path.join(destDir, entry.name);
|
||||
fs.copyFileSync(srcPath, destPath);
|
||||
console.log(`[copy-plugin-modules] Copied ${relPath}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'copy-plugin-modules',
|
||||
writeBundle(options) {
|
||||
const outDir = options.dir || 'dist';
|
||||
const assetsDir = path.join(outDir, 'assets');
|
||||
|
||||
if (!fs.existsSync(assetsDir)) {
|
||||
fs.mkdirSync(assetsDir, { recursive: true });
|
||||
}
|
||||
|
||||
for (const mod of modulePaths) {
|
||||
if (!fs.existsSync(mod.path)) {
|
||||
console.warn(`[copy-plugin-modules] ${mod.name} dist not found: ${mod.path}`);
|
||||
continue;
|
||||
}
|
||||
copyJsFilesRecursively(mod.path, assetsDir);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin to copy engine modules after each build.
|
||||
* 每次构建后复制引擎模块的插件。
|
||||
*/
|
||||
function copyEngineModulesPlugin(): Plugin {
|
||||
const packagesDir = path.resolve(__dirname, '..');
|
||||
|
||||
function getEngineModules() {
|
||||
const modules: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
displayName: string;
|
||||
packageDir: string;
|
||||
moduleJsonPath: string;
|
||||
distPath: string;
|
||||
editorPackage?: string;
|
||||
isCore: boolean;
|
||||
category: string;
|
||||
}> = [];
|
||||
|
||||
let packages: string[];
|
||||
try {
|
||||
packages = fs.readdirSync(packagesDir);
|
||||
} catch {
|
||||
return modules;
|
||||
}
|
||||
|
||||
for (const pkg of packages) {
|
||||
const pkgDir = path.join(packagesDir, pkg);
|
||||
const moduleJsonPath = path.join(pkgDir, 'module.json');
|
||||
|
||||
try {
|
||||
if (!fs.statSync(pkgDir).isDirectory()) continue;
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!fs.existsSync(moduleJsonPath)) continue;
|
||||
|
||||
try {
|
||||
const moduleJson = JSON.parse(fs.readFileSync(moduleJsonPath, 'utf-8'));
|
||||
if (moduleJson.isEngineModule !== false) {
|
||||
// Use outputPath from module.json, default to "dist/index.js"
|
||||
const outputPath = moduleJson.outputPath || 'dist/index.js';
|
||||
const distPath = path.join(pkgDir, outputPath);
|
||||
|
||||
modules.push({
|
||||
id: moduleJson.id || pkg,
|
||||
name: moduleJson.name || `@esengine/${pkg}`,
|
||||
displayName: moduleJson.displayName || pkg,
|
||||
packageDir: pkgDir,
|
||||
moduleJsonPath,
|
||||
distPath,
|
||||
editorPackage: moduleJson.editorPackage,
|
||||
isCore: moduleJson.isCore || false,
|
||||
category: moduleJson.category || 'Other'
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
}
|
||||
}
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'copy-engine-modules',
|
||||
writeBundle(options) {
|
||||
const outDir = options.dir || 'dist';
|
||||
const engineDir = path.join(outDir, 'engine');
|
||||
|
||||
// Clean and recreate engine directory
|
||||
if (fs.existsSync(engineDir)) {
|
||||
fs.rmSync(engineDir, { recursive: true });
|
||||
}
|
||||
fs.mkdirSync(engineDir, { recursive: true });
|
||||
|
||||
const modules = getEngineModules();
|
||||
const moduleInfos: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
displayName: string;
|
||||
hasRuntime: boolean;
|
||||
editorPackage?: string;
|
||||
isCore: boolean;
|
||||
category: string;
|
||||
jsSize?: number;
|
||||
requiresWasm?: boolean;
|
||||
wasmSize?: number;
|
||||
wasmFiles?: string[];
|
||||
}> = [];
|
||||
|
||||
const editorPackages = new Set<string>();
|
||||
|
||||
/**
|
||||
* Calculate total WASM file size in a directory.
|
||||
* 计算目录中 WASM 文件的总大小。
|
||||
*/
|
||||
function getWasmSize(pkgDir: string): number {
|
||||
let totalSize = 0;
|
||||
const checkDirs = [
|
||||
pkgDir,
|
||||
path.join(pkgDir, 'pkg'),
|
||||
path.join(pkgDir, 'dist')
|
||||
];
|
||||
|
||||
for (const dir of checkDirs) {
|
||||
if (!fs.existsSync(dir)) continue;
|
||||
try {
|
||||
const files = fs.readdirSync(dir);
|
||||
for (const file of files) {
|
||||
if (file.endsWith('.wasm')) {
|
||||
const filePath = path.join(dir, file);
|
||||
const stat = fs.statSync(filePath);
|
||||
totalSize += stat.size;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors
|
||||
}
|
||||
}
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
console.log(`[copy-engine-modules] Copying ${modules.length} modules to dist/engine/`);
|
||||
|
||||
for (const module of modules) {
|
||||
const moduleOutputDir = path.join(engineDir, module.id);
|
||||
fs.mkdirSync(moduleOutputDir, { recursive: true });
|
||||
|
||||
// Read full module.json for additional fields
|
||||
// 读取完整 module.json 获取额外字段
|
||||
let moduleJson: Record<string, unknown> = {};
|
||||
try {
|
||||
moduleJson = JSON.parse(fs.readFileSync(module.moduleJsonPath, 'utf-8'));
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
}
|
||||
|
||||
// Copy module.json
|
||||
fs.copyFileSync(module.moduleJsonPath, path.join(moduleOutputDir, 'module.json'));
|
||||
|
||||
// Copy dist/index.js if exists
|
||||
let hasRuntime = false;
|
||||
let jsSize = 0;
|
||||
if (fs.existsSync(module.distPath)) {
|
||||
fs.copyFileSync(module.distPath, path.join(moduleOutputDir, 'index.js'));
|
||||
// Get JS file size
|
||||
jsSize = fs.statSync(module.distPath).size;
|
||||
// Copy source map if exists
|
||||
const sourceMapPath = module.distPath + '.map';
|
||||
if (fs.existsSync(sourceMapPath)) {
|
||||
fs.copyFileSync(sourceMapPath, path.join(moduleOutputDir, 'index.js.map'));
|
||||
}
|
||||
// Copy type definitions if exists
|
||||
// 复制类型定义文件(如果存在)
|
||||
// Handle both .js and .mjs extensions
|
||||
// 处理 .js 和 .mjs 两种扩展名
|
||||
const distDir = path.dirname(module.distPath);
|
||||
const dtsPath = path.join(distDir, 'index.d.ts');
|
||||
if (fs.existsSync(dtsPath)) {
|
||||
fs.copyFileSync(dtsPath, path.join(moduleOutputDir, 'index.d.ts'));
|
||||
}
|
||||
hasRuntime = true;
|
||||
|
||||
// Copy additional included files (e.g., chunks)
|
||||
// 复制额外包含的文件(如 chunk)
|
||||
const includes = moduleJson.includes as string[] | undefined;
|
||||
if (includes && includes.length > 0) {
|
||||
const distDir = path.dirname(module.distPath);
|
||||
for (const pattern of includes) {
|
||||
// Convert glob pattern to regex
|
||||
const regexPattern = pattern
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/\./g, '\\.')
|
||||
.replace(/\*/g, '.*')
|
||||
.replace(/\?/g, '.');
|
||||
const regex = new RegExp(`^${regexPattern}$`);
|
||||
|
||||
// Find matching files in dist directory
|
||||
if (fs.existsSync(distDir)) {
|
||||
const files = fs.readdirSync(distDir);
|
||||
for (const file of files) {
|
||||
if (regex.test(file)) {
|
||||
const srcFile = path.join(distDir, file);
|
||||
const destFile = path.join(moduleOutputDir, file);
|
||||
fs.copyFileSync(srcFile, destFile);
|
||||
jsSize += fs.statSync(srcFile).size;
|
||||
// Copy source map for included file if exists
|
||||
const mapFile = srcFile + '.map';
|
||||
if (fs.existsSync(mapFile)) {
|
||||
fs.copyFileSync(mapFile, destFile + '.map');
|
||||
}
|
||||
console.log(`[copy-engine-modules] Copied include to ${module.id}/: ${file}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate WASM size and copy WASM files if module requires WASM
|
||||
// 如果模块需要 WASM,计算 WASM 大小并复制 WASM 文件
|
||||
const requiresWasm = moduleJson.requiresWasm === true;
|
||||
let wasmSize = 0;
|
||||
const copiedWasmFiles: string[] = [];
|
||||
if (requiresWasm) {
|
||||
wasmSize = getWasmSize(module.packageDir);
|
||||
if (wasmSize > 0) {
|
||||
console.log(`[copy-engine-modules] ${module.id}: WASM size = ${(wasmSize / 1024).toFixed(1)} KB`);
|
||||
}
|
||||
|
||||
// Copy WASM files from wasmPaths defined in module.json
|
||||
// wasmPaths 现在是相对于源包目录的路径,如 "rapier_wasm2d_bg.wasm"
|
||||
// 需要找到实际的 WASM 文件并复制到输出的模块目录
|
||||
const wasmPaths = moduleJson.wasmPaths as string[] | undefined;
|
||||
if (wasmPaths && wasmPaths.length > 0) {
|
||||
for (const wasmRelPath of wasmPaths) {
|
||||
const wasmFileName = path.basename(wasmRelPath);
|
||||
|
||||
// 查找源 WASM 文件的可能位置
|
||||
// wasmPaths 里配置的是相对路径,实际文件在源包里
|
||||
// 对于 @esengine/rapier2d,WASM 在 packages/rapier2d/pkg/ 下
|
||||
const possibleSrcPaths = [
|
||||
// 直接在包目录下(如果 wasmRelPath 就是文件名)
|
||||
path.join(module.packageDir, wasmRelPath),
|
||||
// 在包的 pkg 目录下(wasm-pack 输出)
|
||||
path.join(module.packageDir, 'pkg', wasmFileName),
|
||||
// 在包的 dist 目录下
|
||||
path.join(module.packageDir, 'dist', wasmFileName),
|
||||
];
|
||||
|
||||
// 对于依赖其他包 WASM 的情况,检查依赖包
|
||||
// 例如 physics-rapier2d 依赖 rapier2d 的 WASM
|
||||
const depMatch = moduleJson.name?.toString().match(/@esengine\/(.+)/);
|
||||
if (depMatch) {
|
||||
// 检查同名的依赖包(去掉 physics- 前缀)
|
||||
const baseName = depMatch[1].replace('physics-', '');
|
||||
possibleSrcPaths.push(
|
||||
path.join(packagesDir, baseName, 'pkg', wasmFileName),
|
||||
path.join(packagesDir, baseName, wasmFileName)
|
||||
);
|
||||
}
|
||||
|
||||
let copied = false;
|
||||
for (const srcPath of possibleSrcPaths) {
|
||||
if (fs.existsSync(srcPath)) {
|
||||
const destPath = path.join(moduleOutputDir, wasmFileName);
|
||||
fs.copyFileSync(srcPath, destPath);
|
||||
copiedWasmFiles.push(wasmFileName);
|
||||
console.log(`[copy-engine-modules] Copied WASM to ${module.id}/: ${wasmFileName}`);
|
||||
copied = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!copied) {
|
||||
console.warn(`[copy-engine-modules] WASM file not found: ${wasmRelPath} (tried ${possibleSrcPaths.length} paths)`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy pkg directory if exists (for WASM JS bindings like rapier2d)
|
||||
// 如果存在 pkg 目录则复制(用于 WASM JS 绑定如 rapier2d)
|
||||
// The JS and WASM files must be in the same directory for import.meta.url to work
|
||||
// JS 和 WASM 文件必须在同一目录才能让 import.meta.url 正常工作
|
||||
const pkgDir = path.join(module.packageDir, 'pkg');
|
||||
if (fs.existsSync(pkgDir)) {
|
||||
const pkgOutputDir = path.join(moduleOutputDir, 'pkg');
|
||||
fs.mkdirSync(pkgOutputDir, { recursive: true });
|
||||
const pkgFiles = fs.readdirSync(pkgDir);
|
||||
for (const file of pkgFiles) {
|
||||
// Copy both JS and WASM files to pkg directory
|
||||
// 将 JS 和 WASM 文件都复制到 pkg 目录
|
||||
if (file.endsWith('.js') || file.endsWith('.wasm')) {
|
||||
const srcFile = path.join(pkgDir, file);
|
||||
const destFile = path.join(pkgOutputDir, file);
|
||||
fs.copyFileSync(srcFile, destFile);
|
||||
console.log(`[copy-engine-modules] Copied pkg to ${module.id}/pkg/: ${file}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
moduleInfos.push({
|
||||
id: module.id,
|
||||
name: module.name,
|
||||
displayName: module.displayName,
|
||||
hasRuntime,
|
||||
editorPackage: module.editorPackage,
|
||||
isCore: module.isCore,
|
||||
category: module.category,
|
||||
// Only include jsSize if there's actual runtime code
|
||||
// 只有实际有运行时代码时才包含 jsSize
|
||||
jsSize: jsSize > 0 ? jsSize : undefined,
|
||||
requiresWasm: requiresWasm || undefined,
|
||||
wasmSize: wasmSize > 0 ? wasmSize : undefined,
|
||||
// WASM files that were copied to dist/wasm/
|
||||
// 复制到 dist/wasm/ 的 WASM 文件
|
||||
wasmFiles: copiedWasmFiles.length > 0 ? copiedWasmFiles : undefined
|
||||
});
|
||||
|
||||
if (module.editorPackage) {
|
||||
editorPackages.add(module.editorPackage);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy editor packages
|
||||
for (const editorPkg of editorPackages) {
|
||||
const match = editorPkg.match(/@esengine\/(.+)/);
|
||||
if (!match) continue;
|
||||
|
||||
const pkgName = match[1];
|
||||
const pkgDir = path.join(packagesDir, pkgName);
|
||||
const distPath = path.join(pkgDir, 'dist', 'index.js');
|
||||
|
||||
if (!fs.existsSync(distPath)) continue;
|
||||
|
||||
const editorOutputDir = path.join(engineDir, pkgName);
|
||||
fs.mkdirSync(editorOutputDir, { recursive: true });
|
||||
fs.copyFileSync(distPath, path.join(editorOutputDir, 'index.js'));
|
||||
|
||||
const sourceMapPath = distPath + '.map';
|
||||
if (fs.existsSync(sourceMapPath)) {
|
||||
fs.copyFileSync(sourceMapPath, path.join(editorOutputDir, 'index.js.map'));
|
||||
}
|
||||
}
|
||||
|
||||
// Create index.json
|
||||
const indexData = {
|
||||
version: '1.0.0',
|
||||
generatedAt: new Date().toISOString(),
|
||||
modules: moduleInfos
|
||||
};
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(engineDir, 'index.json'),
|
||||
JSON.stringify(indexData, null, 2)
|
||||
);
|
||||
|
||||
console.log(`[copy-engine-modules] Done! Created dist/engine/index.json`);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const host = process.env.TAURI_DEV_HOST;
|
||||
const wasmPackages: string[] = [];
|
||||
|
||||
/**
|
||||
* 检查包目录是否包含 WASM 文件
|
||||
*/
|
||||
function hasWasmFiles(dirPath: string): boolean {
|
||||
try {
|
||||
const files = fs.readdirSync(dirPath);
|
||||
for (const file of files) {
|
||||
if (file.endsWith('.wasm')) return true;
|
||||
if (file === 'pkg') {
|
||||
const pkgPath = path.join(dirPath, file);
|
||||
const pkgFiles = fs.readdirSync(pkgPath);
|
||||
if (pkgFiles.some(f => f.endsWith('.wasm'))) return true;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描 packages 目录检测 WASM 包
|
||||
*/
|
||||
function detectWasmPackages() {
|
||||
const packagesDir = path.resolve(__dirname, '..');
|
||||
if (!fs.existsSync(packagesDir)) return;
|
||||
|
||||
const packageDirs = fs.readdirSync(packagesDir).filter(dir => {
|
||||
const stat = fs.statSync(path.join(packagesDir, dir));
|
||||
return stat.isDirectory();
|
||||
});
|
||||
|
||||
for (const dir of packageDirs) {
|
||||
const packageJsonPath = path.join(packagesDir, dir, 'package.json');
|
||||
if (fs.existsSync(packageJsonPath)) {
|
||||
try {
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
||||
const packageName = packageJson.name;
|
||||
const packageDir = path.join(packagesDir, dir);
|
||||
|
||||
if (packageName && hasWasmFiles(packageDir)) {
|
||||
wasmPackages.push(packageName);
|
||||
console.log(`[Vite] Detected WASM package: ${packageName}`);
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 扫描 node_modules
|
||||
const nodeModulesDir = path.resolve(__dirname, 'node_modules');
|
||||
if (fs.existsSync(nodeModulesDir)) {
|
||||
scanNodeModulesForWasm(nodeModulesDir);
|
||||
}
|
||||
}
|
||||
|
||||
function scanNodeModulesForWasm(nodeModulesDir: string) {
|
||||
try {
|
||||
const entries = fs.readdirSync(nodeModulesDir);
|
||||
for (const entry of entries) {
|
||||
if (entry.startsWith('.')) continue;
|
||||
|
||||
const entryPath = path.join(nodeModulesDir, entry);
|
||||
const stat = fs.statSync(entryPath);
|
||||
if (!stat.isDirectory()) continue;
|
||||
|
||||
if (entry.startsWith('@')) {
|
||||
const scopedPackages = fs.readdirSync(entryPath);
|
||||
for (const scopedPkg of scopedPackages) {
|
||||
const scopedPath = path.join(entryPath, scopedPkg);
|
||||
if (fs.statSync(scopedPath).isDirectory()) {
|
||||
if (!wasmPackages.includes(`${entry}/${scopedPkg}`) && hasWasmFiles(scopedPath)) {
|
||||
wasmPackages.push(`${entry}/${scopedPkg}`);
|
||||
console.log(`[Vite] Detected WASM package in node_modules: ${entry}/${scopedPkg}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!wasmPackages.includes(entry) && hasWasmFiles(entryPath)) {
|
||||
wasmPackages.push(entry);
|
||||
console.log(`[Vite] Detected WASM package in node_modules: ${entry}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors
|
||||
}
|
||||
}
|
||||
|
||||
detectWasmPackages();
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
wasm(),
|
||||
topLevelAwait(),
|
||||
...react({
|
||||
tsDecorators: true,
|
||||
}),
|
||||
copyPluginModulesPlugin(),
|
||||
copyEngineModulesPlugin(),
|
||||
],
|
||||
clearScreen: false,
|
||||
server: {
|
||||
host: host || false,
|
||||
port: 5173,
|
||||
strictPort: true,
|
||||
hmr: host
|
||||
? {
|
||||
protocol: 'ws',
|
||||
host,
|
||||
port: 5183,
|
||||
}
|
||||
: undefined,
|
||||
fs: {
|
||||
strict: false,
|
||||
},
|
||||
},
|
||||
envPrefix: ['VITE_', 'TAURI_'],
|
||||
build: {
|
||||
target: 'es2021',
|
||||
minify: !process.env.TAURI_DEBUG ? 'esbuild' : false,
|
||||
sourcemap: !!process.env.TAURI_DEBUG,
|
||||
},
|
||||
esbuild: {
|
||||
// 保留类名和函数名,用于跨包插件服务匹配
|
||||
keepNames: true,
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: ['tslib', 'react', 'react-dom', 'zustand', 'lucide-react'],
|
||||
exclude: wasmPackages,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user