Safely evaluate a JavaScript-like expression in given context.
In Buttonwood, we heavily use meta-data (JSON format) to deliver business logic from backend to front-end. We don't want to design a meta-data format too complex to maintain, this tool allows us to define some light logic in pure string, way more flexible than rigid meta-data, much safer and more maintainable than passing js function as string (we did that) from backend to front-end.
This tool was mainly extracted, modified and extended from the expression parser of aurelia-binding.
Install
npm install bcx-expression-evaluator
Document
function evaluate(expression, context, helper, opts)
expression
: the expression string to be evaluatedcontext
: the input model objecthelper
: optional helper objectopts
: optional hashmap, currently only supportrejectAssignment
andstringInterpolationMode
rejectAssignment
rejects assignment in expressionstringInterpolationMode
treats the whole expression like if it's in backticks `expression`
function evaluateStringInterpolation
is a short-cut to call evaluate with stringInterpolationMode option.
Usage (in es6 syntax)
; const context = a: 1 b: 2 c: one: 'one' two: 'two' { return thisa + thisb / 2; }; ; // => 'one';
use some helper
const helper = limit: 5 v1 + v2; ; // => false;
$this
variable
access context object itself with special ; // => the context object; // => 1
$parent
variable
explicitly access helper object with special (carried over from aurelia-binding, might change $parent to $helper in future releases.)
; // => 1; // => 1; // => 2
support es6 string interpolation
; // => '2'
You can evaluate a string interpolation without backtick "`"
; // => '2'; // => '2'
You don't have to escape backtick in stringInterpolationMode
; // => '`2`', beware you need double escape as we are writing expression in string quotes; // => '`2`'; // => '`2`'
safe. It is not an eval in JavaScript, doesn't have access to global JavaScript objects
// => undefined // only have access to context and helper // => 7
silent most of the time
// => undefined, no error thrown // => 'lorem', no error thrown
you can use assignment to mutate context object (or even helper object)
let obj = a: 1 b: 2;; // obj is now {a: 3, b: 2}; // obj is now {a: false, b: 2}
disable assignment if you don't need it
This doesn't eliminate side effect, it would not prevent any function you called in bcx-expression to mutate something.
; // throws error
Difference from real JavaScript expression
bcx-expression looks like JavaScript expression, but there are some difference.
wrong reference results undefined instead of error
let obj = a: 1;objba // => error; // => undefined
default result for +/- operators
Behaviour carried over from aurelia-binding.
// +/- behaviour in normal JavaScript expressionundefined + 1 // => NaN1 + undefined // => NaNnull + 1 // => 11 + null // => 1undefined + undefined // => NaNnull + null // => 0 // in bcx-expression, + and - ignores undefined/null value,// if both left and right parts are (evaluated to) undefined/null, result default to 0; // => 1; // => 1; // => 1; // => 1; // => 0; // => 0
no function expression
// all following JavaScript expressions would not work in bcx-expression{return 1} 1arr // but this would workarr
no regular expression support
Regex syntax is too complex to be supported for our AST (abstract syntax tree).
// regex literal would not work in bcx-expression./\w/
One way to bypass this is to supply regex literal in helper object.
;
some JavaScript operators would not work
typeof
, instanceof
, delete
would not work, because bcx-expression is not real JavaScript.