# @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. ## 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) — _(no submodules)_ - [templates-otel](./src/otel/templates-otel.md) — _(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) — _(no submodules)_ - [templates.schemas](./src/schemas/templates.schemas.md) — _(no submodules)_ - [types](./src/types/index.md) — _(no submodules)_ - [index](./src/types/index.md) — _(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) — _(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) — _(no submodules)_- [templates.schemas](./src/schemas/templates.schemas.md) — _(no submodules)_- [types](./src/types/index.md) — _(no submodules)_- [index](./src/types/index.md) — _(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) — _(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) — _(no submodules)_- [templates.schemas](./src/schemas/templates.schemas.md) — _(no submodules)_- [types](./src/types/index.md) — _(no submodules)_- [index](./src/types/index.md) — _(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) — _(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) — _(no submodules)_- [templates.schemas](./src/schemas/templates.schemas.md) — _(no submodules)_- [types](./src/types/index.md) — _(no submodules)_- [index](./src/types/index.md) — _(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) — _(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) — _(no submodules)_ - [templates.schemas](./src/schemas/templates.schemas.md) — _(no submodules)_- [types](./src/types/index.md) — _(no submodules)_ - [index](./src/types/index.md) — _(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(): Plugin { 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, 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" ```