# core/resolvers/template.resolver.ts

> 36 lines of code and 10 definitions.

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

Listed in [core/resolvers](https://banes-lab.com/api/source/context/core/resolvers.md), after [core/resolvers/reference.resolver.ts](https://banes-lab.com/source/context/core/resolvers/reference.resolver.ts.md).

## Definitions

- `slotMarkOf` (lexical_declaration, line 5)
- `slotIsDangling` (lexical_declaration, line 41, exported)
- `outOfEnum` (lexical_declaration, line 13)
- `resolvePagTemplate` (lexical_declaration, line 31, exported)
- `slotViolation` (lexical_declaration, line 20)
- `substitute` (lexical_declaration, line 27)
- `suppliedValue` (lexical_declaration, line 9)
- `violations` (lexical_declaration, line 35, exported)
- `text` (lexical_declaration, line 36, exported)
- `unresolved` (lexical_declaration, line 37, exported)

## Contained in

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

## Uses

- [configuration/strings/grammar.strings.ts](https://banes-lab.com/source/context/configuration/strings/grammar.strings.ts.md)

## Used by

- [core/factories/grammar.factory.ts](https://banes-lab.com/source/context/core/factories/grammar.factory.ts.md)
- [core/validators/grammar.validator.ts](https://banes-lab.com/source/context/core/validators/grammar.validator.ts.md)

## Linked from

- [configuration/strings](https://banes-lab.com/anatomy/context/folder-context-configuration-strings.md)
- [core/factories](https://banes-lab.com/anatomy/context/folder-context-core-factories.md)
- [core/resolvers](https://banes-lab.com/anatomy/context/folder-context-core-resolvers.md)
- [core/validators](https://banes-lab.com/anatomy/context/folder-context-core-validators.md)

## Source

```typescript
import type { TemplateRecord, TemplateSlot } from "#types/grammar.types";
import { enumViolation, missingRequiredSlot } from "#configuration/strings/grammar.strings";
import type { TemplateResolveResult } from "#types/grammar.document.types";

const slotMarkOf = function slotMarkOf(name: string): string {
    return `{${name}}`;
};

const suppliedValue = function suppliedValue(slots: Record<string, string>, name: string): string {
    return Object.hasOwn(slots, name) ? (slots[name] ?? "") : "";
};

const outOfEnum = function outOfEnum(slot: TemplateSlot, value: string): boolean {
    if (value === "" || !Array.isArray(slot.enum) || slot.enum.length === 0) {
        return false;
    }
    return !slot.enum.includes(value);
};

const slotViolation = function slotViolation(slot: TemplateSlot, value: string): string | null {
    if (slot.required && value === "") {
        return missingRequiredSlot(slot.name);
    }
    return outOfEnum(slot, value) ? enumViolation(slot.name) : null;
};

const substitute = function substitute(body: string, slots: Record<string, string>): string {
    return Object.entries(slots).reduce((text, [name, value]) => text.replaceAll(slotMarkOf(name), value), body);
};

export const resolvePagTemplate = function resolvePagTemplate(
    template: TemplateRecord,
    slots: Record<string, string>,
): TemplateResolveResult {
    const violations = template.slots.flatMap((slot) => slotViolation(slot, suppliedValue(slots, slot.name)) ?? []);
    const text = substitute(template.body, slots);
    const unresolved = template.slots.filter((slot) => text.includes(slotMarkOf(slot.name))).map((slot) => slot.name);
    return { text, unresolved, violations };
};

export const slotIsDangling = function slotIsDangling(template: TemplateRecord, slot: TemplateSlot): boolean {
    return !template.body.includes(slotMarkOf(slot.name));
};
```
