Meziantou.Framework.JavaScriptEngine 1.0.2

Prefix Reserved
dotnet add package Meziantou.Framework.JavaScriptEngine --version 1.0.2
                    
NuGet\Install-Package Meziantou.Framework.JavaScriptEngine -Version 1.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Meziantou.Framework.JavaScriptEngine" Version="1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Meziantou.Framework.JavaScriptEngine" Version="1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="Meziantou.Framework.JavaScriptEngine" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Meziantou.Framework.JavaScriptEngine --version 1.0.2
                    
#r "nuget: Meziantou.Framework.JavaScriptEngine, 1.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Meziantou.Framework.JavaScriptEngine@1.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Meziantou.Framework.JavaScriptEngine&version=1.0.2
                    
Install as a Cake Addin
#tool nuget:?package=Meziantou.Framework.JavaScriptEngine&version=1.0.2
                    
Install as a Cake Tool

Meziantou.Framework.JavaScriptEngine

An ECMAScript engine written in .NET: a hand-written parser, a bytecode compiler and an interpreter. It is validated by test262, the conformance suite of ECMAScript: see the test262 results, and by the Web Tooling Benchmark (babel, TypeScript, prettier, terser... give the same results as in Node.js) and about 60 npm packages (lightningcss, sass, tailwindcss, prettier, Vue, date-fns, d3... loaded as ES modules in a page of Meziantou.Framework.WebPlatform, and compared with Chromium). It implements ECMAScript 2025 (including the proper tail calls of strict code), ECMA-402 (Intl) and Temporal, the stage 3 proposals decorators (with the accessor fields and the metadata of Symbol.metadata), import defer, source phase imports (the module sources are given by the host), immutable ArrayBuffer (transferToImmutable, sliceToImmutable), Await Dictionary (Promise.allKeyed and Promise.allSettledKeyed) and Non-extensible Applies to Private (a private field or method cannot be added to a non-extensible object), and the ShadowRealm, Import Text and Import Bytes proposals. It uses no reflection: it is trimming- and Native AOT-compatible, and its results do not depend on the globalization settings of the process (the Unicode, time zone and CLDR data are part of the library, and the word lists of ICU that Intl.Segmenter uses for Chinese, Japanese, Thai, Lao, Khmer and Burmese come from Meziantou.Framework.Unicode.Dictionaries).

It is the script engine of Meziantou.Framework.WebPlatform, through Meziantou.Framework.WebPlatform.JavaScript.

Run scripts

A JsEngine is an agent: it runs the code of its realms on one thread at a time, and owns the job queue of the promises. A JsRealm has its own global object and intrinsics.

using var engine = new JsEngine(new JsEngineOptions
{
    Timeout = TimeSpan.FromSeconds(5), // per entry into JavaScript
    RecursionLimit = 1_000,
});

var realm = engine.CreateRealm();

// Expose a .NET function to the scripts
using (engine.Enter())
{
    realm.GlobalObject.SetProperty("add", realm.CreateFunction("add", 2, (thisValue, arguments) => arguments[0].AsNumber() + arguments[1].AsNumber()));
}

var result = realm.Execute("const values = [1, 2, 3].map(x => add(x, 10)); values.join('-')");
Assert.Equal("11-12-13", result.AsString());

// The promise jobs run when the host asks for them
realm.Execute("globalThis.log = []; Promise.resolve().then(() => log.push('job')); log.push('script');");
engine.RunJobs();
Assert.Equal("script,job", realm.Execute("log.join()").AsString());

// An uncaught JavaScript error is a JsException
var exception = Assert.Throws<JsException>(() => realm.Execute("null.x"));
Assert.StartsWith("TypeError", exception.Message, StringComparison.Ordinal);

JsEngineOptions limits each entry into JavaScript (a script, a call from .NET): Timeout, MemoryLimit (the bytes allocated by the thread), MaxStatements, RecursionLimit (10,000 calls by default: the frames of the interpreter are not on the .NET stack, and an unbounded recursion throws a RangeError), RegExpTimeout and a CancellationToken. A script that exceeds a limit is stopped with a JsTerminationException, which JavaScript code cannot catch. The options also give the clock (TimeProvider), the time zone, the default locale, the host hooks (JsHost: the compilation of strings by eval, the promise rejection tracking, the job callbacks) and the module loader.

The members of the objects (JsObject.GetProperty, SetProperty, JsRealm.CreateObject...) are used inside a scope of the engine (using var scope = engine.Enter()); JsRealm.Execute, JsEngine.Call and JsEngine.Construct enter it themselves.

Modules

The modules are loaded by a JsModuleLoader: derive from it and override LoadImportedModule to resolve a specifier and complete the request with a module (JsRealm.ParseModule, or for the json, text and bytes values of the type import attribute JsRealm.CreateJsonModule, CreateTextModule and CreateBytesModule), or fail it. A module is loaded, linked, then evaluated, as in the specification:

var files = new Dictionary<string, string>(StringComparer.Ordinal)
{
    ["main.js"] = "import { square } from './math.js'; globalThis.result = square(7);",
    ["math.js"] = "export function square(x) { return x * x; }",
};

using var engine = new JsEngine(new JsEngineOptions { ModuleLoader = new DictionaryModuleLoader(files) });
var realm = engine.CreateRealm();
var module = realm.ParseModule(files["main.js"], "main.js");
module.LoadRequestedModules();
engine.RunJobs();
module.Link();
module.Evaluate();
engine.RunJobs();

Assert.Equal(49, realm.Execute("result").AsNumber());

The value of a source phase import (import source x from and import.source()) is the module source of the module (JsModule.ModuleSource), set by the module loader: a JsHostObject that extends JsRealm.AbstractModuleSource and whose ModuleSourceClassName is set (e.g. WebAssembly.Module). The source phase import of a module that has no module source, such as a JavaScript module, throws a SyntaxError.

Documentation

docs/architecture.md describes the values, the objects, the realms, the compiler and the interpreter.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed.  net11.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Meziantou.Framework.JavaScriptEngine:

Package Downloads
Meziantou.Framework.WebPlatform.JavaScript

Runs the scripts of the documents of Meziantou.Framework.WebPlatform with Meziantou.Framework.JavaScriptEngine (a ScriptEngine with the WebIDL bindings of the DOM).

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.2 0 10/24/2026
1.0.1 44 10/21/2026
1.0.0 44 10/21/2026