251 lines
23 KiB
Markdown
251 lines
23 KiB
Markdown
# @suaveplan/templates - Plugin-based template engine with type-safe contexts, variable interpolation, transform and control-flow plugins, and preset bundles.
|
|
|
|
## Summary
|
|
|
|
A plugin-based string template engine for TypeScript. Resolves `{variable}` and `{variable:transform}` tokens in strings using a priority-sorted, regex-scan pipeline — no eval, no AST parser overhead. Designed for filling templates in notification messages, email subjects, file name patterns, code generation, and similar string-expansion tasks. Ships nine built-in plugins: dot-notation and array-index variable resolution; case, regex, and split transforms; counter, datetime, and UUID dynamic generators; and conditional/loop control flow. Three preset bundles (`minimal`, `standard`, `full`) let you trade features for bundle size. The injectable `Clock` and `Rng` make every rendering call deterministic in tests. Do not confuse with `@suaveplan/i18n`, which handles locale-keyed message catalogs and plural forms — use this package when your source strings are already in the target language and you only need token substitution.
|
|
|
|
<!-- begin:auto-docs -->
|
|
## Documentation tree
|
|
|
|
- [ParseState](./src/engine/ParseState.md) — Provides factory functions and mutation helpers for `ParseState` — the stateful object that the `TemplateEngine` passes…
|
|
- [TemplateEngine](./src/engine/TemplateEngine.md) — The `TemplateEngine` class is the central orchestrator of `@suaveplan/templates`. It manages a priority-sorted registry…
|
|
- [apply-plugin](./src/engine/apply-plugin.md) — Provides the core two-phase plugin application logic used by `TemplateEngine` for both synchronous and asynchronous ren…
|
|
- [error-codes](./src/errors/error-codes.md) — Defines and freezes the complete error-code registry for `@suaveplan/templates` using the `defineCodes` utility from `@…
|
|
- [template-error](./src/errors/template-error.md) — Defines the structured error hierarchy for `@suaveplan/templates`. All errors extend `SuaveplanError` from `@suaveplan/…
|
|
- [otel](./src/otel/templates-otel.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
|
- [templates-otel](./src/otel/templates-otel.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
|
- [ConditionalPlugin](./src/plugins/control/ConditionalPlugin.md) — Conditionally includes or excludes blocks of template content based on context values. The plugin evaluates a condition…
|
|
- [LoopPlugin](./src/plugins/control/LoopPlugin.md) — Iterates over an array in the context object and renders a sub-template once for each element, concatenating the result…
|
|
- [CounterPlugin](./src/plugins/dynamic/CounterPlugin.md) — Provides stateful auto-incrementing counters within a single template rendering pass. Counters are scoped to the `Parse…
|
|
- [DateTimePlugin](./src/plugins/dynamic/DateTimePlugin.md) — Injects current date and time values into templates using a flexible, token-based formatting syntax. Because date-time…
|
|
- [UuidPlugin](./src/plugins/dynamic/UuidPlugin.md) — Generates UUID v4 and URL-safe nano-ID strings inline within templates. Because ID generation depends on `crypto.random…
|
|
- [CaseTransformPlugin](./src/plugins/transform/CaseTransformPlugin.md) — Applies string case transformations to resolved context values. Supports eight case styles covering all common naming c…
|
|
- [RegexPlugin](./src/plugins/transform/RegexPlugin.md) — Applies inline regular-expression operations to resolved context values. The plugin enables match extraction, capture g…
|
|
- [StringSplitPlugin](./src/plugins/transform/StringSplitPlugin.md) — Splits a resolved string value on a configurable delimiter and returns a single element by index. This plugin solves th…
|
|
- [ArrayIndexPlugin](./src/plugins/variables/ArrayIndexPlugin.md) — Resolves bracket-notation array-index references in templates. Handles `{items[0]}`, `{items[-1]}`, and nested `{users[…
|
|
- [DotNotationPlugin](./src/plugins/variables/DotNotationPlugin.md) — Resolves simple variable references using dot-notation paths. This is the primary workhorse plugin for template renderi…
|
|
- [full](./src/presets/full.md) — Provides the complete plugin suite in two variants: `fullPreset()` matches `standardPreset()` exactly — it is the recom…
|
|
- [minimal](./src/presets/minimal.md) — Provides the lightest possible `PluginPreset` for the `TemplateEngine`. It bundles only two plugins — `array-index` and…
|
|
- [standard](./src/presets/standard.md) — Provides a comprehensive `PluginPreset` suitable for the majority of production use cases. It bundles all non-loop plug…
|
|
- [schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
|
- [templates.schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
|
- [types](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
|
- [index](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
|
- [escape](./src/utils/escape.md) — Provides three utility functions for handling escape sequences in template strings. These are low-level primitives used…
|
|
- [format](./src/utils/format.md) — Provides core formatting utilities used by the dynamic plugins: zero-padded number/string padding, date formatting with…
|
|
- [nested-value](./src/utils/nested-value.md) — Provides three functions for traversing, checking, and mutating plain object trees using dot-notation or bracket-notati…
|
|
<!-- end:auto-docs --> - [templates-otel](./src/otel/templates-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [ConditionalPlugin](./src/plugins/control/ConditionalPlugin.md) — Conditionally includes or excludes blocks of template content based on context values. The plugin evaluates a condition…
|
|
- [LoopPlugin](./src/plugins/control/LoopPlugin.md) — Iterates over an array in the context object and renders a sub-template once for each element, concatenating the result…
|
|
- [CounterPlugin](./src/plugins/dynamic/CounterPlugin.md) — Provides stateful auto-incrementing counters within a single template rendering pass. Counters are scoped to the `Parse…
|
|
- [DateTimePlugin](./src/plugins/dynamic/DateTimePlugin.md) — Injects current date and time values into templates using a flexible, token-based formatting syntax. Because date-time…
|
|
- [UuidPlugin](./src/plugins/dynamic/UuidPlugin.md) — Generates UUID v4 and URL-safe nano-ID strings inline within templates. Because ID generation depends on `crypto.random…
|
|
- [CaseTransformPlugin](./src/plugins/transform/CaseTransformPlugin.md) — Applies string case transformations to resolved context values. Supports eight case styles covering all common naming c…
|
|
- [RegexPlugin](./src/plugins/transform/RegexPlugin.md) — Applies inline regular-expression operations to resolved context values. The plugin enables match extraction, capture g…
|
|
- [StringSplitPlugin](./src/plugins/transform/StringSplitPlugin.md) — Splits a resolved string value on a configurable delimiter and returns a single element by index. This plugin solves th…
|
|
- [ArrayIndexPlugin](./src/plugins/variables/ArrayIndexPlugin.md) — Resolves bracket-notation array-index references in templates. Handles `{items[0]}`, `{items[-1]}`, and nested `{users[…
|
|
- [DotNotationPlugin](./src/plugins/variables/DotNotationPlugin.md) — Resolves simple variable references using dot-notation paths. This is the primary workhorse plugin for template renderi…
|
|
- [full](./src/presets/full.md) — Provides the complete plugin suite in two variants: `fullPreset()` matches `standardPreset()` exactly — it is the recom…
|
|
- [minimal](./src/presets/minimal.md) — Provides the lightest possible `PluginPreset` for the `TemplateEngine`. It bundles only two plugins — `array-index` and…
|
|
- [standard](./src/presets/standard.md) — Provides a comprehensive `PluginPreset` suitable for the majority of production use cases. It bundles all non-loop plug…
|
|
- [schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [templates.schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_- [index](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_- [escape](./src/utils/escape.md) — Provides three utility functions for handling escape sequences in template strings. These are low-level primitives used…
|
|
- [format](./src/utils/format.md) — Provides core formatting utilities used by the dynamic plugins: zero-padded number/string padding, date formatting with…
|
|
- [nested-value](./src/utils/nested-value.md) — Provides three functions for traversing, checking, and mutating plain object trees using dot-notation or bracket-notati…
|
|
- [templates-otel](./src/otel/templates-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [ConditionalPlugin](./src/plugins/control/ConditionalPlugin.md) — Conditionally includes or excludes blocks of template content based on context values. The plugin evaluates a condition…
|
|
- [LoopPlugin](./src/plugins/control/LoopPlugin.md) — Iterates over an array in the context object and renders a sub-template once for each element, concatenating the result…
|
|
- [CounterPlugin](./src/plugins/dynamic/CounterPlugin.md) — Provides stateful auto-incrementing counters within a single template rendering pass. Counters are scoped to the `Parse…
|
|
- [DateTimePlugin](./src/plugins/dynamic/DateTimePlugin.md) — Injects current date and time values into templates using a flexible, token-based formatting syntax. Because date-time…
|
|
- [UuidPlugin](./src/plugins/dynamic/UuidPlugin.md) — Generates UUID v4 and URL-safe nano-ID strings inline within templates. Because ID generation depends on `crypto.random…
|
|
- [CaseTransformPlugin](./src/plugins/transform/CaseTransformPlugin.md) — Applies string case transformations to resolved context values. Supports eight case styles covering all common naming c…
|
|
- [RegexPlugin](./src/plugins/transform/RegexPlugin.md) — Applies inline regular-expression operations to resolved context values. The plugin enables match extraction, capture g…
|
|
- [StringSplitPlugin](./src/plugins/transform/StringSplitPlugin.md) — Splits a resolved string value on a configurable delimiter and returns a single element by index. This plugin solves th…
|
|
- [ArrayIndexPlugin](./src/plugins/variables/ArrayIndexPlugin.md) — Resolves bracket-notation array-index references in templates. Handles `{items[0]}`, `{items[-1]}`, and nested `{users[…
|
|
- [DotNotationPlugin](./src/plugins/variables/DotNotationPlugin.md) — Resolves simple variable references using dot-notation paths. This is the primary workhorse plugin for template renderi…
|
|
- [full](./src/presets/full.md) — Provides the complete plugin suite in two variants: `fullPreset()` matches `standardPreset()` exactly — it is the recom…
|
|
- [minimal](./src/presets/minimal.md) — Provides the lightest possible `PluginPreset` for the `TemplateEngine`. It bundles only two plugins — `array-index` and…
|
|
- [standard](./src/presets/standard.md) — Provides a comprehensive `PluginPreset` suitable for the majority of production use cases. It bundles all non-loop plug…
|
|
- [schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [templates.schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_- [index](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_- [escape](./src/utils/escape.md) — Provides three utility functions for handling escape sequences in template strings. These are low-level primitives used…
|
|
- [format](./src/utils/format.md) — Provides core formatting utilities used by the dynamic plugins: zero-padded number/string padding, date formatting with…
|
|
- [nested-value](./src/utils/nested-value.md) — Provides three functions for traversing, checking, and mutating plain object trees using dot-notation or bracket-notati…
|
|
- [templates-otel](./src/otel/templates-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [ConditionalPlugin](./src/plugins/control/ConditionalPlugin.md) — Conditionally includes or excludes blocks of template content based on context values. The plugin evaluates a condition…
|
|
- [LoopPlugin](./src/plugins/control/LoopPlugin.md) — Iterates over an array in the context object and renders a sub-template once for each element, concatenating the result…
|
|
- [CounterPlugin](./src/plugins/dynamic/CounterPlugin.md) — Provides stateful auto-incrementing counters within a single template rendering pass. Counters are scoped to the `Parse…
|
|
- [DateTimePlugin](./src/plugins/dynamic/DateTimePlugin.md) — Injects current date and time values into templates using a flexible, token-based formatting syntax. Because date-time…
|
|
- [UuidPlugin](./src/plugins/dynamic/UuidPlugin.md) — Generates UUID v4 and URL-safe nano-ID strings inline within templates. Because ID generation depends on `crypto.random…
|
|
- [CaseTransformPlugin](./src/plugins/transform/CaseTransformPlugin.md) — Applies string case transformations to resolved context values. Supports eight case styles covering all common naming c…
|
|
- [RegexPlugin](./src/plugins/transform/RegexPlugin.md) — Applies inline regular-expression operations to resolved context values. The plugin enables match extraction, capture g…
|
|
- [StringSplitPlugin](./src/plugins/transform/StringSplitPlugin.md) — Splits a resolved string value on a configurable delimiter and returns a single element by index. This plugin solves th…
|
|
- [ArrayIndexPlugin](./src/plugins/variables/ArrayIndexPlugin.md) — Resolves bracket-notation array-index references in templates. Handles `{items[0]}`, `{items[-1]}`, and nested `{users[…
|
|
- [DotNotationPlugin](./src/plugins/variables/DotNotationPlugin.md) — Resolves simple variable references using dot-notation paths. This is the primary workhorse plugin for template renderi…
|
|
- [full](./src/presets/full.md) — Provides the complete plugin suite in two variants: `fullPreset()` matches `standardPreset()` exactly — it is the recom…
|
|
- [minimal](./src/presets/minimal.md) — Provides the lightest possible `PluginPreset` for the `TemplateEngine`. It bundles only two plugins — `array-index` and…
|
|
- [standard](./src/presets/standard.md) — Provides a comprehensive `PluginPreset` suitable for the majority of production use cases. It bundles all non-loop plug…
|
|
- [schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [templates.schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_- [index](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_- [escape](./src/utils/escape.md) — Provides three utility functions for handling escape sequences in template strings. These are low-level primitives used…
|
|
- [format](./src/utils/format.md) — Provides core formatting utilities used by the dynamic plugins: zero-padded number/string padding, date formatting with…
|
|
- [nested-value](./src/utils/nested-value.md) — Provides three functions for traversing, checking, and mutating plain object trees using dot-notation or bracket-notati…
|
|
- [templates-otel](./src/otel/templates-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [ConditionalPlugin](./src/plugins/control/ConditionalPlugin.md) — Conditionally includes or excludes blocks of template content based on context values. The plugin evaluates a condition…
|
|
- [LoopPlugin](./src/plugins/control/LoopPlugin.md) — Iterates over an array in the context object and renders a sub-template once for each element, concatenating the result…
|
|
- [CounterPlugin](./src/plugins/dynamic/CounterPlugin.md) — Provides stateful auto-incrementing counters within a single template rendering pass. Counters are scoped to the `Parse…
|
|
- [DateTimePlugin](./src/plugins/dynamic/DateTimePlugin.md) — Injects current date and time values into templates using a flexible, token-based formatting syntax. Because date-time…
|
|
- [UuidPlugin](./src/plugins/dynamic/UuidPlugin.md) — Generates UUID v4 and URL-safe nano-ID strings inline within templates. Because ID generation depends on `crypto.random…
|
|
- [CaseTransformPlugin](./src/plugins/transform/CaseTransformPlugin.md) — Applies string case transformations to resolved context values. Supports eight case styles covering all common naming c…
|
|
- [RegexPlugin](./src/plugins/transform/RegexPlugin.md) — Applies inline regular-expression operations to resolved context values. The plugin enables match extraction, capture g…
|
|
- [StringSplitPlugin](./src/plugins/transform/StringSplitPlugin.md) — Splits a resolved string value on a configurable delimiter and returns a single element by index. This plugin solves th…
|
|
- [ArrayIndexPlugin](./src/plugins/variables/ArrayIndexPlugin.md) — Resolves bracket-notation array-index references in templates. Handles `{items[0]}`, `{items[-1]}`, and nested `{users[…
|
|
- [DotNotationPlugin](./src/plugins/variables/DotNotationPlugin.md) — Resolves simple variable references using dot-notation paths. This is the primary workhorse plugin for template renderi…
|
|
- [full](./src/presets/full.md) — Provides the complete plugin suite in two variants: `fullPreset()` matches `standardPreset()` exactly — it is the recom…
|
|
- [minimal](./src/presets/minimal.md) — Provides the lightest possible `PluginPreset` for the `TemplateEngine`. It bundles only two plugins — `array-index` and…
|
|
- [standard](./src/presets/standard.md) — Provides a comprehensive `PluginPreset` suitable for the majority of production use cases. It bundles all non-loop plug…
|
|
- [schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_ - [templates.schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_ - [index](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_- [escape](./src/utils/escape.md) — Provides three utility functions for handling escape sequences in template strings. These are low-level primitives used…
|
|
- [format](./src/utils/format.md) — Provides core formatting utilities used by the dynamic plugins: zero-padded number/string padding, date formatting with…
|
|
- [nested-value](./src/utils/nested-value.md) — Provides three functions for traversing, checking, and mutating plain object trees using dot-notation or bracket-notati…
|
|
|
|
## Features
|
|
|
|
- Priority-sorted plugin pipeline (CRITICAL → HIGH → MEDIUM → LOW → MINIMAL)
|
|
- 9 built-in plugins covering variables, transforms, dynamic generation, and control flow
|
|
- 3 preset bundles (minimal, standard, full)
|
|
- Synchronous and asynchronous rendering paths
|
|
- `compile()` for reusable template objects
|
|
- Injectable `Clock` and `Rng` for deterministic testing
|
|
- `ParseState` per render pass — counters, cache, custom plugin state
|
|
- Zod schemas for runtime configuration validation
|
|
- 100% test coverage on all modules
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
bun add @suaveplan/templates
|
|
```
|
|
|
|
## Quick Start
|
|
|
|
```ts
|
|
import { TemplateEngine, standardPreset } from "@suaveplan/templates";
|
|
|
|
const engine = new TemplateEngine().usePreset(standardPreset());
|
|
|
|
engine.parse("Hello, {name:pascal}! You have {count} messages.", {
|
|
name: "alice smith",
|
|
count: 5,
|
|
});
|
|
// → "Hello, AliceSmith! You have 5 messages."
|
|
```
|
|
|
|
## Plugins
|
|
|
|
### Variable Resolution
|
|
|
|
| Plugin | Module | Priority |
|
|
|--------|--------|----------|
|
|
| [DotNotationPlugin](src/plugins/variables/DotNotationPlugin.md) | `{name}`, `{user.email}` | LOW (20) |
|
|
| [ArrayIndexPlugin](src/plugins/variables/ArrayIndexPlugin.md) | `{items[0]}`, `{arr[-1]}` | LOW (20) |
|
|
|
|
### Transforms
|
|
|
|
| Plugin | Module | Priority |
|
|
|--------|--------|----------|
|
|
| [CaseTransformPlugin](src/plugins/transform/CaseTransformPlugin.md) | `{key:pascal}`, `{val:snake}` | MEDIUM (50) |
|
|
| [RegexPlugin](src/plugins/transform/RegexPlugin.md) | `{str:/pattern/flags}` | MEDIUM (50) |
|
|
| [StringSplitPlugin](src/plugins/transform/StringSplitPlugin.md) | `{email:@[0]}` | MEDIUM (50) |
|
|
|
|
### Dynamic
|
|
|
|
| Plugin | Module | Priority |
|
|
|--------|--------|----------|
|
|
| [CounterPlugin](src/plugins/dynamic/CounterPlugin.md) | `{_counter}`, `{_counter:name:1:3}` | HIGH (80) |
|
|
| [DateTimePlugin](src/plugins/dynamic/DateTimePlugin.md) | `{_date}`, `{_timestamp:s}` | HIGH (80) |
|
|
| [UuidPlugin](src/plugins/dynamic/UuidPlugin.md) | `{_uuid}`, `{_nanoid:10}` | HIGH (80) |
|
|
|
|
### Control Flow
|
|
|
|
| Plugin | Module | Priority |
|
|
|--------|--------|----------|
|
|
| [ConditionalPlugin](src/plugins/control/ConditionalPlugin.md) | `{if:cond}...{/if}` | CRITICAL (95) |
|
|
| [LoopPlugin](src/plugins/control/LoopPlugin.md) | `{each:items:el}...{/each}` | CRITICAL (95) |
|
|
|
|
## Presets
|
|
|
|
| Preset | Module | Included plugins |
|
|
|--------|--------|-----------------|
|
|
| [minimalPreset](src/presets/minimal.md) | dot-notation + array-index | 2 |
|
|
| [standardPreset](src/presets/standard.md) | all except loop | 9 |
|
|
| [fullPreset](src/presets/full.md) | same as standard | 9 |
|
|
| [fullPresetWithLoops](src/presets/full.md) | all 10 plugins | 10 |
|
|
|
|
## Engine API
|
|
|
|
| Symbol | Module |
|
|
|--------|--------|
|
|
| [TemplateEngine](src/engine/TemplateEngine.md) | Core engine class |
|
|
| [ParseState](src/engine/ParseState.md) | Render state interface and helpers |
|
|
|
|
## Types & Schemas
|
|
|
|
| Symbol | Module |
|
|
|--------|--------|
|
|
| [types](src/types/index.md) | All interfaces, enums, and `SKIP_PLUGIN` |
|
|
| [templates.schemas](src/schemas/templates.schemas.md) | Zod validation schemas |
|
|
|
|
## Utilities
|
|
|
|
| Symbol | Module |
|
|
|--------|--------|
|
|
| [nested-value](src/utils/nested-value.md) | `getNestedValue`, `hasNestedPath`, `setNestedValue` |
|
|
| [format](src/utils/format.md) | `pad`, `formatDate`, `generateUuid`, `generateNanoid` |
|
|
| [escape](src/utils/escape.md) | `escapeTemplate`, `unescapeTemplate` |
|
|
|
|
## Errors
|
|
|
|
| Symbol | Module |
|
|
|--------|--------|
|
|
| [error-codes](src/errors/error-codes.md) | `TEMPLATE_CODES` registry |
|
|
| [template-error](src/errors/template-error.md) | `TemplateError`, `PluginError`, `MaxDepthError` |
|
|
|
|
## Writing a Custom Plugin
|
|
|
|
```ts
|
|
import type { Plugin, PluginMatch, TemplateContext } from "@suaveplan/templates";
|
|
import { PluginPriority, SKIP_PLUGIN } from "@suaveplan/templates";
|
|
|
|
function createUpperBoldPlugin<TCtx extends TemplateContext>(): Plugin<TCtx> {
|
|
return {
|
|
name: "upper-bold",
|
|
priority: PluginPriority.MEDIUM,
|
|
pattern: /\{bold:([^}]+)\}/g,
|
|
transform(match: PluginMatch): string | typeof SKIP_PLUGIN {
|
|
const text = match.groups?.[1];
|
|
if (!text) return SKIP_PLUGIN;
|
|
return `**${text.toUpperCase()}**`;
|
|
},
|
|
};
|
|
}
|
|
|
|
const engine = new TemplateEngine().use(createUpperBoldPlugin());
|
|
engine.parse("Welcome {bold:alice}!", {});
|
|
// → "Welcome **ALICE**!"
|
|
```
|
|
|
|
## Loop Support
|
|
|
|
Loops require injecting the engine's internal render function:
|
|
|
|
```ts
|
|
import { TemplateEngine, fullPresetWithLoops } from "@suaveplan/templates";
|
|
import type { ParseState } from "@suaveplan/templates";
|
|
|
|
const engine = new TemplateEngine();
|
|
engine.usePreset(
|
|
fullPresetWithLoops(
|
|
(t: string, c: Record<string, unknown>, s: ParseState) =>
|
|
(engine as unknown as { parseInternal: typeof fn }).parseInternal(t, c, s)
|
|
)
|
|
);
|
|
|
|
engine.parse(
|
|
"{each:items:i}{_number}. {i.name}\n{/each}",
|
|
{ items: [{ name: "Alice" }, { name: "Bob" }] }
|
|
);
|
|
// → "1. Alice\n2. Bob\n"
|
|
```
|
|
|