Text Replacer v3
Powerful string template processor with variables, control structures, and transformation pipes
Text Replacer v3 Documentation
The Text Replacer v3 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
- Migration from v2
- API Reference
Overview
The Text Replacer v3 uses a new 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
- Extensible architecture for custom pipes
Key Features
- π§ Extensible pipe system - Easy to add custom transformations
- π― Type-safe - Full TypeScript support
- π Performance optimized - Efficient template processing
- π Recursive resolution - Handle nested variable references
- π Rich data handling - Support for objects, arrays, and complex data structures
Basic Usage
import { replaceVariables } from "./utils/text-replacer-v3";
const context = { name: "John", age: 30 };
const template = "Hello #{show name}, you are #{show age} years old!";
const result = replaceVariables(template, context);
console.log(result); // "Hello John, you are 30 years old!"
Shortcut Syntax
For convenience, you can omit the show keyword when accessing variables. This shortcut syntax makes templates more concise and easier to read:
import { replaceVariables } from "./utils/text-replacer-v3";
const context = { name: "John", age: 30 };
// Both syntaxes work identically:
const template1 = "Hello #{show name}, you are #{show age} years old!";
const template2 = "Hello #{name}, you are #{age} years old!"; // Shortcut syntax
const result = replaceVariables(template2, context);
console.log(result); // "Hello John, you are 30 years old!"
Key Points:
#{property}is automatically converted to#{show property}- Works with all features: nested properties, arrays, pipes, etc.
- Control structures (
if,unless,each) still require explicit keywords - You can mix both syntaxes in the same template
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}
<div>Admin Panel</div>
#{/if}
`;
// Unless condition
const template2 = `
#{unless user.isActive}
<div>Account suspended</div>
#{/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 = `
<ul>
#{each products as product}
<li>#{product.name} - $#{product.price}</li>
#{/each}
</ul>
`;
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}
<h2>#{dept.name}</h2>
#{if dept.employees}
<ul>
#{each dept.employees as emp}
<li>#{emp.name} - #{emp.role}</li>
#{/each}
</ul>
#{/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
showfor 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);
Migration from v2
Syntax Changes
| v2 Syntax | v3 Syntax | Notes |
|---|---|---|
${variable} |
#{show variable} |
New explicit show syntax |
#{if condition} |
#{if condition} |
Unchanged |
#{each items} |
#{each items as item} |
Requires 'as' keyword |
Pipe Changes
| v2 Pipe | v3 Pipe | Changes |
|---|---|---|
format |
format:pattern |
Now supports custom patterns |
default:value |
default value |
Space-separated instead of colon |
Migration Example
// v2 Template
const v2Template = `
Hello ${user.name}!
#{if user.isActive}
${user.email}
#{/if}
`;
// v3 Template
const v3Template = `
Hello #{show user.name}!
#{if user.isActive}
#{show user.email}
#{/if}
`;
API Reference
replaceVariables<T>(content: string, context: T, options?: Options): string
Processes a template string with the given context.
Parameters
content: string- The template string to processcontext: T extends Json- The context object containing dataoptions?: 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}whenremoveUnmappedContent: 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 = `
<div>
<label>Name: #{user.name}</label>
#{if errors.name}
<span class="error">#{errors.name}</span>
#{/if}
</div>
<div>
<label>Email: #{user.email}</label>
#{if errors.email}
<span class="error">#{errors.email}</span>
#{/if}
</div>
`;
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 v3 system effectively with both traditional #{show property} and shortcut #{property} syntax.