import fg from "fast-glob"; import { collectMeasureStructure } from "../internal/measure/structure/structure"; import { collectMeasureVersions } from "../internal/measure/versions/versions"; import { type CommandResult, silentFailure, success } from "../lib/command-result"; import { findMissingComponentTests } from "./component-tests"; export interface VerifyConfig { modulesRoot: string; appsRoot: string; cwd: string; installedVersion: string; } export interface CheckOutcome { name: string; ok: boolean; } /** Pass only when every check passes. */ export function verifyResult(outcomes: CheckOutcome[]): CommandResult { return outcomes.every((o) => o.ok) ? success() : silentFailure(1); } function section(title: string): void { console.log(`\n=== ${title} ===`); } /** * Run the deterministic checks and exit non-zero on any violation: * * - versions — version consistency across package manifests * - structure — required directories and file naming rules * - component-tests — every app component has a co-located test */ export async function runVerify(config: VerifyConfig): Promise { const { modulesRoot, appsRoot, cwd, installedVersion } = config; const outcomes: CheckOutcome[] = []; const moduleDirs = await fg(`${modulesRoot}/*`, { cwd, onlyDirectories: true, deep: 1 }); const appDirs = await fg(`${appsRoot}/*`, { cwd, onlyDirectories: true, deep: 1 }); section("versions"); const versions = await collectMeasureVersions({ appsRoot, modulesRoot, cwd, installedVersion }); if (versions.status === "ok") { console.log(" ok"); } else { for (const f of versions.findings.filter((f) => f.severity !== "info")) { console.log(` ${f.severity}: ${f.subject} — ${f.message}`); } } outcomes.push({ name: "versions", ok: versions.status === "ok" }); section("structure"); const structureViolations = []; if (moduleDirs.length > 0) { const s = await collectMeasureStructure({ modulesRoot }, cwd); structureViolations.push(...(s.modules?.violations ?? [])); } for (const appDir of appDirs) { const s = await collectMeasureStructure({ appRoot: appDir }, cwd); structureViolations.push(...(s.apps?.violations ?? [])); } if (structureViolations.length === 0) { console.log(" ok"); } else { for (const v of structureViolations) { console.log(` ${v.type}: ${v.path}${v.rule ? ` (${v.rule})` : ""}`); } } outcomes.push({ name: "structure", ok: structureViolations.length === 0 }); section("component-tests"); const missingTests = []; for (const appDir of appDirs) { missingTests.push(...(await findMissingComponentTests(appDir, cwd))); } if (missingTests.length === 0) { console.log(" ok"); } else { for (const m of missingTests) { console.log( ` missing-component-test: ${m.sourcePath} (add ${m.expectedTest}, or mark the file /* no-test: */)`, ); } } outcomes.push({ name: "component-tests", ok: missingTests.length === 0 }); section("verify summary"); for (const o of outcomes) { console.log(` ${o.ok ? "PASS" : "FAIL"} ${o.name}`); } return verifyResult(outcomes); }