( propValue: string, configuration? )
| 28 | * @returns A callable accessor compiled from the expression string. |
| 29 | */ |
| 30 | export function parseExpressionString( |
| 31 | propValue: string, |
| 32 | configuration? |
| 33 | ): (row: Record<string, unknown>) => unknown { |
| 34 | // NOTE: Can be null which represents invalid function. Return null so that prop can be omitted |
| 35 | if (propValue in cachedExpressionMap) { |
| 36 | return cachedExpressionMap[propValue]; |
| 37 | } |
| 38 | |
| 39 | let func; |
| 40 | // Compile with expression-eval |
| 41 | const ast = parse(propValue); |
| 42 | if (ast.type === 'Identifier') { |
| 43 | func = row => { |
| 44 | return get(row, propValue); |
| 45 | }; |
| 46 | } else { |
| 47 | // NOTE: To avoid security risks, the arguments passed to the |
| 48 | // compiled expression must only give access to pure data (no globals etc) |
| 49 | // We disable function call syntax |
| 50 | traverse(ast, node => { |
| 51 | if (node.type === 'CallExpression') { |
| 52 | throw new Error('Function calls not allowed in JSON expressions'); |
| 53 | } |
| 54 | }); |
| 55 | // TODO Something like `expressionEval.eval(ast, {row});` would be useful for unpacking arrays |
| 56 | func = row => { |
| 57 | return evaluate(ast, row); |
| 58 | }; |
| 59 | } |
| 60 | |
| 61 | // Cache the compiled function |
| 62 | cachedExpressionMap[propValue] = func; |
| 63 | return func; |
| 64 | } |
| 65 | |
| 66 | /** |
| 67 | * Recursively visits nodes in an expression AST. |
no test coverage detected
searching dependent graphs…