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:
214
packages/editor/editor-app/scripts/bundle-runtime.mjs
Normal file
214
packages/editor/editor-app/scripts/bundle-runtime.mjs
Normal file
@@ -0,0 +1,214 @@
|
||||
/**
|
||||
* Bundle runtime files for production build
|
||||
* 为生产构建打包运行时文件
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname } from 'path';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const editorPath = path.resolve(__dirname, '..');
|
||||
const rootPath = path.resolve(editorPath, '../..');
|
||||
const bundleDir = path.join(editorPath, 'src-tauri', 'runtime');
|
||||
|
||||
// Create bundle directory
|
||||
if (!fs.existsSync(bundleDir)) {
|
||||
fs.mkdirSync(bundleDir, { recursive: true });
|
||||
console.log(`Created bundle directory: ${bundleDir}`);
|
||||
}
|
||||
|
||||
// Files to bundle
|
||||
// 需要打包的文件
|
||||
const filesToBundle = [
|
||||
{
|
||||
src: path.join(rootPath, 'packages/platform-web/dist/index.mjs'),
|
||||
dst: path.join(bundleDir, 'platform-web.mjs')
|
||||
},
|
||||
{
|
||||
src: path.join(rootPath, 'packages/engine/pkg/es_engine_bg.wasm'),
|
||||
dst: path.join(bundleDir, 'es_engine_bg.wasm')
|
||||
},
|
||||
{
|
||||
src: path.join(rootPath, 'packages/engine/pkg/es_engine.js'),
|
||||
dst: path.join(bundleDir, 'es_engine.js')
|
||||
}
|
||||
];
|
||||
|
||||
// Type definition files for IDE intellisense
|
||||
// 用于 IDE 智能感知的类型定义文件
|
||||
const typesDir = path.join(bundleDir, 'types');
|
||||
if (!fs.existsSync(typesDir)) {
|
||||
fs.mkdirSync(typesDir, { recursive: true });
|
||||
console.log(`Created types directory: ${typesDir}`);
|
||||
}
|
||||
|
||||
const typeFilesToBundle = [
|
||||
{
|
||||
src: path.join(rootPath, 'packages/core/dist/index.d.ts'),
|
||||
dst: path.join(typesDir, 'ecs-framework.d.ts')
|
||||
},
|
||||
{
|
||||
src: path.join(rootPath, 'packages/engine-core/dist/index.d.ts'),
|
||||
dst: path.join(typesDir, 'engine-core.d.ts')
|
||||
}
|
||||
];
|
||||
|
||||
// Copy files
|
||||
let success = true;
|
||||
for (const { src, dst } of filesToBundle) {
|
||||
try {
|
||||
if (!fs.existsSync(src)) {
|
||||
console.error(`Source file not found: ${src}`);
|
||||
console.log('Please build the runtime modules first:');
|
||||
console.log(' npm run build --workspace=@esengine/platform-web');
|
||||
console.log(' cd packages/engine && wasm-pack build --target web');
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
fs.copyFileSync(src, dst);
|
||||
const stats = fs.statSync(dst);
|
||||
console.log(`✓ Bundled ${path.basename(dst)} (${(stats.size / 1024).toFixed(2)} KB)`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to bundle ${path.basename(src)}: ${error.message}`);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy type definition files (optional - don't fail if not found)
|
||||
// 复制类型定义文件(可选 - 找不到不报错)
|
||||
for (const { src, dst } of typeFilesToBundle) {
|
||||
try {
|
||||
if (!fs.existsSync(src)) {
|
||||
console.warn(`Type definition not found: ${src}`);
|
||||
console.log(' Build packages first: pnpm --filter @esengine/core build');
|
||||
continue;
|
||||
}
|
||||
|
||||
fs.copyFileSync(src, dst);
|
||||
const stats = fs.statSync(dst);
|
||||
console.log(`✓ Bundled type definition ${path.basename(dst)} (${(stats.size / 1024).toFixed(2)} KB)`);
|
||||
} catch (error) {
|
||||
console.warn(`Failed to bundle type definition ${path.basename(src)}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy engine modules directory from dist/engine to src-tauri/engine
|
||||
// 复制引擎模块目录从 dist/engine 到 src-tauri/engine
|
||||
const engineSrcDir = path.join(editorPath, 'dist', 'engine');
|
||||
const engineDstDir = path.join(editorPath, 'src-tauri', 'engine');
|
||||
|
||||
/**
|
||||
* Recursively copy directory
|
||||
* 递归复制目录
|
||||
*/
|
||||
function copyDirRecursive(src, dst) {
|
||||
if (!fs.existsSync(src)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fs.existsSync(dst)) {
|
||||
fs.mkdirSync(dst, { recursive: true });
|
||||
}
|
||||
|
||||
const entries = fs.readdirSync(src, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
const srcPath = path.join(src, entry.name);
|
||||
const dstPath = path.join(dst, entry.name);
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
copyDirRecursive(srcPath, dstPath);
|
||||
} else {
|
||||
fs.copyFileSync(srcPath, dstPath);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (fs.existsSync(engineSrcDir)) {
|
||||
// Remove old engine directory if exists
|
||||
if (fs.existsSync(engineDstDir)) {
|
||||
fs.rmSync(engineDstDir, { recursive: true });
|
||||
}
|
||||
|
||||
if (copyDirRecursive(engineSrcDir, engineDstDir)) {
|
||||
// Count files
|
||||
let fileCount = 0;
|
||||
function countFiles(dir) {
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory()) {
|
||||
countFiles(path.join(dir, entry.name));
|
||||
} else {
|
||||
fileCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
countFiles(engineDstDir);
|
||||
console.log(`✓ Copied engine modules directory (${fileCount} files)`);
|
||||
}
|
||||
} else {
|
||||
console.warn(`Engine modules directory not found: ${engineSrcDir}`);
|
||||
console.log(' Build editor-app first: pnpm --filter @esengine/editor-app build');
|
||||
}
|
||||
|
||||
// Copy esbuild binary for user code compilation
|
||||
// 复制 esbuild 二进制文件用于用户代码编译
|
||||
const binDir = path.join(editorPath, 'src-tauri', 'bin');
|
||||
if (!fs.existsSync(binDir)) {
|
||||
fs.mkdirSync(binDir, { recursive: true });
|
||||
console.log(`Created bin directory: ${binDir}`);
|
||||
}
|
||||
|
||||
// Platform-specific esbuild binary paths
|
||||
// 平台特定的 esbuild 二进制路径
|
||||
const esbuildSources = {
|
||||
win32: path.join(rootPath, 'node_modules/@esbuild/win32-x64/esbuild.exe'),
|
||||
darwin: path.join(rootPath, 'node_modules/@esbuild/darwin-x64/bin/esbuild'),
|
||||
linux: path.join(rootPath, 'node_modules/@esbuild/linux-x64/bin/esbuild'),
|
||||
};
|
||||
|
||||
const platform = process.platform;
|
||||
const esbuildSrc = esbuildSources[platform];
|
||||
const esbuildDst = path.join(binDir, platform === 'win32' ? 'esbuild.exe' : 'esbuild');
|
||||
|
||||
let esbuildBundled = false;
|
||||
if (esbuildSrc && fs.existsSync(esbuildSrc)) {
|
||||
try {
|
||||
fs.copyFileSync(esbuildSrc, esbuildDst);
|
||||
// Ensure executable permission on Unix
|
||||
if (platform !== 'win32') {
|
||||
fs.chmodSync(esbuildDst, 0o755);
|
||||
}
|
||||
const stats = fs.statSync(esbuildDst);
|
||||
console.log(`✓ Bundled esbuild binary (${(stats.size / 1024 / 1024).toFixed(2)} MB)`);
|
||||
esbuildBundled = true;
|
||||
} catch (error) {
|
||||
console.warn(`Failed to bundle esbuild: ${error.message}`);
|
||||
console.log(' User code compilation will require global esbuild installation');
|
||||
}
|
||||
} else {
|
||||
console.warn(`esbuild binary not found for platform ${platform}: ${esbuildSrc}`);
|
||||
console.log(' User code compilation will require global esbuild installation');
|
||||
}
|
||||
|
||||
// Create a placeholder file if esbuild was not bundled
|
||||
// Tauri requires resources patterns to match at least one file
|
||||
// 如果 esbuild 没有打包,创建占位文件
|
||||
// Tauri 要求资源模式至少匹配一个文件
|
||||
if (!esbuildBundled) {
|
||||
const placeholderPath = path.join(binDir, '.gitkeep');
|
||||
fs.writeFileSync(placeholderPath, '# Placeholder for Tauri resources\n# esbuild binary will be bundled during release build\n');
|
||||
console.log('✓ Created placeholder in bin directory');
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
console.error('Runtime bundling failed');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('Runtime files bundled successfully!');
|
||||
47
packages/editor/editor-app/scripts/kill-dev-server.js
Normal file
47
packages/editor/editor-app/scripts/kill-dev-server.js
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* 清理开发服务器进程
|
||||
* 用于 Windows 平台自动清理残留的 Vite 进程
|
||||
*/
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
const PORT = 5173;
|
||||
|
||||
try {
|
||||
console.log(`正在查找占用端口 ${PORT} 的进程...`);
|
||||
|
||||
// Windows 命令
|
||||
const result = execSync(`netstat -ano | findstr :${PORT}`, { encoding: 'utf8' });
|
||||
|
||||
// 解析 PID
|
||||
const lines = result.split('\n');
|
||||
const pids = new Set();
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.includes('LISTENING')) {
|
||||
const parts = line.trim().split(/\s+/);
|
||||
const pid = parts[parts.length - 1];
|
||||
if (pid && pid !== '0') {
|
||||
pids.add(pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pids.size === 0) {
|
||||
console.log(`✓ 端口 ${PORT} 未被占用`);
|
||||
} else {
|
||||
console.log(`发现 ${pids.size} 个进程占用端口 ${PORT}`);
|
||||
for (const pid of pids) {
|
||||
try {
|
||||
// Windows 需要使用 /F /PID 而不是 //F //PID
|
||||
execSync(`taskkill /F /PID ${pid}`, { encoding: 'utf8', stdio: 'ignore' });
|
||||
console.log(`✓ 已终止进程 PID: ${pid}`);
|
||||
} catch (e) {
|
||||
console.log(`✗ 无法终止进程 PID: ${pid}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// 如果 netstat 没有找到结果,会抛出错误,这是正常的
|
||||
console.log(`✓ 端口 ${PORT} 未被占用`);
|
||||
}
|
||||
33
packages/editor/editor-app/scripts/sync-version.js
Normal file
33
packages/editor/editor-app/scripts/sync-version.js
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* 同步 package.json 和 tauri.conf.json 的版本号
|
||||
* 在 npm version 命令执行后自动运行
|
||||
*/
|
||||
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
// 读取 package.json
|
||||
const packageJsonPath = join(__dirname, '../package.json');
|
||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
||||
const newVersion = packageJson.version;
|
||||
|
||||
// 读取 tauri.conf.json
|
||||
const tauriConfigPath = join(__dirname, '../src-tauri/tauri.conf.json');
|
||||
const tauriConfig = JSON.parse(readFileSync(tauriConfigPath, 'utf8'));
|
||||
|
||||
// 更新 tauri.conf.json 的版本号
|
||||
const oldVersion = tauriConfig.version;
|
||||
tauriConfig.version = newVersion;
|
||||
|
||||
// 写回文件(保持格式)
|
||||
writeFileSync(tauriConfigPath, JSON.stringify(tauriConfig, null, 2) + '\n', 'utf8');
|
||||
|
||||
console.log(`✓ Version synced: ${oldVersion} → ${newVersion}`);
|
||||
console.log(` - package.json: ${newVersion}`);
|
||||
console.log(` - tauri.conf.json: ${newVersion}`);
|
||||
Reference in New Issue
Block a user