Files
openclaw/scripts/check-ts-max-loc.ts

81 lines
2.1 KiB
TypeScript
Raw Normal View History

import { execFileSync } from "node:child_process";
2026-01-14 01:08:15 +00:00
import { existsSync } from "node:fs";
import { readFile } from "node:fs/promises";
type ParsedArgs = {
maxLines: number;
};
function parseArgs(argv: string[]): ParsedArgs {
let maxLines = 500;
for (let index = 0; index < argv.length; index++) {
const arg = argv[index];
if (arg === "--max") {
const next = argv[index + 1];
2026-01-31 21:29:14 +09:00
if (!next || Number.isNaN(Number(next))) {
throw new Error("Missing/invalid --max value");
}
2026-01-14 01:08:15 +00:00
maxLines = Number(next);
index++;
continue;
}
}
return { maxLines };
}
function gitLsFilesAll(): string[] {
// Include untracked files too so local refactors dont “pass” by accident.
const stdout = execFileSync("git", ["ls-files", "--cached", "--others", "--exclude-standard"], {
encoding: "utf8",
});
return stdout
.split("\n")
.map((line) => line.trim())
.filter(Boolean);
}
async function countLines(filePath: string): Promise<number> {
const content = await readFile(filePath, "utf8");
// Count physical lines. Keeps the rule simple + predictable.
return content.split("\n").length;
}
async function main() {
// Makes `... | head` safe.
process.stdout.on("error", (error: NodeJS.ErrnoException) => {
2026-01-31 21:29:14 +09:00
if (error.code === "EPIPE") {
process.exit(0);
}
2026-01-14 01:08:15 +00:00
throw error;
});
const { maxLines } = parseArgs(process.argv.slice(2));
const files = gitLsFilesAll()
.filter((filePath) => existsSync(filePath))
.filter((filePath) => filePath.endsWith(".ts") || filePath.endsWith(".tsx"));
const results = await Promise.all(
files.map(async (filePath) => ({ filePath, lines: await countLines(filePath) })),
);
const offenders = results
.filter((result) => result.lines > maxLines)
2026-01-31 21:29:14 +09:00
.toSorted((a, b) => b.lines - a.lines);
2026-01-14 01:08:15 +00:00
2026-01-31 21:29:14 +09:00
if (!offenders.length) {
return;
}
2026-01-14 01:08:15 +00:00
// Minimal, grep-friendly output.
for (const offender of offenders) {
// eslint-disable-next-line no-console
console.log(`${offender.lines}\t${offender.filePath}`);
}
process.exitCode = 1;
}
await main();