feat: add optional frontend adapter recipes
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
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`,
|
||||
);
|
||||
Reference in New Issue
Block a user