core/analyzers/concern.analyzer.ts

core/analyzers/concern.analyzer.ts is a file in GovLab Patterns. 51 lines of code and 15 definitions.

import type { CallEdge, CodeFinding, CodeSymbol } from "#types/code.types";
import { FINDING_KINDS, REMEDY, crossConcernDetail } from "#configuration/strings/code.strings";
import { keyOf, scopeOf } from "#core/formatters/definition.formatter";
import { MEDIUM_SEVERITY } from "#configuration/constants/report.constants";
import { ROLE_DEFINITION } from "#core/classifiers/syntax.classifier";

const REL_CONCERN = 7;
const CONCERN_SPAN = 4;
const CONFIDENCE = "medium";
const PATH_SEP = "/";
const WRAPPER_DIRS: ReadonlySet<string> = new Set(["src", "internal", "lib", "pkg", "cmd", "app"]);
const FACTORY_PREFIXES: readonly string[] = ["create", "build", "make", "new", "setup", "register", "compose", "wire"];

const concernOf = function concernOf(file: string): string {
    const [top = "", next = ""] = file.split(PATH_SEP);
    return WRAPPER_DIRS.has(top) && file.includes(PATH_SEP) ? next : top;
};

const isFactory = function isFactory(name: string): boolean {
    const lower = name.toLowerCase();
    return FACTORY_PREFIXES.some((prefix) => lower.startsWith(prefix));
};

export const crossConcernFindings = function crossConcernFindings(
    symbols: readonly CodeSymbol[],
    edges: readonly CallEdge[],
): CodeFinding[] {
    const spans = new Map<string, Set<string>>();
    for (const edge of edges) {
        const concern = concernOf(scopeOf(edge.to));
        spans.set(edge.from, (spans.get(edge.from) ?? new Set<string>()).add(concern));
    }
    const callable = new Map(
        symbols
            .filter((symbol) => symbol.role === ROLE_DEFINITION && symbol.callable)
            .map((symbol) => [keyOf(symbol.file, symbol.name), symbol]),
    );
    return [...callable]
        .filter(([key, def]) => !isFactory(def.name) && (spans.get(key)?.size ?? 0) >= CONCERN_SPAN)
        .map(([key, def]) => {
            const span = [...(spans.get(key) ?? [])].sort((a, b) => a.localeCompare(b));
            return {
                confidence: CONFIDENCE,
                detail: crossConcernDetail(span.length),
                file: def.file,
                kind: FINDING_KINDS.crossConcern,
                line: def.line,
                members: span,
                name: def.name,
                relevance: REL_CONCERN,
                remedy: REMEDY.get(FINDING_KINDS.crossConcern) ?? "",
                severity: MEDIUM_SEVERITY,
            };
        });
};