Vega Expression Parser and Code Generator
vega-expression is a JavaScript/TypeScript library that provides a secure and configurable expression parser and code generator, forming a core component of the broader Vega visualization toolkit. It processes a limited subset of JavaScript expressions into an Abstract Syntax Tree (AST) and then generates `eval`'able JavaScript code. The library intentionally restricts language features like assignment operators, `new` expressions, and control flow to prioritize security and prevent unwanted side effects, making it suitable for user-provided expressions in visualization contexts. It is currently in active development, with version 6.2.0 as the latest stable release at the time of this entry, following a fairly active release cadence that often aligns with the larger Vega monorepo. Its key differentiators include its security-first approach, a stripped-down Esprima-based parser, and highly configurable code generation options for managing constants, functions, and variable scopes, including tracking data field dependencies.
Common errors
-
ReferenceError: require is not defined
cause Attempting to use CommonJS `require()` syntax in a project after upgrading to `vega-expression` v6.0.0 or later, which is ESM-only.fixReplace `const { parse } = require('vega-expression');` with `import { parse } from 'vega-expression';`. Ensure your `package.json` has `"type": "module"` or use `.mjs` file extensions for ESM files. -
Error: Invalid AST Node Type: AssignmentExpression
cause The parsed expression contains forbidden JavaScript syntax, such as an assignment operator (`=`).fixRewrite the expression to use only the allowed subset of JavaScript. Avoid assignment operators, `new` expressions, `for`/`while` loops, and direct method calls on nested properties. -
ReferenceError: myGlobalVar is not defined
cause The generated code attempts to access a global variable (`myGlobalVar`) that was not explicitly allowed or provided in the `codegen` options.fixWhen creating the code generator, use the `allowed` array for variables within the expression scope, or the `globalvar` option to specify the object through which global variables should be looked up.
Warnings
- breaking Vega v6.0.0, including `vega-expression`, transitioned to ESM-only. CommonJS `require()` statements will no longer work, leading to import errors.
- breaking A prototype pollution vulnerability (GHSA-7f2v-3qq3-vvjf, CVE-2025-59840) was identified in `vega-expression` versions prior to `5.33.1`. This could allow cross-site scripting (XSS) via expressions abusing `toString` calls in specific environments. While fixed in newer versions, users on older `v5.x` releases should upgrade immediately.
- gotcha The `vega-expression` language is a restricted subset of JavaScript. It does not allow assignment operators (`=`, `+=`, etc.), pre/postfix updates (`++`), `new` expressions, or most control flow statements (`for`, `while`, `switch`). Function calls involving nested properties (e.g., `foo.bar()`) are also forbidden.
- gotcha When using `codegen`, the `Function` constructor is used to evaluate the generated code. This can violate Content Security Policies (CSPs) that prohibit `unsafe-eval`. For CSP-compliant evaluation, consider using the `vega-interpreter` package, which evaluates expressions by traversing the AST directly.
Install
-
npm install vega-expression -
yarn add vega-expression -
pnpm add vega-expression
Imports
- parse
const parse = require('vega-expression').parse;import { parse } from 'vega-expression'; - codegen
const codegen = require('vega-expression').codegen;import { codegen } from 'vega-expression'; - CodegenOptions
import type { CodegenOptions } from 'vega-expression';
Quickstart
import { parse, codegen } from 'vega-expression';
interface DataItem {
value: number;
category: string;
}
// 1. Define the expression string
const expressionString = "datum.value * 2 + 5";
// 2. Parse the expression into an AST
const ast = parse(expressionString);
console.log('Parsed AST:', JSON.stringify(ast, null, 2));
// 3. Create a code generator, specifying the field variable ('datum' in this case)
// and any allowed globals/functions if needed.
const generator = codegen({
fieldvar: 'datum',
allowed: ['datum'], // 'datum' is an allowed variable in the expression scope
// You can also provide custom functions or constants
functions: () => ({
pow: (args: any[]) => `Math.pow(${args[0]}, ${args[1]})`
})
});
// 4. Generate the executable code from the AST
const { code, fields, globals } = generator(ast);
console.log('\nGenerated Code:', code);
console.log('Referenced Fields:', fields);
console.log('Referenced Globals:', globals);
// 5. Evaluate and use the generated function
// NOTE: Using `Function` constructor is generally discouraged due to CSP issues.
// For Vega runtime, `vega-interpreter` offers CSP-compliant evaluation.
const evaluateExpression = new Function('datum', `return ${code};`);
const sampleData: DataItem = { value: 10, category: 'A' };
const result = evaluateExpression(sampleData);
console.log(`\nResult for { value: 10 } (10 * 2 + 5):`, result); // Expected: 25
const anotherData: DataItem = { value: 7, category: 'B' };
const anotherResult = evaluateExpression(anotherData);
console.log(`Result for { value: 7 } (7 * 2 + 5):`, anotherResult); // Expected: 19