eval-estree-expression
Version:
Safely evaluate JavaScript (estree) expressions, sync and async.
392 lines (269 loc) • 11.8 kB
Markdown
Safely evaluate JavaScript (estree) expressions, sync and async.
Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
- [Install](#install)
- [Usage](#usage)
* [Usage with Babel](#usage-with-babel)
* [Usage with Esprima](#usage-with-esprima)
- [API](#api)
* [.evaluate](#evaluate)
* [.evaluate.sync](#evaluatesync)
* [.variables](#variables)
- [Options](#options)
* [booleanLogicalOperators](#booleanlogicaloperators)
* [functions](#functions)
* [Top-Level await](#top-level-await)
* [generate](#generate)
* [regexOperator](#regexoperator)
* [strict](#strict)
* [withMembers](#withmembers)
- [Examples](#examples)
* [Operators](#operators)
- [About](#about)
_(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_
## Install
Install with [npm](https://www.npmjs.com/):
```sh
$ npm install --save eval-estree-expression
```
## Usage
Requires [Node.js](https://nodejs.org/en/download/) version 14 or greater.
Evaluates an [estree](https://github.com/estree/estree) expression from [@babel/parser][], [esprima](http://esprima.org), [acorn](https://github.com/acornjs/acorn), or any other library that parses and returns a valid `estree` expression.
```js
const { evaluate } = require('eval-estree-expression');
// or
import { evaluate } from 'eval-estree-expression';
evaluate.sync(expressionTree[, context]); // sync
evaluate(expressionTree[, context]).then(console.log); // async
```
See the [unit tests](./test) for hundreds of additional usage examples.
**Params**
The `evaluate` function takes the following arguments:
* `expressionTree` **{object}** - a valid [estree](https://github.com/estree/estree) expression AST.
* `context` **{object}** - a data object with values to replace variables in expressions
Most of the examples in this document assume the following setup code is used:
```js
const { evaluate } = require('eval-estree-expression');
const { parseExpression } = require('@babel/parser');
// parse your JavaScript expression
const ast = parseExpression('1 + 2');
// evaluate synchronously
console.log(evaluate.sync(ast)); //=> 3
// or asynchronously
console.log(await evaluate(ast)); //=> 3
```
[ ][esprimar] doesn't have a "parseExpression" method like @babel/parser, so you'll need to return the expression from the AST, like so:
```js
const { parse } = require('esprima');
const { evaluate } = require('eval-estree-expression');
const ast = parse('[1, 2, 3].map(n => n * x);').body[0].expression;
// evaluate synchronously
console.log(evaluate.sync(ast)); // => [2, 4, 6]
// or asynchronously
console.log(await evaluate(ast)); // => [2, 4, 6]
```
Evaluate expressions asynchronously.
```js
console.log(await evaluate(parse('1 + 2'))); //=> 3
console.log(await evaluate(parse('5 * 2'))); //=> 10
console.log(await evaluate(parse('1 > 2'))); //=> false
console.log(await evaluate(parse('1 < 2'))); //=> true
// with context object
console.log(await evaluate(parse('page.title === "home"'), { page: { title: 'home' } })); //=> true
```
Evaluate expressions synchronously.
```js
console.log(evaluate.sync(parse('1 + 2'))); //=> 3
console.log(evaluate.sync(parse('5 * 2'))); //=> 10
console.log(evaluate.sync(parse('1 > 2'))); //=> false
console.log(evaluate.sync(parse('1 < 2'))); //=> true
// with context object
console.log(evaluate.sync(parse('page.title === "home"'), { page: { title: 'home' } })); //=> true
```
Get an array of variables from an expression:
```js
const { parseExpression } = require('@babel/parser');
const { variables } = require('eval-estree-expression');
console.log(variables(parseExpression('x * (y * 3) + z.y.x'))); //=> ['x', 'y', 'z']
console.log(variables(parseExpression('(a || b) ? c + d : e * f'))); //=> ['a', 'b', 'c', 'd', 'e', 'f']
```
Type: `boolean`
Default: `undefined`
Force logical operators to return a boolean result.
```js
console.log(await evaluate(parse('a && b'), { a: undefined, b: true })); //=> undefined
console.log(await evaluate(parse('a && b'), { a: undefined, b: false })); //=> undefined
console.log(await evaluate(parse('a || b'), { a: false, b: null })); //=> null
console.log(await evaluate(parse('a || b'), { a: false, b: undefined })); //=> undefined
//
// With booleanLogicalOperators enabled
//
const options = {
booleanLogicalOperators: true
};
console.log(await evaluate(parse('a || b'), { a: false, b: null }, options)); //=> false
console.log(await evaluate(parse('a && b'), { a: undefined, b: true }, options)); //=> false
console.log(await evaluate(parse('a && b'), { a: undefined, b: false }, options)); //=> false
console.log(await evaluate(parse('a || b'), { a: false, b: undefined }, options)); //=> false
```
Type: `boolean`
Default: `false`
Allow function calls to be evaluated. This is unsafe, please enable this option at your own risk.
**Example**
```js
const { parse } = require('esprima');
const { generate } = require('escodegen');
const { evaluate } = require('eval-estree-expression');
const options = {
functions: true
};
// works with native methods
console.log(evaluate.sync(parse('/([a-z]+)/.exec(" foo ")'), { x: 2 }, options));
//=> [ 'foo', 'foo', index: 1, input: ' foo ', groups: undefined ]
// and functions defined on the context
console.log(evaluate.sync('a.upper("b")', { a: { upper: v => v.toUpperCase() } }, options));
//=> 'B'
```
However, this does NOT support function expressions or function statements.
To enable function statements and expressions (not just function calls) to be evaluated, you must also use the [generate](#generate) option.
### Top-Level await
To use top-level `await` with, you need to:
1. Enable `functions`
2. Enable `allowAwaitOutsideFunction`. This option is passed directly to the babel parser.
```ts
const { evaluate } = require('eval-estree-expression');
const { parseExpression } = require('@babel/parser');
const opts = { functions: true, allowAwaitOutsideFunction: true };
const e = (input, ctx, options) => {
return evaluate(parseExpression(input, options), ctx, options);
};
const run = async () => {
console.log(await e('await 1', {}, opts)); //=> 1
console.log(await e('Promise.resolve(1)', { Promise }, opts)); //=> 1
console.log(await e('await Promise.resolve(1)', { Promise }, opts)); //=> 1
};
run();
```
Type: `boolean`
Default: `undefined`
Enable support for function statements and expressions by enabling the [functions](
**Example**
```js
const escodegen = require('escodegen');
const { parse } = require('esprima');
const { evaluate } = require('eval-estree-expression');
const options = {
functions: true,
generate: escodegen.generate
};
console.log(await evaluate(parse('[1, 2, 3].map(n => n * x);'), { x: 2 }, options)); // => [2, 4, 6]
```
Type: `boolean`
Default: `true`
Enable the `=~` regex operator to support testing values without using functions (example `name =~ /^a.*c$/`).
**Why is this needed?**
In expressions, if you wish to test a value using a regular expression, you have two options:
1. Enable function support so that you can use methods like `.test()` and `.match()`, or
2. Use this option, which uses a special syntax to match against regular expressions _without evaluating any functions_.
In other words, instead of having to do this:
```js
console.log(evaluate.sync(parse('/^ab+c$/ig.test("abbbbbc")'), {}, { functions: true }));
```
You can do this:
```js
console.log(evaluate.sync(parse('name =~ /^a.*c$/'), { name: 'abc' }));
console.log(evaluate.sync(parse('name =~ regex'), { name: 'abc', regex: /^a.*c$/ }));
```
Type: `boolean`
Default: `false`
Throw an error when variables are undefined.
Type: `boolean`
Default: `undefined`
Used with the [variables](
Supports all JavaScript operators with the exception of assignment operators (`=`, `+=`, etc):
```js
// Arithmetic operators
evaluate('a + b');
evaluate('a - b');
evaluate('a / b');
evaluate('a * b');
evaluate('a % b');
evaluate('a ** b');
// Relational operators
evaluate('a instanceof b');
evaluate('a < b');
evaluate('a > b');
evaluate('a <= b');
evaluate('a >= b');
// Equality operators
evaluate('a !== b');
evaluate('a === b');
evaluate('a != b');
evaluate('a == b');
// Bitwise shift operators
evaluate('a << b');
evaluate('a >> b');
evaluate('a >>> b');
// Binary bitwise operators
evaluate('a & b');
evaluate('a | b');
evaluate('a ^ b');
// Binary logical operators
evaluate('a && b'); // Logical AND.
evaluate('a || b'); // Logical OR.
evaluate('a ?? b'); // Nullish Coalescing Operator.
```
<details>
<summary><strong>Contributing</strong></summary>
Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
</details>
<details>
<summary><strong>Running Tests</strong></summary>
Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
```sh
$ npm install && npm test
```
</details>
<details>
<summary><strong>Building docs</strong></summary>
_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
To generate the readme, run the following command:
```sh
$ npm install -g verbose/verb
```
</details>
You might also be interested in these projects:
[ ](https://www.npmjs.com/package/whence): Add context awareness to your apps and frameworks by safely evaluating user-defined conditional expressions. Useful… [more](https://github.com/jonschlinkert/whence) | [homepage](https://github.com/jonschlinkert/whence "Add context awareness to your apps and frameworks by safely evaluating user-defined conditional expressions. Useful for evaluating expressions in config files, prompts, key bindings, completions, templates, and many other user cases.")
| **Commits** | **Contributor** |
| --- | --- |
| 42 | [jonschlinkert](https://github.com/jonschlinkert) |
| 1 | [6utt3rfly](https://github.com/6utt3rfly) |
| 1 | [freshgum-bubbles](https://github.com/freshgum-bubbles) |
**Jon Schlinkert**
* [GitHub Profile](https://github.com/jonschlinkert)
* [Twitter Profile](https://twitter.com/jonschlinkert)
* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)
Copyright © 2025, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT License](LICENSE).
***
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on February 06, 2025._
>