# core/loaders/readme.loader.ts

> 113 lines of code and 22 definitions.

Tree: Bane's Lab Content
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/content#file-content-core-loaders-readme-loader-ts
Source text: https://banes-lab.com/source/content/core/loaders/readme.loader.ts.txt

Listed in [core/loaders](https://banes-lab.com/api/source/content/core/loaders.md), after [core/loaders/payload.loader.ts](https://banes-lab.com/source/content/core/loaders/payload.loader.ts.md) and before [core/loaders/section.loader.ts](https://banes-lab.com/source/content/core/loaders/section.loader.ts.md).

## Definitions

- `metaOf` (lexical_declaration, line 101)
- `readmeInputsOf` (lexical_declaration, line 107, exported)
- `diagramOf` (lexical_declaration, line 50)
- `tabsOf` (lexical_declaration, line 79)
- `lessonsOf` (lexical_declaration, line 36)
- `sectionOf` (lexical_declaration, line 55)
- `readStructuredPage` (lexical_declaration, line 87, exported)
- `tabOf` (lexical_declaration, line 71)
- `subsectionOf` (lexical_declaration, line 46)
- `problem` (lexical_declaration, line 38)
- `principle` (lexical_declaration, line 39)
- `block` (lexical_declaration, line 51)
- `subsections` (lexical_declaration, line 56)
- `blocks` (lexical_declaration, line 57)
- `tabs` (lexical_declaration, line 80)
- `parsed` (lexical_declaration, line 88, exported)
- `content` (lexical_declaration, line 102, exported)
- `meta` (lexical_declaration, line 103)
- `methodology` (lexical_declaration, line 108, exported)
- `faq` (lexical_declaration, line 109, exported)
- `exhibits` (lexical_declaration, line 110, exported)
- `present` (lexical_declaration, line 111, exported)

## Contained in

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

## Uses

- [core/selectors/payload.selector.ts](https://banes-lab.com/source/content/core/selectors/payload.selector.ts.md)

## Used by

- [core/loaders/chapter.loader.ts](https://banes-lab.com/source/content/core/loaders/chapter.loader.ts.md)

## Linked from

- [core/loaders](https://banes-lab.com/anatomy/content/folder-content-core-loaders.md)
- [core/selectors](https://banes-lab.com/anatomy/content/folder-content-core-selectors.md)

## Source

```typescript
import {
    DIAGRAM_KIND,
    EXHIBIT_PAGES,
    LESSON_KIND,
    PAYLOAD_BLOCKS_KEY,
    PAYLOAD_DESCRIPTION_KEY,
    PAYLOAD_DIAGRAM_KEY,
    PAYLOAD_INTRO_KEY,
    PAYLOAD_META_KEY,
    PAYLOAD_PRINCIPLE_KEY,
    PAYLOAD_PROBLEM_KEY,
    PAYLOAD_SUBSECTIONS_KEY,
    PAYLOAD_SUBTITLE_KEY,
    PAYLOAD_TEXT_KEY,
} from "#configuration/constants/readme.constants";
import { FAQ_PAGE, METHODOLOGY_PAGE } from "@banes-lab/web/core/ids/page.ids.ts";
import {
    PAYLOAD_ID_KEY,
    PAYLOAD_KIND_KEY,
    PAYLOAD_LABEL_KEY,
    PAYLOAD_SECTIONS_KEY,
    PAYLOAD_TITLE_KEY,
} from "#configuration/constants/leak.constants";
import type {
    ReadmeInputs,
    ReadmeLesson,
    ReadmePage,
    ReadmeSection,
    ReadmeSubsection,
    ReadmeTab,
} from "#types/readme.types";
import { contentOf, isRecord, listAt, tabsIn, textAt } from "#core/selectors/payload.selector";
import { payloadFileOf } from "#core/loaders/payload.loader";
import { readJsonOrNull } from "#core/persistence/report.persistence";

const lessonsOf = function lessonsOf(blocks: readonly Record<string, unknown>[]): ReadmeLesson[] {
    return blocks.flatMap((block) => {
        const problem = textAt(block, PAYLOAD_PROBLEM_KEY);
        const principle = textAt(block, PAYLOAD_PRINCIPLE_KEY);
        return block[PAYLOAD_KIND_KEY] === LESSON_KIND && problem !== null && principle !== null
            ? [{ principle, problem }]
            : [];
    });
};

const subsectionOf = function subsectionOf(record: Record<string, unknown>): ReadmeSubsection {
    return { content: textAt(record, PAYLOAD_TEXT_KEY), title: textAt(record, PAYLOAD_TITLE_KEY) ?? "" };
};

const diagramOf = function diagramOf(blocks: readonly Record<string, unknown>[]): string | null {
    const block = blocks.find((candidate) => candidate[PAYLOAD_KIND_KEY] === DIAGRAM_KIND);
    return block === undefined ? null : textAt(block, PAYLOAD_DIAGRAM_KEY);
};

const sectionOf = function sectionOf(record: Record<string, unknown>): ReadmeSection {
    const subsections = listAt(record, PAYLOAD_SUBSECTIONS_KEY);
    const blocks = [
        ...listAt(record, PAYLOAD_BLOCKS_KEY),
        ...subsections.flatMap((sub) => listAt(sub, PAYLOAD_BLOCKS_KEY)),
    ];
    return {
        diagram: diagramOf(blocks),
        id: textAt(record, PAYLOAD_ID_KEY) ?? "",
        intro: textAt(record, PAYLOAD_INTRO_KEY),
        lessons: lessonsOf(blocks),
        subsections: subsections.map(subsectionOf),
        title: textAt(record, PAYLOAD_TITLE_KEY) ?? "",
    };
};

const tabOf = function tabOf(record: Record<string, unknown>): ReadmeTab {
    return {
        id: textAt(record, PAYLOAD_ID_KEY) ?? "",
        label: textAt(record, PAYLOAD_LABEL_KEY) ?? "",
        sections: listAt(record, PAYLOAD_SECTIONS_KEY).map(sectionOf),
    };
};

const tabsOf = function tabsOf(content: Record<string, unknown>, page: string): ReadmeTab[] {
    const tabs = tabsIn(content);
    if (tabs.length > 0) {
        return tabs.map(tabOf);
    }
    return [{ id: page, label: page, sections: listAt(content, PAYLOAD_SECTIONS_KEY).map(sectionOf) }];
};

export const readStructuredPage = function readStructuredPage(page: string): ReadmePage | null {
    const parsed = readJsonOrNull(payloadFileOf(page));
    const content = contentOf(parsed);
    if (!isRecord(parsed) || content === null) {
        return null;
    }
    return {
        description: textAt(parsed, PAYLOAD_DESCRIPTION_KEY) ?? "",
        page,
        tabs: tabsOf(content, page),
        title: textAt(parsed, PAYLOAD_LABEL_KEY) ?? "",
    };
};

const metaOf = function metaOf(page: string, key: string): string {
    const content = contentOf(readJsonOrNull(payloadFileOf(page)));
    const meta = content === null ? null : content[PAYLOAD_META_KEY];
    return isRecord(meta) ? (textAt(meta, key) ?? "") : "";
};

export const readmeInputsOf = function readmeInputsOf(): ReadmeInputs | null {
    const methodology = readStructuredPage(METHODOLOGY_PAGE);
    const faq = readStructuredPage(FAQ_PAGE);
    const exhibits = EXHIBIT_PAGES.map(readStructuredPage);
    const present = exhibits.filter((page): page is ReadmePage => page !== null);
    if (methodology === null || faq === null || present.length !== exhibits.length) {
        return null;
    }
    return {
        exhibits: present,
        faq,
        heading: metaOf(METHODOLOGY_PAGE, PAYLOAD_TITLE_KEY),
        methodology,
        subtitle: metaOf(METHODOLOGY_PAGE, PAYLOAD_SUBTITLE_KEY),
    };
};
```
