rules/eslint/closure-no-unreachable-file.eslint.rule.ts
rules/eslint/closure-no-unreachable-file.eslint.rule.ts is a file in GovLab Extension Host. 118 lines of code and 24 definitions.
import type { LocalRule, RuleContext, RuleListener } from "../../types/rule.types.ts";
import { MEMBER_ROOT, isSourceFile, normalizePath } from "../../shared/resolvers/anchor.resolver.ts";
import { loadClosureGraph, normalizeImport } from "../../shared/loaders/graph.loader.ts";
import type { ClosureGraph } from "../../types/closure.types.ts";
import { STAGED_FUTURE_ALLOWLIST } from "../../shared/allowlists/export.allowlist.ts";
import { concernSuffix } from "../../shared/manifests/taxonomy.manifest.ts";
import { defineCheck } from "@govlab/context/check";
import { listener } from "../../shared/factories/listener.factory.ts";
const ENTRYPOINT_SUFFIX = concernSuffix("entrypoint");
const buildGraph = function buildGraph(closure: ClosureGraph): Map<string, string[]> {
const graph = new Map<string, string[]>();
const add = function add(file: string, from: string): void {
const target = normalizeImport(file, from);
if (target === null) {
return;
}
const targets = graph.get(file) ?? [];
targets.push(target);
graph.set(file, targets);
};
for (const imp of closure.imports) {
add(imp.file, imp.from);
}
for (const imp of closure.sideEffectImports) {
add(imp.file, imp.from);
}
return graph;
};
const seedsOf = function seedsOf(closure: ClosureGraph): string[] {
const files = new Set<string>();
for (const e of closure.exports) {
files.add(e.file);
}
for (const i of closure.imports) {
files.add(i.file);
}
for (const i of closure.sideEffectImports) {
files.add(i.file);
}
const seeds = [...files].filter((f) => f.endsWith(ENTRYPOINT_SUFFIX));
for (const imp of closure.externalConsumers) {
const target = normalizeImport(imp.file, imp.from);
if (target !== null) {
seeds.push(target);
}
}
return seeds;
};
const computeReachable = function computeReachable(closure: ClosureGraph): Set<string> {
const graph = buildGraph(closure);
const stack = seedsOf(closure);
const reachable = new Set<string>();
while (stack.length > 0) {
const file = stack.pop();
if (file === undefined || reachable.has(file)) {
continue;
}
reachable.add(file);
stack.push(...(graph.get(file) ?? []));
}
return reachable;
};
const GRAPH = loadClosureGraph();
const REACHABLE = GRAPH === null ? null : computeReachable(GRAPH);
const EXCLUDED = new Set(STAGED_FUTURE_ALLOWLIST.map((entry) => entry.file));
const MEMBER_PREFIX = ((): string => {
const norm = normalizePath(MEMBER_ROOT);
return norm.endsWith("/") ? norm.slice(0, -1) : norm;
})();
const toRelative = function toRelative(filename: string): string {
const norm = normalizePath(filename);
return norm.startsWith(MEMBER_PREFIX) ? norm.slice(MEMBER_PREFIX.length + 1) : norm;
};
const isInsideMember = function isInsideMember(filename: string): boolean {
return normalizePath(filename).startsWith(`${MEMBER_PREFIX}/`);
};
export default {
create(context: RuleContext): RuleListener {
const { filename } = context;
if (!isInsideMember(filename) || !isSourceFile(filename)) {
return {};
}
const rel = toRelative(filename);
if (EXCLUDED.has(rel) || rel.endsWith(".d.ts")) {
return {};
}
if (REACHABLE === null) {
return listener({
program(_view, node) {
context.report({ messageId: "graphMissing", node });
},
});
}
if (REACHABLE.has(rel)) {
return {};
}
return listener({
program(_view, node) {
context.report({ data: { file: rel }, messageId: "unreachable", node });
},
});
},
meta: {
docs: {
checks: defineCheck({ detects: ["architecture:zombie-code"], enforces: [] }),
description:
"Every file in the governed member must be reachable from a declared entrypoint, a centralized test, a build config, or a glob barrel. The export-level liveness rules run per export and are silenced by the staged-future allowlist, so a file whose every export is allowlisted disappears from their view entirely — an unwired subsystem then passes the gate. This rule asserts the file itself is connected. The allowlist may exempt a file, but `closure-allowlist-entries-are-wired` holds every entry to the same bar: a whole-file entry nothing imports is reported as unwired, so the list cannot quietly accumulate.",
},
messages: {
graphMissing:
"The closure graph is missing, so this rule fails closed rather than passing vacuously. Run the gate, whose auto-fix stage rebuilds the graph before linting reads it.",
unreachable:
"File `{{ file }}` is not reachable from any entrypoint, centralized test, build config, or glob barrel. Nothing imports it, directly or transitively. Wire it to a consumer or delete it — an unreachable file is not staged capability, it is code that cannot run.",
},
schema: [],
type: "problem",
},
} satisfies LocalRule;