Title: Text Replacer v3 Description: Powerful string template processor with variables, control structures, and transformation pipes Tags: text-replacer, templates, variables, 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}
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 = ; 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} #{/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); 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(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 v3 system effectively with both traditional #{show property} and shortcut #{property} syntax.