55 lines
2.3 KiB
TypeScript
55 lines
2.3 KiB
TypeScript
import { spawnSync } from "node:child_process";
|
|
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
const sourceFiles = collectSourceFiles(path.join(rootDir, "src"));
|
|
let explicitAnyCount = 0;
|
|
for (const file of sourceFiles) {
|
|
const text = readFileSync(file, "utf8");
|
|
const stripped = stripComments(text);
|
|
const matches = [
|
|
...stripped.matchAll(/:\s*any\b/gu),
|
|
...stripped.matchAll(/<\s*any\s*>/gu),
|
|
...stripped.matchAll(/as\s+any\b/gu),
|
|
...stripped.matchAll(/Array\s*<\s*any\s*>/gu)
|
|
];
|
|
if (matches.length > 0) {
|
|
explicitAnyCount += matches.length;
|
|
console.error(` ${path.relative(rootDir, file)}: ${matches.length} explicit any usage(s)`);
|
|
}
|
|
}
|
|
if (explicitAnyCount > 0) {
|
|
console.error(`hwlab-cloud-web tsc-check: ${explicitAnyCount} explicit any usage(s) in React source`);
|
|
process.exit(2);
|
|
}
|
|
|
|
const tscBin = path.join(rootDir, "node_modules/.bin/tsc");
|
|
const tscArgs = ["--noEmit", "--project", path.join(rootDir, "tsconfig.json"), "--strict"];
|
|
const tscResult = spawnSync(tscBin, tscArgs, { cwd: rootDir, encoding: "utf8" });
|
|
if (tscResult.stdout) process.stdout.write(tscResult.stdout);
|
|
if (tscResult.stderr) process.stderr.write(tscResult.stderr);
|
|
if (tscResult.error) console.error(`hwlab-cloud-web tsc-check: failed to start ${tscBin}: ${tscResult.error.message}`);
|
|
if (tscResult.status !== 0) {
|
|
console.error(`hwlab-cloud-web tsc-check: strict TypeScript failed with exit ${tscResult.status ?? "unknown"}`);
|
|
process.exit(tscResult.status ?? 2);
|
|
}
|
|
console.log("hwlab-cloud-web tsc-check: passed (strict React TSX, 0 explicit any)");
|
|
|
|
function collectSourceFiles(dir: string): string[] {
|
|
const out: string[] = [];
|
|
for (const entry of readdirSync(dir)) {
|
|
if (entry === "node_modules" || entry === "dist" || entry.startsWith(".")) continue;
|
|
const full = path.join(dir, entry);
|
|
const stat = statSync(full);
|
|
if (stat.isDirectory()) out.push(...collectSourceFiles(full));
|
|
else if (/\.(ts|tsx)$/u.test(full)) out.push(full);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function stripComments(text: string): string {
|
|
return text.replace(/\/\*[\s\S]*?\*\//gu, "").replace(/(^|[^:])\/\/.*$/gmu, "$1");
|
|
}
|