#!/usr/bin/env node
/**
* Worker Generator CLI
* 从 WorkerEntitySystem 子类生成 Worker 文件
* Generate Worker files from WorkerEntitySystem subclasses
*/
import { Command } from 'commander';
import chalk from 'chalk';
import * as path from 'path';
import * as fs from 'fs';
import { parseWorkerSystems } from './parser';
import { generateWorkerFiles } from './generator';
import type { GeneratorConfig } from './types';
const packageJson = require('../package.json');
const program = new Command();
program
.name('esengine-worker-gen')
.description('Generate Worker files from WorkerEntitySystem classes for WeChat Mini Game and other platforms')
.version(packageJson.version);
program
.option('-s, --src
', 'Source directory to scan', './src')
.option('-o, --out ', 'Output directory for Worker files', './workers')
.option('-w, --wechat', 'Generate WeChat Mini Game compatible code', false)
.option('-m, --mapping', 'Generate worker-mapping.json file', true)
.option('-t, --tsconfig ', 'Path to tsconfig.json')
.option('-v, --verbose', 'Verbose output', false)
.action((options) => {
run(options);
});
function run(options: {
src: string;
out: string;
wechat: boolean;
mapping: boolean;
tsconfig?: string;
verbose: boolean;
}) {
console.log(chalk.cyan('\n🔧 ESEngine Worker Generator\n'));
// 解析路径
// Resolve paths
const srcDir = path.resolve(process.cwd(), options.src);
const outDir = path.resolve(process.cwd(), options.out);
// 检查源目录是否存在
// Check if source directory exists
if (!fs.existsSync(srcDir)) {
console.error(chalk.red(`Error: Source directory not found: ${srcDir}`));
process.exit(1);
}
// 查找 tsconfig.json
// Find tsconfig.json
let tsConfigPath = options.tsconfig;
if (!tsConfigPath) {
const defaultTsConfig = path.join(process.cwd(), 'tsconfig.json');
if (fs.existsSync(defaultTsConfig)) {
tsConfigPath = defaultTsConfig;
}
}
const config: GeneratorConfig = {
srcDir,
outDir,
wechat: options.wechat,
generateMapping: options.mapping,
tsConfigPath,
verbose: options.verbose,
};
console.log(chalk.gray(`Source directory: ${srcDir}`));
console.log(chalk.gray(`Output directory: ${outDir}`));
console.log(chalk.gray(`WeChat mode: ${options.wechat ? 'Yes' : 'No'}`));
if (tsConfigPath) {
console.log(chalk.gray(`TypeScript config: ${tsConfigPath}`));
}
console.log();
// 解析源文件
// Parse source files
console.log(chalk.yellow('Scanning for WorkerEntitySystem classes...'));
const systems = parseWorkerSystems(config);
if (systems.length === 0) {
console.log(chalk.yellow('\n⚠️ No WorkerEntitySystem subclasses found.'));
console.log(chalk.gray('Make sure your classes:'));
console.log(chalk.gray(' - Extend WorkerEntitySystem'));
console.log(chalk.gray(' - Have a workerProcess method'));
console.log(chalk.gray(' - Are in .ts files under the source directory'));
return;
}
console.log(chalk.green(`\n✓ Found ${systems.length} WorkerEntitySystem class(es):`));
for (const system of systems) {
const configStatus = system.workerScriptPath
? chalk.green(`✓ workerScriptPath: '${system.workerScriptPath}'`)
: chalk.yellow('⚠ No workerScriptPath configured');
console.log(chalk.gray(` - ${system.className}`));
console.log(chalk.gray(` ${path.relative(process.cwd(), system.filePath)}`));
console.log(` ${configStatus}`);
}
console.log();
// 生成 Worker 文件
// Generate Worker files
console.log(chalk.yellow('Generating Worker files...'));
const result = generateWorkerFiles(systems, config);
// 输出结果
// Output results
console.log();
if (result.success.length > 0) {
console.log(chalk.green(`✓ Successfully generated ${result.success.length} Worker file(s):`));
for (const item of result.success) {
const relativePath = path.relative(process.cwd(), item.outputPath).replace(/\\/g, '/');
if (item.configuredPath) {
console.log(chalk.green(` ✓ ${item.className} -> ${relativePath}`));
} else {
console.log(chalk.yellow(` ⚠ ${item.className} -> ${relativePath} (需要配置 workerScriptPath)`));
}
}
}
if (result.errors.length > 0) {
console.log(chalk.red(`\n✗ Failed to generate ${result.errors.length} Worker file(s):`));
for (const item of result.errors) {
console.log(chalk.red(` - ${item.className}: ${item.error}`));
}
}
// 提示未配置 workerScriptPath 的类
// Remind about classes without workerScriptPath
if (result.skipped.length > 0) {
console.log(chalk.yellow('\n⚠️ 以下类未配置 workerScriptPath,请在构造函数中添加配置:'));
console.log(chalk.yellow(' The following classes need workerScriptPath configuration:\n'));
for (const item of result.skipped) {
console.log(chalk.white(` // ${item.className}`));
console.log(chalk.cyan(` super(matcher, {`));
console.log(chalk.cyan(` workerScriptPath: '${item.suggestedPath}',`));
console.log(chalk.cyan(` // ... 其他配置`));
console.log(chalk.cyan(` });`));
console.log();
}
}
// 使用提示(只有当有已配置路径的成功项时)
// Usage tips (only when there are success items with configured path)
const configuredSuccess = result.success.filter(item => item.configuredPath);
if (configuredSuccess.length > 0) {
console.log(chalk.green('\n✅ 已按照代码中的 workerScriptPath 配置生成 Worker 文件!'));
console.log(chalk.gray(' Worker files generated according to workerScriptPath in your code!'));
console.log(chalk.gray('\n 下一步 | Next steps:'));
console.log(chalk.gray(' 1. 确保 game.json 配置了 workers 目录'));
console.log(chalk.gray(' Ensure game.json has workers directory configured'));
if (options.mapping) {
console.log(chalk.gray('\n 已生成映射文件 | Mapping file generated:'));
console.log(chalk.white(` import mapping from '${path.relative(process.cwd(), outDir)}/worker-mapping.json'`));
}
}
console.log();
}
program.parse();