UNPKG

@nent/core

Version:

Functional elements to add routing, data-binding, dynamic HTML, declarative actions, audio, video, and so much more. Supercharge static HTML files into web apps without script or builds.

107 lines (106 loc) 3.65 kB
/*! * NENT 2022 */ import { removeLineFeeds, requireValue, toBoolean, warnIf, } from '../common'; import { evalExpression } from './evaluate.worker'; import { dataState } from './state'; import { hasToken, resolveTokens } from './tokens'; const operatorRegex = /(in |for |[><+\-=])/gi; const jsonRegEx = /(\{.*?\})/g; const alphabet = 'abcdefghijklmnopqrstuvwxyz'; /** * It returns true if the given string contains an operator * @param {string} value - The value of the attribute. * @returns A boolean value. */ export function hasExpression(value) { return value.match(operatorRegex); } /** * It takes a string, finds all the JSON in it, replaces the JSON with a variable, and returns the * string with the JSON replaced and an object with the JSON and the variable * @param {string} expression - The expression to convert * @returns An object with two properties: data and expression. */ export function convertFromJson(expression) { const data = {}; let newExpression = removeLineFeeds(expression).split(`'`).join(`"`); let index = 0; let match; let resultingExpression = newExpression.slice(); while ((match = jsonRegEx.exec(newExpression))) { const json = match[1]; const value = JSON.parse(json); let variable = alphabet[index]; data[variable] = value; index++; resultingExpression = resultingExpression .split(json) .join(variable); } return { data, expression: resultingExpression, }; } /** * It takes an expression and a context, and returns the result of evaluating the expression * @param {string} expression - The expression to evaluate. * @param {ExpressionContext} context - ExpressionContext = {} * @returns A promise that resolves to a number, boolean, or string. */ async function evaluate(expression, context = {}) { requireValue(expression, 'expression'); if (!hasExpression(expression)) return expression; try { context.null = null; const resolved = convertFromJson(expression); Object.assign(context, resolved.data); return await evalExpression(resolved.expression, context); } catch (error) { warnIf(dataState.debug, `An exception was raised evaluating expression '${expression}': ${error}`); return false; } } /** * This function first resolves any data-tokens, then passes the response to the * {evaluate} function. * * @export evaluateExpression * @param {string} expression * @param {*} [context={}] * @return {*} {Promise<any>} */ export async function evaluateExpression(expression, context = {}) { requireValue(expression, 'expression'); const detokenizedExpression = await resolveTokens(expression, true); return evaluate(detokenizedExpression, context); } /** * This function first resolves any data-tokens, then passes the response to the * {evaluate} function, but uses the value to determine a true/false. * * @export * @param {string} expression * @param {ExpressionContext} [context={}] * @return {*} {Promise<boolean>} */ export async function evaluatePredicate(expression, context = {}) { requireValue(expression, 'expression'); let workingExpression = expression.slice(); if (hasToken(workingExpression)) workingExpression = await resolveTokens(workingExpression, true); if (!workingExpression) return false; const negation = workingExpression.startsWith('!'); if (negation) { workingExpression = workingExpression.slice(1, workingExpression.length); } let result = toBoolean(workingExpression); if (hasExpression(workingExpression)) { result = await evaluate(workingExpression, context); } return negation ? !result : result; }