import { mkdir, readFile, stat, writeFile } from "node:fs/promises"; import { scanOptionalRecipeSources, scanProductionBundle, validateRecipeCatalog, } from "./lib/optional-recipes.mjs"; /** @param {string} name @param {string} fallback */ const argument = (name, fallback) => { const index = process.argv.indexOf(name); return index === -1 ? fallback : process.argv[index + 1]; }; const catalogPath = argument( "--catalog", "config/recipes/frontend-capability-recipes.json", ); const sourceRoot = argument("--source-root", "src"); const distRoot = argument("--dist-root", "dist"); const artifactPath = argument( "--artifact", "artifacts/quality/optional-recipes.json", ); const requireDist = process.argv.includes("--require-dist"); const catalog = JSON.parse(await readFile(catalogPath, "utf8")); const packageDocument = JSON.parse(await readFile("package.json", "utf8")); const catalogViolations = validateRecipeCatalog(catalog, packageDocument); const sourceViolations = await scanOptionalRecipeSources(sourceRoot); const bundlePresent = await stat(`${distRoot}/.vite/manifest.json`) .then(() => true) .catch(() => false); const bundleViolations = await scanProductionBundle(distRoot); const violations = [ ...catalogViolations.map((ruleId) => ({ ruleId, path: catalogPath })), ...sourceViolations, ...bundleViolations.map((path) => ({ ruleId: "UNSELECTED_RECIPE_IN_PRODUCTION_BUNDLE", path, })), ...(requireDist && !bundlePresent ? [{ ruleId: "PRODUCTION_BUNDLE_MISSING", path: distRoot }] : []), ]; const report = { schemaVersion: 1, decisionId: "VD-10", selectedCapabilities: [], recipeCount: Array.isArray(catalog.recipes) ? catalog.recipes.length : 0, productionRuntimeDependencies: catalog.productionRuntimeDependencies ?? null, bundleStatus: bundlePresent ? bundleViolations.length === 0 ? "PASS" : "FAIL" : "NOT_BUILT", violations, passed: violations.length === 0, }; await mkdir("artifacts/quality", { recursive: true }); await writeFile(artifactPath, `${JSON.stringify(report, null, 2)}\n`); if (violations.length > 0) { process.stderr.write( `Optional recipe contract failed:\n${violations .map((violation) => `${violation.ruleId}: ${violation.path}`) .join("\n")}\n`, ); process.exit(1); } process.stdout.write( `Optional recipes: PASS (${report.recipeCount} recipe-only capabilities, bundle=${report.bundleStatus})\n`, );