feat(cli): add CLI tool for adding ECS framework to existing projects (#339)

* feat(cli): add CLI tool for adding ECS framework to existing projects

- Support Cocos Creator 2.x/3.x, LayaAir 3.x, and Node.js platforms
- Auto-detect project type based on directory structure
- Generate ECSManager with full configuration (debug, remote debug, WebSocket URL)
- Auto-install dependencies with npm/yarn/pnpm detection
- Platform-specific decorators and lifecycle methods

* chore: add changeset for @esengine/cli

* fix(ci): fix YAML syntax error in ai-issue-helper workflow

* fix(cli): resolve file system race conditions (CodeQL)

* chore(ci): remove unused and broken workflows

* fix(ci): fix YAML encoding in release.yml
This commit is contained in:
YHH
2025-12-26 16:18:59 +08:00
committed by GitHub
parent 155411e743
commit c4f7a13b74
24 changed files with 1703 additions and 786 deletions

View File

@@ -0,0 +1,254 @@
import type { FileEntry, PlatformAdapter, ProjectConfig } from './types.js';
/**
* @zh Cocos Creator 3.x 平台适配器
* @en Cocos Creator 3.x platform adapter
*/
export const cocosAdapter: PlatformAdapter = {
id: 'cocos',
name: 'Cocos Creator 3.x',
description: 'Generate ECS integration for Cocos Creator 3.x projects',
getDependencies() {
return {
'@esengine/ecs-framework': 'latest'
};
},
getDevDependencies() {
return {};
},
getScripts() {
return {};
},
generateFiles(config: ProjectConfig): FileEntry[] {
const files: FileEntry[] = [];
files.push({
path: 'assets/scripts/ecs/ECSManager.ts',
content: generateECSManager(config)
});
files.push({
path: 'assets/scripts/ecs/components/PositionComponent.ts',
content: generatePositionComponent()
});
files.push({
path: 'assets/scripts/ecs/systems/MovementSystem.ts',
content: generateMovementSystem()
});
files.push({
path: 'assets/scripts/ecs/README.md',
content: generateReadme(config)
});
return files;
}
};
function generateECSManager(config: ProjectConfig): string {
return `import { _decorator, Component, director } from 'cc';
import { Core, Scene, type ICoreConfig } from '@esengine/ecs-framework';
import { MovementSystem } from './systems/MovementSystem';
const { ccclass, property } = _decorator;
/**
* Game Scene - Define your game systems here
*/
class GameScene extends Scene {
initialize(): void {
this.name = '${config.name}';
this.addSystem(new MovementSystem());
// Add more systems here...
}
onStart(): void {
// Create your initial entities here
}
}
/**
* ECS Manager - Bridge between Cocos Creator and ESEngine ECS
*
* Attach this component to a node in your scene.
* All game logic should be implemented in ECS Systems.
*/
@ccclass('ECSManager')
export class ECSManager extends Component {
/** @zh 调试模式 @en Debug mode */
@property({ tooltip: 'Enable debug mode for ECS framework' })
debug = false;
/** @zh 跨场景保持 @en Keep across scenes */
@property({ tooltip: 'Keep this node alive across scenes' })
persistent = true;
/** @zh 启用远程调试 @en Enable remote debugging */
@property({ tooltip: 'Connect to ECS debugger via WebSocket' })
remoteDebug = false;
/** @zh WebSocket调试地址 @en WebSocket debug URL */
@property({ tooltip: 'WebSocket URL for remote debugging' })
debugUrl = 'ws://localhost:9229';
/** @zh 自动重连 @en Auto reconnect */
@property({ tooltip: 'Auto reconnect when connection lost' })
autoReconnect = true;
private static _instance: ECSManager | null = null;
private _scene!: GameScene;
static get instance() { return ECSManager._instance; }
get scene() { return this._scene; }
onLoad() {
if (ECSManager._instance) {
this.destroy();
return;
}
ECSManager._instance = this;
if (this.persistent) {
director.addPersistRootNode(this.node);
}
const config: ICoreConfig = {
debug: this.debug
};
// 配置远程调试
if (this.remoteDebug && this.debugUrl) {
config.debugConfig = {
enabled: true,
websocketUrl: this.debugUrl,
autoReconnect: this.autoReconnect,
channels: {
entities: true,
systems: true,
performance: true,
components: true,
scenes: true
}
};
}
Core.create(config);
this._scene = new GameScene();
Core.setScene(this._scene);
}
update(dt: number) {
Core.update(dt);
}
onDestroy() {
if (ECSManager._instance === this) {
ECSManager._instance = null;
Core.destroy();
}
}
}
`;
}
function generatePositionComponent(): string {
return `import { Component, ECSComponent } from '@esengine/ecs-framework';
/**
* Position component - stores entity position
*/
@ECSComponent('Position')
export class PositionComponent extends Component {
x = 0;
y = 0;
constructor(x = 0, y = 0) {
super();
this.x = x;
this.y = y;
}
}
`;
}
function generateMovementSystem(): string {
return `import { EntitySystem, Matcher, Entity, Time, ECSSystem } from '@esengine/ecs-framework';
import { PositionComponent } from '../components/PositionComponent';
/**
* Movement system - processes entities with PositionComponent
*
* Customize this system for your game logic.
*/
@ECSSystem('MovementSystem')
export class MovementSystem extends EntitySystem {
constructor() {
super(Matcher.empty().all(PositionComponent));
}
protected process(entities: readonly Entity[]): void {
for (const entity of entities) {
const position = entity.getComponent(PositionComponent)!;
// Update position using Time.deltaTime
// position.x += velocity.dx * Time.deltaTime;
}
}
}
`;
}
function generateReadme(config: ProjectConfig): string {
return `# ${config.name} - ECS Module
This module integrates ESEngine ECS framework with Cocos Creator.
## Quick Start
1. Attach \`ECSManager\` component to a node in your scene
2. Create your own components in \`components/\` folder
3. Create your systems in \`systems/\` folder
4. Register systems in \`ECSManager.start()\`
## Creating Components
\`\`\`typescript
import { Component } from '@esengine/ecs-framework';
export class MyComponent extends Component {
// Your data here
health: number = 100;
reset() {
this.health = 100;
}
}
\`\`\`
## Creating Systems
\`\`\`typescript
import { EntitySystem, Matcher, Entity } from '@esengine/ecs-framework';
import { MyComponent } from '../components/MyComponent';
export class MySystem extends EntitySystem {
constructor() {
super(Matcher.all(MyComponent));
}
protected processEntity(entity: Entity, dt: number): void {
const comp = entity.getComponent(MyComponent)!;
// Process entity
}
}
\`\`\`
## Documentation
- [ESEngine ECS Framework](https://github.com/esengine/esengine)
`;
}

View File

@@ -0,0 +1,259 @@
import type { FileEntry, PlatformAdapter, ProjectConfig } from './types.js';
/**
* @zh Cocos Creator 2.x 平台适配器
* @en Cocos Creator 2.x platform adapter
*/
export const cocos2Adapter: PlatformAdapter = {
id: 'cocos2',
name: 'Cocos Creator 2.x',
description: 'Generate ECS integration for Cocos Creator 2.x projects',
getDependencies() {
return {
'@esengine/ecs-framework': 'latest'
};
},
getDevDependencies() {
return {};
},
getScripts() {
return {};
},
generateFiles(config: ProjectConfig): FileEntry[] {
const files: FileEntry[] = [];
files.push({
path: 'assets/scripts/ecs/ECSManager.ts',
content: generateECSManager(config)
});
files.push({
path: 'assets/scripts/ecs/components/PositionComponent.ts',
content: generatePositionComponent()
});
files.push({
path: 'assets/scripts/ecs/systems/MovementSystem.ts',
content: generateMovementSystem()
});
files.push({
path: 'assets/scripts/ecs/README.md',
content: generateReadme(config)
});
return files;
}
};
function generateECSManager(config: ProjectConfig): string {
return `import { Core, Scene, ICoreConfig } from '@esengine/ecs-framework';
import { MovementSystem } from './systems/MovementSystem';
const { ccclass, property } = cc._decorator;
/**
* Game Scene - Define your game systems here
*/
class GameScene extends Scene {
initialize(): void {
this.name = '${config.name}';
this.addSystem(new MovementSystem());
// Add more systems here...
}
onStart(): void {
// Create your initial entities here
}
}
/**
* ECS Manager - Bridge between Cocos Creator 2.x and ESEngine ECS
*
* Attach this component to a node in your scene.
* All game logic should be implemented in ECS Systems.
*/
@ccclass
export default class ECSManager extends cc.Component {
/** @zh 调试模式 @en Debug mode */
@property({ tooltip: 'Enable debug mode for ECS framework' })
debug: boolean = false;
/** @zh 跨场景保持 @en Keep across scenes */
@property({ tooltip: 'Keep this node alive across scenes' })
persistent: boolean = true;
/** @zh 启用远程调试 @en Enable remote debugging */
@property({ tooltip: 'Connect to ECS debugger via WebSocket' })
remoteDebug: boolean = false;
/** @zh WebSocket调试地址 @en WebSocket debug URL */
@property({ tooltip: 'WebSocket URL for remote debugging' })
debugUrl: string = 'ws://localhost:9229';
/** @zh 自动重连 @en Auto reconnect */
@property({ tooltip: 'Auto reconnect when connection lost' })
autoReconnect: boolean = true;
private static _instance: ECSManager | null = null;
private _scene!: GameScene;
static get instance() { return ECSManager._instance; }
get scene() { return this._scene; }
onLoad() {
if (ECSManager._instance) {
this.node.destroy();
return;
}
ECSManager._instance = this;
if (this.persistent) {
cc.game.addPersistRootNode(this.node);
}
const config: ICoreConfig = {
debug: this.debug
};
// 配置远程调试
if (this.remoteDebug && this.debugUrl) {
config.debugConfig = {
enabled: true,
websocketUrl: this.debugUrl,
autoReconnect: this.autoReconnect,
channels: {
entities: true,
systems: true,
performance: true,
components: true,
scenes: true
}
};
}
Core.create(config);
this._scene = new GameScene();
Core.setScene(this._scene);
}
update(dt: number) {
Core.update(dt);
}
onDestroy() {
if (ECSManager._instance === this) {
ECSManager._instance = null;
Core.destroy();
}
}
}
`;
}
function generatePositionComponent(): string {
return `import { Component, ECSComponent } from '@esengine/ecs-framework';
/**
* Position component - stores entity position
*/
@ECSComponent('Position')
export class PositionComponent extends Component {
x = 0;
y = 0;
constructor(x = 0, y = 0) {
super();
this.x = x;
this.y = y;
}
}
`;
}
function generateMovementSystem(): string {
return `import { EntitySystem, Matcher, Entity, Time, ECSSystem } from '@esengine/ecs-framework';
import { PositionComponent } from '../components/PositionComponent';
/**
* Movement system - processes entities with PositionComponent
*
* Customize this system for your game logic.
*/
@ECSSystem('MovementSystem')
export class MovementSystem extends EntitySystem {
constructor() {
super(Matcher.empty().all(PositionComponent));
}
protected process(entities: readonly Entity[]): void {
for (const entity of entities) {
const position = entity.getComponent(PositionComponent)!;
// Update position using Time.deltaTime
// position.x += velocity.dx * Time.deltaTime;
}
}
}
`;
}
function generateReadme(config: ProjectConfig): string {
return `# ${config.name} - ECS Module
This module integrates ESEngine ECS framework with Cocos Creator 2.x.
## Quick Start
1. Attach \`ECSManager\` component to a node in your scene
2. Create your own components in \`components/\` folder
3. Create your systems in \`systems/\` folder
4. Register systems in \`ECSManager.onLoad()\`
## Cocos Creator 2.x Notes
- Use \`cc._decorator\` for decorators
- Use \`cc.Component\` as base class
- Use \`cc.game.addPersistRootNode()\` for persistent nodes
## Creating Components
\`\`\`typescript
import { Component } from '@esengine/ecs-framework';
export class MyComponent extends Component {
health: number = 100;
reset() {
this.health = 100;
}
}
\`\`\`
## Creating Systems
\`\`\`typescript
import { EntitySystem, Matcher, Entity } from '@esengine/ecs-framework';
import { MyComponent } from '../components/MyComponent';
export class MySystem extends EntitySystem {
constructor() {
super(Matcher.all(MyComponent));
}
protected processEntity(entity: Entity, dt: number): void {
const comp = entity.getComponent(MyComponent)!;
// Process entity
}
}
\`\`\`
## Documentation
- [ESEngine ECS Framework](https://github.com/esengine/esengine)
- [Cocos Creator 2.x Docs](https://docs.cocos.com/creator/2.4/manual/)
`;
}

View File

@@ -0,0 +1,54 @@
import { cocosAdapter } from './cocos.js';
import { cocos2Adapter } from './cocos2.js';
import { layaAdapter } from './laya.js';
import { nodejsAdapter } from './nodejs.js';
import type { AdapterRegistry, PlatformAdapter, PlatformType } from './types.js';
export * from './types.js';
export { cocosAdapter } from './cocos.js';
export { cocos2Adapter } from './cocos2.js';
export { layaAdapter } from './laya.js';
export { nodejsAdapter } from './nodejs.js';
/**
* @zh 平台适配器注册表
* @en Platform adapter registry
*/
export const adapters: AdapterRegistry = {
cocos: cocosAdapter,
cocos2: cocos2Adapter,
laya: layaAdapter,
nodejs: nodejsAdapter
};
/**
* @zh 获取平台适配器
* @en Get platform adapter
*/
export function getAdapter(platform: PlatformType): PlatformAdapter {
const adapter = adapters[platform];
if (!adapter) {
throw new Error(`Unknown platform: ${platform}`);
}
return adapter;
}
/**
* @zh 获取所有可用平台
* @en Get all available platforms
*/
export function getPlatforms(): PlatformType[] {
return Object.keys(adapters) as PlatformType[];
}
/**
* @zh 获取平台选项(用于交互式提示)
* @en Get platform choices (for interactive prompts)
*/
export function getPlatformChoices(): Array<{ title: string; value: PlatformType; description: string }> {
return Object.values(adapters).map((adapter) => ({
title: adapter.name,
value: adapter.id,
description: adapter.description
}));
}

View File

@@ -0,0 +1,245 @@
import type { FileEntry, PlatformAdapter, ProjectConfig } from './types.js';
/**
* @zh Laya 3.x 平台适配器
* @en Laya 3.x platform adapter
*/
export const layaAdapter: PlatformAdapter = {
id: 'laya',
name: 'Laya 3.x',
description: 'Generate ECS integration for LayaAir 3.x projects',
getDependencies() {
return {
'@esengine/ecs-framework': 'latest'
};
},
getDevDependencies() {
return {};
},
getScripts() {
return {};
},
generateFiles(config: ProjectConfig): FileEntry[] {
const files: FileEntry[] = [];
files.push({
path: 'src/ecs/ECSManager.ts',
content: generateECSManager(config)
});
files.push({
path: 'src/ecs/components/PositionComponent.ts',
content: generatePositionComponent()
});
files.push({
path: 'src/ecs/systems/MovementSystem.ts',
content: generateMovementSystem()
});
files.push({
path: 'src/ecs/README.md',
content: generateReadme(config)
});
return files;
}
};
function generateECSManager(config: ProjectConfig): string {
return `import { Core, Scene, type ICoreConfig } from '@esengine/ecs-framework';
import { MovementSystem } from './systems/MovementSystem';
const { regClass, property } = Laya;
/**
* Game Scene - Define your game systems here
*/
class GameScene extends Scene {
initialize(): void {
this.name = '${config.name}';
this.addSystem(new MovementSystem());
// Add more systems here...
}
onStart(): void {
// Create your initial entities here
}
}
/**
* ECS Manager - Bridge between LayaAir and ESEngine ECS
*
* Attach this script to a node in your scene via Laya IDE.
* All game logic should be implemented in ECS Systems.
*/
@regClass()
export class ECSManager extends Laya.Script {
/** @zh 调试模式 @en Debug mode */
@property({ type: Boolean, caption: 'Debug', tips: 'Enable debug mode for ECS framework' })
debug = false;
/** @zh 启用远程调试 @en Enable remote debugging */
@property({ type: Boolean, caption: 'Remote Debug', tips: 'Connect to ECS debugger via WebSocket' })
remoteDebug = false;
/** @zh WebSocket调试地址 @en WebSocket debug URL */
@property({ type: String, caption: 'Debug URL', tips: 'WebSocket URL for remote debugging (e.g., ws://localhost:9229)' })
debugUrl = 'ws://localhost:9229';
/** @zh 自动重连 @en Auto reconnect */
@property({ type: Boolean, caption: 'Auto Reconnect', tips: 'Auto reconnect when connection lost' })
autoReconnect = true;
private static _instance: ECSManager | null = null;
private _scene!: GameScene;
static get instance() { return ECSManager._instance; }
get scene() { return this._scene; }
onAwake(): void {
if (ECSManager._instance) {
this.destroy();
return;
}
ECSManager._instance = this;
const config: ICoreConfig = {
debug: this.debug
};
// 配置远程调试
if (this.remoteDebug && this.debugUrl) {
config.debugConfig = {
enabled: true,
websocketUrl: this.debugUrl,
autoReconnect: this.autoReconnect,
channels: {
entities: true,
systems: true,
performance: true,
components: true,
scenes: true
}
};
}
Core.create(config);
this._scene = new GameScene();
Core.setScene(this._scene);
}
onUpdate(): void {
Core.update(Laya.timer.delta / 1000);
}
onDestroy(): void {
if (ECSManager._instance === this) {
ECSManager._instance = null;
Core.destroy();
}
}
}
`;
}
function generatePositionComponent(): string {
return `import { Component, ECSComponent } from '@esengine/ecs-framework';
/**
* Position component - stores entity position
*/
@ECSComponent('Position')
export class PositionComponent extends Component {
x = 0;
y = 0;
constructor(x = 0, y = 0) {
super();
this.x = x;
this.y = y;
}
}
`;
}
function generateMovementSystem(): string {
return `import { EntitySystem, Matcher, Entity, Time, ECSSystem } from '@esengine/ecs-framework';
import { PositionComponent } from '../components/PositionComponent';
/**
* Movement system - processes entities with PositionComponent
*
* Customize this system for your game logic.
*/
@ECSSystem('MovementSystem')
export class MovementSystem extends EntitySystem {
constructor() {
super(Matcher.empty().all(PositionComponent));
}
protected process(entities: readonly Entity[]): void {
for (const entity of entities) {
const position = entity.getComponent(PositionComponent)!;
// Update position using Time.deltaTime
// position.x += velocity.dx * Time.deltaTime;
}
}
}
`;
}
function generateReadme(config: ProjectConfig): string {
return `# ${config.name} - ECS Module
This module integrates ESEngine ECS framework with LayaAir 3.x.
## Quick Start
1. In Laya IDE, attach \`ECSManager\` script to a node in your scene
2. Create your own components in \`components/\` folder
3. Create your systems in \`systems/\` folder
4. Register systems in \`ECSManager.onAwake()\`
## Creating Components
\`\`\`typescript
import { Component } from '@esengine/ecs-framework';
export class MyComponent extends Component {
health: number = 100;
reset() {
this.health = 100;
}
}
\`\`\`
## Creating Systems
\`\`\`typescript
import { EntitySystem, Matcher, Entity } from '@esengine/ecs-framework';
import { MyComponent } from '../components/MyComponent';
export class MySystem extends EntitySystem {
constructor() {
super(Matcher.all(MyComponent));
}
protected processEntity(entity: Entity, dt: number): void {
const comp = entity.getComponent(MyComponent)!;
// Process entity
}
}
\`\`\`
## Documentation
- [ESEngine ECS Framework](https://github.com/esengine/esengine)
- [LayaAir Documentation](https://layaair.com/)
`;
}

View File

@@ -0,0 +1,348 @@
import type { FileEntry, PlatformAdapter, ProjectConfig } from './types.js';
/**
* @zh Node.js 平台适配器
* @en Node.js platform adapter
*/
export const nodejsAdapter: PlatformAdapter = {
id: 'nodejs',
name: 'Node.js',
description: 'Generate standalone Node.js project with ECS (for servers, CLI tools, simulations)',
getDependencies() {
return {
'@esengine/ecs-framework': 'latest'
};
},
getDevDependencies() {
return {
'@types/node': '^20.0.0',
'tsx': '^4.0.0',
'typescript': '^5.0.0'
};
},
getScripts() {
return {
'dev': 'tsx watch src/index.ts',
'start': 'tsx src/index.ts',
'build': 'tsc',
'build:start': 'tsc && node dist/index.js'
};
},
generateFiles(config: ProjectConfig): FileEntry[] {
const files: FileEntry[] = [];
files.push({
path: 'src/index.ts',
content: generateIndex(config)
});
files.push({
path: 'src/Game.ts',
content: generateGame(config)
});
files.push({
path: 'src/components/PositionComponent.ts',
content: generatePositionComponent()
});
files.push({
path: 'src/systems/MovementSystem.ts',
content: generateMovementSystem()
});
files.push({
path: 'tsconfig.json',
content: generateTsConfig()
});
files.push({
path: 'README.md',
content: generateReadme(config)
});
return files;
}
};
function generateIndex(config: ProjectConfig): string {
return `import { Game } from './Game.js';
const game = new Game();
// Handle graceful shutdown
process.on('SIGINT', () => {
console.log('\\nShutting down...');
game.stop();
process.exit(0);
});
process.on('SIGTERM', () => {
game.stop();
process.exit(0);
});
// Start the game
game.start();
console.log('[${config.name}] Game started. Press Ctrl+C to stop.');
`;
}
function generateGame(config: ProjectConfig): string {
return `import { Core, Scene, type ICoreConfig } from '@esengine/ecs-framework';
import { MovementSystem } from './systems/MovementSystem.js';
/**
* Game configuration options
*/
export interface GameOptions {
/** @zh 调试模式 @en Debug mode */
debug?: boolean;
/** @zh 目标帧率 @en Target FPS */
targetFPS?: number;
/** @zh 远程调试配置 @en Remote debug configuration */
remoteDebug?: {
/** @zh 启用远程调试 @en Enable remote debugging */
enabled: boolean;
/** @zh WebSocket地址 @en WebSocket URL */
url: string;
/** @zh 自动重连 @en Auto reconnect */
autoReconnect?: boolean;
};
}
/**
* Game Scene - Define your game systems here
*/
class GameScene extends Scene {
initialize(): void {
this.name = '${config.name}';
this.addSystem(new MovementSystem());
// Add more systems here...
}
onStart(): void {
// Create your initial entities here
}
}
/**
* Main game class with ECS game loop
*
* Features:
* - Configurable debug mode and FPS
* - Remote debugging via WebSocket
* - Fixed timestep game loop
* - Graceful start/stop
*/
export class Game {
private readonly _scene: GameScene;
private readonly _targetFPS: number;
private _running = false;
private _tickInterval: ReturnType<typeof setInterval> | null = null;
private _lastTime = 0;
get scene() { return this._scene; }
get running() { return this._running; }
constructor(options: GameOptions = {}) {
const { debug = false, targetFPS = 60, remoteDebug } = options;
this._targetFPS = targetFPS;
const config: ICoreConfig = { debug };
// 配置远程调试
if (remoteDebug?.enabled && remoteDebug.url) {
config.debugConfig = {
enabled: true,
websocketUrl: remoteDebug.url,
autoReconnect: remoteDebug.autoReconnect ?? true,
channels: {
entities: true,
systems: true,
performance: true,
components: true,
scenes: true
}
};
}
Core.create(config);
this._scene = new GameScene();
Core.setScene(this._scene);
}
start(): void {
if (this._running) return;
this._running = true;
this._lastTime = performance.now();
this._tickInterval = setInterval(() => {
const now = performance.now();
Core.update((now - this._lastTime) / 1000);
this._lastTime = now;
}, 1000 / this._targetFPS);
}
stop(): void {
if (!this._running) return;
this._running = false;
if (this._tickInterval) {
clearInterval(this._tickInterval);
this._tickInterval = null;
}
Core.destroy();
}
}
`;
}
function generatePositionComponent(): string {
return `import { Component, ECSComponent } from '@esengine/ecs-framework';
/**
* Position component - stores entity position
*/
@ECSComponent('Position')
export class PositionComponent extends Component {
x = 0;
y = 0;
constructor(x = 0, y = 0) {
super();
this.x = x;
this.y = y;
}
}
`;
}
function generateMovementSystem(): string {
return `import { EntitySystem, Matcher, Entity, Time, ECSSystem } from '@esengine/ecs-framework';
import { PositionComponent } from '../components/PositionComponent.js';
/**
* Movement system - processes entities with PositionComponent
*
* Customize this system for your game logic.
*/
@ECSSystem('MovementSystem')
export class MovementSystem extends EntitySystem {
constructor() {
super(Matcher.empty().all(PositionComponent));
}
protected process(entities: readonly Entity[]): void {
for (const entity of entities) {
const position = entity.getComponent(PositionComponent)!;
// Update position using Time.deltaTime
// position.x += velocity.dx * Time.deltaTime;
}
}
}
`;
}
function generateTsConfig(): string {
return `{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ES2022"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
`;
}
function generateReadme(config: ProjectConfig): string {
return `# ${config.name}
A Node.js project using ESEngine ECS framework.
## Quick Start
\`\`\`bash
# Install dependencies
npm install
# Run in development mode (with hot reload)
npm run dev
# Build and run
npm run build:start
\`\`\`
## Project Structure
\`\`\`
src/
├── index.ts # Entry point
├── Game.ts # Game loop and ECS setup
├── components/ # ECS components (data)
│ └── PositionComponent.ts
└── systems/ # ECS systems (logic)
└── MovementSystem.ts
\`\`\`
## Creating Components
\`\`\`typescript
import { Component } from '@esengine/ecs-framework';
export class HealthComponent extends Component {
current = 100;
max = 100;
reset(): void {
this.current = this.max;
}
}
\`\`\`
## Creating Systems
\`\`\`typescript
import { EntitySystem, Matcher, Entity } from '@esengine/ecs-framework';
import { HealthComponent } from '../components/HealthComponent.js';
export class HealthSystem extends EntitySystem {
constructor() {
super(Matcher.all(HealthComponent));
}
protected processEntity(entity: Entity, dt: number): void {
const health = entity.getComponent(HealthComponent)!;
// Your logic here
}
}
\`\`\`
## Use Cases
- Game servers
- CLI tools with complex logic
- Simulations
- Automated testing
## Documentation
- [ESEngine ECS Framework](https://github.com/esengine/esengine)
`;
}

View File

@@ -0,0 +1,77 @@
/**
* @zh 项目配置
* @en Project configuration
*/
export interface ProjectConfig {
/** @zh 项目名称 @en Project name */
name: string;
/** @zh 目标平台 @en Target platform */
platform: PlatformType;
/** @zh 项目路径 @en Project path */
path: string;
}
/**
* @zh 支持的平台类型
* @en Supported platform types
*/
export type PlatformType = 'cocos' | 'cocos2' | 'laya' | 'nodejs';
/**
* @zh 文件入口
* @en File entry
*/
export interface FileEntry {
/** @zh 相对路径 @en Relative path */
path: string;
/** @zh 文件内容 @en File content */
content: string;
}
/**
* @zh 平台适配器接口
* @en Platform adapter interface
*
* @zh 每个平台只需实现这个接口,即可支持项目生成
* @en Each platform only needs to implement this interface to support project generation
*/
export interface PlatformAdapter {
/** @zh 平台标识 @en Platform identifier */
readonly id: PlatformType;
/** @zh 平台显示名称 @en Platform display name */
readonly name: string;
/** @zh 平台描述 @en Platform description */
readonly description: string;
/**
* @zh 获取平台特定的依赖
* @en Get platform-specific dependencies
*/
getDependencies(): Record<string, string>;
/**
* @zh 获取平台特定的开发依赖
* @en Get platform-specific dev dependencies
*/
getDevDependencies(): Record<string, string>;
/**
* @zh 生成平台特定的文件
* @en Generate platform-specific files
*/
generateFiles(config: ProjectConfig): FileEntry[];
/**
* @zh 获取 package.json 的 scripts
* @en Get package.json scripts
*/
getScripts(): Record<string, string>;
}
/**
* @zh 平台适配器注册表类型
* @en Platform adapter registry type
*/
export type AdapterRegistry = Record<PlatformType, PlatformAdapter>;