UNPKG

jsonblade

Version:

A powerful and modular JSON template engine with extensible filters

259 lines 11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.initializeFilters = initializeFilters; exports.compileJSONTemplate = compileJSONTemplate; exports.compileJSONTemplateAsync = compileJSONTemplateAsync; exports.evaluateExpression = evaluateExpression; exports.evaluateExpressionAsync = evaluateExpressionAsync; exports.getObjectPath = getObjectPath; const filter_registry_1 = require("./filter-registry"); const string_filters_1 = require("./filters/string-filters"); const array_filters_1 = require("./filters/array-filters"); const object_filters_1 = require("./filters/object-filters"); const logic_filters_1 = require("./filters/logic-filters"); const date_filters_1 = require("./filters/date-filters"); const number_filters_1 = require("./filters/number-filters"); const validation_filters_1 = require("./filters/validation-filters"); const template_config_1 = require("./template-config"); let filtersInitialized = false; function initializeFilters() { if (filtersInitialized) return; (0, string_filters_1.registerStringFilters)(); (0, array_filters_1.registerArrayFilters)(); (0, object_filters_1.registerObjectFilters)(); (0, logic_filters_1.registerLogicFilters)(); (0, date_filters_1.registerDateFilters)(); (0, number_filters_1.registerNumberFilters)(); (0, validation_filters_1.registerValidationFilters)(); filtersInitialized = true; } function compileJSONTemplate(template, data, functions) { initializeFilters(); // Handle empty template if (!template || template.trim() === "") { return ""; } const stringInterpolated = template.replace(/"([^"]*)"/g, (match, content) => { if (!content.includes("{{")) { return match; } const interpolated = content.replace(/{{\s*([^}]+)\s*}}/g, (_, expr) => { const value = evaluateExpression(expr.trim(), data, functions); if (value == null) return ""; const stringValue = String(value); // Escape quotes and backslashes for JSON string context return stringValue.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); }); return `"${interpolated}"`; }); const fullyInterpolated = stringInterpolated.replace(/{{\s*([^}]+)\s*}}/g, (_, expr) => { const value = evaluateExpression(expr.trim(), data, functions); return JSON.stringify(value ?? null); }); return JSON.parse(fullyInterpolated); } async function compileJSONTemplateAsync(template, data, functions) { initializeFilters(); // Handle empty template if (!template || template.trim() === "") { return ""; } const stringInterpolated = await replaceAsync(template, /"([^"]*)"/g, async (match, content) => { if (!content.includes("{{")) { return match; } const interpolated = await replaceAsync(content, /{{\s*([^}]+)\s*}}/g, async (_, expr) => { const value = await evaluateExpressionAsync(expr.trim(), data, functions); if (value == null) return ""; const stringValue = String(value); return stringValue.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); }); return `"${interpolated}"`; }); const fullyInterpolated = await replaceAsync(stringInterpolated, /{{\s*([^}]+)\s*}}/g, async (_, expr) => { const value = await evaluateExpressionAsync(expr.trim(), data, functions); return JSON.stringify(value ?? null); }); return JSON.parse(fullyInterpolated); } // Helper function for async string replace async function replaceAsync(str, regex, asyncFn) { const promises = []; str.replace(regex, (match, ...args) => { const promise = asyncFn(match, ...args); promises.push(promise); return match; }); const data = await Promise.all(promises); return str.replace(regex, () => data.shift()); } function evaluateExpression(expr, data, functions) { const parts = expr.split("|").map((p) => p.trim()); const [rawPath, ...filterParts] = parts; // Détecter les appels de fonction dans rawPath const functionCallMatch = rawPath.match(/^(\w+)\((.*?)\)$/); let value; if (functionCallMatch) { // C'est un appel de fonction const [, functionName, argsString] = functionCallMatch; // Chercher la fonction dans le registre fourni const templateFunction = functions?.find((f) => f.name === functionName); if (templateFunction) { // Parser les arguments let args = []; if (argsString.trim()) { args = argsString.split(",").map((arg) => { const trimmed = arg.trim(); if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) { return trimmed.slice(1, -1); } // Try to parse as number if (!isNaN(Number(trimmed)) && trimmed !== "") { return Number(trimmed); } // Try to resolve as data path first, fallback to literal string const pathValue = getObjectPath(trimmed, data); return pathValue !== null && pathValue !== undefined ? pathValue : trimmed; }); } // Appeler la fonction value = templateFunction.func(...args); } else { // Fonction introuvable, fallback vers getObjectPath value = getObjectPath(rawPath, data); } } else { // Path normal, utiliser getObjectPath value = getObjectPath(rawPath, data); } for (const part of filterParts) { const match = part.match(/^(\w+)(?:\((.*?)\))?$/); if (!match) continue; const [, name, argsString] = match; const fn = (0, filter_registry_1.getFilter)(name); if (!fn) { const config = (0, template_config_1.getTemplateConfig)(); const error = (0, template_config_1.createTemplateError)("UNKNOWN_FILTER", `Unknown filter: ${name}`, { filter: name, expression: expr }); (0, template_config_1.handleTemplateError)(error, config); continue; } let args = []; if (argsString) { args = argsString.split(",").map((arg) => { const trimmed = arg.trim(); if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) { return trimmed.slice(1, -1); } // Try to resolve as data path first, fallback to literal string const pathValue = getObjectPath(trimmed, data); return pathValue !== null && pathValue !== undefined ? pathValue : trimmed; }); } value = fn(value, ...args); } return value; } async function evaluateExpressionAsync(expr, data, functions) { const parts = expr.split("|").map((p) => p.trim()); const [rawPath, ...filterParts] = parts; // Détecter les appels de fonction dans rawPath const functionCallMatch = rawPath.match(/^(\w+)\((.*?)\)$/); let value; if (functionCallMatch) { // C'est un appel de fonction const [, functionName, argsString] = functionCallMatch; // Chercher la fonction dans le registre fourni const templateFunction = functions?.find((f) => f.name === functionName); if (templateFunction) { // Parser les arguments let args = []; if (argsString.trim()) { args = argsString.split(",").map((arg) => { const trimmed = arg.trim(); if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) { return trimmed.slice(1, -1); } // Try to parse as number if (!isNaN(Number(trimmed)) && trimmed !== "") { return Number(trimmed); } // Try to resolve as data path first, fallback to literal string const pathValue = getObjectPath(trimmed, data); return pathValue !== null && pathValue !== undefined ? pathValue : trimmed; }); } // Appeler la fonction (peut être async) value = await templateFunction.func(...args); } else { // Fonction introuvable, fallback vers getObjectPath value = getObjectPath(rawPath, data); } } else { // Path normal, utiliser getObjectPath value = getObjectPath(rawPath, data); } // Note: Les filtres ne sont pas async dans cette implémentation // Si besoin, il faudrait créer un système de filtres async séparé for (const part of filterParts) { const match = part.match(/^(\w+)(?:\((.*?)\))?$/); if (!match) continue; const [, name, argsString] = match; const fn = (0, filter_registry_1.getFilter)(name); if (!fn) { const config = (0, template_config_1.getTemplateConfig)(); const error = (0, template_config_1.createTemplateError)("UNKNOWN_FILTER", `Unknown filter: ${name}`, { filter: name, expression: expr }); (0, template_config_1.handleTemplateError)(error, config); continue; } let args = []; if (argsString) { args = argsString.split(",").map((arg) => { const trimmed = arg.trim(); if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) { return trimmed.slice(1, -1); } // Try to resolve as data path first, fallback to literal string const pathValue = getObjectPath(trimmed, data); return pathValue !== null && pathValue !== undefined ? pathValue : trimmed; }); } value = fn(value, ...args); } return value; } function getObjectPath(path, data) { // Handle special case where path is just "." meaning current item if (path === ".") { return data; } const parts = path.split("."); let current = data; for (const part of parts) { if (current == null || typeof current !== "object") return null; current = current[part]; } return current; } //# sourceMappingURL=json-template.utils.js.map