codemods/entrypoints/sink.entrypoint.ts
codemods/entrypoints/sink.entrypoint.ts is a file in GovLab Extension Host. 80 lines of code and 29 definitions.
import { CODEMOD_TSCONFIGS, programFor, repoSourceFiles } from "../selectors/program.selector.ts";
import type { WriteFinding, WriteScope } from "../../types/analyzer.types.ts";
import { dirname, join } from "node:path";
import { existsSync, readFileSync } from "node:fs";
import type { Edit } from "../../types/codemod.types.ts";
import { ROOT } from "@ssot/paths";
import { applyCodemod } from "../selectors/codemod.selector.ts";
import { defineCheck } from "@govlab/context/check";
import { loadGovlabConfig } from "@govlab/quality/config";
import { scanSourceFile } from "../analyzers/sink.analyzer.ts";
const RULE_ID = "no-raw-file-write";
const RULE_KEY = "govlab-write/no-raw-file-write";
const WRITER_MODULE = "@govlab/canonical-write";
const MANIFEST = "package.json";
const isRecord = function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
};
const ownerModulesOf = function ownerModulesOf(rules: unknown): Set<string> {
const entry = isRecord(rules) ? rules[RULE_KEY] : undefined;
const options: unknown = Array.isArray(entry) ? entry.at(1) : undefined;
const modules = isRecord(options) ? options["modules"] : undefined;
return new Set(Array.isArray(modules) ? modules.filter((name): name is string => typeof name === "string") : []);
};
const nearestManifest = function nearestManifest(fileName: string): string | null {
let folder = dirname(fileName);
while (folder !== dirname(folder)) {
const candidate = join(folder, MANIFEST);
if (existsSync(candidate)) {
return candidate;
}
folder = dirname(folder);
}
return null;
};
const declaresWriter = function declaresWriter(fileName: string): boolean {
const manifest = nearestManifest(fileName);
if (manifest === null) {
return false;
}
const parsed: unknown = JSON.parse(readFileSync(manifest, "utf8"));
const dependencies = isRecord(parsed) ? parsed["dependencies"] : undefined;
return isRecord(dependencies) && WRITER_MODULE in dependencies;
};
const qualityConfig = await loadGovlabConfig(ROOT);
const writeScope: WriteScope = { declaresWriter, owners: ownerModulesOf(qualityConfig.eslint?.rules) };
const collect = function collect(): WriteFinding[] {
const seen = new Set<string>();
return CODEMOD_TSCONFIGS.flatMap((tsconfig) => {
const program = programFor(tsconfig);
const checker = program.getTypeChecker();
return repoSourceFiles(program).flatMap((source) => scanSourceFile(checker, source, writeScope));
}).filter((finding) => {
const key = `${finding.file}:${String(finding.start)}`;
const fresh = !seen.has(key);
seen.add(key);
return fresh;
});
};
const buildEdits = function buildEdits(findings: readonly WriteFinding[]): Map<string, Edit[]> {
const byFile = new Map<string, Edit[]>();
for (const finding of findings) {
const known = byFile.get(finding.fileName);
const imports = known === undefined ? finding.importEdits : [];
const edit: Edit = { end: finding.end, replacement: finding.replacement, start: finding.start };
byFile.set(finding.fileName, [...(known ?? []), ...imports, edit]);
}
return byFile;
};
applyCodemod({
appliedNoun: "raw file write(s) routed through the owner's verbatim writer",
blockedMessage: (finding) =>
`A raw file write outside the module that owns file writes skips what the owner guarantees. This write cannot be rewritten automatically (${finding.reason ?? ""}).`,
checks: defineCheck({ detects: [], enforces: ["architecture:idempotency", "architecture:determinism"] }),
editsByFile: buildEdits,
findings: collect(),
gateOnBlocked: false,
label: (finding) => finding.replacement,
programCount: CODEMOD_TSCONFIGS.length,
ruleId: RULE_ID,
});