npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

bcx-expression-evaluator

v1.2.1

Published

Safely evaluate a JavaScript like expression in given context.

Downloads

7,563

Readme

bcx-expression-evaluator CI

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 evaluated
  • context: the input model object
  • helper: optional helper object
  • opts: optional hashmap, currently only support rejectAssignment and stringInterpolationMode
    • rejectAssignment rejects assignment in expression
    • stringInterpolationMode 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)

import {evaluate, evaluateStringInterpolation} from 'bcx-expression-evaluator';

const context = {
    a: 1,
    b: 2,
    c: {
    one: 'one',
    two: 'two'
    },
    avg: function() { return (this.a + this.b) / 2; }
};

evaluate('avg() > a ? c.one : c.two', context);  // => 'one';

use some helper

const helper = {
    limit: 5,
    sum: (v1, v2) => v1 + v2
};

evaluate('sum(a, b) > limit', context, helper);  // => false;

access context object itself with special $this variable

evaluate('$this', context); // => the context object
evaluate('$this.a', context); // => 1

explicitly access helper object with special $parent variable

(carried over from aurelia-binding, might change $parent to $helper in future releases.)

evaluate('a', {a:1}, {a:2}); // => 1
evaluate('$this.a', {a:1}, {a:2}); // => 1
evaluate('$parent.a', {a:1}, {a:2}); // => 2

support es6 string interpolation

evaluate('`${a+1}`', {a:1}); // => '2'

You can evaluate a string interpolation without backtick "`"

evaluate('${a+1}', {a:1}, null, {stringInterpolationMode: true}); // => '2'
evaluateStringInterpolation('${a+1}', {a:1}); // => '2'

You don't have to escape backtick in stringInterpolationMode

evaluate('`\\`${a+1}\\``', {a:1}); // => '`2`', beware you need double escape as we are writing expression in string quotes
evaluate('`${a+1}`', {a:1}, null, {stringInterpolationMode: true}); // => '`2`'
evaluateStringInterpolation('`${a+1}`', {a:1}); // => '`2`'

safe. It is not an eval in JavaScript, doesn't have access to global JavaScript objects

evaluate('parseInt(a, 10)', {a:"7"}) // => undefined

// only have access to context and helper
evaluate('parseInt(a, 10)', {a:"7"}, {parseInt: parseInt}) // => 7

silent most of the time

evaluate('a.b', {}) // => undefined, no error thrown
evaluate('a.b || c', {c: 'lorem'}) // => 'lorem', no error thrown

you can use assignment to mutate context object (or even helper object)

let obj = {a: 1, b: 2};
evaluate('a = 3', obj); // obj is now {a: 3, b: 2}
evaluate('b > 3 ? (a = true) : (a = false)', obj); // 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.

evaluate('a=1', {a:0}, null, {rejectAssignment: true}); // 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};
obj.b.a // => error
evaluate('b.a', obj); // => undefined

default result for +/- operators

Behaviour carried over from aurelia-binding.

// +/- behaviour in normal JavaScript expression
undefined + 1 // => NaN
1 + undefined // => NaN
null + 1 // => 1
1 + null // => 1
undefined + undefined // => NaN
null + 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
evaluate('undefined + 1'); // => 1
evaluate('1 + undefined'); // => 1
evaluate('null + 1'); // => 1
evaluate('1 + null'); // => 1
evaluate('undefined + undefined'); // => 0
evaluate('null + null'); // => 0

no function expression

// all following JavaScript expressions would not work in bcx-expression
(function(){return 1})()
(() => 1)()
arr.sort((a, b) => a > b)

// but this would work
arr.sort(aHelperFunc)

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/.test(string)

One way to bypass this is to supply regex literal in helper object.

evaluate('tester.test(str)', {str: '%'}, {tester: /\w/});

some JavaScript operators would not work

typeof, instanceof, delete would not work, because bcx-expression is not real JavaScript.

BUTTONWOODCX™ PTY LTD.