# core/converters/index.converter.ts

> 89 lines of code and 20 definitions.

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

Listed in [core/converters](https://banes-lab.com/api/source/build-scripts/core/converters.md), after [core/converters/identifier.converter.ts](https://banes-lab.com/source/build-scripts/core/converters/identifier.converter.ts.md) and before [core/converters/index.filter.converter.ts](https://banes-lab.com/source/build-scripts/core/converters/index.filter.converter.ts.md).

## Definitions

- `indexLeaves` (lexical_declaration, line 75, exported)
- `partLeaves` (lexical_declaration, line 48)
- `indexPlansOf` (lexical_declaration, line 20, exported)
- `PART_REF_MARK` (lexical_declaration, line 18)
- `collectionRefs` (lexical_declaration, line 21, exported)
- `collections` (lexical_declaration, line 24, exported)
- `pages` (lexical_declaration, line 25, exported)
- `source` (lexical_declaration, line 26, exported)
- `facetLevels` (lexical_declaration, line 27, exported)
- `entriesOf` (lexical_declaration, line 41)
- `entry` (lexical_declaration, line 43)
- `parts` (lexical_declaration, line 49)
- `leaves` (lexical_declaration, line 50)
- `part` (lexical_declaration, line 51)
- `title` (lexical_declaration, line 52)
- `identity` (lexical_declaration, line 53)
- `summaries` (lexical_declaration, line 64)
- `listed` (lexical_declaration, line 81, exported)
- `data` (lexical_declaration, line 85, exported)
- `isIndexHead` (lexical_declaration, line 93, exported)

## Contained in

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

## Uses

- [configuration/strings/catalog.strings.ts](https://banes-lab.com/source/build-scripts/configuration/strings/catalog.strings.ts.md)
- [core/converters/index.filter.converter.ts](https://banes-lab.com/source/build-scripts/core/converters/index.filter.converter.ts.md)
- [core/converters/index.page.converter.ts](https://banes-lab.com/source/build-scripts/core/converters/index.page.converter.ts.md)
- [core/formatters/index.formatter.ts](https://banes-lab.com/source/build-scripts/core/formatters/index.formatter.ts.md)
- [core/stores/catalog.store.ts](https://banes-lab.com/source/build-scripts/core/stores/catalog.store.ts.md)

## Used by

- [core/coordinators/catalog.index.coordinator.ts](https://banes-lab.com/source/build-scripts/core/coordinators/catalog.index.coordinator.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/coordinators](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-coordinators.md)
- [core/formatters](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-formatters.md)
- [core/stores](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-stores.md)

## Source

```typescript
import { CATALOG_FILE_BUDGET, INDEX_PART_BUDGET, INDEX_PART_KIND } from "#configuration/constants/catalog.constants";
import type { Entries, IndexPart, IndexPlan, IndexPlans, IndexScope } from "#types/index.types";
import type { Entry, Identity, Leaf } from "#types/catalog.types";
import {
    collectionIndexPlans,
    facetIndexPlans,
    facetLevelPlans,
    levelIndexPlans,
} from "#core/converters/index.filter.converter";
import { emptyIndex, indexPartTitle } from "#configuration/strings/catalog.strings";
import { packedParts, serialize } from "#core/stores/catalog.store";
import { pageIndexPlans, tabIndexPlans } from "#core/converters/index.page.converter";
import { Buffer } from "node:buffer";
import { indexPart } from "#core/resolvers/catalog.resolver";
import { renderIndex } from "#core/formatters/index.formatter";
import { sourceIndexPlans } from "#core/converters/index.source.converter";

const PART_REF_MARK = "/";

export const indexPlansOf = function indexPlansOf(scope: IndexScope): IndexPlans {
    const collectionRefs = new Map(
        [...scope.collections.entries()].map(([collection, index]) => [collection, Object.keys(index)]),
    );
    const collections = collectionIndexPlans(collectionRefs, scope.groups, scope.site);
    const pages = pageIndexPlans(scope.discovery, scope.plans);
    const source = sourceIndexPlans(scope.files, scope.localPath);
    const facetLevels = facetLevelPlans(scope.groups);
    return {
        collections,
        facetCollections: facetLevels.collections,
        facetFields: facetLevels.fields,
        facets: facetIndexPlans(scope.groups),
        folders: source.folders,
        levels: levelIndexPlans({ collections, facets: facetLevels.collections, pages, trees: source.trees }),
        pages,
        tabs: tabIndexPlans(scope.discovery, scope.plans),
        trees: source.trees,
    };
};

const entriesOf = function entriesOf(entries: Entries, refs: readonly string[]): readonly Entry[] {
    return refs.flatMap((ref) => {
        const entry = entries.get(ref);
        return entry === undefined ? [] : [entry];
    });
};

const partLeaves = function partLeaves(plan: IndexPlan, listed: readonly Entry[], site: string): readonly Leaf[] {
    const parts = packedParts(listed, INDEX_PART_BUDGET);
    const leaves = parts.map((entries, index): Leaf => {
        const part = index + 1;
        const title = indexPartTitle(plan.identity.title, part, parts.length);
        const identity: Identity = {
            address: indexPart(plan.identity.address, String(part)),
            href: null,
            kind: INDEX_PART_KIND,
            ref: plan.identity.ref + PART_REF_MARK + String(part),
            summary: null,
            title,
        };
        const data = { entries, part, ref: identity.ref, title, total: parts.length };
        return { data, identity, markdown: renderIndex(identity, { ref: identity.ref, title }, entries, site) };
    });
    const summaries = leaves.map((leaf, index): IndexPart => ({
        count: parts[index]?.length ?? 0,
        first: parts[index]?.at(0)?.title ?? "",
        json: site + leaf.identity.address.json,
        last: parts[index]?.at(-1)?.title ?? "",
        markdown: leaf.identity.address.markdown === null ? null : site + leaf.identity.address.markdown,
    }));
    const data = { ...plan.data, count: listed.length, parts: summaries };
    return [...leaves, { data, identity: plan.identity, markdown: renderIndex(plan.identity, data, [], site) }];
};

export const indexLeaves = function indexLeaves(
    plans: readonly IndexPlan[],
    entries: Entries,
    site: string,
): readonly Leaf[] {
    return plans.flatMap((plan) => {
        const listed = entriesOf(entries, plan.refs);
        if (listed.length === 0) {
            throw new Error(emptyIndex(plan.identity.ref));
        }
        const data = { ...plan.data, entries: listed };
        if (Buffer.byteLength(serialize(data)) > CATALOG_FILE_BUDGET) {
            return partLeaves(plan, listed, site);
        }
        return [{ data, identity: plan.identity, markdown: renderIndex(plan.identity, plan.data, listed, site) }];
    });
};

export const isIndexHead = function isIndexHead(entry: Entry): boolean {
    return entry.kind !== INDEX_PART_KIND;
};
```
