96 lines
2.7 KiB
JavaScript
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;
|
|
}
|