core/factories/finding.factory.ts

core/factories/finding.factory.ts is a file in GovLab Quality. 139 lines of code and 20 definitions.

import type { AdvisoryContext, TerminationSignal, ToolExit } from "#types/tool.types";
import type { AdvisoryFindingInput, Finding, NoticeSpec, PassOutcome, RunResult } from "#types/finding.types";
import {
    NO_STDERR_DETAIL,
    UNKNOWN_SIGNAL,
    signalDeathMessage,
    spawnErrorMessage,
    toolErrorMessage,
} from "#configuration/strings/tool.strings";
import { spawnDetail, tailLines } from "#core/selectors/failure.selector";
import { POSITION } from "#configuration/constants/tool.constants";
import { PROJECT_TOOL } from "#configuration/constants/validation.constants";
import { formatFindings } from "#core/formatters/report.formatter";

export const EMPTY_RESULT: RunResult = { findings: [], fixedCount: 0, output: "" };

export const reportedPass = function reportedPass(findings: Finding[]): PassOutcome {
    return { findings, terminal: false };
};

export const terminatedPass = function terminatedPass(result: RunResult): PassOutcome {
    return { result, terminal: true };
};

export const findingsResult = function findingsResult(findings: Finding[], fixedCount = 0): RunResult {
    return { findings, fixedCount, output: formatFindings(findings) };
};

export const fixedResult = function fixedResult(reported: readonly Finding[], residual: Finding[]): RunResult {
    return findingsResult(residual, Math.max(0, reported.length - residual.length));
};

export const toolFinding = function toolFinding(
    input: AdvisoryFindingInput & { advisory?: boolean; fixable?: boolean },
): Finding {
    return {
        advisory: input.advisory ?? false,
        column: input.column,
        ecosystem: input.ecosystem,
        file: input.file,
        fixable: input.fixable ?? false,
        line: input.line,
        message: input.message,
        ruleId: input.ruleId,
        severity: "error",
        tool: input.tool,
    };
};

export const projectFinding = function projectFinding(input: {
    file: string;
    message: string;
    ruleId: string;
    suggestion: string;
}): Finding {
    return {
        advisory: false,
        column: 0,
        ecosystem: "",
        file: input.file,
        fixable: false,
        line: 0,
        message: input.message,
        ruleId: input.ruleId,
        severity: "error",
        suggestion: input.suggestion,
        tool: PROJECT_TOOL,
    };
};

export const advisoryFinding = function advisoryFinding(input: AdvisoryFindingInput): Finding {
    return toolFinding({ ...input, advisory: true });
};

const buildNotice = function buildNotice(context: AdvisoryContext, spec: NoticeSpec): RunResult {
    const finding: Finding = {
        advisory: spec.advisory,
        column: POSITION,
        ecosystem: context.ecosystem,
        file: context.root,
        fixable: false,
        line: POSITION,
        message: spec.message,
        ruleId: spec.ruleId,
        severity: spec.severity,
        tool: context.tool,
    };
    return { findings: [finding], fixedCount: 0, output: spec.output ?? spec.message };
};

const failureSpec = function failureSpec(context: AdvisoryContext, ruleId: string, message: string): NoticeSpec {
    return { advisory: context.gating !== true, message, ruleId, severity: context.failureSeverity ?? "notice" };
};

export const advisoryNotice = function advisoryNotice(
    context: AdvisoryContext,
    ruleId: string,
    message: string,
): RunResult {
    return buildNotice(context, { advisory: true, message, ruleId, severity: "notice" });
};

export const gatingNotice = function gatingNotice(
    context: AdvisoryContext,
    ruleId: string,
    message: string,
): RunResult {
    return buildNotice(context, failureSpec(context, ruleId, message));
};

export const toolErrorRule = function toolErrorRule(tool: string): string {
    return `${tool}/tool-error`;
};

export const toolFailure = function toolFailure(context: AdvisoryContext, message: string): RunResult {
    return gatingNotice(context, toolErrorRule(context.tool), message);
};

export const notInstalled = function notInstalled(context: AdvisoryContext): RunResult {
    return buildNotice(context, {
        advisory: true,
        message: context.installHint,
        output: context.notInstalledOutput ?? context.installHint,
        ruleId: `${context.tool}/not-installed`,
        severity: "notice",
    });
};

export const spawnFailure = function spawnFailure(context: AdvisoryContext, error: Error): RunResult {
    const message = spawnErrorMessage(context.tool, spawnDetail(error));
    return buildNotice(context, failureSpec(context, `${context.tool}/spawn-error`, message));
};

export const toolError = function toolError(context: AdvisoryContext, exit: ToolExit): RunResult {
    const message = toolErrorMessage(context.tool, exit.status, tailLines(exit.stderr, NO_STDERR_DETAIL));
    return gatingNotice(context, toolErrorRule(context.tool), message);
};

export const signalDeathResult = function signalDeathResult(
    source: { ecosystem: string; root: string; tool: string },
    signal: TerminationSignal | null,
): RunResult {
    const message = signalDeathMessage(source.tool, signal ?? UNKNOWN_SIGNAL);
    const finding: Finding = {
        advisory: false,
        column: POSITION,
        ecosystem: source.ecosystem,
        file: source.root,
        fixable: false,
        line: POSITION,
        message,
        ruleId: toolErrorRule(source.tool),
        severity: "error",
        tool: source.tool,
    };
    return { findings: [finding], fixedCount: 0, output: message };
};