UNPKG

js-in-strings

Version:

A library for rendering templates with JavaScript expressions

104 lines (102 loc) 4.44 kB
'use strict'; // src/index.ts function renderTemplateWithJS(template, context, options = {}) { const [openDelimiter, closeDelimiter] = options.delimiters || ["{", "}"]; const escOpenDelimiter = openDelimiter.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); const escCloseDelimiter = closeDelimiter.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); const expressionRegex = new RegExp(`${escOpenDelimiter}([^${escOpenDelimiter}${escCloseDelimiter}]*?)${escCloseDelimiter}`, "g"); const singleExpressionRegex = new RegExp(`^${escOpenDelimiter}([\\s\\S]*?)${escCloseDelimiter}$`); const mergedContext = { ...context, ...options.contextExtensions }; if (options.returnRawValues) { const trimmedTemplate = template.trim(); const match = trimmedTemplate.match(singleExpressionRegex); if (match) { try { const expression = match[1].trim(); const contextKeys = Object.keys(mergedContext); const contextValues = Object.values(mergedContext); const isIIFE = /^\s*\(.*\)\s*\(.*\)/.test(expression) || // (function(){})() pattern /^\s*\(.*=>.*\)\s*\(/.test(expression); const hasDeclarations = /\b(const|let|var|function|class)\b/.test(expression); const hasMultipleStatements = expression.includes(";"); let result; try { if (options.unsafeEval) { const contextVars = Object.entries(mergedContext).map(([key, value]) => `const ${key} = ${JSON.stringify(value)};`).join("\n"); result = eval(`${contextVars} ${expression}`); } else { let code; if (isIIFE) { code = `(${expression})`; } else if (hasDeclarations || hasMultipleStatements) { if (expression.includes("return ")) { code = `(function() { ${expression} })();`; } else { const lastSemicolonIndex = expression.lastIndexOf(";"); if (lastSemicolonIndex !== -1 && lastSemicolonIndex < expression.length - 1) { const lastExpression = expression.substring(lastSemicolonIndex + 1).trim(); code = `(function() { ${expression.substring(0, lastSemicolonIndex + 1)} return ${lastExpression}; })();`; } else { code = `(function() { ${expression}; return undefined; })();`; } } } else { code = expression; } const secureEval = (code2, context2) => { const sandbox = {}; Object.entries(context2).forEach(([key, value]) => { sandbox[key] = value; }); const indirectEval = (code3) => { return Function("sandbox", `with(sandbox) { return ${code3}; }`)(sandbox); }; return indirectEval(code2); }; result = secureEval(code, mergedContext); } } catch (error) { console.error(`Error evaluating expression:`, error); return `[Error: ${error instanceof Error ? error.message : String(error)}]`; } return result; } catch (error) { console.error(`Error evaluating expression:`, error); return `[Error: ${error instanceof Error ? error.message : String(error)}]`; } } } return template.replace(expressionRegex, (match, expression) => { try { let result; if (options.unsafeEval) { const contextVars = Object.entries(mergedContext).map(([key, value]) => `const ${key} = ${JSON.stringify(value)};`).join("\n"); result = eval(`${contextVars} (${expression})`); } else { const secureEval = (code, context2) => { const sandbox = {}; Object.entries(context2).forEach(([key, value]) => { sandbox[key] = value; }); return Function("sandbox", `with(sandbox) { return (${code}); }`)(sandbox); }; result = secureEval(expression, mergedContext); } if (result === void 0 || result === null) { return ""; } return String(result); } catch (error) { console.error(`Error evaluating expression "${expression}":`, error); return `[Error: ${error instanceof Error ? error.message : String(error)}]`; } }); } exports.renderTemplateWithJS = renderTemplateWithJS; //# sourceMappingURL=index.cjs.map //# sourceMappingURL=index.cjs.map