feat(tools): paths.mjs 路径解析与工程发现
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,27 @@
|
|||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import { dirname, join, resolve } from 'node:path';
|
||||||
|
import { readFileSync, readdirSync, existsSync } from 'node:fs';
|
||||||
|
|
||||||
|
// 本文件位于 <monorepo>/scripts/lib/paths.mjs → 上溯两级为 monorepo 根
|
||||||
|
export const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
||||||
|
export const HOST_PROJECT = join(ROOT, 'YouleNexus');
|
||||||
|
export const FRAMEWORK_SRC = join(HOST_PROJECT, 'assets', 'framework');
|
||||||
|
export const GAMES_DIR = join(ROOT, 'games');
|
||||||
|
export const SEED_DIR = join(ROOT, 'templates', 'game-seed');
|
||||||
|
|
||||||
|
export function readCreatorVersion(projectDir) {
|
||||||
|
try {
|
||||||
|
const pkg = JSON.parse(readFileSync(join(projectDir, 'package.json'), 'utf8'));
|
||||||
|
return pkg?.creator?.version ?? null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listGameProjects(gamesDir = GAMES_DIR) {
|
||||||
|
if (!existsSync(gamesDir)) return [];
|
||||||
|
return readdirSync(gamesDir, { withFileTypes: true })
|
||||||
|
.filter((d) => d.isDirectory())
|
||||||
|
.map((d) => join(gamesDir, d.name))
|
||||||
|
.filter((dir) => existsSync(join(dir, 'package.json')));
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
||||||
|
import { tmpdir } from 'node:os';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
|
||||||
|
// 构造一个临时 monorepo:YouleNexus(host) + framework 真源 + 空 games/
|
||||||
|
export function makeTempMonorepo() {
|
||||||
|
const root = mkdtempSync(join(tmpdir(), 'youle-mono-'));
|
||||||
|
const host = join(root, 'YouleNexus');
|
||||||
|
const framework = join(host, 'assets', 'framework');
|
||||||
|
mkdirSync(framework, { recursive: true });
|
||||||
|
writeFileSync(
|
||||||
|
join(host, 'package.json'),
|
||||||
|
JSON.stringify({ name: 'YouleNexus', uuid: 'host-uuid', creator: { version: '3.8.8' } }, null, 2)
|
||||||
|
);
|
||||||
|
writeFileSync(join(framework, 'README.md'), '# framework src\n');
|
||||||
|
const games = join(root, 'games');
|
||||||
|
mkdirSync(games, { recursive: true });
|
||||||
|
return { root, host, framework, games };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在 gamesDir 下造一个子游戏工程(含 package.json)
|
||||||
|
export function makeGameProject(gamesDir, name, version = '3.8.8') {
|
||||||
|
const dir = join(gamesDir, name);
|
||||||
|
mkdirSync(join(dir, 'assets'), { recursive: true });
|
||||||
|
writeFileSync(
|
||||||
|
join(dir, 'package.json'),
|
||||||
|
JSON.stringify({ name, uuid: `${name}-uuid`, creator: { version } }, null, 2)
|
||||||
|
);
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 造一个最小种子工程
|
||||||
|
export function makeSeed(root) {
|
||||||
|
const seed = join(root, 'templates', 'game-seed');
|
||||||
|
mkdirSync(join(seed, 'assets'), { recursive: true });
|
||||||
|
mkdirSync(join(seed, 'settings'), { recursive: true });
|
||||||
|
writeFileSync(
|
||||||
|
join(seed, 'package.json'),
|
||||||
|
JSON.stringify({ name: 'game-seed', uuid: 'seed-uuid', creator: { version: '3.8.8' } }, null, 2)
|
||||||
|
);
|
||||||
|
writeFileSync(join(seed, 'settings', 'v2.json'), '{}\n');
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cleanup(root) {
|
||||||
|
rmSync(root, { recursive: true, force: true });
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { test } from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
import { writeFileSync, mkdirSync } from 'node:fs';
|
||||||
|
import { makeTempMonorepo, makeGameProject, cleanup } from './helpers.mjs';
|
||||||
|
import { readCreatorVersion, listGameProjects } from '../lib/paths.mjs';
|
||||||
|
|
||||||
|
test('readCreatorVersion 读取 package.json 的 creator.version', () => {
|
||||||
|
const mono = makeTempMonorepo();
|
||||||
|
try {
|
||||||
|
assert.equal(readCreatorVersion(mono.host), '3.8.8');
|
||||||
|
} finally {
|
||||||
|
cleanup(mono.root);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('readCreatorVersion 缺字段返回 null', () => {
|
||||||
|
const mono = makeTempMonorepo();
|
||||||
|
try {
|
||||||
|
const dir = makeGameProject(mono.games, 'nover');
|
||||||
|
// 覆写成无 creator 字段
|
||||||
|
writeFileSync(join(dir, 'package.json'), JSON.stringify({ name: 'nover' }));
|
||||||
|
assert.equal(readCreatorVersion(dir), null);
|
||||||
|
} finally {
|
||||||
|
cleanup(mono.root);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('listGameProjects 只列出含 package.json 的子目录', () => {
|
||||||
|
const mono = makeTempMonorepo();
|
||||||
|
try {
|
||||||
|
makeGameProject(mono.games, 'doudizhu');
|
||||||
|
makeGameProject(mono.games, 'niuniu');
|
||||||
|
mkdirSync(join(mono.games, 'not-a-project')); // 无 package.json,应被忽略
|
||||||
|
const found = listGameProjects(mono.games).map((p) => p.split(/[\\/]/).pop()).sort();
|
||||||
|
assert.deepEqual(found, ['doudizhu', 'niuniu']);
|
||||||
|
} finally {
|
||||||
|
cleanup(mono.root);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('listGameProjects 对不存在的目录返回空数组', () => {
|
||||||
|
assert.deepEqual(listGameProjects('/no/such/dir'), []);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user