Files
2026-05-27 23:47:53 +01:00

23 KiB

@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 — Provides factory functions and mutation helpers for ParseState — the stateful object that the TemplateEngine passes…
  • TemplateEngine — The TemplateEngine class is the central orchestrator of @suaveplan/templates. It manages a priority-sorted registry…
  • apply-plugin — Provides the core two-phase plugin application logic used by TemplateEngine for both synchronous and asynchronous ren…
  • error-codes — Defines and freezes the complete error-code registry for @suaveplan/templates using the defineCodes utility from `@…
  • template-error — Defines the structured error hierarchy for @suaveplan/templates. All errors extend SuaveplanError from `@suaveplan/…
  • otel — (no submodules)
  • ConditionalPlugin — Conditionally includes or excludes blocks of template content based on context values. The plugin evaluates a condition…
  • LoopPlugin — Iterates over an array in the context object and renders a sub-template once for each element, concatenating the result…
  • CounterPlugin — Provides stateful auto-incrementing counters within a single template rendering pass. Counters are scoped to the `Parse…
  • DateTimePlugin — Injects current date and time values into templates using a flexible, token-based formatting syntax. Because date-time…
  • UuidPlugin — Generates UUID v4 and URL-safe nano-ID strings inline within templates. Because ID generation depends on `crypto.random…
  • CaseTransformPlugin — Applies string case transformations to resolved context values. Supports eight case styles covering all common naming c…
  • RegexPlugin — Applies inline regular-expression operations to resolved context values. The plugin enables match extraction, capture g…
  • StringSplitPlugin — Splits a resolved string value on a configurable delimiter and returns a single element by index. This plugin solves th…
  • ArrayIndexPlugin — Resolves bracket-notation array-index references in templates. Handles {items[0]}, {items[-1]}, and nested `{users[…
  • DotNotationPlugin — Resolves simple variable references using dot-notation paths. This is the primary workhorse plugin for template renderi…
  • full — Provides the complete plugin suite in two variants: fullPreset() matches standardPreset() exactly — it is the recom…
  • minimal — Provides the lightest possible PluginPreset for the TemplateEngine. It bundles only two plugins — array-index and…
  • standard — Provides a comprehensive PluginPreset suitable for the majority of production use cases. It bundles all non-loop plug…
  • schemas — (no submodules)
  • types — (no submodules)
    • index — (no submodules)
  • escape — Provides three utility functions for handling escape sequences in template strings. These are low-level primitives used…
  • format — Provides core formatting utilities used by the dynamic plugins: zero-padded number/string padding, date formatting with…
  • nested-value — 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 — Iterates over an array in the context object and renders a sub-template once for each element, concatenating the result…
  • CounterPlugin — Provides stateful auto-incrementing counters within a single template rendering pass. Counters are scoped to the `Parse…
  • DateTimePlugin — Injects current date and time values into templates using a flexible, token-based formatting syntax. Because date-time…
  • UuidPlugin — Generates UUID v4 and URL-safe nano-ID strings inline within templates. Because ID generation depends on `crypto.random…
  • CaseTransformPlugin — Applies string case transformations to resolved context values. Supports eight case styles covering all common naming c…
  • RegexPlugin — Applies inline regular-expression operations to resolved context values. The plugin enables match extraction, capture g…
  • StringSplitPlugin — Splits a resolved string value on a configurable delimiter and returns a single element by index. This plugin solves th…
  • ArrayIndexPlugin — Resolves bracket-notation array-index references in templates. Handles {items[0]}, {items[-1]}, and nested `{users[…
  • DotNotationPlugin — Resolves simple variable references using dot-notation paths. This is the primary workhorse plugin for template renderi…
  • full — Provides the complete plugin suite in two variants: fullPreset() matches standardPreset() exactly — it is the recom…
  • minimal — Provides the lightest possible PluginPreset for the TemplateEngine. It bundles only two plugins — array-index and…
  • standard — Provides a comprehensive PluginPreset suitable for the majority of production use cases. It bundles all non-loop plug…
  • schemas — (no submodules)- templates.schemas — (no submodules)- types — (no submodules)- index — (no submodules)- escape — Provides three utility functions for handling escape sequences in template strings. These are low-level primitives used…
  • format — Provides core formatting utilities used by the dynamic plugins: zero-padded number/string padding, date formatting with…
  • nested-value — Provides three functions for traversing, checking, and mutating plain object trees using dot-notation or bracket-notati…
  • templates-otel — (no submodules)- ConditionalPlugin — Conditionally includes or excludes blocks of template content based on context values. The plugin evaluates a condition…
  • LoopPlugin — Iterates over an array in the context object and renders a sub-template once for each element, concatenating the result…
  • CounterPlugin — Provides stateful auto-incrementing counters within a single template rendering pass. Counters are scoped to the `Parse…
  • DateTimePlugin — Injects current date and time values into templates using a flexible, token-based formatting syntax. Because date-time…
  • UuidPlugin — Generates UUID v4 and URL-safe nano-ID strings inline within templates. Because ID generation depends on `crypto.random…
  • CaseTransformPlugin — Applies string case transformations to resolved context values. Supports eight case styles covering all common naming c…
  • RegexPlugin — Applies inline regular-expression operations to resolved context values. The plugin enables match extraction, capture g…
  • StringSplitPlugin — Splits a resolved string value on a configurable delimiter and returns a single element by index. This plugin solves th…
  • ArrayIndexPlugin — Resolves bracket-notation array-index references in templates. Handles {items[0]}, {items[-1]}, and nested `{users[…
  • DotNotationPlugin — Resolves simple variable references using dot-notation paths. This is the primary workhorse plugin for template renderi…
  • full — Provides the complete plugin suite in two variants: fullPreset() matches standardPreset() exactly — it is the recom…
  • minimal — Provides the lightest possible PluginPreset for the TemplateEngine. It bundles only two plugins — array-index and…
  • standard — Provides a comprehensive PluginPreset suitable for the majority of production use cases. It bundles all non-loop plug…
  • schemas — (no submodules)- templates.schemas — (no submodules)- types — (no submodules)- index — (no submodules)- escape — Provides three utility functions for handling escape sequences in template strings. These are low-level primitives used…
  • format — Provides core formatting utilities used by the dynamic plugins: zero-padded number/string padding, date formatting with…
  • nested-value — Provides three functions for traversing, checking, and mutating plain object trees using dot-notation or bracket-notati…
  • templates-otel — (no submodules)- ConditionalPlugin — Conditionally includes or excludes blocks of template content based on context values. The plugin evaluates a condition…
  • LoopPlugin — Iterates over an array in the context object and renders a sub-template once for each element, concatenating the result…
  • CounterPlugin — Provides stateful auto-incrementing counters within a single template rendering pass. Counters are scoped to the `Parse…
  • DateTimePlugin — Injects current date and time values into templates using a flexible, token-based formatting syntax. Because date-time…
  • UuidPlugin — Generates UUID v4 and URL-safe nano-ID strings inline within templates. Because ID generation depends on `crypto.random…
  • CaseTransformPlugin — Applies string case transformations to resolved context values. Supports eight case styles covering all common naming c…
  • RegexPlugin — Applies inline regular-expression operations to resolved context values. The plugin enables match extraction, capture g…
  • StringSplitPlugin — Splits a resolved string value on a configurable delimiter and returns a single element by index. This plugin solves th…
  • ArrayIndexPlugin — Resolves bracket-notation array-index references in templates. Handles {items[0]}, {items[-1]}, and nested `{users[…
  • DotNotationPlugin — Resolves simple variable references using dot-notation paths. This is the primary workhorse plugin for template renderi…
  • full — Provides the complete plugin suite in two variants: fullPreset() matches standardPreset() exactly — it is the recom…
  • minimal — Provides the lightest possible PluginPreset for the TemplateEngine. It bundles only two plugins — array-index and…
  • standard — Provides a comprehensive PluginPreset suitable for the majority of production use cases. It bundles all non-loop plug…
  • schemas — (no submodules)- templates.schemas — (no submodules)- types — (no submodules)- index — (no submodules)- escape — Provides three utility functions for handling escape sequences in template strings. These are low-level primitives used…
  • format — Provides core formatting utilities used by the dynamic plugins: zero-padded number/string padding, date formatting with…
  • nested-value — Provides three functions for traversing, checking, and mutating plain object trees using dot-notation or bracket-notati…
  • templates-otel — (no submodules)- ConditionalPlugin — Conditionally includes or excludes blocks of template content based on context values. The plugin evaluates a condition…
  • LoopPlugin — Iterates over an array in the context object and renders a sub-template once for each element, concatenating the result…
  • CounterPlugin — Provides stateful auto-incrementing counters within a single template rendering pass. Counters are scoped to the `Parse…
  • DateTimePlugin — Injects current date and time values into templates using a flexible, token-based formatting syntax. Because date-time…
  • UuidPlugin — Generates UUID v4 and URL-safe nano-ID strings inline within templates. Because ID generation depends on `crypto.random…
  • CaseTransformPlugin — Applies string case transformations to resolved context values. Supports eight case styles covering all common naming c…
  • RegexPlugin — Applies inline regular-expression operations to resolved context values. The plugin enables match extraction, capture g…
  • StringSplitPlugin — Splits a resolved string value on a configurable delimiter and returns a single element by index. This plugin solves th…
  • ArrayIndexPlugin — Resolves bracket-notation array-index references in templates. Handles {items[0]}, {items[-1]}, and nested `{users[…
  • DotNotationPlugin — Resolves simple variable references using dot-notation paths. This is the primary workhorse plugin for template renderi…
  • full — Provides the complete plugin suite in two variants: fullPreset() matches standardPreset() exactly — it is the recom…
  • minimal — Provides the lightest possible PluginPreset for the TemplateEngine. It bundles only two plugins — array-index and…
  • standard — Provides a comprehensive PluginPreset suitable for the majority of production use cases. It bundles all non-loop plug…
  • schemas — (no submodules) - templates.schemas — (no submodules)- types — (no submodules) - index — (no submodules)- escape — Provides three utility functions for handling escape sequences in template strings. These are low-level primitives used…
  • format — Provides core formatting utilities used by the dynamic plugins: zero-padded number/string padding, date formatting with…
  • nested-value — 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

bun add @suaveplan/templates

Quick Start

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 {name}, {user.email} LOW (20)
ArrayIndexPlugin {items[0]}, {arr[-1]} LOW (20)

Transforms

Plugin Module Priority
CaseTransformPlugin {key:pascal}, {val:snake} MEDIUM (50)
RegexPlugin {str:/pattern/flags} MEDIUM (50)
StringSplitPlugin {email:@[0]} MEDIUM (50)

Dynamic

Plugin Module Priority
CounterPlugin {_counter}, {_counter:name:1:3} HIGH (80)
DateTimePlugin {_date}, {_timestamp:s} HIGH (80)
UuidPlugin {_uuid}, {_nanoid:10} HIGH (80)

Control Flow

Plugin Module Priority
ConditionalPlugin {if:cond}...{/if} CRITICAL (95)
LoopPlugin {each:items:el}...{/each} CRITICAL (95)

Presets

Preset Module Included plugins
minimalPreset dot-notation + array-index 2
standardPreset all except loop 9
fullPreset same as standard 9
fullPresetWithLoops all 10 plugins 10

Engine API

Symbol Module
TemplateEngine Core engine class
ParseState Render state interface and helpers

Types & Schemas

Symbol Module
types All interfaces, enums, and SKIP_PLUGIN
templates.schemas Zod validation schemas

Utilities

Symbol Module
nested-value getNestedValue, hasNestedPath, setNestedValue
format pad, formatDate, generateUuid, generateNanoid
escape escapeTemplate, unescapeTemplate

Errors

Symbol Module
error-codes TEMPLATE_CODES registry
template-error TemplateError, PluginError, MaxDepthError

Writing a Custom Plugin

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:

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"