Files
clean-architecture-frontend…/scripts/check-optional-recipes.mjs
T

75 lines
2.4 KiB
JavaScript

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`,
);