chore: remove redundant asset generation scripts

This commit is contained in:
kdletters
2026-05-22 16:35:35 +08:00
parent b89d4940a9
commit 2ba4691bc0
65 changed files with 0 additions and 20353 deletions

View File

@@ -1,321 +0,0 @@
import {spawn} from 'node:child_process';
import {existsSync} from 'node:fs';
import {cp, mkdir, readdir, rm, stat} from 'node:fs/promises';
import path from 'node:path';
import {fileURLToPath} from 'node:url';
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
const REPO_ROOT = path.resolve(SCRIPT_DIR, '..');
const MODULE_PATH = path.join(REPO_ROOT, 'server-rs', 'crates', 'spacetime-module');
const TARGETS = [
{
name: 'Rust',
lang: 'rust',
tempName: 'rs',
outDir: path.join(
REPO_ROOT,
'server-rs',
'crates',
'spacetime-client',
'src',
'module_bindings',
),
entryFile: path.join(
REPO_ROOT,
'server-rs',
'crates',
'spacetime-client',
'src',
'module_bindings.rs',
),
},
];
const args = new Set(process.argv.slice(2));
const KNOWN_ARGS = new Set(['--rust-only']);
for (const arg of args) {
if (!KNOWN_ARGS.has(arg)) {
console.error(`[spacetime:generate] 未知参数: ${arg}`);
process.exit(1);
}
}
if (!existsSync(path.join(MODULE_PATH, 'Cargo.toml'))) {
console.error(`[spacetime:generate] 未找到模块: ${MODULE_PATH}`);
process.exit(1);
}
const tempRoot = resolveTempRoot();
assertSafeTempRoot(tempRoot);
const selectedTargets = TARGETS.filter((target) => shouldRunTarget(target.lang));
if (selectedTargets.length === 0) {
console.error('[spacetime:generate] 没有需要生成的目标。');
process.exit(1);
}
await mkdir(tempRoot, {recursive: true});
for (const target of selectedTargets) {
const tempOutDir = path.join(tempRoot, target.tempName);
await recreateTempDir(tempOutDir);
console.log(`[spacetime:generate] 生成 ${target.name} bindings 到短路径: ${tempOutDir}`);
await generateBindings(target, tempOutDir);
const fileCount = await countFiles(tempOutDir);
if (fileCount === 0) {
throw new Error(`${target.name} bindings 未生成任何文件。`);
}
console.log(`[spacetime:generate] 同步 ${fileCount} 个文件到 ${target.outDir}`);
await replaceGeneratedDir(tempOutDir, target.outDir);
await moveGeneratedEntryFile(target);
}
await rm(tempRoot, {recursive: true, force: true});
console.log('[spacetime:generate] bindings 生成完成。');
function shouldRunTarget(lang) {
if (args.has('--rust-only')) {
return lang === 'rust';
}
return true;
}
function resolveTempRoot() {
if (process.env.GENARRATIVE_BINDGEN_TEMP_ROOT) {
return path.resolve(process.env.GENARRATIVE_BINDGEN_TEMP_ROOT);
}
// Windows 下 SpacetimeDB CLI 2.1.0 会把所有生成文件路径一次性传给 formatter
// Rust bindings 文件数较多,输出到仓库深目录时容易触发 CreateProcess 路径总长限制。
if (process.platform === 'win32') {
return path.join(path.parse(REPO_ROOT).root, '.genarrative-bindgen');
}
return path.join(REPO_ROOT, 'tmp', 'spacetime-bindgen');
}
async function recreateTempDir(dir) {
assertInside(dir, tempRoot, '临时生成目录');
await rm(dir, {recursive: true, force: true});
await mkdir(dir, {recursive: true});
}
async function replaceGeneratedDir(fromDir, toDir) {
assertInside(toDir, REPO_ROOT, '仓库生成目录');
await rm(toDir, {recursive: true, force: true});
await mkdir(toDir, {recursive: true});
const entries = await readdir(fromDir, {withFileTypes: true});
for (const entry of entries) {
await cp(path.join(fromDir, entry.name), path.join(toDir, entry.name), {
recursive: true,
force: true,
});
}
}
async function moveGeneratedEntryFile(target) {
if (!target.entryFile) {
return;
}
assertInside(target.entryFile, REPO_ROOT, '生成入口文件');
const generatedModFile = path.join(target.outDir, 'mod.rs');
if (!existsSync(generatedModFile)) {
throw new Error(`${target.name} bindings 缺少入口文件: ${generatedModFile}`);
}
await rm(target.entryFile, {force: true});
await cp(generatedModFile, target.entryFile, {force: true});
await rm(generatedModFile, {force: true});
}
function assertInside(candidate, parent, label) {
const relative = path.relative(path.resolve(parent), path.resolve(candidate));
if (relative === '' || relative.startsWith('..') || path.isAbsolute(relative)) {
throw new Error(`${label} 不在预期目录内: ${candidate}`);
}
}
function assertSafeTempRoot(dir) {
const resolved = path.resolve(dir);
const parsed = path.parse(resolved);
const basename = path.basename(resolved).toLowerCase();
if (resolved === path.resolve(REPO_ROOT) || resolved === parsed.root) {
throw new Error(`临时根目录不允许指向仓库或磁盘根目录: ${resolved}`);
}
if (!basename.includes('bindgen')) {
throw new Error(`临时根目录必须是明确的 bindings 生成目录: ${resolved}`);
}
}
function buildGenerateArgs(target, outDir) {
const generateArgs = [
'generate',
'--no-config',
'--lang',
target.lang,
'--out-dir',
outDir,
'--module-path',
MODULE_PATH,
'--include-private',
'--yes',
];
return generateArgs;
}
async function generateBindings(target, outDir) {
const result = await run('spacetime', buildGenerateArgs(target, outDir), {
allowGeneratedFormatFailure: target.lang === 'rust',
});
if (result.generatedFormatFailed) {
// Windows 下 SpacetimeDB CLI 2.1.0 会把所有 Rust 文件一次性传给 formatter
// 这里只接管“文件已生成但 CLI 格式化失败”的尾段,并仍然只同步生成目录。
console.warn(
`[spacetime:generate] ${target.name} bindings 已生成,但 SpacetimeDB CLI 自带格式化失败;改用短路径分批 rustfmt。`,
);
await formatRustBindings(outDir);
}
}
async function formatRustBindings(outDir) {
const rustFiles = await collectRustFiles(outDir);
if (rustFiles.length === 0) {
throw new Error(`Rust bindings 未生成任何 .rs 文件,无法格式化: ${outDir}`);
}
for (const chunk of chunkCommandArgs(rustFiles)) {
await run('rustfmt', ['--edition', '2024', ...chunk]);
}
}
async function collectRustFiles(dir) {
const files = [];
const entries = await readdir(dir, {withFileTypes: true});
for (const entry of entries) {
const entryPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
files.push(...(await collectRustFiles(entryPath)));
continue;
}
if (entry.isFile() && entry.name.endsWith('.rs')) {
files.push(entryPath);
}
}
return files;
}
function chunkCommandArgs(argsToChunk) {
// Windows CreateProcess 受命令行长度限制;分批能避免 bindings 文件变多后再次失败。
const maxCommandLineChars = process.platform === 'win32' ? 20_000 : 100_000;
const chunks = [];
let current = [];
let currentLength = 0;
for (const arg of argsToChunk) {
const argLength = arg.length + 3;
if (current.length > 0 && currentLength + argLength > maxCommandLineChars) {
chunks.push(current);
current = [];
currentLength = 0;
}
current.push(arg);
currentLength += argLength;
}
if (current.length > 0) {
chunks.push(current);
}
return chunks;
}
function run(command, commandArgs, options = {}) {
return new Promise((resolve, reject) => {
const child = spawn(command, commandArgs, {
cwd: REPO_ROOT,
env: process.env,
shell: false,
stdio: ['ignore', 'pipe', 'pipe'],
});
let output = '';
child.stdout.on('data', (chunk) => {
const text = chunk.toString();
output += text;
process.stdout.write(text);
});
child.stderr.on('data', (chunk) => {
const text = chunk.toString();
output += text;
process.stderr.write(text);
});
child.on('error', reject);
child.on('exit', (code, signal) => {
if (signal) {
reject(new Error(`${command} 被信号中断: ${signal}`));
return;
}
const generatedFormatFailed = output.includes('Could not format generated files');
if (generatedFormatFailed && options.allowGeneratedFormatFailure) {
console.warn(`[spacetime:generate] ${command} generated files but formatting failed; continuing with validation.`);
resolve({generatedFormatFailed});
return;
}
if (generatedFormatFailed) {
reject(new Error(`${command} generated files but formatting failed.`));
return;
}
if (code === 0) {
resolve({generatedFormatFailed: false});
return;
}
reject(new Error(`${command} 退出码: ${code ?? 'unknown'}`));
});
});
}
async function countFiles(dir) {
let count = 0;
const entries = await readdir(dir, {withFileTypes: true});
for (const entry of entries) {
const entryPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
count += await countFiles(entryPath);
continue;
}
if (entry.isFile() || (await stat(entryPath)).isFile()) {
count += 1;
}
}
return count;
}