# core/analyzers/concern.analyzer.ts

> 51 lines of code and 15 definitions.

Tree: GovLab Patterns
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/patterns#file-patterns-core-analyzers-concern-analyzer-ts
Source text: https://banes-lab.com/source/patterns/core/analyzers/concern.analyzer.ts.txt

Listed in [core/analyzers](https://banes-lab.com/api/source/patterns/core/analyzers.md), after [core/analyzers/code.analyzer.ts](https://banes-lab.com/source/patterns/core/analyzers/code.analyzer.ts.md) and before [core/analyzers/definition.analyzer.ts](https://banes-lab.com/source/patterns/core/analyzers/definition.analyzer.ts.md).

## Definitions

- `crossConcernFindings` (lexical_declaration, line 24, exported)
- `isFactory` (lexical_declaration, line 19)
- `REL_CONCERN` (lexical_declaration, line 7)
- `CONCERN_SPAN` (lexical_declaration, line 8)
- `CONFIDENCE` (lexical_declaration, line 9)
- `PATH_SEP` (lexical_declaration, line 10)
- `WRAPPER_DIRS` (lexical_declaration, line 11)
- `FACTORY_PREFIXES` (lexical_declaration, line 12)
- `concernOf` (lexical_declaration, line 14)
- `[top = "", next = ""]` (lexical_declaration, line 15)
- `lower` (lexical_declaration, line 20)
- `spans` (lexical_declaration, line 28, exported)
- `concern` (lexical_declaration, line 30, exported)
- `callable` (lexical_declaration, line 33, exported)
- `span` (lexical_declaration, line 41, exported)

## Contained in

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

## Uses

- [configuration/strings/code.strings.ts](https://banes-lab.com/source/patterns/configuration/strings/code.strings.ts.md)

## Used by

- [core/coordinators/code.coordinator.ts](https://banes-lab.com/source/patterns/core/coordinators/code.coordinator.ts.md)

## Linked from

- [configuration/strings](https://banes-lab.com/anatomy/patterns/folder-patterns-configuration-strings.md)
- [core/analyzers](https://banes-lab.com/anatomy/patterns/folder-patterns-core-analyzers.md)
- [core/coordinators](https://banes-lab.com/anatomy/patterns/folder-patterns-core-coordinators.md)

## Source

```typescript
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,
            };
        });
};
```
