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 🙏

© 2026 – Pkg Stats / Ryan Hefner

expression-language

v2.5.4

Published

Javascript implementation of symfony/expression-language

Downloads

3,478

Readme

Javascript implementation of the Symfony/ExpressionLanguage

The idea is to be able to evaluate the same expressions client-side (in Javascript with this library) and server-side (in PHP with the Symfony/ExpressionLanguage).

License: MIT

Feature parity

Below is the current parity of this library with Symfony's ExpressionLanguage features. All items default to supported status.

| Category | Feature | Supported | |-------------------------|------------------------------------------------------------------------------|-----------| | Literals | Strings (single and double quotes) | ✅ | | Literals | Numbers (integers, decimals, decimals without leading zero) with underscores | ✅ | | Literals | Arrays (JSON-like [ ... ]) | ✅ | | Literals | Hashes/Objects (JSON-like { key: value }) | ✅ | | Literals | Booleans (true/false) | ✅ | | Literals | null | ✅ | | Literals | Exponential/scientific notation | ✅ | | Literals | Block comments /* ... */ inside expressions | ✅ | | Escapes | Backslash escaping in strings and regexes | ✅ | | Escapes | Control characters need escaping (e.g., \n) | ✅ | | Objects | Access public properties with dot syntax (obj.prop) | ✅ | | Objects | Call methods with dot syntax (obj.method(...)) | ✅ | | Objects | Null-safe operator (obj?.prop / obj?.method()) | ✅ | | Nullish | Null-coalescing operator (a ?? b) | ✅ | | Functions | constant() | ✅ | | Functions | enum() | ✅ | | Functions | min() | ✅ | | Functions | max() | ✅ | | Arrays | Access array items with bracket syntax (arr[...]) | ✅ | | Operators: Arithmetic | +, -, *, /, %, ** | ✅ | | Operators: Bitwise | &, | , ^ | ✅ | | Operators: Bitwise | ~ (not), <<, >> | ✅ | | Operators: Comparison | ==, ===, !=, !==, <, >, <=, >= | ✅ | | Operators: Comparison | matches (regex) | ✅ | | Operators: String tests | contains, starts with, ends with | ✅ | | Operators: Logical | not/!, and/&&, or/||, xor | ✅ | | Operators: String | ~ (concatenation) | ✅ | | Operators: Array | in, not in (strict comparison) | ✅ | | Operators: Numeric | .. (range) | ✅ | | Operators: Ternary | a ? b : c, a ?: b, a ? b | ✅ | | Other | Null-safe operator (?.) | ✅ | | Other | Null-coalescing operator (??) | ✅ | | Precedence | Operator precedence as per Symfony docs | ✅ | | fromPhp() | Supported as fromJavascript() | ✅ | | Symfony Built-ins | Security expression variables | ⛔️ | | Symfony Built-ins | Service container expression variables | ⛔️ | | Symfony Built-ins | Routing expression variables | ⛔️ |

Notes: Symfony Built-ins are not supported in the javascript environment

Installation

NPM/Yarn

npm install expression-language
# or
yarn add expression-language

Browser

You can also use this library directly in the browser by including it via a script tag:

<!-- Unminified version for development -->
<script src="https://unpkg.com/expression-language/dist/expression-language.js"></script>
<!-- or minified version for production -->
<script src="https://unpkg.com/expression-language/dist/expression-language.min.js"></script>

Examples

NPM/Yarn Setup

import {ExpressionLanguage} from "expression-language";

let expressionLanguage = new ExpressionLanguage();

Browser Setup


<script src="https://unpkg.com/expression-language/dist/expression-language.min.js"></script>
<script>
    // The library is available as a global ExpressionLanguage object
    const expressionLanguage = new ExpressionLanguage.ExpressionLanguage();
</script>

A complete browser example is available in the examples/browser-usage.html file.

Basic

let result = expressionLanguage.evaluate('1 + 1');
// result is 2.

Multiple clauses

let result = expressionLanguage.evaluate(
    'a > 0 && b != a',
    {
        a: 1,
        b: 2
    }
);
// result is true

Object and Array access

let expression = 'a[2] === "three" and b.myMethod(a[1]) === "bar two"';
let values = {
    a: ["one", "two", "three"],
    b: {
        myProperty: "foo",
        myMethod: function (word) {
            return "bar " + word;
        }
    }
};
let result = expressionLanguage.evaluate(expression, values);
// result is true

Registering custom functions

You can register functions in two main ways. Make sure to register functions before calling evaluate(), compile(), or parse(); otherwise a LogicException will be thrown.

  • Using register(name, compiler, evaluator):
import { ExpressionLanguage } from 'expression-language';

const el = new ExpressionLanguage();

// Define how the function should compile to JavaScript and how it should evaluate at runtime.
el.register(
  'double',
  // compiler: receives the compiled argument strings and must return JS source
  (x) => `((+${x}) * 2)`,
  // evaluator: receives (values, ...args) and returns the result
  (values, x) => Number(x) * 2
);

console.log(el.evaluate('double(21)')); // 42
console.log(el.compile('double(a)', ['a'])); // '((+a) * 2)'
  • Using addFunction with an ExpressionFunction instance:
import { ExpressionLanguage, ExpressionFunction } from 'expression-language';

const el = new ExpressionLanguage();
const timesFn = new ExpressionFunction(
  'times',
  (a, b) => `(${a} * ${b})`,
  (values, a, b) => a * b
);

el.addFunction(timesFn);

console.log(el.evaluate('times(6, 7)')); // 42

Using providers

Providers are a convenient way to bundle and register multiple functions. A provider exposes a getFunctions() method that returns an array of ExpressionFunction instances. You can register providers via the constructor or with registerProvider().

  • Built-in providers you can use out of the box:

    • BasicProvider: isset()
    • StringProvider: strtolower, strtoupper, explode, strlen, strstr, stristr, substr
    • ArrayProvider: implode, count, array_intersect
    • DateProvider: date, strtotime
  • Registering built-in providers:

import { ExpressionLanguage, StringProvider, ArrayProvider, DateProvider, BasicProvider } from 'expression-language';

// Pass providers in the constructor (array or any iterable)
const el = new ExpressionLanguage(null, [
  new StringProvider(),
  new ArrayProvider(),
  new DateProvider(),
  new BasicProvider(),
]);

console.log(el.evaluate('strtoupper("hello")')); // 'HELLO'
console.log(el.evaluate('count([1,2,3])')); // 3
console.log(el.evaluate('isset(foo.bar)', { foo: { bar: 1 } })); // true
  • Creating your own provider:
import { ExpressionLanguage, ExpressionFunction } from 'expression-language';

class MathProvider {
  getFunctions() {
    return [
      new ExpressionFunction(
        'clamp',
        (x, min, max) => `Math.min(${max}, Math.max(${min}, ${x}))`,
        (values, x, min, max) => Math.min(max, Math.max(min, x))
      ),
      new ExpressionFunction(
        'pct',
        (value, total) => `(((${value}) / (${total})) * 100)`,
        (values, value, total) => (value / total) * 100
      )
    ];
  }
}

const el = new ExpressionLanguage();
el.registerProvider(new MathProvider());

console.log(el.evaluate('clamp(150, 0, 100)')); // 100
console.log(el.evaluate('pct(2, 8)')); // 25

Using ExpressionFunction.fromJavascript()

Use this helper to wrap an existing JavaScript function (resolved from the global object) as an ExpressionFunction.

Rules and tips:

  • If you pass a namespaced/dotted path like 'Math.max', you must also provide an explicit expression function name (e.g., 'max').
  • For non-namespaced global functions (e.g., 'myFn'), the expression function name defaults to the same name.
  • The function must exist on globalThis (window in browsers, global in Node). If it does not exist, an error is thrown.

Examples:

import { ExpressionLanguage, ExpressionFunction } from 'expression-language';

const el = new ExpressionLanguage();

// 1) Non-namespaced global function
globalThis.mySum = (a, b) => a + b; // or window.mySum in browser
const sumFn = ExpressionFunction.fromJavascript('mySum');
el.addFunction(sumFn);
// 2) Namespaced (dotted) function requires an explicit expression name
const maxFn = ExpressionFunction.fromJavascript('Math.max', 'max');
el.addFunction(maxFn);

console.log(el.evaluate('mySum(20, 22)')); // 42
console.log(el.evaluate('max(1, 3, 2)')); // 3

// Note: min/max are already built-in and compile to Math.min/Math.max.

Note: Register functions or providers before calling evaluate(), compile(), or parse(); late registration will throw a LogicException.

Using IGNORE_* flags

These flags let you relax strict validation when parsing expressions via the high-level API. They are useful for linting or building tools where variables/functions may be unknown at parse time.

  • IGNORE_UNKNOWN_VARIABLES: allows names that are not provided in the names list.
  • IGNORE_UNKNOWN_FUNCTIONS: allows calling functions that are not registered.
  • You can combine flags with bitwise OR (|).

Examples:

import { ExpressionLanguage, IGNORE_UNKNOWN_VARIABLES, IGNORE_UNKNOWN_FUNCTIONS } from 'expression-language';

const el = new ExpressionLanguage();

// 1) Allow unknown variables when parsing via ExpressionLanguage
el.parse('foo.bar', [], IGNORE_UNKNOWN_VARIABLES);

// 2) Allow unknown functions when parsing via ExpressionLanguage
el.parse('myFn()', [], IGNORE_UNKNOWN_FUNCTIONS);

// 3) Allow both unknown functions and variables
el.parse('myFn(foo)', [], IGNORE_UNKNOWN_FUNCTIONS | IGNORE_UNKNOWN_VARIABLES);

Linting:

import { ExpressionLanguage, IGNORE_UNKNOWN_VARIABLES, IGNORE_UNKNOWN_FUNCTIONS } from 'expression-language';

const el = new ExpressionLanguage();

// Validate expressions without executing them
el.lint('a > 0 && myFn(foo)', ['a'], IGNORE_UNKNOWN_FUNCTIONS | IGNORE_UNKNOWN_VARIABLES);

// By default (flags = 0), unknowns throw:
try {
  el.lint('myFn(foo)');
} catch (e) {
  console.warn('Lint failed as expected:', e.message);
}

Notes:

  • Passing null for the names parameter is deprecated; use IGNORE_UNKNOWN_VARIABLES instead when you want to allow unknown variables.

Continuous Integration and Deployment

This package uses GitHub Actions for automated workflows:

  1. NPM Publishing: Automatically publishes to npm when the package version changes
  2. GitHub Releases: Automatically creates GitHub releases with changelogs and distribution files

For Maintainers

If you're maintaining this package, you'll need to set up the following:

NPM Publishing

Set up trusted publisher in npmjs.org by following the instructions here.

GitHub Releases

The GitHub release workflow automatically:

  • Checks if the package version has changed
  • Builds the project to generate distribution files
  • Creates a GitHub release with the new version tag
  • Generates a changelog based on commit messages
  • Attaches the distribution files to the release

No additional setup is required for GitHub releases as it uses the default GITHUB_TOKEN.

Once set up, any push to the main branch will trigger these workflows when the package version changes.