feat(tools): new-game 一键新建子游戏工程
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import { join } from 'node:path';
|
||||||
|
import { pathToFileURL } from 'node:url';
|
||||||
|
import { SEED_DIR, GAMES_DIR, FRAMEWORK_SRC } from './lib/paths.mjs';
|
||||||
|
import { copySeed, setProjectIdentity, scaffoldGameAssets } from './lib/scaffold.mjs';
|
||||||
|
import { ensureJunction } from './lib/junction.mjs';
|
||||||
|
|
||||||
|
const NAME_RE = /^[a-z0-9][a-z0-9_-]*$/;
|
||||||
|
|
||||||
|
// 新建一款子游戏工程。opts 可注入路径用于测试。
|
||||||
|
export function newGame(name, opts = {}) {
|
||||||
|
const seedDir = opts.seedDir ?? SEED_DIR;
|
||||||
|
const gamesDir = opts.gamesDir ?? GAMES_DIR;
|
||||||
|
const frameworkSrc = opts.frameworkSrc ?? FRAMEWORK_SRC;
|
||||||
|
|
||||||
|
if (!name || !NAME_RE.test(name)) {
|
||||||
|
throw new Error(`非法子游戏名称: "${name}"(仅限小写字母/数字/-/_,且以字母数字开头)`);
|
||||||
|
}
|
||||||
|
const projectDir = join(gamesDir, name);
|
||||||
|
|
||||||
|
copySeed(seedDir, projectDir); // 目标已存在会抛错
|
||||||
|
setProjectIdentity(projectDir, name);
|
||||||
|
scaffoldGameAssets(projectDir, name);
|
||||||
|
const link = join(projectDir, 'assets', 'framework');
|
||||||
|
ensureJunction(link, frameworkSrc);
|
||||||
|
|
||||||
|
return { projectDir, link };
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLI 入口
|
||||||
|
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
||||||
|
const name = process.argv[2];
|
||||||
|
if (!name) {
|
||||||
|
console.error('用法: npm run new-game <name>');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
const { projectDir } = newGame(name);
|
||||||
|
console.log(`已创建子游戏工程: ${projectDir}`);
|
||||||
|
console.log(`下一步: 用 Cocos Creator 打开该目录`);
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { test } from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
import { existsSync, readFileSync } from 'node:fs';
|
||||||
|
import { makeTempMonorepo, makeSeed, cleanup } from './helpers.mjs';
|
||||||
|
import { newGame } from '../new-game.mjs';
|
||||||
|
import { isLink } from '../lib/junction.mjs';
|
||||||
|
|
||||||
|
test('newGame 生成工程:改身份 + 挂 framework junction + 铺 game 骨架', () => {
|
||||||
|
const mono = makeTempMonorepo();
|
||||||
|
try {
|
||||||
|
const seed = makeSeed(mono.root);
|
||||||
|
const result = newGame('doudizhu', { seedDir: seed, gamesDir: mono.games, frameworkSrc: mono.framework });
|
||||||
|
const dest = join(mono.games, 'doudizhu');
|
||||||
|
assert.equal(result.projectDir, dest);
|
||||||
|
const pkg = JSON.parse(readFileSync(join(dest, 'package.json'), 'utf8'));
|
||||||
|
assert.equal(pkg.name, 'doudizhu');
|
||||||
|
assert.notEqual(pkg.uuid, 'seed-uuid');
|
||||||
|
const link = join(dest, 'assets', 'framework');
|
||||||
|
assert.ok(isLink(link));
|
||||||
|
assert.equal(readFileSync(join(link, 'README.md'), 'utf8'), '# framework src\n');
|
||||||
|
assert.ok(existsSync(join(dest, 'assets', 'game', 'GameModule.ts')));
|
||||||
|
} finally {
|
||||||
|
cleanup(mono.root);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('newGame 拒绝重名工程', () => {
|
||||||
|
const mono = makeTempMonorepo();
|
||||||
|
try {
|
||||||
|
const seed = makeSeed(mono.root);
|
||||||
|
const opts = { seedDir: seed, gamesDir: mono.games, frameworkSrc: mono.framework };
|
||||||
|
newGame('doudizhu', opts);
|
||||||
|
assert.throws(() => newGame('doudizhu', opts), /已存在/);
|
||||||
|
} finally {
|
||||||
|
cleanup(mono.root);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('newGame 校验名称非空且合法', () => {
|
||||||
|
const mono = makeTempMonorepo();
|
||||||
|
try {
|
||||||
|
const seed = makeSeed(mono.root);
|
||||||
|
const opts = { seedDir: seed, gamesDir: mono.games, frameworkSrc: mono.framework };
|
||||||
|
assert.throws(() => newGame('', opts), /名称/);
|
||||||
|
assert.throws(() => newGame('Bad Name!', opts), /名称/);
|
||||||
|
} finally {
|
||||||
|
cleanup(mono.root);
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user