# core/coordinators/package.coordinator.ts

> 111 lines of code and 19 definitions.

Tree: GovLab Patterns
Language: typescript
Layer: application
Canonical: https://banes-lab.com/anatomy/patterns#file-patterns-core-coordinators-package-coordinator-ts
Source text: https://banes-lab.com/source/patterns/core/coordinators/package.coordinator.ts.txt

Listed in [core/coordinators](https://banes-lab.com/api/source/patterns/core/coordinators.md), after [core/coordinators/field.coordinator.ts](https://banes-lab.com/source/patterns/core/coordinators/field.coordinator.ts.md) and before [core/coordinators/page.coordinator.ts](https://banes-lab.com/source/patterns/core/coordinators/page.coordinator.ts.md).

## Definitions

- `walkArtifacts` (lexical_declaration, line 28)
- `buildModule` (lexical_declaration, line 45, exported)
- `Rendered` (type_alias_declaration, line 26)
- `out` (lexical_declaration, line 33)
- `symbols` (lexical_declaration, line 46, exported)
- `records` (lexical_declaration, line 47, exported)
- `insight` (lexical_declaration, line 48, exported)
- `graph` (lexical_declaration, line 49, exported)
- `diagnosed` (lexical_declaration, line 50, exported)
- `findings` (lexical_declaration, line 51, exported)
- `context` (lexical_declaration, line 54, exported)
- `rendered` (lexical_declaration, line 55, exported)
- `buildModuleReport` (lexical_declaration, line 91, exported)
- `entries` (lexical_declaration, line 96, exported)
- `fanIn` (lexical_declaration, line 97, exported)
- `count` (lexical_declaration, line 100, exported)
- `testUses` (lexical_declaration, line 105, exported)
- `title` (lexical_declaration, line 106, exported)
- `built` (lexical_declaration, line 107, exported)

## Contained in

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

## Uses

- [core/converters/definition.converter.ts](https://banes-lab.com/source/patterns/core/converters/definition.converter.ts.md)
- [core/counters/report.counter.ts](https://banes-lab.com/source/patterns/core/counters/report.counter.ts.md)
- [core/formatters/report.formatter.ts](https://banes-lab.com/source/patterns/core/formatters/report.formatter.ts.md)
- [core/selectors/code.selector.ts](https://banes-lab.com/source/patterns/core/selectors/code.selector.ts.md)

## Linked from

- [core/converters](https://banes-lab.com/anatomy/patterns/folder-patterns-core-converters.md)
- [core/coordinators](https://banes-lab.com/anatomy/patterns/folder-patterns-core-coordinators.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)
- [core/selectors](https://banes-lab.com/anatomy/patterns/folder-patterns-core-selectors.md)

## Source

```typescript
import type {
    BuiltModule,
    FileEntry,
    ModuleBuild,
    ModuleReportBuild,
    ModuleScope,
    RepoContext,
} from "#types/package.types";
import { buildAnalysis, buildFindings } from "#core/formatters/report.formatter";
import { codeGraph, codeInsight } from "#core/analyzers/code.analyzer";
import { entriesFor, testUsesOf } from "#core/loaders/source.loader";
import { sortedEdges, unresolvedNames } from "#core/selectors/code.selector";
import { NESTED_SUFFIX } from "#configuration/constants/report.constants";
import type { PageOutput } from "#types/page.types";
import type { WalkCell } from "#types/walk.types";
import { basename } from "node:path";
import { buildGroup } from "#core/converters/folder.converter";
import { definitionRecords } from "#core/converters/definition.converter";
import { diagnose } from "#core/coordinators/code.coordinator";
import { keyOf } from "#core/formatters/definition.formatter";
import { metricsOf } from "#core/counters/report.counter";
import { planNode } from "#core/converters/page.converter";
import { renderOne } from "#core/coordinators/page.coordinator";
import { titleFor } from "#core/resolvers/package.resolver";

type Rendered = readonly [string, PageOutput];

const walkArtifacts = function walkArtifacts<T>(
    rendered: readonly Rendered[],
    main: (output: PageOutput) => T,
    nested: (output: PageOutput) => T,
): Map<string, T> {
    const out = new Map<string, T>();
    for (const [base, output] of rendered) {
        if (output.mainSvg.length > 0) {
            out.set(base, main(output));
        }
        if (output.nestedSvg.length > 0) {
            out.set(`${base}${NESTED_SUFFIX}`, nested(output));
        }
    }
    return out;
};

export const buildModule = function buildModule(entries: readonly FileEntry[], build: ModuleBuild): BuiltModule {
    const symbols = entries.flatMap((entry) => entry.symbols);
    const records = entries.flatMap((entry) => entry.records);
    const insight = codeInsight(symbols, records);
    const graph = codeGraph(symbols);
    const diagnosed = diagnose(symbols, { graph, records, testUses: build.testUses ?? new Set() });
    const findings = [...(build.extraFindings ?? []), ...diagnosed].sort(
        (left, right) => right.relevance - left.relevance || JSON.stringify(left).localeCompare(JSON.stringify(right)),
    );
    const context = { fanIn: build.fanIn ?? new Map<string, number>(), findings, graph, insight, title: build.title };
    const rendered = planNode(buildGroup(entries, "", build.moduleName), []).map((page): Rendered => [
        page.page,
        renderOne(page, context),
    ]);
    return {
        analysis: buildAnalysis({ findingsCount: findings.length, graph, insight, title: build.title }),
        cells: walkArtifacts<WalkCell[]>(
            rendered,
            (output) => output.mainCells,
            (output) => output.nestedCells,
        ),
        findings: buildFindings(build.title, findings),
        findingsData: findings,
        pages: new Map(rendered.map(([base, output]): [string, string] => [base, output.html])),
        report: {
            definitions: definitionRecords(insight, graph),
            edges: sortedEdges(graph),
            findings,
            metrics: metricsOf({
                edges: graph.edges,
                findings,
                symbols: insight.symbols,
                unresolved: graph.external.length,
            }),
            module: build.title,
            pages: rendered.map(([, output]) => output.report),
            unresolvedCalls: unresolvedNames(graph),
        },
        svgs: walkArtifacts<string>(
            rendered,
            (output) => output.mainSvg,
            (output) => output.nestedSvg,
        ),
    };
};

export const buildModuleReport = async function buildModuleReport(
    moduleDir: string,
    ctx: RepoContext,
    scope: ModuleScope,
): Promise<ModuleReportBuild> {
    const entries = await entriesFor(moduleDir, scope.pruned);
    const fanIn = new Map(
        entries.flatMap((entry) =>
            entry.symbols.flatMap((symbol): [string, number][] => {
                const count = ctx.fanIn.get(keyOf(moduleDir, symbol.name));
                return count === undefined ? [] : [[symbol.name, count]];
            }),
        ),
    );
    const testUses = await testUsesOf(moduleDir, scope.pruned);
    const title = titleFor(scope.root, moduleDir);
    const built = buildModule(entries, {
        extraFindings: ctx.importCycles.get(moduleDir) ?? [],
        fanIn,
        moduleName: basename(moduleDir),
        testUses,
        title,
    });
    return { built, entries, title };
};
```
