# core/formatters/index.formatter.ts

> 65 lines of code and 14 definitions.

Tree: Bane's Lab Build Scripts
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/build-scripts#file-build-scripts-core-formatters-index-formatter-ts
Source text: https://banes-lab.com/source/build-scripts/core/formatters/index.formatter.ts.txt

Listed in [core/formatters](https://banes-lab.com/api/source/build-scripts/core/formatters.md), after [core/formatters/icons.formatter.ts](https://banes-lab.com/source/build-scripts/core/formatters/icons.formatter.ts.md) and before [core/formatters/learning.formatter.ts](https://banes-lab.com/source/build-scripts/core/formatters/learning.formatter.ts.md).

## Definitions

- `renderIndex` (lexical_declaration, line 29, exported)
- `renderSiteIndex` (lexical_declaration, line 56, exported)
- `facetsOf` (lexical_declaration, line 20)
- `SEPARATOR` (lexical_declaration, line 7)
- `SUMMARY_JOINER` (lexical_declaration, line 8)
- `entryLine` (lexical_declaration, line 10)
- `target` (lexical_declaration, line 11)
- `summary` (lexical_declaration, line 12)
- `partLine` (lexical_declaration, line 16)
- `fields` (lexical_declaration, line 21)
- `line` (lexical_declaration, line 23)
- `alternate` (lexical_declaration, line 35, exported)
- `grammarLine` (lexical_declaration, line 51)
- `markdown` (lexical_declaration, line 52)

## Contained in

- [core/formatters](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-formatters.md)

## Uses

- [configuration/strings/catalog.strings.ts](https://banes-lab.com/source/build-scripts/configuration/strings/catalog.strings.ts.md)
- [core/formatters/markdown.formatter.ts](https://banes-lab.com/source/build-scripts/core/formatters/markdown.formatter.ts.md)
- [core/resolvers/catalog.resolver.ts](https://banes-lab.com/source/build-scripts/core/resolvers/catalog.resolver.ts.md)

## Used by

- [core/converters/catalog.converter.ts](https://banes-lab.com/source/build-scripts/core/converters/catalog.converter.ts.md)
- [core/converters/index.converter.ts](https://banes-lab.com/source/build-scripts/core/converters/index.converter.ts.md)
- [core/converters/learning.catalog.converter.ts](https://banes-lab.com/source/build-scripts/core/converters/learning.catalog.converter.ts.md)

## Linked from

- [configuration/strings](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-configuration-strings.md)
- [core/converters](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-converters.md)
- [core/formatters](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-formatters.md)
- [core/resolvers](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-resolvers.md)

## Source

```typescript
import type { Entry, GrammarData, Identity, SiteData } from "#types/catalog.types";
import type { FacetSummary, IndexData, IndexPart } from "#types/index.types";
import { SITE_INDEX_INTRO, versionLine } from "#configuration/strings/catalog.strings";
import { addressLines, blocks, code, heading, quote, section } from "#core/formatters/markdown.formatter";
import { siteIndex } from "#core/resolvers/catalog.resolver";

const SEPARATOR = ", ";
const SUMMARY_JOINER = ": ";

const entryLine = function entryLine(entry: Entry): string {
    const target = entry.markdown ?? entry.json;
    const summary = entry.summary === null ? "" : SUMMARY_JOINER + entry.summary;
    return `[${entry.title}](${target})${summary}`;
};

const partLine = function partLine(part: IndexPart): string {
    return `[${part.first} to ${part.last}](${part.markdown ?? part.json}) (${String(part.count)})`;
};

const facetsOf = function facetsOf(facets: readonly FacetSummary[]): readonly string[] {
    const fields = new Map<string, string[]>();
    for (const facet of facets) {
        const line = `[${facet.value}](${facet.markdown}) (${String(facet.count)})`;
        fields.set(facet.field, [...(fields.get(facet.field) ?? []), line]);
    }
    return [...fields.entries()].map(([field, values]) => `${code(field)}: ${values.join(SEPARATOR)}`);
};

export const renderIndex = function renderIndex(
    identity: Identity,
    data: IndexData,
    entries: readonly Entry[],
    site: string,
): string {
    const alternate = data.alternate ?? null;
    return blocks([
        heading(identity.title),
        quote(identity.summary),
        addressLines([
            ["Canonical", identity.href === null ? null : site + identity.href],
            ["Page as Markdown", alternate?.markdown ?? null],
            ["Page as JSON", alternate?.json ?? null],
            ["This index as JSON", site + identity.address.json],
        ]),
        section("Fields", facetsOf(data.facets ?? [])),
        section("Parts", (data.parts ?? []).map(partLine)),
        section("Entries", entries.map(entryLine)),
    ]);
};

const grammarLine = function grammarLine(row: GrammarData): string {
    const markdown = row.markdown === null ? "" : ` or ${code(row.markdown)}`;
    return `${row.level}: ${code(row.json)}${markdown}`;
};

export const renderSiteIndex = function renderSiteIndex(data: SiteData, site: string): string {
    return blocks([
        heading(data.title),
        quote(data.summary),
        SITE_INDEX_INTRO,
        data.canonical,
        versionLine(data.version, data.updated, data.build),
        section("Address patterns", data.grammar.map(grammarLine)),
        section("Pages", data.pages.map(entryLine)),
        section("Documents", data.documents.map(entryLine)),
        section("Ontology records", data.collections.map(entryLine)),
        section("Source trees", data.trees.map(entryLine)),
        section("Other indexes", data.queries.map(entryLine)),
        `## Use and attribution\n\n${data.consent}`,
        addressLines([["This index as JSON", site + siteIndex().json]]),
    ]);
};
```
