fix(core-eslint): fail loudly on unparseable manifest in wiring gate
usecase-must-be-wired returned {} when the manifest parsed to null,
silently disabling the error-level gate when the manifest existed but
could not be read. It now reports unparseableManifest on Program in
that case; a genuinely missing manifest stays a no-op (that is
feature-must-have-manifest's job).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import fs from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { parseManifestUseCases } from "./_manifest-ast.js";
|
import { parseManifestUseCases } from "./_manifest-ast.js";
|
||||||
import {
|
import {
|
||||||
@@ -39,6 +40,8 @@ export default {
|
|||||||
messages: {
|
messages: {
|
||||||
missing:
|
missing:
|
||||||
'Use case "{{useCase}}" is declared in {{feature}}.manifest.ts but not wired through wireUseCase({ name: "{{useCase}}", ... }) in this binder. Add the wireUseCase call or remove the manifest entry.',
|
'Use case "{{useCase}}" is declared in {{feature}}.manifest.ts but not wired through wireUseCase({ name: "{{useCase}}", ... }) in this binder. Add the wireUseCase call or remove the manifest entry.',
|
||||||
|
unparseableManifest:
|
||||||
|
"feature.manifest.ts for {{feature}} exists but could not be parsed by the conformance rules; the wiring gate cannot run. Fix the manifest shape (defineFeature({...} as const)).",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
create(context) {
|
create(context) {
|
||||||
@@ -49,8 +52,23 @@ export default {
|
|||||||
|
|
||||||
const featureRoot = featureRootForFile(filename, repoRoot);
|
const featureRoot = featureRootForFile(filename, repoRoot);
|
||||||
if (!featureRoot) return {};
|
if (!featureRoot) return {};
|
||||||
const manifest = parseManifestUseCases(manifestPathForFeature(featureRoot));
|
const manifestPath = manifestPathForFeature(featureRoot);
|
||||||
if (!manifest) return {};
|
const manifest = parseManifestUseCases(manifestPath);
|
||||||
|
if (!manifest) {
|
||||||
|
// An unparseable manifest must FAIL the gate, not silently disable it.
|
||||||
|
if (fs.existsSync(manifestPath)) {
|
||||||
|
return {
|
||||||
|
Program(node) {
|
||||||
|
context.report({
|
||||||
|
node,
|
||||||
|
messageId: "unparseableManifest",
|
||||||
|
data: { feature: path.basename(featureRoot) },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
const declaredNames = Object.keys(manifest);
|
const declaredNames = Object.keys(manifest);
|
||||||
if (declaredNames.length === 0) return {};
|
if (declaredNames.length === 0) return {};
|
||||||
const featureName = path.basename(featureRoot);
|
const featureName = path.basename(featureRoot);
|
||||||
|
|||||||
@@ -153,6 +153,35 @@ describe("usecase-must-be-wired", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("reports unparseableManifest when the manifest exists but cannot be parsed", () => {
|
||||||
|
const { repoRoot, binderFile } = makeFixture({
|
||||||
|
manifestUseCases: {},
|
||||||
|
binderBody: `export function bindProductionDemo(ctx) {}`,
|
||||||
|
});
|
||||||
|
// Overwrite the manifest with a shape the AST walker cannot read
|
||||||
|
// (no defineFeature call) — the gate must fail loudly, not no-op.
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(repoRoot, "packages", "demo", "src", "feature.manifest.ts"),
|
||||||
|
`export const demoManifest = { name: "demo" };`,
|
||||||
|
);
|
||||||
|
tester.run("usecase-must-be-wired", rule, {
|
||||||
|
valid: [],
|
||||||
|
invalid: [
|
||||||
|
{
|
||||||
|
filename: binderFile,
|
||||||
|
code: fs.readFileSync(binderFile, "utf8"),
|
||||||
|
options: [{ repoRoot }],
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
messageId: "unparseableManifest",
|
||||||
|
data: { feature: "demo" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("is a no-op when the feature manifest declares no use cases", () => {
|
it("is a no-op when the feature manifest declares no use cases", () => {
|
||||||
const { repoRoot, binderFile } = makeFixture({
|
const { repoRoot, binderFile } = makeFixture({
|
||||||
manifestUseCases: {},
|
manifestUseCases: {},
|
||||||
|
|||||||
Reference in New Issue
Block a user