#!/usr/bin/env node /** * Copy Engine Modules Script * 复制引擎模块脚本 * * Copies module.json and dist/index.js from each engine module package * to packages/editor-app/dist/engine/{moduleId}/ directory. * 将每个引擎模块包的 module.json 和 dist/index.js 复制到 * packages/editor-app/dist/engine/{moduleId}/ 目录。 * * This ensures the same directory structure is used in development and production. * 这确保开发和生产环境使用相同的目录结构。 * * Usage: * node scripts/copy-engine-modules.mjs * pnpm run copy-modules * * Output structure: * packages/editor-app/dist/ * engine/ * index.json <- Module registry index * sprite/ * module.json * index.js * tilemap/ * module.json * index.js * sprite-editor/ * index.js * ... */ import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const rootDir = path.resolve(__dirname, '..'); const packagesDir = path.join(rootDir, 'packages'); const outputDir = path.join(packagesDir, 'editor-app', 'dist', 'engine'); /** * Get all engine modules (packages with module.json) * 获取所有引擎模块(带 module.json 的包) */ function getEngineModules() { const modules = []; let packages; try { packages = fs.readdirSync(packagesDir); } catch { console.error(`[copy-modules] Error: Cannot read packages directory: ${packagesDir}`); return modules; } for (const pkg of packages) { const pkgDir = path.join(packagesDir, pkg); const moduleJsonPath = path.join(pkgDir, 'module.json'); // Skip if not a directory try { if (!fs.statSync(pkgDir).isDirectory()) continue; } catch { continue; } // Skip if no module.json if (!fs.existsSync(moduleJsonPath)) continue; try { const moduleJson = JSON.parse(fs.readFileSync(moduleJsonPath, 'utf-8')); // Only include if isEngineModule is not explicitly false if (moduleJson.isEngineModule !== false) { modules.push({ id: moduleJson.id || pkg, name: moduleJson.name || `@esengine/${pkg}`, displayName: moduleJson.displayName || pkg, packageDir: pkgDir, moduleJsonPath, distPath: path.join(pkgDir, 'dist', 'index.js'), editorPackage: moduleJson.editorPackage, isCore: moduleJson.isCore || false, category: moduleJson.category || 'Other' }); } } catch (err) { console.warn(` ⚠ Warning: Failed to parse ${moduleJsonPath}: ${err.message}`); } } return modules; } /** * Copy a single runtime module to output directory * 将单个运行时模块复制到输出目录 */ function copyModule(module) { const moduleOutputDir = path.join(outputDir, module.id); // Create output directory if (!fs.existsSync(moduleOutputDir)) { fs.mkdirSync(moduleOutputDir, { recursive: true }); } // Copy module.json const destModuleJson = path.join(moduleOutputDir, 'module.json'); fs.copyFileSync(module.moduleJsonPath, destModuleJson); // Copy dist/index.js if exists // 如果存在则拷贝 dist/index.js let hasRuntime = false; let hasTypes = false; let sizeKB = 'N/A'; if (fs.existsSync(module.distPath)) { const destIndexJs = path.join(moduleOutputDir, 'index.js'); fs.copyFileSync(module.distPath, destIndexJs); // Also copy source map if exists // 如果存在则拷贝 source map const sourceMapPath = module.distPath + '.map'; if (fs.existsSync(sourceMapPath)) { fs.copyFileSync(sourceMapPath, destIndexJs + '.map'); } const stats = fs.statSync(module.distPath); sizeKB = (stats.size / 1024).toFixed(1); hasRuntime = true; } // Copy type definitions (.d.ts) if exists // 如果存在则拷贝类型定义文件 (.d.ts) const typesPath = module.distPath.replace(/\.js$/, '.d.ts'); if (fs.existsSync(typesPath)) { const destDts = path.join(moduleOutputDir, 'index.d.ts'); fs.copyFileSync(typesPath, destDts); hasTypes = true; } return { hasRuntime, hasTypes, sizeKB }; } /** * Copy an editor package * 复制编辑器包 */ function copyEditorPackage(editorPackageName) { // Extract package folder name from @esengine/xxx-editor const match = editorPackageName.match(/@esengine\/(.+)/); if (!match) return null; const pkgName = match[1]; const pkgDir = path.join(packagesDir, pkgName); const distPath = path.join(pkgDir, 'dist', 'index.js'); if (!fs.existsSync(distPath)) { return null; } const editorOutputDir = path.join(outputDir, pkgName); if (!fs.existsSync(editorOutputDir)) { fs.mkdirSync(editorOutputDir, { recursive: true }); } const destIndexJs = path.join(editorOutputDir, 'index.js'); fs.copyFileSync(distPath, destIndexJs); // Copy source map if exists const sourceMapPath = distPath + '.map'; if (fs.existsSync(sourceMapPath)) { fs.copyFileSync(sourceMapPath, destIndexJs + '.map'); } const stats = fs.statSync(distPath); return { name: pkgName, sizeKB: (stats.size / 1024).toFixed(1) }; } /** * Main function */ function main() { console.log('\n📦 Copying engine modules to editor-app/dist/engine/\n'); // Ensure editor-app/dist exists const editorDistDir = path.join(packagesDir, 'editor-app', 'dist'); if (!fs.existsSync(editorDistDir)) { fs.mkdirSync(editorDistDir, { recursive: true }); } // Clean and recreate output directory if (fs.existsSync(outputDir)) { fs.rmSync(outputDir, { recursive: true }); } fs.mkdirSync(outputDir, { recursive: true }); // Get all engine modules const modules = getEngineModules(); if (modules.length === 0) { console.log(' No engine modules found.\n'); return; } // Collect editor packages const editorPackages = new Set(); // Copy runtime modules console.log('Runtime modules:'); const moduleInfos = []; for (const module of modules) { const { hasRuntime, hasTypes, sizeKB } = copyModule(module); if (hasRuntime) { const typesIndicator = hasTypes ? ' +types' : ''; console.log(` ✓ ${module.id} (${sizeKB} KB${typesIndicator})`); } else { console.log(` ○ ${module.id} (config only)`); } moduleInfos.push({ id: module.id, name: module.name, displayName: module.displayName, hasRuntime, hasTypes, editorPackage: module.editorPackage, isCore: module.isCore, category: module.category }); if (module.editorPackage) { editorPackages.add(module.editorPackage); } } // Copy editor packages if (editorPackages.size > 0) { console.log('\nEditor packages:'); for (const editorPkg of editorPackages) { const result = copyEditorPackage(editorPkg); if (result) { console.log(` ✓ ${result.name} (${result.sizeKB} KB)`); } else { console.log(` ⚠ ${editorPkg} (not built)`); } } } // Create index.json with all module info const indexData = { version: '1.0.0', generatedAt: new Date().toISOString(), modules: moduleInfos }; fs.writeFileSync( path.join(outputDir, 'index.json'), JSON.stringify(indexData, null, 2) ); console.log(`\n✅ Copied ${modules.length} modules to dist/engine/`); console.log(` Index file: dist/engine/index.json\n`); } // Run main();