games-development-ai/scripts/check-workspace-scripts.mjs
2026-06-02 20:50:36 +08:00

96 lines
2.7 KiB
JavaScript

import { readdir, readFile } from "node:fs/promises";
import path from "node:path";
const rootDir = process.cwd();
const requiredScripts = ["lint", "typecheck", "test", "build", "dev:smoke"];
// 只支持当前仓库声明的一层通配符,例如 apps/* 和 packages/*。
function parseWorkspacePatterns(source) {
return source
.split("\n")
.map((line) => line.trim())
.filter((line) => line.startsWith("- "))
.map((line) => line.slice(2).trim().replace(/^["']|["']$/g, ""));
}
async function pathExists(filePath) {
try {
await readFile(filePath, "utf8");
return true;
} catch (error) {
if (error && error.code === "ENOENT") {
return false;
}
throw error;
}
}
async function findWorkspaceManifests(patterns) {
const manifests = [];
for (const pattern of patterns) {
if (!pattern.endsWith("/*")) {
throw new Error(`不支持的 workspace pattern: ${pattern}`);
}
const baseDir = path.join(rootDir, pattern.slice(0, -2));
try {
const entries = await readdir(baseDir, { withFileTypes: true });
for (const entry of entries) {
if (!entry.isDirectory()) {
continue;
}
const manifestPath = path.join(baseDir, entry.name, "package.json");
if (await pathExists(manifestPath)) {
manifests.push(manifestPath);
}
}
} catch (error) {
if (error && error.code === "ENOENT") {
continue;
}
throw error;
}
}
return manifests;
}
async function readManifest(manifestPath) {
const raw = await readFile(manifestPath, "utf8");
try {
return JSON.parse(raw);
} catch (error) {
throw new Error(`${path.relative(rootDir, manifestPath)} 不是有效 JSON: ${error.message}`);
}
}
const workspaceConfigPath = path.join(rootDir, "pnpm-workspace.yaml");
const workspaceConfig = await readFile(workspaceConfigPath, "utf8");
const manifests = await findWorkspaceManifests(parseWorkspacePatterns(workspaceConfig));
const failures = [];
for (const manifestPath of manifests) {
const manifest = await readManifest(manifestPath);
const missingScripts = requiredScripts.filter((scriptName) => !manifest.scripts?.[scriptName]);
if (missingScripts.length > 0) {
failures.push({
packageName: manifest.name || path.relative(rootDir, path.dirname(manifestPath)),
manifestPath,
missingScripts
});
}
}
if (failures.length > 0) {
console.error("以下 workspace package 缺少必需脚本:");
for (const failure of failures) {
console.error(
`- ${failure.packageName} (${path.relative(rootDir, failure.manifestPath)}): ${failure.missingScripts.join(", ")}`
);
}
process.exitCode = 1;
}