Contexts and scripts

Contexts and scripts are mere a ways to organize and optimize metaesEval calls. In project like Vanillin, there are possibly hundreds of scripts defined at the same time, most likely running cooperatively. Contexts and scripts are completely optional for the runtime, but are essential for bigger and more complex applications design.


Context is a place where scripts can be executed. It's basically a simple abstraction layer over metaesEval that remembers some default values - like default environment.

If you've ever developed browser extension, you can remember there are page scripts and content scripts. Page scripts are normal script tags, but page scripts run in a different context next to page scripts and can't easily see window object.

Similarly, context concept semantically defines a place where code will be executed (maybe in a different process, maybe on server, maybe in ServiceWorker, WebWorker?) and what global variables will be available for each script.


const context = new MetaesContext(
  // default success callback
  // default error callback
  // global scope
  { Math }

// or even
const context2 = consoleLoggingMetaesContext({ Math });
// context2.evaluate(...);

As can you see, once defined callbacks and environment doesn't have to be repeated over and over again.

What is desirable, they can be overriden per call:

context.evaluate("2+x", null, null, { x: 2 });

Using nulls means keeping default values. (TODO: check) Here is TypeScript definitions used for contexts:

export type Evaluate = (
  input: Script | Source,
  c?: Continuation | null,
  cerr?: ErrorContinuation | null,
  environment?: Environment | object,
  config?: Partial<EvaluationConfig>
) => void;

export interface Context {
  evaluate: Evaluate;

This means the only method required by Context is evaluate. Evaluation may happen anywhere: including server, other process as long objects transfer from memory to memory is provided.


Script is a container for the code which can be executed inside the context. Script can be produced from string (as in native JavaScript) which is parsed by JavaScript parser, from native JavaScript function or from JSON object. Actually, script can be produced from any source, but eventually all kinds of inputs should be transformed to AST. One of the advandates of scripts is they don't have to be reparsed every time they're evaluated. They also have attributed with an unique ID (per host interpreter), which is useful for debugging and orchestration, and more.


const script = createScript(`console.log('hello world')`);
metaesEval(script, console.log, console.error, { console });

Example with enabled caching:

const cache = createCache();
const script = createScript(`console.log('hello world')`, cache);

// won't be parsed again, AST object will be taken from cache
const script2 = createScript(`console.log('hello world')`, cache);