@bernierllc/email-template-engine
Version:
Email template processing engine with compilation, rendering, and text generation
81 lines (79 loc) • 2.61 kB
JavaScript
;
/*
Copyright (c) 2025 Bernier LLC
This file is licensed to the client under a limited-use license.
The client may use and modify this code *only within the scope of the project it was delivered for*.
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkBalancedBraces = checkBalancedBraces;
exports.checkUnclosedBlocks = checkUnclosedBlocks;
exports.isBuiltInHelper = isBuiltInHelper;
exports.inferVariableType = inferVariableType;
/**
* Check if template has balanced braces
*/
function checkBalancedBraces(template) {
let count = 0;
for (let i = 0; i < template.length; i++) {
if (template[i] === '{' && template[i + 1] === '{') {
count++;
i++; // Skip next brace
}
else if (template[i] === '}' && template[i + 1] === '}') {
count--;
i++; // Skip next brace
if (count < 0) {
return { balanced: false, position: i };
}
}
}
return { balanced: count === 0, position: count !== 0 ? template.length : undefined };
}
/**
* Check for unclosed block helpers
*/
function checkUnclosedBlocks(template) {
const unclosed = [];
const blockStack = [];
const blockRegex = /\{\{#(\w+).*?\}\}|\{\{\/(\w+)\}\}/g;
let match;
while ((match = blockRegex.exec(template)) !== null) {
if (match[1]) {
// Opening block
blockStack.push(match[1]);
}
else if (match[2]) {
// Closing block
const last = blockStack.pop();
if (last !== match[2]) {
unclosed.push(match[2]);
}
}
}
// Any remaining blocks are unclosed
return [...unclosed, ...blockStack];
}
/**
* Check if a variable name is a built-in Handlebars helper
*/
function isBuiltInHelper(variable) {
const builtIns = ['if', 'unless', 'each', 'with', 'log', 'lookup', 'else'];
const firstWord = variable.split(/[\s.]/)[0];
return builtIns.includes(firstWord);
}
/**
* Infer variable type from path
*/
function inferVariableType(path) {
// Simple heuristic based on variable name
if (path.includes('.length') || path.includes('.count'))
return 'number';
if (path.includes('.is') || path.includes('.has'))
return 'boolean';
if (path.includes('.items') || path.includes('.list'))
return 'array';
if (path.includes('.'))
return 'object';
return 'string';
}