Title: Text Replacer
Description: Powerful string template processor with variables, control structures, and transformation pipes
Tags: text-replacer, templates, variables, pipes
---
The Text Replacer is a powerful, extensible string template processor that supports advanced template features including variable replacement, control structures, and transformation pipes.
Table of Contents
Overview
Basic Usage
Template Syntax
Variable Replacement
Control Structures
Transformation Pipes
Advanced Features
API Reference
Overview
The Text Replacer uses a template syntax with #{...} delimiters and supports:
Variable replacement with nested property access
Control structures (if/unless/each loops)
Transformation pipes for data formatting
Recursive variable resolution
Template Syntax
Variable Expressions
Variables can be accessed using either the explicit #{show ...} syntax or the shortcut #{...} syntax:
// Explicit syntax (traditional)
#{show userName}
#{show user.name}
#{show user.profile.email}
// Shortcut syntax (recommended for simplicity)
#{userName}
#{user.name}
#{user.profile.email}
// Both work with nested properties
#{show address.street}
#{address.street} // Shortcut - cleaner and easier to read
// Both work with array access
#{show users.0.name}
#{users.0.name} // Shortcut
// Both work with transformation pipes
#{show price | round:2}
#{price | round:2} // Shortcut
#{show date | format:dd.MM.yyyy}
#{date | format:dd.MM.yyyy} // Shortcut
Control Structures
Conditional Blocks
Control structures still use explicit keywords, but you can use shortcut syntax for variables inside them:
// If condition - using shortcut syntax for variables
#{if user.isActive}
Welcome back, #{user.name}!
#{/if}
// Unless condition - mixing both syntaxes is fine
#{unless user.isBlocked}
You have access to this content, #{show user.email}.
#{/unless}
Loops
// Each loop - using shortcut syntax
#{each items as item}
#{item.name}: #{item.price}
#{/each}
// With index access
#{each users as user}
#{userIndex}: #{user.name}
#{/each}
Variable Replacement
Basic Variables
const context = { greeting: "Hello", name: "World" };
// Using traditional syntax
const template1 = "#{show greeting} #{show name}!";
// Using shortcut syntax (recommended)
const template2 = "#{greeting} #{name}!";
const result = replaceVariables(template2, context);
// Output: "Hello World!"
Nested Properties
The shortcut syntax really shines with nested properties:
const context = {
user: {
personal: {
firstName: "John",
lastName: "Doe"
}
}
};
// Traditional syntax
const template1 = "#{show user.personal.firstName} #{show user.personal.lastName}";
// Shortcut syntax - much cleaner!
const template2 = "#{user.personal.firstName} #{user.personal.lastName}";
const result = replaceVariables(template2, context);
// Output: "John Doe"
Real-World Example: Address Formatting
const context = {
address: {
street: "Hauptstraße 123",
city: "Berlin",
zip: "10115",
country: "Germany"
}
};
// Shortcut syntax makes this very readable
const template = "#{address.street}, #{address.zip} #{address.city}, #{address.country}";
const result = replaceVariables(template, context);
// Output: "Hauptstraße 123, 10115 Berlin, Germany"
Array Access
const context = {
colors: ["red", "green", "blue"],
users: [
{ name: "Alice", age: 25 },
{ name: "Bob", age: 30 }
]
};
// Shortcut syntax with arrays
const template = "Color: #{colors.1}, User: #{users.0.name}";
const result = replaceVariables(template, context);
// Output: "Color: green, User: Alice"
Recursive Variables
For complex recursive patterns, you may want to use the explicit show keyword:
const context = {
settings: { theme: "dark", language: "en" },
currentSetting: "theme",
prefix: "settings"
};
// For nested recursive variables, explicit 'show' is recommended
const template = "Current theme: #{show #{show prefix}.#{show currentSetting}}";
const result = replaceVariables(template, context);
// Output: "Current theme: dark"
Control Structures
If/Unless Conditions
const context = { user: { isAdmin: true, isActive: false } };
// If condition
const template1 =
#{if user.isAdmin}
Admin Panel
#{/if}
;
// Unless condition
const template2 =
#{unless user.isActive}
Account suspended
#{/unless}
;
Each Loops
const context = {
products: [
{ name: "Laptop", price: 999 },
{ name: "Mouse", price: 25 },
{ name: "Keyboard", price: 75 }
]
};
// Using shortcut syntax inside loops
const template =
#{each products as product}
- #{product.name} - $#{product.price}
#{/each}
;
Nested Control Structures
const context = {
departments: [
{
name: "Engineering",
employees: [
{ name: "Alice", role: "Developer" },
{ name: "Bob", role: "Designer" }
]
},
{
name: "Marketing",
employees: [
{ name: "Charlie", role: "Manager" }
]
}
]
};
// Shortcut syntax works great in nested structures
const template =
#{each departments as dept}
#{dept.name}
#{if dept.employees}
#{each dept.employees as emp}
- #{emp.name} - #{emp.role}
#{/each}
#{/if}
#{/each}
;
Transformation Pipes
Pipes transform data before output. Use the | symbol to apply pipes. The shortcut syntax works seamlessly with pipes:
// Traditional syntax
#{show value | pipeName}
#{show value | pipeName:parameter}
#{show value | pipe1 | pipe2:param}
// Shortcut syntax (recommended)
#{value | pipeName}
#{value | pipeName:parameter}
#{value | pipe1 | pipe2:param}
Text Transformation Pipes
uppercase
Converts text to uppercase.
const context = { name: "john" };
// Using shortcut syntax
const template = "Hello #{name | uppercase}!";
// Output: "Hello JOHN!"
lowercase
Converts text to lowercase.
const context = { name: "JOHN" };
const template = "Hello #{name | lowercase}!";
// Output: "Hello john!"
capitalize
Capitalizes the first letter, lowercases the rest.
const context = { name: "jOHN" };
const template = "Hello #{name | capitalize}!";
// Output: "Hello John!"
Date Formatting Pipes
format
Formats dates using date-fns format strings.
const context = {
timestamp: 1734885600000,
dateString: "2024-12-22"
};
// Default format (yyyy-MM-dd) - shortcut syntax
const template1 = "Date: #{timestamp | format}";
// Output: "Date: 2024-12-22"
// Custom format
const template2 = "Date: #{timestamp | format:dd.MM.yyyy}";
// Output: "Date: 22.12.2024"
// Time format
const template3 = "Time: #{timestamp | format:HH:mm:ss}";
// Output: "Time: 14:30:00"
// Real-world example
const template4 = "Created on #{timestamp | format:dd.MM.yyyy} at #{timestamp | format:HH:mm}";
// Output: "Created on 22.12.2024 at 14:30"
humanizeDuration
Converts milliseconds to human-readable duration.
const context = { duration: 3665000 }; // 1 hour, 1 minute, 5 seconds
const template = "Duration: #{duration | humanizeDuration}";
// Output: "Duration: 1h 1m 5s"
Number Formatting Pipes
round
Rounds numbers to specified decimal places.
const context = { price: 12.456789 };
// Default (0 decimal places)
const template1 = "Price: #{price | round}";
// Output: "Price: 12"
// 2 decimal places - shortcut syntax
const template2 = "Price: #{price | round:2}";
// Output: "Price: 12,46" (German format with comma)
// Real-world example
const context2 = { total: 1234.56 };
const template3 = "Total: €#{total | round:2}";
// Output: "Total: €1234,56"
calc
Performs arithmetic operations.
const context = {
price: 100,
taxRate: 0.19,
quantity: 3
};
// Multiplication - shortcut syntax
const template1 = "Tax: #{price | calc:multiply:0.19}";
// Output: "Tax: 19"
// Addition
const template2 = "Total: #{show price | calc:add:50}";
// Output: "Total: 150"
// Supported operations: add, subtract, multiply, divide
truncateToRange
Constrains numbers to a specified range.
const context = {
score: 150,
temperature: -5
};
// Constrain to 0-100 range
const template1 = "Score: #{show score | truncateToRange:0:100}";
// Output: "Score: 100"
// Constrain temperature
const template2 = "Temp: #{show temperature | truncateToRange:0:50}";
// Output: "Temp: 0"
Array Processing Pipes
sum
Calculates the sum of array elements.
const context = {
numbers: [1, 2, 3, 4, 5],
items: [
{ price: 10.50 },
{ price: 25.00 },
{ price: 15.75 }
]
};
// Sum simple array
const template1 = "Total: #{show numbers | sum}";
// Output: "Total: 15"
// Sum with path
const template2 = "Total: #{show items | sum:price}";
// Output: "Total: 51.25"
Data Transformation Pipes
mapping
Maps values from one set to another.
const context = {
status: "active",
priority: "high"
};
// Map status codes to labels
const template1 = "Status: #{show status | mapping:active,inactive,pending:Active,Inactive,Pending}";
// Output: "Status: Active"
// With default value
const template2 = "Priority: #{show priority | mapping:low,medium:Low,Medium:Unknown}";
// Output: "Priority: Unknown"
translate
Translates inline translation objects.
const context = {
message: {
en: "Hello",
de: "Hallo",
fr: "Bonjour"
}
};
// Default language (de)
const template1 = "#{show message | translate}";
// Output: "Hallo"
// Specific language
const template2 = "#{show message | translate:en}";
// Output: "Hello"
default
Provides fallback values for null/undefined/empty values.
const context = {
name: null,
title: "",
description: "Some text"
};
// Shortcut syntax with default pipe
const template =
Name: #{name | default Anonymous}
Title: #{title | default Untitled}
Description: #{description | default No description}
;
// Output:
// Name: Anonymous
// Title: Untitled
// Description: Some text
Chaining Pipes
Pipes can be chained for complex transformations. The shortcut syntax keeps this very readable:
const context = {
price: 123.456,
name: "premium product"
};
// Chain multiple pipes - shortcut syntax
const template =
Product: #{name | capitalize}
Price: $#{price | calc:multiply:1.2 | round:2}
;
// Output:
// Product: Premium Product
// Price: $148,15
More Chaining Examples
// Complex price calculation with tax
const context = {
basePrice: 99.99,
taxRate: 0.19
};
const template = "Final Price: €#{basePrice | calc:add:#{basePrice | calc:multiply:0.19} | round:2}";
// Output: "Final Price: €118,99"
// Text transformation chain
const context2 = { userName: "JOHN DOE" };
const template2 = "Welcome, #{userName | lowercase | capitalize}!";
// Output: "Welcome, John Doe!"
Advanced Features
Shortcut Syntax Best Practices
✅ DO:
Use shortcut syntax for simple variable access: #{user.name}
Use shortcut syntax with pipes: #{price | round:2}
Mix both syntaxes in the same template if needed
Use explicit show for very complex recursive patterns
❌ DON'T:
Don't use shortcut syntax for control structures: Use #{if condition}, not #{condition} alone
Don't worry about consistency - both syntaxes work identically
Error Handling
The template processor gracefully handles errors with both syntaxes:
const context = { user: { name: "John" } };
// Missing property with default - shortcut syntax
const template1 = "Age: #{user.age | default Unknown}";
// Output: "Age: Unknown"
// Invalid pipe parameters
const template2 = "#{user.name | round:invalid}";
// Output: "John" (pipe ignored, original value returned)
// Note: When using shortcut syntax with removeUnmappedContent: false,
// missing variables will show as #{show property} instead of #{property}
const template3 = "Status: #{user.status}";
// With removeUnmappedContent: false → "Status: #{show user.status}"
// With removeUnmappedContent: true → "Status: "
Performance Optimization
Lazy evaluation: Only processes templates when needed
Efficient parsing: Optimized regex patterns
Shortcut preprocessing: Converts #{...} to #{show ...} once, then processes normally
Caching: Pipes cache results where possible
Loop protection: Prevents infinite recursion
Configuration Options
const options = {
removeUnmappedContent: true // Remove variables that can't be resolved
};
const result = replaceVariables(template, context, options);
API Reference
replaceVariables(content: string, context: T, options?: Options): string
Processes a template string with the given context.
Parameters
content: string - The template string to process
context: T extends Json - The context object containing data
options?: Options - Optional configuration
Options
interface Options {
removeUnmappedContent?: boolean; // Default: false
}
Returns
string - The processed template string
Example
// Using shortcut syntax
const result = replaceVariables(
"Hello #{name}!",
{ name: "World" },
{ removeUnmappedContent: true }
);
// Using traditional syntax (also works)
const result2 = replaceVariables(
"Hello #{show name}!",
{ name: "World" },
{ removeUnmappedContent: true }
);
Built-in Pipes
All pipes work with both traditional #{show property | pipe} and shortcut #{property | pipe} syntax:
| Pipe | Syntax | Description |
| ------------------ | ---------------------------- | ----------------------- |
| uppercase | \| uppercase | Convert to uppercase |
| lowercase | \| lowercase | Convert to lowercase |
| capitalize | \| capitalize | Capitalize first letter |
| format | \| format:pattern | Format dates |
| round | \| round:decimals | Round numbers |
| calc | \| calc:operation:value | Arithmetic operations |
| humanizeDuration | \| humanizeDuration | Format durations |
| truncateToRange | \| truncateToRange:min:max | Constrain to range |
| mapping | \| mapping:from:to:default | Map values |
| sum | \| sum:path? | Sum array elements |
| translate | \| translate:language? | Translate i18n objects |
| default | \| default value | Fallback values |
Error Handling
The template processor handles errors gracefully:
Missing variables: Returns empty string or original template (based on options)
Invalid pipes: Ignores the pipe and returns original value
Malformed expressions: Leaves expression unchanged
Infinite recursion: Protected by iteration limits
Shortcut syntax: Unmapped variables show as #{show property} when removeUnmappedContent: false
Best Practices
Use shortcut syntax for cleaner, more readable templates: #{user.name} instead of #{show user.name}
Use meaningful variable names in your context objects
Test edge cases with null/undefined values
Chain pipes logically from general to specific transformations
Use default pipes for optional values
Keep templates readable with proper indentation
Validate context data before processing templates
Mix syntaxes freely - both work identically, choose based on readability
Common Patterns
Form Templates
const context = {
user: { name: "John", email: "john@example.com" },
errors: { name: null, email: "Invalid email" }
};
// Using shortcut syntax for cleaner forms
const template =
#{if errors.name}
#{errors.name}
#{/if}
#{if errors.email}
#{errors.email}
#{/if}
;
Email Templates
const context = {
user: { name: "John", orders: [{ id: 1, total: 99.99 }] },
company: { name: "ACME Corp" }
};
// Shortcut syntax makes email templates very readable
const template =
Dear #{user.name | capitalize},
Thank you for your recent orders:
#{each user.orders as order}
Order ##{order.id}: $#{order.total | round:2}
#{/each}
Best regards,
#{company.name}
;
Address Formatting Template
const context = {
customer: {
name: "Alice Johnson",
address: {
street: "Hauptstraße 123",
city: "Berlin",
zip: "10115",
country: "Germany"
}
}
};
// Real-world address template with shortcut syntax
const template =
Shipping Address:
#{customer.name}
#{customer.address.street}
#{customer.address.zip} #{customer.address.city}
#{customer.address.country}
;
// Output:
// Shipping Address:
// Alice Johnson
// Hauptstraße 123
// 10115 Berlin
// Germany
Report Templates
const context = {
report: {
title: "Sales Report",
period: "2024-Q1",
data: [
{ month: "Jan", sales: 10000 },
{ month: "Feb", sales: 12000 },
{ month: "Mar", sales: 15000 }
]
}
};
// Shortcut syntax for reports
const template =
#{report.title}
Period: #{report.period}
Monthly Sales
#{each report.data as item}
#{item.month}: $#{item.sales | round:0}
#{/each}
Total: $#{report.data | sum:sales | round:0}
;
Invoice Template Example
const context = {
invoice: {
number: "INV-2024-001",
date: 1734885600000,
customer: {
name: "ABC Company",
address: "Main Street 45, 12345 Berlin"
},
items: [
{ description: "Consulting Services", quantity: 8, rate: 150 },
{ description: "Software License", quantity: 1, rate: 500 }
],
taxRate: 0.19
}
};
// Complete invoice template using shortcut syntax
const template =
INVOICE #{invoice.number}
Date: #{invoice.date | format:dd.MM.yyyy}
Bill To:
#{invoice.customer.name}
#{invoice.customer.address}
Items:
#{each invoice.items as item}
#{item.description}
Quantity: #{item.quantity} x €#{item.rate} = €#{item.quantity | calc:multiply:#{item.rate}}
#{/each}
Subtotal: €#{invoice.items | sum:rate}
Tax (19%): €#{invoice.items | sum:rate | calc:multiply:0.19 | round:2}
Total: €#{invoice.items | sum:rate | calc:multiply:1.19 | round:2}
;
This documentation provides a comprehensive guide to using the Text Replacer system with both traditional #{show property} and shortcut #{property} syntax.