codemods/entrypoints/identifier.entrypoint.ts

codemods/entrypoints/identifier.entrypoint.ts is a file in GovLab Extension Host. 51 lines of code and 13 definitions.

import { CODEMOD_TSCONFIGS, programFor, repoSourceFiles } from "../selectors/program.selector.ts";
import type { Edit } from "../../types/codemod.types.ts";
import type { IdentifierFinding } from "../../types/analyzer.types.ts";
import { applyCodemod } from "../selectors/codemod.selector.ts";
import { defineCheck } from "@govlab/context/check";
import { scanProgram } from "../analyzers/identifier.analyzer.ts";

const RULE_ID = "naming-convention";

const buildEdits = function buildEdits(findings: readonly IdentifierFinding[]): Map<string, Edit[]> {
    const byFile = new Map<string, Edit[]>();
    for (const finding of findings) {
        for (const location of finding.locations) {
            const edit: Edit = { end: location.end, replacement: finding.to, start: location.start };
            byFile.set(location.fileName, [...(byFile.get(location.fileName) ?? []), edit]);
        }
    }
    return byFile;
};

const locationKey = function locationKey(location: { fileName: string; start: number }): string {
    return `${location.fileName}:${String(location.start)}`;
};

const merged = function merged(
    existing: Readonly<IdentifierFinding>,
    addition: Readonly<IdentifierFinding>,
): IdentifierFinding {
    const seen = new Set(existing.locations.map(locationKey));
    const extra = addition.locations.filter((location) => !seen.has(locationKey(location)));
    return { ...existing, locations: [...existing.locations, ...extra], reason: addition.reason ?? existing.reason };
};

const collect = function collect(): IdentifierFinding[] {
    const byDeclaration = new Map<string, IdentifierFinding>();
    for (const tsconfig of CODEMOD_TSCONFIGS) {
        const program = programFor(tsconfig);
        for (const finding of scanProgram(program, repoSourceFiles(program))) {
            const key = `${finding.file}:${String(finding.line)}:${finding.from}`;
            const existing = byDeclaration.get(key);
            byDeclaration.set(key, existing === undefined ? finding : merged(existing, finding));
        }
    }
    return [...byDeclaration.values()];
};

applyCodemod({
    appliedNoun: "PascalCase const reference(s) renamed to camelCase",
    blockedMessage: (finding) => `'${finding.from}' cannot be renamed to '${finding.to}': ${finding.reason ?? ""}`,
    checks: defineCheck({ detects: [], enforces: ["architecture:standardization"] }),
    editsByFile: buildEdits,
    findings: collect(),
    gateOnBlocked: false,
    label: (finding) => `${finding.from} → ${finding.to}`,
    programCount: CODEMOD_TSCONFIGS.length,
    ruleId: RULE_ID,
});