paperclip/packages/paperclip-runner/scripts/lib/numbered-milestones.mjs

88 lines
2.2 KiB
JavaScript

import { execFile } from "node:child_process";
import { readFile } from "node:fs/promises";
import { extname, resolve } from "node:path";
import { promisify } from "node:util";
const execFileAsync = promisify(execFile);
const textExtensions = new Set([
".css",
".html",
".js",
".json",
".md",
".mjs",
".rs",
".sh",
".toml",
".ts",
".tsx",
".yaml",
".yml",
]);
const selfExcludedFiles = new Set([
"scripts/check-numbered-milestones.test.mjs",
"scripts/lib/numbered-milestones.mjs",
]);
export const defaultPackageRoot = resolve(
new URL("../..", import.meta.url).pathname,
);
export function findNumberedMilestoneNames(value) {
const matcher = new RegExp(String.raw`\bphases?[\s._/-]*0*\d+[a-z]?`, "gi");
return [...value.matchAll(matcher)].map((match) => ({
index: match.index ?? 0,
value: match[0],
}));
}
export async function listTrackedPackageFiles(packageRoot = defaultPackageRoot) {
const { stdout } = await execFileAsync("git", ["ls-files", "-z", "--", "."], {
cwd: packageRoot,
encoding: "utf8",
maxBuffer: 16 * 1024 * 1024,
});
return stdout.split("\0").filter(Boolean);
}
export async function checkNumberedMilestones({
packageRoot = defaultPackageRoot,
trackedFiles,
} = {}) {
const files = trackedFiles ?? await listTrackedPackageFiles(packageRoot);
const violations = [];
for (const relativePath of files) {
for (const match of findNumberedMilestoneNames(relativePath)) {
violations.push({
kind: "path",
file: relativePath,
line: 1,
value: match.value,
});
}
if (!textExtensions.has(extname(relativePath)) || selfExcludedFiles.has(relativePath)) {
continue;
}
const contents = await readFile(resolve(packageRoot, relativePath), "utf8");
for (const match of findNumberedMilestoneNames(contents)) {
violations.push({
kind: "content",
file: relativePath,
line: contents.slice(0, match.index).split("\n").length,
value: match.value,
});
}
}
return violations;
}
export function formatNumberedMilestoneViolation(violation) {
return `${violation.file}:${violation.line} contains numbered milestone label ${JSON.stringify(violation.value)}`;
}