# core/analyzers/code.analyzer.ts

> 104 lines of code and 30 definitions.

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

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

## Definitions

- `flowOf` (lexical_declaration, line 13)
- `byPosition` (lexical_declaration, line 70)
- `codeInsight` (lexical_declaration, line 94, exported)
- `definitionKeys` (lexical_declaration, line 20)
- `uniqueKeyByName` (lexical_declaration, line 36)
- `byDegree` (lexical_declaration, line 74)
- `toStat` (lexical_declaration, line 78)
- `FLOW_ENTRY` (lexical_declaration, line 8)
- `FLOW_RELAY` (lexical_declaration, line 9)
- `FLOW_LEAF` (lexical_declaration, line 10)
- `FLOW_ISOLATED` (lexical_declaration, line 11)
- `definitionFiles` (lexical_declaration, line 28)
- `map` (lexical_declaration, line 29)
- `out` (lexical_declaration, line 37)
- `[only]` (lexical_declaration, line 39)
- `externalFrom` (lexical_declaration, line 47)
- `codeGraph` (lexical_declaration, line 51, exported)
- `defFiles` (lexical_declaration, line 53, exported)
- `unique` (lexical_declaration, line 54, exported)
- `calls` (lexical_declaration, line 55, exported)
- `edges` (lexical_declaration, line 61, exported)
- `same` (lexical_declaration, line 62, exported)
- `to` (lexical_declaration, line 63, exported)
- `from` (lexical_declaration, line 64, exported)
- `defKeys` (lexical_declaration, line 98, exported)
- `{ edges }` (lexical_declaration, line 99, exported)
- `inDeg` (lexical_declaration, line 100, exported)
- `outDeg` (lexical_declaration, line 101, exported)
- `stats` (lexical_declaration, line 106, exported)
- `distribution` (lexical_declaration, line 107, exported)

## Contained in

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

## Uses

- [core/counters/base.counter.ts](https://banes-lab.com/source/patterns/core/counters/base.counter.ts.md)
- [core/formatters/definition.formatter.ts](https://banes-lab.com/source/patterns/core/formatters/definition.formatter.ts.md)

## Linked from

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

## Source

```typescript
import type { CallEdge, CodeGraph, CodeInsight, CodeSymbol, ExternalCall, SymbolStat } from "#types/code.types";
import { ROLE_CALL, ROLE_DEFINITION } from "#core/classifiers/syntax.classifier";
import { MODULE_SCOPE } from "#configuration/constants/report.constants";
import { increment } from "#core/counters/base.counter";
import { keyOf } from "#core/formatters/definition.formatter";
import { syntaxDistribution } from "#core/analyzers/syntax.analyzer";

const FLOW_ENTRY = "entry";
const FLOW_RELAY = "relay";
const FLOW_LEAF = "leaf";
const FLOW_ISOLATED = "isolated";

const flowOf = function flowOf(inDegree: number, outDegree: number): string {
    if (inDegree === 0) {
        return outDegree === 0 ? FLOW_ISOLATED : FLOW_ENTRY;
    }
    return outDegree === 0 ? FLOW_LEAF : FLOW_RELAY;
};

const definitionKeys = function definitionKeys(symbols: readonly CodeSymbol[]): Map<string, CodeSymbol> {
    return new Map(
        symbols
            .filter((symbol) => symbol.role === ROLE_DEFINITION)
            .map((symbol) => [keyOf(symbol.file, symbol.name), symbol]),
    );
};

const definitionFiles = function definitionFiles(symbols: readonly CodeSymbol[]): Map<string, Set<string>> {
    const map = new Map<string, Set<string>>();
    for (const symbol of symbols.filter((entry) => entry.role === ROLE_DEFINITION)) {
        map.set(symbol.name, (map.get(symbol.name) ?? new Set<string>()).add(symbol.file));
    }
    return map;
};

const uniqueKeyByName = function uniqueKeyByName(defFiles: ReadonlyMap<string, Set<string>>): Map<string, string> {
    const out = new Map<string, string>();
    for (const [name, files] of defFiles) {
        const [only] = [...files];
        if (files.size === 1 && only !== undefined) {
            out.set(name, keyOf(only, name));
        }
    }
    return out;
};

const externalFrom = function externalFrom(symbol: CodeSymbol): ExternalCall {
    return { caller: symbol.enclosing, file: symbol.file, line: symbol.line, name: symbol.name };
};

export const codeGraph = function codeGraph(symbols: readonly CodeSymbol[]): CodeGraph {
    const defKeys = definitionKeys(symbols);
    const defFiles = definitionFiles(symbols);
    const unique = uniqueKeyByName(defFiles);
    const calls = symbols.filter(
        (symbol) =>
            symbol.role === ROLE_CALL &&
            !symbol.member &&
            defKeys.get(keyOf(symbol.file, symbol.enclosing))?.callable === true,
    );
    const edges = calls.flatMap((call): CallEdge[] => {
        const same = keyOf(call.file, call.name);
        const to = defKeys.has(same) ? same : (unique.get(call.name) ?? "");
        const from = keyOf(call.file, call.enclosing);
        return to === "" || to === from ? [] : [{ file: call.file, from, line: call.line, to }];
    });
    return { edges, external: calls.filter((call) => !defFiles.has(call.name)).map(externalFrom) };
};

const byPosition = function byPosition(a: SymbolStat, b: SymbolStat): number {
    return a.file.localeCompare(b.file) || a.line - b.line || a.name.localeCompare(b.name);
};

const byDegree = function byDegree(a: SymbolStat, b: SymbolStat): number {
    return b.inDegree - a.inDegree || b.outDegree - a.outDegree || byPosition(a, b);
};

const toStat = function toStat(symbol: CodeSymbol, inDegree: number, outDegree: number): SymbolStat {
    return {
        callable: symbol.callable,
        exported: symbol.exported,
        file: symbol.file,
        flow: flowOf(inDegree, outDegree),
        inDegree,
        kind: symbol.kind,
        line: symbol.line,
        local: symbol.enclosing !== MODULE_SCOPE,
        name: symbol.name,
        outDegree,
        role: symbol.role,
    };
};

export const codeInsight = function codeInsight(
    symbols: readonly CodeSymbol[],
    records: readonly Record<string, unknown>[],
): CodeInsight {
    const defKeys = definitionKeys(symbols);
    const { edges } = codeGraph(symbols);
    const inDeg = new Map<string, number>();
    const outDeg = new Map<string, number>();
    for (const edge of edges) {
        increment(outDeg, edge.from);
        increment(inDeg, edge.to);
    }
    const stats = [...defKeys].map(([key, symbol]) => toStat(symbol, inDeg.get(key) ?? 0, outDeg.get(key) ?? 0));
    const distribution = syntaxDistribution(records);
    return {
        definitions: defKeys.size,
        edges: edges.length,
        invariants: distribution.invariants,
        symbols: stats.toSorted(byDegree),
        variants: distribution.variants,
    };
};
```
