# core/parsers/tool.knip.parser.ts

> 67 lines of code and 14 definitions.

Tree: GovLab Quality
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/quality#file-quality-core-parsers-tool-knip-parser-ts
Source text: https://banes-lab.com/source/quality/core/parsers/tool.knip.parser.ts.txt

Listed in [core/parsers](https://banes-lab.com/api/source/quality/core/parsers.md), after [core/parsers/tool.kics.parser.ts](https://banes-lab.com/source/quality/core/parsers/tool.kics.parser.ts.md) and before [core/parsers/tool.lintr.parser.ts](https://banes-lab.com/source/quality/core/parsers/tool.lintr.parser.ts.md).

## Definitions

- `parseKnipOutput` (lexical_declaration, line 71, exported)
- `itemName` (lexical_declaration, line 25)
- `issueFindings` (lexical_declaration, line 49)
- `itemFinding` (lexical_declaration, line 33)
- `TOOL` (lexical_declaration, line 8)
- `FILE_RULE_ID` (lexical_declaration, line 9)
- `CATEGORY_KEYS` (lexical_declaration, line 10)
- `isUnusedFile` (lexical_declaration, line 21, exported)
- `record` (lexical_declaration, line 29)
- `located` (lexical_declaration, line 37)
- `file` (lexical_declaration, line 50)
- `unusedFile` (lexical_declaration, line 51)
- `items` (lexical_declaration, line 64)
- `value` (lexical_declaration, line 65)

## Contained in

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

## Uses

- [configuration/strings/tool.strings.ts](https://banes-lab.com/source/quality/configuration/strings/tool.strings.ts.md)
- [core/factories/finding.factory.ts](https://banes-lab.com/source/quality/core/factories/finding.factory.ts.md)
- [core/parsers/record.parser.ts](https://banes-lab.com/source/quality/core/parsers/record.parser.ts.md)
- [core/selectors/record.selector.ts](https://banes-lab.com/source/quality/core/selectors/record.selector.ts.md)

## Used by

- [core/adapters/tool.knip.adapter.ts](https://banes-lab.com/source/quality/core/adapters/tool.knip.adapter.ts.md)

## Linked from

- [configuration/strings](https://banes-lab.com/anatomy/quality/folder-quality-configuration-strings.md)
- [core/adapters](https://banes-lab.com/anatomy/quality/folder-quality-core-adapters.md)
- [core/factories](https://banes-lab.com/anatomy/quality/folder-quality-core-factories.md)
- [core/parsers](https://banes-lab.com/anatomy/quality/folder-quality-core-parsers.md)
- [core/selectors](https://banes-lab.com/anatomy/quality/folder-quality-core-selectors.md)

## Source

```typescript
import { UNUSED_FILE, UNUSED_LABELS, unusedItem } from "#configuration/strings/tool.strings";
import { isRecord, numberField, stringField } from "#core/selectors/record.selector";
import type { Finding } from "#types/finding.types";
import { POSITION } from "#configuration/constants/tool.constants";
import { jsonRecordsAt } from "#core/parsers/record.parser";
import { toolFinding } from "#core/factories/finding.factory";

const TOOL = "knip";
const FILE_RULE_ID = "knip/files";
const CATEGORY_KEYS = [
    "exports",
    "types",
    "dependencies",
    "devDependencies",
    "unlisted",
    "unresolved",
    "duplicates",
    "binaries",
];

export const isUnusedFile = function isUnusedFile(files: unknown): boolean {
    return Array.isArray(files) ? files.length > 0 : files === true;
};

const itemName = function itemName(item: unknown): string {
    if (typeof item === "string") {
        return item;
    }
    const record = isRecord(item) ? item : {};
    return stringField(record, "name") || stringField(record, "symbol");
};

const itemFinding = function itemFinding(
    item: unknown,
    context: { ecosystem: string; file: string; key: string },
): Finding {
    const located = isRecord(item) ? item : {};
    return toolFinding({
        column: numberField(located, "col", POSITION),
        ecosystem: context.ecosystem,
        file: context.file,
        line: numberField(located, "line", POSITION),
        message: unusedItem(UNUSED_LABELS[context.key] ?? context.key, itemName(item)),
        ruleId: `${TOOL}/${context.key}`,
        tool: TOOL,
    });
};

const issueFindings = function issueFindings(issue: Record<string, unknown>, ecosystem: string): Finding[] {
    const file = stringField(issue, "file");
    const unusedFile = isUnusedFile(issue["files"])
        ? [
              toolFinding({
                  column: POSITION,
                  ecosystem,
                  file,
                  line: POSITION,
                  message: UNUSED_FILE,
                  ruleId: FILE_RULE_ID,
                  tool: TOOL,
              }),
          ]
        : [];
    const items = CATEGORY_KEYS.flatMap((key) => {
        const value = issue[key];
        return Array.isArray(value) ? value.map((item) => itemFinding(item, { ecosystem, file, key })) : [];
    });
    return [...unusedFile, ...items];
};

export const parseKnipOutput = function parseKnipOutput(stdout: string, ecosystem: string): Finding[] {
    return stdout.length > 0 ? jsonRecordsAt(stdout, "issues").flatMap((issue) => issueFindings(issue, ecosystem)) : [];
};
```
