# shared/analyzers/liveness.analyzer.ts

> 92 lines of code and 19 definitions.

Tree: Governance tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/governance#file-governance-shared-analyzers-liveness-analyzer-ts
Source text: https://banes-lab.com/source/governance/shared/analyzers/liveness.analyzer.ts.txt

Listed in [shared/analyzers](https://banes-lab.com/api/source/governance/shared/analyzers.md), after [shared/analyzers/generator.analyzer.ts](https://banes-lab.com/source/governance/shared/analyzers/generator.analyzer.ts.md) and before [shared/analyzers/location.analyzer.ts](https://banes-lab.com/source/governance/shared/analyzers/location.analyzer.ts.md).

## Definitions

- `addEdge` (lexical_declaration, line 5)
- `walkReachable` (lexical_declaration, line 47, exported)
- `buildImportGraph` (lexical_declaration, line 15, exported)
- `findEntrypoints` (lexical_declaration, line 26, exported)
- `externalReach` (lexical_declaration, line 67)
- `edges` (lexical_declaration, line 10)
- `seen` (lexical_declaration, line 31, exported)
- `allFiles` (lexical_declaration, line 32, exported)
- `reachableFiles` (lexical_declaration, line 48, exported)
- `reachableExports` (lexical_declaration, line 49, exported)
- `stack` (lexical_declaration, line 50, exported)
- `file` (lexical_declaration, line 52, exported)
- `keys` (lexical_declaration, line 68)
- `target` (lexical_declaration, line 70)
- `computeDeadExports` (lexical_declaration, line 82, exported)
- `graph` (lexical_declaration, line 88, exported)
- `entrypoints` (lexical_declaration, line 89, exported)
- `reachable` (lexical_declaration, line 90, exported)
- `key` (lexical_declaration, line 95, exported)

## Uses

- [shared/loaders/barrel.loader.ts](https://banes-lab.com/source/governance/shared/loaders/barrel.loader.ts.md)

## Used by

- [rules/eslint/closure-no-browser-derivation.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-no-browser-derivation.eslint.rule.ts.md)

## Source

```typescript
import type { BarrelPattern, ClosureGraph, ExportEntry, ImportGraph, Reachable } from "../../types/closure.types.ts";
import { matchesBarrelPattern } from "../loaders/barrel.loader.ts";
import { normalizeImport } from "../loaders/graph.loader.ts";

const addEdge = function addEdge(graph: ImportGraph, file: string, from: string, names: string[]): void {
    const target = normalizeImport(file, from);
    if (target === null) {
        return;
    }
    const edges = graph.get(file) ?? [];
    edges.push({ names, target });
    graph.set(file, edges);
};

export const buildImportGraph = function buildImportGraph(closure: ClosureGraph): ImportGraph {
    const graph: ImportGraph = new Map();
    for (const imp of closure.imports) {
        addEdge(graph, imp.file, imp.from, imp.names);
    }
    for (const imp of closure.sideEffectImports) {
        addEdge(graph, imp.file, imp.from, []);
    }
    return graph;
};

export const findEntrypoints = function findEntrypoints(
    closure: ClosureGraph,
    entrySuffixes: readonly string[],
    barrels: readonly BarrelPattern[],
): Set<string> {
    const seen = new Set<string>();
    const allFiles = new Set<string>();
    for (const e of closure.exports) {
        allFiles.add(e.file);
    }
    for (const imp of closure.imports) {
        allFiles.add(imp.file);
    }
    for (const file of allFiles) {
        if (entrySuffixes.some((suffix) => file.endsWith(suffix)) || matchesBarrelPattern(file, barrels)) {
            seen.add(file);
        }
    }
    return seen;
};

export const walkReachable = function walkReachable(entrypoints: Iterable<string>, graph: ImportGraph): Reachable {
    const reachableFiles = new Set<string>();
    const reachableExports = new Set<string>();
    const stack = [...entrypoints];
    while (stack.length > 0) {
        const file = stack.pop();
        if (file === undefined || reachableFiles.has(file)) {
            continue;
        }
        reachableFiles.add(file);
        for (const imp of graph.get(file) ?? []) {
            for (const name of imp.names) {
                reachableExports.add(`${imp.target}::${name}`);
            }
            stack.push(imp.target);
        }
    }
    return { reachableExports, reachableFiles };
};

const externalReach = function externalReach(closure: ClosureGraph, graph: ImportGraph): string[] {
    const keys: string[] = [];
    for (const imp of closure.externalConsumers) {
        const target = normalizeImport(imp.file, imp.from);
        if (target === null) {
            continue;
        }
        for (const name of imp.names) {
            keys.push(`${target}::${name}`);
        }
        keys.push(...walkReachable([target], graph).reachableExports);
    }
    return keys;
};

export const computeDeadExports = function computeDeadExports(
    closure: ClosureGraph,
    entrySuffixes: readonly string[],
    barrels: readonly BarrelPattern[],
    isAllowed: (key: string) => boolean,
): ExportEntry[] {
    const graph = buildImportGraph(closure);
    const entrypoints = findEntrypoints(closure, entrySuffixes, barrels);
    const reachable = new Set([
        ...walkReachable(entrypoints, graph).reachableExports,
        ...externalReach(closure, graph),
    ]);
    return closure.exports.filter((e) => {
        const key = `${e.file}::${e.name}`;
        return !reachable.has(key) && !isAllowed(key);
    });
};
```
