import path from "node:path"; import fg from "fast-glob"; import { ERP_KIT_PACKAGE_NAME } from "../../../../util"; import { type CommandResult, success } from "../../../lib/command-result"; import { readJsonFile } from "../../../lib/parse-json"; import { checkCompatRange, compat } from "./compat-range"; import { checkErpKitVersion } from "./erp-kit-version"; import { buildSpecifierResolver, ManifestSchema, type PnpmConfigReader, type VersionFinding, type WorkspacePackage, } from "./shared"; import { checkSkillsVersion } from "./skills-version"; export interface MeasureVersionsResult { status: "ok" | "violations"; violationCount: number; findings: VersionFinding[]; } async function discoverWorkspacePackages(cwd: string, appsRoot: string, modulesRoot: string) { const patterns = [ "package.json", `${appsRoot}/*/backend/package.json`, `${appsRoot}/*/frontend/package.json`, `${modulesRoot}/*/package.json`, ]; const matches = await fg(patterns, { cwd, onlyFiles: true, ignore: ["**/node_modules/**"], }); const packages: WorkspacePackage[] = []; const findings: VersionFinding[] = []; for (const relativePath of matches) { const result = readJsonFile(path.join(cwd, relativePath), ManifestSchema, relativePath); if (result.ok) { packages.push({ relativePath, manifest: result.data }); } else { findings.push({ severity: "error", check: "manifest-parse", subject: relativePath, message: result.error, }); } } return { packages, findings }; } export async function collectMeasureVersions({ appsRoot, modulesRoot, cwd, installedVersion, configReader, }: { appsRoot: string; modulesRoot: string; cwd: string; installedVersion: string; // Injectable pnpm-config source; defaults to shelling out to `pnpm config list --json`. configReader?: PnpmConfigReader; }): Promise { const { packages, findings } = await discoverWorkspacePackages(cwd, appsRoot, modulesRoot); const resolve = buildSpecifierResolver( cwd, packages, [ERP_KIT_PACKAGE_NAME, ...Object.keys(compat)], configReader, ); findings.push( ...checkCompatRange(packages, resolve), ...checkErpKitVersion(packages, installedVersion, resolve), ...(await checkSkillsVersion(cwd, installedVersion)), ); const violationCount = findings.filter((f) => f.severity !== "info").length; return { status: violationCount === 0 ? "ok" : "violations", violationCount, findings, }; } export async function runInternalMeasureVersions(config: { appsRoot: string; modulesRoot: string; cwd: string; installedVersion: string; }): Promise { const result = await collectMeasureVersions(config); console.log(JSON.stringify(result, null, 2)); return success(); }