UNPKG

@silexlabs/grapesjs-data-source

Version:
196 lines 7.96 kB
"use strict"; /* * Silex website builder, free/libre no-code tool for makers. * Copyright (c) 2023 lexoyo and Silex Labs foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.handlePreviewIndex = handlePreviewIndex; exports.evaluatePropertyToken = evaluatePropertyToken; exports.evaluateExpressionTokens = evaluateExpressionTokens; exports.evaluateStateToken = evaluateStateToken; exports.resolveStateExpression = resolveStateExpression; exports.evaluateFilterToken = evaluateFilterToken; const types_1 = require("../types"); const state_1 = require("./state"); const token_1 = require("./token"); const utils_1 = require("../utils"); /** * Handle preview index for array data */ function handlePreviewIndex(value, token) { if (typeof token.previewIndex === 'undefined') { return value; } if (Array.isArray(value)) { return value[token.previewIndex]; } return value; } /** * Evaluate a property token */ function evaluatePropertyToken(token, remaining, context, prevValues) { var _a; // Handle "fixed" property (hard coded string set by the user) if (token.fieldId === types_1.FIXED_TOKEN_ID) { return evaluateExpressionTokens(remaining, context, (_a = token.options) === null || _a === void 0 ? void 0 : _a.value); } // Get data object let prevObj; if (typeof prevValues === 'undefined' || prevValues === null) { if (!token.dataSourceId) { throw new Error(`Data source ID is missing for token: ${JSON.stringify(token)}`); } prevObj = context.previewData[token.dataSourceId]; } else { prevObj = prevValues; } // Get the next value let value = prevObj ? prevObj[token.fieldId] : null; // Handle preview index if resolvePreviewIndex is true // Don't resolve if next token is a filter - filters need to operate on full arrays if (context.resolvePreviewIndex) { const nextToken = remaining[0]; if (!nextToken || nextToken.type !== 'filter') { value = handlePreviewIndex(value, token); } } // For non-final tokens, handle preview index only if next token is not a filter // Filters need to operate on full arrays, not individual items if (remaining.length > 0 && !context.resolvePreviewIndex) { const nextToken = remaining[0]; if (nextToken.type !== 'filter') { value = handlePreviewIndex(value, token); } } // Special handling for items state // @ts-expect-error - Runtime property check for items handling if (token.isItems && typeof token.previewIndex !== 'undefined') { if (remaining.length > 0) { value = [value]; } } return evaluateExpressionTokens(remaining, context, value); } /** * Evaluate expression tokens recursively */ function evaluateExpressionTokens(expression, context, prevValues = null) { if (expression.length === 0) { return prevValues; } // Always create defensive copies of tokens to prevent mutations from affecting original data const cleanExpression = expression.map(token => (Object.assign({}, token))); const [token, ...rest] = cleanExpression; switch (token.type) { case 'state': { return evaluateStateToken(token, rest, context, prevValues); } case 'property': { return evaluatePropertyToken(token, rest, context, prevValues); } case 'filter': { return evaluateFilterToken(token, rest, context, prevValues); } default: throw new Error(`Unsupported token type: ${JSON.stringify(token)}`); } } /** * Evaluate a state token */ function evaluateStateToken(state, remaining, context, prevValues) { var _a; const resolvedExpression = resolveStateExpression(state, context.component, context); if (!resolvedExpression) { throw new Error(`Unable to resolve state: ${JSON.stringify(state)}`); } // Special handling for items state - always wrap result in array when resolvePreviewIndex is true const previewIndex = (_a = resolvedExpression[resolvedExpression.length - 1]) === null || _a === void 0 ? void 0 : _a.previewIndex; if (state.storedStateId === 'items' && typeof previewIndex !== 'undefined') { // @ts-expect-error - Adding runtime property for items state handling resolvedExpression[0].isItems = true; } return evaluateExpressionTokens([...resolvedExpression, ...remaining], context, prevValues); } /** * Resolve a state token to its expression */ function resolveStateExpression(state, component, context) { const parent = (0, state_1.getParentByPersistentId)(state.componentId, component); if (!parent) { console.error('Component not found for state', state, component.get('id-plugin-data-source')); return null; } // Get the expression of the state const storedState = (0, state_1.getState)(parent, state.storedStateId, state.exposed); if (!(storedState === null || storedState === void 0 ? void 0 : storedState.expression)) { console.warn('State is not defined on component', parent.getId(), state, storedState); return null; } // Create a minimal DataTree-like object for fromStored compatibility return storedState.expression .flatMap((token) => { var _a; switch (token.type) { case 'state': { return (_a = resolveStateExpression((0, token_1.fromStored)(token, component.getId()), parent, context)) !== null && _a !== void 0 ? _a : []; } default: return token; } }); } /** * Evaluate a filter token (Liquid filter) */ function evaluateFilterToken(token, remaining, context, prevValues) { var _a; const options = Object.entries(token.options).reduce((acc, [key, value]) => { // If value is a primitive (number, string, boolean), use it directly // Only evaluate as expression if it's an actual expression object/array const expression = (0, utils_1.toExpression)(value); acc[key] = expression ? evaluateExpressionTokens(expression, context, null) : value; return acc; }, {}); const filter = context.filters.find(f => f.id === token.id); if (!filter) { throw new Error(`Filter not found: ${token.id}`); } let value; try { value = filter.apply(prevValues, options); } catch (e) { console.warn(`Filter "${filter.id}" error:`, e, { filter: filter.id, prevValues, options, valueType: typeof prevValues, isArray: Array.isArray(prevValues), isNull: prevValues === null, }); // Mimic behavior of liquid - return null on error return null; } // Don't resolve before filters - they need arrays const nextIsFilter = remaining.length > 0 && ((_a = remaining[0]) === null || _a === void 0 ? void 0 : _a.type) === 'filter'; if (!nextIsFilter && (context.resolvePreviewIndex || remaining.length > 0)) { value = handlePreviewIndex(value, token); } return evaluateExpressionTokens(remaining, context, value); } //# sourceMappingURL=expressionEvaluator.js.map