jse-eval
    TypeScript icon, indicating that this package has built-in type declarations

    1.5.0 • Public • Published

    jse-eval

    Latest NPM release License CI

    Credits

    Heavily based on expression-eval and jsep, with thanks to their awesome work.

    Forked from expression-eval v5.0.0. Many thanks to @donmccurdy for the initial package

    JavaScript expression parsing and evaluation.

    IMPORTANT: As mentioned under Security below, this library does not attempt to provide a secure sandbox for evaluation. Evaluation involving user inputs (expressions or values) may lead to unsafe behavior. If your project requires a secure sandbox, consider alternatives such as vm2.

    Usage

    Evaluates an estree expression from jsep (as well as @babel/parser, esprima, acorn, or any other library that parses and returns a valid estree expression).

    Install

    Install:

    npm install --save jse-eval
    

    Import:

    // ES6
    import { parse, evaluate, compile, jsep } from 'jse-eval';
    
    // CommonJS
    const { parse, evaluate, compile, jsep } = require('jse-eval');
    
    // UMD / standalone script
    const { parse, evaluate, compile, jsep } = window.jseEval;

    API

    Parsing

    import { parse } from 'jse-eval';
    const ast = parse('1 + foo');

    The result of the parse is an AST (abstract syntax tree), like:

    {
      "type": "BinaryExpression",
      "operator": "+",
      "left": {
        "type": "Literal",
        "value": 1,
        "raw": "1"
      },
      "right": {
        "type": "Identifier",
        "name": "foo"
      }
    }

    Evaluation

    import { parse, evaluate } from 'jse-eval';
    const ast = parse('a + b / c'); // abstract syntax tree (AST)
    const value = eval(ast, {a: 2, b: 2, c: 5}); // 2.4
    
    // alternatively:
    const value = await evalAsync(ast, {a: 2, b: 2, c: 5}); // 2.4

    Compilation

    import { compile } from 'jse-eval';
    const fn = compile('foo.bar + 10');
    fn({foo: {bar: 'baz'}}); // 'baz10'
    
    // alternatively:
    import { compileAsync } from 'jse-eval';
    const fn = compileAsync('foo.bar + 10');
    fn({foo: {bar: 'baz'}}); // 'baz10'

    One-Line Parse + Evaluation

    import { evalExpr } from 'jse-eval';
    evalExpr('foo.bar + 10', {foo: {bar: 'baz'}}); // baz10
    
    // alternatively:
    import { evalExprAsync } from 'jse-eval';
    evalExprAsync('foo.bar + 10', {foo: {bar: 'baz'}}); // baz10

    JSEP Plugins

    import { registerPlugin } from 'jse-eval';
    registerPlugin(
      require('@jsep-plugin/arrow'),
      require('@jsep-plugin/assignment'),
      require('@jsep-plugin/async-await'),
      require('@jsep-plugin/new'),
      require('@jsep-plugin/object'),
      require('@jsep-plugin/regex'),
      require('@jsep-plugin/spread'),
      require('@jsep-plugin/template'),
      require('@jsep-plugin/ternary')
    );
    
    // or alternatively:
    const { jsep } = require('jse-eval');
    jsep.plugins.register(
      require('@jsep-plugin/arrow'),
      require('@jsep-plugin/assignment'),
      ...
    );

    Extending evaluation

    To modify the evaluation, use any of the modification methods:

    • addUnaryOp(operator, evaluator). Will add the operator to jsep, and the function to evaluate the operator
    • addBinaryOp(operator, precedence | evaluator, evaluator). Will add the operator to jsep at the given precedence (if provided), and the function to evaluate the operator
    • addEvaluator(nodeType, evaluator). Will add the evaluator function to the map of functions for each node type. This evaluator will be called with the ExpressionEval instance bound to it. The evaluator is responsible for handling both sync and async, as needed, but can use the this.isAsync or this.evalSyncAsync() to help.
      • If the node type is unknown, jse-eval will check for a default node type handler before throwing an error for an unknown node type. If any other behavior is desired, this can be overridden by providing a new default evaluator.

    Extensions may also be added as plugins using the registerPlugin(myPlugin1, myPlugin2...) method. The plugins are extensions of the JSEP format. If the init method is defined in the plugin, then the plugin will be added to JSEP, and/or if the initEval method is defined in the plugin, then the initEval method will be called with the JseEval class as both this and as an argument so the plugin code may extend as necessary.

    Example Extensions:

    import * as expr from 'jse-eval';
    
    expr.addBinaryOp('**', 11, true, (a, b) => a ** b);
    console.log(expr.evalExpr('2 ** 3 ** 2')); // 512
    
    expr.addBinaryOp('^', 11, (a, b) => Math.pow(a, b)); // Replace XOR with Exponent
    console.log(expr.evalExpr('3^2')); // 9
    
    expr.addEvaluator('TestNodeType', function(node) {
      return node.test + this.context.string
    });
    console.log(expr.eval({ type: 'TestNodeType', test: 'testing ' }, { string: 'jse-eval' })); // 'testing jse-eval'
    
    const myPlugin = {
      name: 'Exponentiation',
      init(jsep) {
        // if only adding to jsep. Otherwise it's redundant with initEval
        jsep.addBinaryOp('**', 11, true);
      },
      initEval(JseEval) {
        JseEval.addBinaryOp('**', (a, b) => a ** b);
      },
    };
    expr.registerPlugin(myPlugin);
    console.log(expr.evalExpr('2 ** 3 ** 2')); // 512

    Node Types Supported:

    This project will try to stay current with all JSEP's node types::

    • ArrayExpression
    • LogicalExpression/BinaryExpression
    • CallExpression
    • ConditionalExpression
    • Compound Compound support will evaluate each expression and return the result of the final one
    • Identifier
    • Literal
    • MemberExpression
    • ThisExpression
    • UnaryExpression

    As well as the optional plugin node types:

    • ArrowFunctionExpression
    • AssignmentExpression/UpdateExpression
    • AwaitExpression
    • NewExpression
    • ObjectExpression
    • SpreadElement
    • TaggedTemplateExpression/TemplateLiteral

    Related Packages

    Depending on your specific use-case, there are other related packages available, including:

    Security

    Although this package does avoid the use of eval(), it cannot guarantee that user-provided expressions, or user-provided inputs to evaluation, will not modify the state or behavior of your application. This library does not attempt to provide a secure sandbox for evaluation. Evaluation of arbitrary user inputs (expressions or values) may lead to unsafe behavior. If your project requires a secure sandbox, consider alternatives such as vm2.

    Contributing

    Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on the guidelines for contributing and then feel free to submit a PR with your contribution.

    Code of Conduct

    Help us keep this project open and inclusive. Please read and follow the Code of Conduct.

    License

    MIT License.

    Install

    npm i jse-eval

    DownloadsWeekly Downloads

    57

    Version

    1.5.0

    License

    MIT

    Unpacked Size

    205 kB

    Total Files

    13

    Last publish

    Collaborators

    • 6utt3rfly