# core/loaders/site.loader.ts

> 114 lines of code and 24 definitions.

Tree: Bane's Lab Build Scripts
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/build-scripts#file-build-scripts-core-loaders-site-loader-ts
Source text: https://banes-lab.com/source/build-scripts/core/loaders/site.loader.ts.txt

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

## Definitions

- `exporter` (lexical_declaration, line 42)
- `loadFrom` (lexical_declaration, line 74, exported)
- `sectionTexts` (lexical_declaration, line 27)
- `sectionReader` (lexical_declaration, line 64)
- `visit` (lexical_declaration, line 77, exported)
- `SectionSource` (interface_declaration, line 20)
- `{ content, page, path, renderer }` (lexical_declaration, line 32)
- `element` (lexical_declaration, line 34)
- `recordings` (lexical_declaration, line 48)
- `content` (lexical_declaration, line 51)
- `held` (lexical_declaration, line 66)
- `runner` (lexical_declaration, line 75, exported)
- `links` (lexical_declaration, line 76, exported)
- `ids` (lexical_declaration, line 79, exported)
- `registry` (lexical_declaration, line 80, exported)
- `site` (lexical_declaration, line 86, exported)
- `strings` (lexical_declaration, line 87, exported)
- `company` (lexical_declaration, line 90, exported)
- `terms` (lexical_declaration, line 91, exported)
- `renderer` (lexical_declaration, line 95, exported)
- `markup` (lexical_declaration, line 99, exported)
- `text` (lexical_declaration, line 100, exported)
- `surface` (lexical_declaration, line 101, exported)
- `exported` (lexical_declaration, line 102, exported)

## Contained in

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

## Uses

- [configuration/strings/page.strings.ts](https://banes-lab.com/source/build-scripts/configuration/strings/page.strings.ts.md)
- [core/loaders/surface.loader.ts](https://banes-lab.com/source/build-scripts/core/loaders/surface.loader.ts.md)
- [core/validators/figure.validator.ts](https://banes-lab.com/source/build-scripts/core/validators/figure.validator.ts.md)

## Used by

- [core/plugins/site.plugin.ts](https://banes-lab.com/source/build-scripts/core/plugins/site.plugin.ts.md)

## Linked from

- [configuration/strings](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-configuration-strings.md)
- [core/loaders](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-loaders.md)
- [core/plugins](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-plugins.md)
- [core/validators](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-validators.md)

## Source

```typescript
import type {
    Links,
    Loaded,
    PageRegistry,
    PageRenderer,
    PageShell,
    RouteExporter,
    TextConverter,
    TranscriptRenderer,
} from "#types/page.types";
import { type ViteDevServer, createServerModuleRunner } from "vite";
import { missingSectionElement, noSectionText } from "#configuration/strings/page.strings";
import type { Inline } from "#types/site.types";
import { RUNNER_MODULES } from "#configuration/constants/loader.constants";
import type { SectionExporter } from "#types/section.types";
import { assertFiguresFilled } from "#core/validators/figure.validator";
import { installDocument } from "#core/adapters/document.adapter";
import { loadRecordings } from "#core/loaders/surface.loader";

interface SectionSource {
    readonly content: Element;
    readonly page: string;
    readonly path: string;
    readonly renderer: PageRenderer;
}

const sectionTexts = function sectionTexts(
    source: SectionSource,
    ids: readonly string[],
    text: TextConverter,
): readonly string[] {
    const { content, page, path, renderer } = source;
    return ids.map((id) => {
        const element = renderer.renderStaticSection(page, path, id) ?? content.querySelector(`[id="${id}"]`);
        if (element === null) {
            throw new Error(missingSectionElement(path, id));
        }
        return text.exportText(element);
    });
};

const exporter = function exporter(
    renderer: PageRenderer,
    visit: (path: string) => void,
    text: TextConverter & TranscriptRenderer,
    exported: Map<string, readonly string[]>,
): RouteExporter {
    let recordings: readonly unknown[] | null = null;
    return (path, page, sections) => {
        visit(path);
        const content = renderer.renderStaticPage(page).at(0);
        if (content === undefined) {
            exported.set(path, []);
            return "";
        }
        recordings ??= loadRecordings();
        text.renderTranscripts(content, recordings);
        assertFiguresFilled(content, path);
        exported.set(path, sectionTexts({ content, page, path, renderer }, sections, text));
        return text.exportText(content);
    };
};

const sectionReader = function sectionReader(exported: ReadonlyMap<string, readonly string[]>): SectionExporter {
    return (path, ids) => {
        const held = exported.get(path);
        if (held?.length !== ids.length) {
            throw new Error(noSectionText(path, ids.length));
        }
        return held;
    };
};

export const loadFrom = async function loadFrom(server: ViteDevServer): Promise<Loaded> {
    const runner = createServerModuleRunner(server.environments.ssr);
    const links = await runner.import<Links>(RUNNER_MODULES.links.path);
    const visit = installDocument(links.SITE_URL);
    await runner.import(RUNNER_MODULES.records.path);
    const ids = await runner.import<Record<string, unknown>>(RUNNER_MODULES.pageIds.path);
    const registry = await runner.import<PageRegistry>(RUNNER_MODULES.pageRegistry.path);
    await Promise.all(
        Object.values(ids)
            .filter((id): id is string => typeof id === "string")
            .map(async (id) => registry.loadCompletePage(id)),
    );
    const site = await runner.import<{ readonly MAIN_ID: string }>(RUNNER_MODULES.siteIds.path);
    const strings = await runner.import<{ readonly HOME_DESCRIPTION: string; readonly SITE_NAME: string }>(
        RUNNER_MODULES.pageStrings.path,
    );
    const company = await runner.import<{ readonly COMPANY_OWNER: string }>(RUNNER_MODULES.company.path);
    const terms = await runner.import<{
        readonly TRAINING_LICENSE_TERMS: string;
        readonly TRAINING_PERMISSION: string;
    }>(RUNNER_MODULES.terms.path);
    const renderer: PageRenderer = {
        ...(await runner.import<Omit<PageRenderer, PageShell>>(RUNNER_MODULES.pageRenderer.path)),
        ...(await runner.import<Pick<PageRenderer, PageShell>>(RUNNER_MODULES.pageShell.path)),
    };
    const markup = await runner.import<{ readonly markdownOf: Inline }>(RUNNER_MODULES.markup.path);
    const text = await runner.import<TextConverter>(RUNNER_MODULES.text.path);
    const surface = await runner.import<TranscriptRenderer>(RUNNER_MODULES.surface.path);
    const exported = new Map<string, readonly string[]>();
    return {
        author: company.COMPANY_OWNER,
        consent: `${terms.TRAINING_PERMISSION} ${terms.TRAINING_LICENSE_TERMS}`,
        exportSections: sectionReader(exported),
        exportText: exporter(renderer, visit, { ...text, ...surface }, exported),
        ids: Object.values(ids).filter((value): value is string => typeof value === "string"),
        inline: markup.markdownOf,
        links,
        main: site.MAIN_ID,
        name: strings.SITE_NAME,
        registry,
        renderer,
        runner,
        summary: strings.HOME_DESCRIPTION,
        visit,
    };
};
```
