# core/loaders/grammar.loader.ts

> 89 lines of code and 17 definitions.

Tree: GovLab Context
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/context#file-context-core-loaders-grammar-loader-ts
Source text: https://banes-lab.com/source/context/core/loaders/grammar.loader.ts.txt

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

## Definitions

- `terminalsOf` (lexical_declaration, line 59)
- `fileOf` (lexical_declaration, line 34)
- `loadBundledData` (lexical_declaration, line 64, exported)
- `GrammarLoad` (interface_declaration, line 28)
- `parsed` (lexical_declaration, line 35)
- `tracked` (lexical_declaration, line 40)
- `declaredRecords` (lexical_declaration, line 45)
- `terminals` (lexical_declaration, line 60)
- `load` (lexical_declaration, line 65, exported)
- `productionsFile` (lexical_declaration, line 66, exported)
- `keywords` (lexical_declaration, line 67, exported)
- `documentTypes` (lexical_declaration, line 73, exported)
- `productions` (lexical_declaration, line 79, exported)
- `template` (lexical_declaration, line 85, exported)
- `raws` (lexical_declaration, line 86, exported)
- `templates` (lexical_declaration, line 87, exported)
- `record` (lexical_declaration, line 89, exported)

## Contained in

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

## Uses

- [core/predicates/record.predicate.ts](https://banes-lab.com/source/context/core/predicates/record.predicate.ts.md)

## Linked from

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

## Source

```typescript
import { CHECK_KEY, RECORDS_KEY } from "#configuration/constants/ontology.constants";
import {
    DOCUMENT_TYPES_FILE,
    KEYWORDS_FILE,
    PRODUCTIONS_FILE,
    TEMPLATES_SOURCE,
    TEMPLATE_FILE_PREFIX,
    TERMINALS_KEY,
} from "#configuration/constants/grammar.constants";
import type { GrammarFile, PagData } from "#types/grammar.types";
import {
    normalizeDocumentType,
    normalizeKeyword,
    normalizeProduction,
    normalizeTemplate,
    refused,
} from "#core/normalizers/grammar.normalizer";
import { readJsonDir, readJsonFile } from "#core/loaders/ontology.loader";
import type { CheckTable } from "#core/stores/check.store";
import { PAG_KINDS } from "#configuration/schemas/grammar.schema";
import type { ReadAudit } from "#core/observers/record.observer";
import { absolutePath } from "@ssot/paths";
import { asString } from "#core/normalizers/field.normalizer";
import { isObject } from "#core/predicates/record.predicate";
import { join } from "node:path";
import { keywordIdOf } from "#core/selectors/grammar.selector";

interface GrammarLoad {
    audit: ReadAudit;
    checks: CheckTable;
    dir: string;
}

const fileOf = function fileOf(load: GrammarLoad, source: string, kind: string): GrammarFile {
    const parsed = readJsonFile(join(load.dir, source));
    if (!isObject(parsed)) {
        load.audit.reject(parsed, source);
        return { kind, object: {}, source };
    }
    const tracked = load.audit.track(parsed, source);
    load.audit.attribution(tracked);
    return { kind, object: tracked, source };
};

const declaredRecords = function declaredRecords<T>(
    load: GrammarLoad,
    file: GrammarFile,
    normalize: (raw: Record<string, unknown>) => T,
    idOf: (record: T) => string,
): T[] {
    load.checks.forKind(file.kind, file.object[CHECK_KEY]);
    return load.audit.records(file.object[RECORDS_KEY], file.source).map((raw) => {
        const record = normalize(raw);
        load.checks.forRecord(file.kind, idOf(record), raw[CHECK_KEY]);
        return record;
    });
};

const terminalsOf = function terminalsOf(file: GrammarFile): string[] {
    const terminals = file.object[TERMINALS_KEY];
    return Array.isArray(terminals) ? terminals.map(asString) : [];
};

export const loadBundledData = function loadBundledData(audit: ReadAudit, checks: CheckTable): PagData {
    const load: GrammarLoad = { audit, checks, dir: absolutePath("govlab.context.grammar") };
    const productionsFile = fileOf(load, PRODUCTIONS_FILE, PAG_KINDS.production);
    const keywords = declaredRecords(
        load,
        fileOf(load, KEYWORDS_FILE, PAG_KINDS.keyword),
        refused(PAG_KINDS.keyword, normalizeKeyword),
        keywordIdOf,
    );
    const documentTypes = declaredRecords(
        load,
        fileOf(load, DOCUMENT_TYPES_FILE, PAG_KINDS.documentType),
        refused(PAG_KINDS.documentType, normalizeDocumentType),
        (record) => record.type,
    );
    const productions = declaredRecords(
        load,
        productionsFile,
        refused(PAG_KINDS.production, normalizeProduction),
        (record) => record.lhs,
    );
    const template = refused(PAG_KINDS.template, normalizeTemplate);
    const raws = readJsonDir(load.dir, { only: (name) => name.startsWith(TEMPLATE_FILE_PREFIX) });
    const templates = audit.records(raws, TEMPLATES_SOURCE).map((raw) => {
        audit.attribution(raw);
        const record = template(raw);
        checks.forRecord(PAG_KINDS.template, record.type, raw[CHECK_KEY]);
        return record;
    });
    return { documentTypes, keywords, productions, templates, terminals: terminalsOf(productionsFile) };
};
```
