# core/formatters/report.formatter.ts

> 59 lines of code and 13 definitions.

Tree: GovLab Quality
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/quality#file-quality-core-formatters-report-formatter-ts
Source text: https://banes-lab.com/source/quality/core/formatters/report.formatter.ts.txt

Listed in [core/formatters](https://banes-lab.com/api/source/quality/core/formatters.md), after [core/formatters/dependency.formatter.ts](https://banes-lab.com/source/quality/core/formatters/dependency.formatter.ts.md) and before [core/formatters/stylelint.formatter.ts](https://banes-lab.com/source/quality/core/formatters/stylelint.formatter.ts.md).

## Definitions

- `formatFindings` (lexical_declaration, line 33, exported)
- `groupByFile` (lexical_declaration, line 6)
- `markOf` (lexical_declaration, line 14)
- `JSON_INDENT` (lexical_declaration, line 4)
- `byFile` (lexical_declaration, line 7)
- `countsOf` (lexical_declaration, line 21)
- `out` (lexical_declaration, line 34, exported)
- `message` (lexical_declaration, line 38, exported)
- `renderHuman` (lexical_declaration, line 48, exported)
- `body` (lexical_declaration, line 53, exported)
- `renderJson` (lexical_declaration, line 62, exported)
- `{ advisory, errors, notices }` (lexical_declaration, line 63, exported)
- `summary` (lexical_declaration, line 64, exported)

## Contained in

- [core/formatters](https://banes-lab.com/anatomy/quality/folder-quality-core-formatters.md)

## Used by

- [core/factories/finding.factory.ts](https://banes-lab.com/source/quality/core/factories/finding.factory.ts.md)

## Linked from

- [core/factories](https://banes-lab.com/anatomy/quality/folder-quality-core-factories.md)
- [core/formatters](https://banes-lab.com/anatomy/quality/folder-quality-core-formatters.md)

## Source

```typescript
import type { Finding } from "#types/finding.types";
import { runSummary } from "#configuration/strings/tool.strings";

const JSON_INDENT = 4;

const groupByFile = function groupByFile(findings: readonly Finding[]): Map<string, Finding[]> {
    const byFile = new Map<string, Finding[]>();
    for (const finding of findings) {
        byFile.set(finding.file, [...(byFile.get(finding.file) ?? []), finding]);
    }
    return byFile;
};

const markOf = function markOf(finding: Finding): string {
    if (finding.severity === "notice") {
        return "notice";
    }
    return finding.advisory ? "advisory" : finding.severity;
};

const countsOf = function countsOf(findings: readonly Finding[]): {
    advisory: number;
    errors: number;
    notices: number;
} {
    return {
        advisory: findings.filter((finding) => finding.severity === "error" && finding.advisory).length,
        errors: findings.filter((finding) => finding.severity === "error" && !finding.advisory).length,
        notices: findings.filter((finding) => finding.severity === "notice").length,
    };
};

export const formatFindings = function formatFindings(findings: readonly Finding[]): string {
    const out: string[] = [];
    for (const [file, fileFindings] of groupByFile(findings)) {
        out.push(file);
        for (const finding of fileFindings) {
            const message = finding.message.split("\n").join("\n    ");
            out.push(
                `  ${String(finding.line)}:${String(finding.column)}  ${markOf(finding)}  ${message}  ${finding.ruleId} (${finding.tool})`,
            );
        }
        out.push("");
    }
    return out.join("\n").trimEnd();
};

export const renderHuman = function renderHuman(
    outputs: readonly string[],
    findings: readonly Finding[],
    fixedCount: number,
): string {
    const body = outputs
        .map((output) => output.trim())
        .filter((output) => output.length > 0)
        .join("\n\n");
    const { advisory, errors, notices } = countsOf(findings);
    const summary = runSummary(errors, advisory, notices, fixedCount);
    return body.length > 0 ? `${body}\n\n${summary}\n` : `${summary}\n`;
};

export const renderJson = function renderJson(findings: readonly Finding[], fixedCount: number): string {
    const { advisory, errors, notices } = countsOf(findings);
    const summary = { advisory, errors, fixed: fixedCount, notices, total: findings.length };
    return `${JSON.stringify({ findings, summary }, null, JSON_INDENT)}\n`;
};
```
