# core/converters/template.converter.ts

> 46 lines of code and 15 definitions.

Tree: Site tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/tree#file-core-converters-template-converter-ts
Source text: https://banes-lab.com/source/tree/core/converters/template.converter.ts.txt

Listed in [core/converters](https://banes-lab.com/api/source/tree/core/converters.md), after [core/converters/surface.converter.ts](https://banes-lab.com/source/tree/core/converters/surface.converter.ts.md) and before [core/converters/text.converter.ts](https://banes-lab.com/source/tree/core/converters/text.converter.ts.md).

## Definitions

- `templatePartsOf` (lexical_declaration, line 10, exported)
- `holes` (lexical_declaration, line 14, exported)
- `literals` (lexical_declaration, line 15, exported)
- `at` (lexical_declaration, line 16, exported)
- `open` (lexical_declaration, line 17, exported)
- `close` (lexical_declaration, line 18, exported)
- `bindingValueOf` (lexical_declaration, line 30)
- `head` (lexical_declaration, line 31)
- `start` (lexical_declaration, line 32)
- `quote` (lexical_declaration, line 33)
- `valueStart` (lexical_declaration, line 37)
- `end` (lexical_declaration, line 38)
- `value` (lexical_declaration, line 39)
- `foldedOf` (lexical_declaration, line 43, exported)
- `values` (lexical_declaration, line 44, exported)

## Source

```typescript
import {
    BINDING_ASSIGN,
    BINDING_HEAD,
    HOLE_CLOSE,
    HOLE_OPEN,
    QUOTE_MARKS,
} from "#configuration/constants/anatomy.constants";
import type { TemplateParts } from "#types/code.types";

export const templatePartsOf = function templatePartsOf(text: string): TemplateParts | null {
    if (!text.includes(HOLE_OPEN)) {
        return null;
    }
    const holes: string[] = [];
    const literals: string[] = [];
    let at = 0;
    let open = text.indexOf(HOLE_OPEN);
    let close = open === -1 ? -1 : text.indexOf(HOLE_CLOSE, open + HOLE_OPEN.length);
    while (open !== -1 && close !== -1) {
        literals.push(text.slice(at, open));
        holes.push(text.slice(open + HOLE_OPEN.length, close).trim());
        at = close + HOLE_CLOSE.length;
        open = text.indexOf(HOLE_OPEN, at);
        close = open === -1 ? -1 : text.indexOf(HOLE_CLOSE, open + HOLE_OPEN.length);
    }
    literals.push(text.slice(at));
    return { holes, literals };
};

const bindingValueOf = function bindingValueOf(source: string, name: string): string | null {
    const head = BINDING_HEAD + name + BINDING_ASSIGN;
    const start = source.indexOf(head);
    const quote = start === -1 ? "" : source.charAt(start + head.length);
    if (quote.length === 0 || !QUOTE_MARKS.includes(quote)) {
        return null;
    }
    const valueStart = start + head.length + quote.length;
    const end = source.indexOf(quote, valueStart);
    const value = end === -1 ? "" : source.slice(valueStart, end);
    return value.length === 0 || value.includes(HOLE_OPEN) ? null : value;
};

export const foldedOf = function foldedOf(parts: TemplateParts, source: string): string | null {
    const values = parts.holes.map((hole) => bindingValueOf(source, hole));
    if (values.some((value) => value === null)) {
        return null;
    }
    return parts.literals.map((literal, at) => literal + (values.at(at) ?? "")).join("");
};
```
