lightview
Version:
A reactive UI library with features of Bau, Juris, and HTMX plus safe LLM UI generation
83 lines (72 loc) • 2.99 kB
JavaScript
/**
* JPRX CALC HELPER
* Safe expression evaluation using expr-eval.
* Uses the global $ helper for reactive path lookups.
*/
import { Parser } from 'expr-eval';
import { resolvePath, unwrapSignal } from '../parser.js';
/**
* Evaluates a mathematical expression string.
* Supports $() for reactive path lookups within the expression.
*
* @param {string} expression - The expression to evaluate (e.g., "$('/price') * 1.08")
* @param {object} context - The JPRX context for path resolution
* @returns {number|string} - The result of the evaluation
*
* @example
* =calc("$('/width') * $('/height')")
* =calc("5 + 3 * 2")
* =calc("($('/subtotal') + $('/tax')) * 0.9")
*/
export const calc = (expression, context) => {
if (typeof expression !== 'string') {
return expression;
}
let processedExpression = expression;
try {
const pathResolver = (path) => {
let currentPath = path;
let value;
let depth = 0;
// Recursively resolve if the value is another path string (e.g., "/c/display")
while (typeof currentPath === 'string' && (currentPath.startsWith('/') || currentPath.startsWith('=/')) && depth < 5) {
const normalizedPath = currentPath.startsWith('/') ? '=' + currentPath : currentPath;
const resolved = resolvePath(normalizedPath, context);
value = unwrapSignal(resolved);
// If the new value is a different path string, keep going
if (typeof value === 'string' && (value.startsWith('/') || value.startsWith('=/')) && value !== currentPath) {
currentPath = value;
depth++;
} else {
break;
}
}
if (typeof value === 'number') return value;
if (typeof value === 'string') {
const num = parseFloat(value);
if (!isNaN(num) && isFinite(Number(value))) return num;
return value === '' ? 0 : `"${value.replace(/"/g, '\\"')}"`;
}
return value === undefined || value === null ? 0 : value;
};
const pathRegex = /\$\(\s*['"](.*?)['"]\s*\)/g;
processedExpression = expression.replace(pathRegex, (match, path) => {
const val = pathResolver(path);
return val;
});
const parser = new Parser();
const parsed = parser.parse(processedExpression);
return parsed.evaluate();
} catch (error) {
console.error('JPRX calc error:', error.message);
console.error('Original expression:', expression);
console.error('Processed expression:', processedExpression);
return NaN;
}
};
/**
* Register the calc helper.
*/
export const registerCalcHelpers = (register) => {
register('calc', calc, { pathAware: true });
};