# core/loaders/readme.loader.ts

> 126 lines of code and 26 definitions.

Tree: Build tree
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/build#file-build-core-loaders-readme-loader-ts
Source text: https://banes-lab.com/assets/sources/source.10cbcc1874f90ff8f2f3c54972bf8af9f12b1890457d4e91c2ad5e0c43363b86.generated.txt

## Definitions

- `textAt` (lexical_declaration, line 41)
- `isRecord` (lexical_declaration, line 37)
- `metaOf` (lexical_declaration, line 116)
- `listAt` (lexical_declaration, line 46)
- `readmeInputsOf` (lexical_declaration, line 123, exported)
- `diagramOf` (lexical_declaration, line 65)
- `tabsOf` (lexical_declaration, line 94)
- `lessonsOf` (lexical_declaration, line 51)
- `sectionOf` (lexical_declaration, line 70)
- `readStructuredPage` (lexical_declaration, line 102, exported)
- `tabOf` (lexical_declaration, line 86)
- `subsectionOf` (lexical_declaration, line 61)
- `value` (lexical_declaration, line 47)
- `problem` (lexical_declaration, line 53)
- `principle` (lexical_declaration, line 54)
- `block` (lexical_declaration, line 66)
- `subsections` (lexical_declaration, line 71)
- `blocks` (lexical_declaration, line 72)
- `tabs` (lexical_declaration, line 95)
- `parsed` (lexical_declaration, line 117, exported)
- `content` (lexical_declaration, line 118, exported)
- `meta` (lexical_declaration, line 119)
- `methodology` (lexical_declaration, line 124, exported)
- `faq` (lexical_declaration, line 125, exported)
- `exhibits` (lexical_declaration, line 126, exported)
- `present` (lexical_declaration, line 127, exported)

## Used by

- [core/loaders/chapter.loader.ts](https://banes-lab.com/source/build/core/loaders/chapter.loader.ts.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_CONTENT_KEY,
    PAYLOAD_ID_KEY,
    PAYLOAD_KIND_KEY,
    PAYLOAD_LABEL_KEY,
    PAYLOAD_SECTIONS_KEY,
    PAYLOAD_TABS_KEY,
    PAYLOAD_TITLE_KEY,
} from "#configuration/constants/leak.constants";
import type {
    ReadmeInputs,
    ReadmeLesson,
    ReadmePage,
    ReadmeSection,
    ReadmeSubsection,
    ReadmeTab,
} from "#types/readme.types";
import { payloadFileOf } from "#core/loaders/payload.loader";
import { readJsonOrNull } from "#core/persistence/report.persistence";

const isRecord = function isRecord(value: unknown): value is Record<string, unknown> {
    return typeof value === "object" && value !== null;
};

const textAt = function textAt(record: Record<string, unknown>, key: string): string | null {
    const value = record[key];
    return typeof value === "string" ? value : null;
};

const listAt = function listAt(record: Record<string, unknown>, key: string): Record<string, unknown>[] {
    const value = record[key];
    return Array.isArray(value) ? value.filter(isRecord) : [];
};

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 = listAt(content, PAYLOAD_TABS_KEY);
    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));
    if (!isRecord(parsed) || !isRecord(parsed[PAYLOAD_CONTENT_KEY])) {
        return null;
    }
    const content = parsed[PAYLOAD_CONTENT_KEY];
    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 parsed = readJsonOrNull(payloadFileOf(page));
    const content = isRecord(parsed) ? parsed[PAYLOAD_CONTENT_KEY] : null;
    const meta = isRecord(content) ? content[PAYLOAD_META_KEY] : null;
    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),
    };
};
```
