JavaScript Template Engine
This challenge tasks you with building a simple yet functional template engine in JavaScript. A template engine allows you to dynamically generate HTML or other text-based content by embedding variables and logic within a predefined template string. This is incredibly useful for creating dynamic web pages, email content, or configuration files.
Problem Description
You need to create a JavaScript function, renderTemplate, that takes two arguments:
template: A string containing the template with placeholders and basic control structures.data: An object containing key-value pairs representing the data to be inserted into the template.
The renderTemplate function should process the template string, replacing placeholders with corresponding values from the data object and executing simple control structures.
Key Requirements:
- Variable Interpolation: Support for basic variable interpolation using a specific syntax (e.g.,
{{variableName}}). - Conditional Logic: Support for simple
if/elseconditions (e.g.,{{#if condition}} ... {{/if}}and{{#if condition}} ... {{else}} ... {{/if}}). Theconditioncan be a simple variable name that evaluates to truthy or falsy. - Looping: Support for iterating over arrays (e.g.,
{{#each arrayName}} ... {{/each}}). Inside the loop, you should be able to access the current item and potentially its index. - Sanitization (Optional but Recommended): Basic HTML sanitization for interpolated variables to prevent cross-site scripting (XSS) vulnerabilities.
- Nesting: Support for nesting control structures (e.g., an
eachloop within anifstatement).
Expected Behavior:
The function should return a new string with the template processed. Placeholders should be replaced, and control structures should be evaluated and rendered appropriately.
Edge Cases to Consider:
- Variables present in the template but not in the
dataobject. - Variables in the
dataobject that are not used in the template. - Empty template strings.
- Empty
dataobjects. - Nested control structures with different types.
- Conditions that evaluate to
false. - Arrays that are empty or
null/undefined.
Examples
Example 1: Variable Interpolation
Input:
template = "Hello, {{name}}! You are {{age}} years old."
data = { name: "Alice", age: 30 }
Output:
"Hello, Alice! You are 30 years old."
Explanation: The variables 'name' and 'age' from the template are replaced with their corresponding values from the data object.
Example 2: Conditional Logic
Input:
template = "User status: {{#if isLoggedIn}}Online{{else}}Offline{{/if}}"
data = { isLoggedIn: true }
Output:
"User status: Online"
Explanation: The '#if' condition 'isLoggedIn' is true, so the content within the '#if' block is rendered. If 'isLoggedIn' were false, 'Offline' would be rendered.
Example 3: Looping and Nesting
Input:
template = "<ul>{{#each items}}<li>{{this}} (Index: {{@index}})</li>{{/each}}</ul>"
data = { items: ["Apple", "Banana", "Cherry"] }
Output:
"<ul><li>Apple (Index: 0)</li><li>Banana (Index: 1)</li><li>Cherry (Index: 2)</li></ul>"
Explanation: The '#each' directive iterates over the 'items' array. '{{this}}' refers to the current item, and '{{@index}}' refers to its index within the array.
Example 4: Combined and Nested Structures
Input:
template = "Welcome, {{user.name}}! Your orders: {{#if orders.length}}<ul>{{#each orders}}<li>{{this.item}} - ${{this.price}}</li>{{/each}}</ul>{{else}}No orders found.{{/if}}"
data = {
user: { name: "Bob" },
orders: [
{ item: "Laptop", price: 1200 },
{ item: "Mouse", price: 25 }
]
}
Output:
"Welcome, Bob! Your orders: <ul><li>Laptop - $1200</li><li>Mouse - $25</li></ul>"
Explanation: This example shows nested 'if' and 'each' structures, accessing properties within nested objects.
Example 5: Edge Case - Missing Variable
Input:
template = "Hello, {{name}}!"
data = {}
Output:
"Hello, !"
Explanation: When a variable is not found in the data, it's replaced with an empty string. (Or, you might choose to throw an error or use a default value - specify your preferred behavior).
Constraints
- The template engine should be implemented as a single JavaScript function.
- The template syntax for variables is
{{variableName}}or{{object.nestedProperty}}. - The template syntax for conditional logic is
{{#if expression}} ... {{/if}}and{{#if expression}} ... {{else}} ... {{/if}}. Theexpressioncan be a variable name or a path likeobject.property. - The template syntax for looping is
{{#each arrayName}} ... {{/each}}. Inside the loop,{{this}}refers to the current item, and{{@index}}refers to its index. You can also access nested properties like{{this.propertyName}}. - The template engine should handle basic JavaScript truthy/falsy values for conditions (e.g.,
true,false,0,"",null,undefined, arrays, objects). - Performance is not a primary concern for this challenge, but avoid overly inefficient methods like repeated string concatenation in a loop if easily avoidable.
Notes
- Consider how you will parse the template string. Regular expressions can be very helpful here.
- Think about the order of operations: variable interpolation, then conditionals, then loops, or vice-versa? A recursive approach might be useful for handling nested structures.
- For variable access, consider how to handle dot notation for nested objects (e.g.,
user.profile.name). - For sanitization, a simple approach would be to replace characters like
<,>,&,",'with their HTML entities. - Decide on the behavior for missing variables. Replacing with an empty string is a common and reasonable default.
- The
@indexvariable ineachloops is a convention to provide the index. You can choose to support it or not, but it's a common feature.