The JS 2 compiler is a Loopdive project focused on ahead-of-time compilation of real JavaScript to WebAssembly.


Mission: Next generation JavaScript.

The mission of the JS² compiler project is to make WebAssembly a drop-in deployment target for existing JavaScript and npm packages, running modules without embedding a JS runtime and enabling more secure, flexible dependency management.

JavaScript logo

Not a New Language

Compile existing JavaScript and npm packages without rewriting them into a new language or restricted subset. Stay compatible with the JavaScript ecosystem.

WebAssembly logo

JS Host or Standalone

Deploy compiled modules across JavaScript hosts and standalone WebAssembly runtimes without embedding a JS engine in the module or hand-writing glue code.

🔒

Module Sandboxing

Reduce supply chain attack surface by isolating JavaScript modules from each other inside the same application and keeping filesystem, network, DOM, and globals behind explicit imports.

🧩

Dependency Injection

Link modules through explicit imports so consumers can choose implementations, control upgrades, and swap dependencies for each environment.


Goal: 100% JavaScript compatibility.

Which JS language features compile to WebAssembly today, which still rely on host support, and which are still on the roadmap. Status is derived from ECMAScript Test262 pass rates and known compiler limitations.

Compatibility Test Report

ECMAScript Conformance Pass Rate Over Time

Pass rate Tests passed Total tests run
ES3 / Core
Primitive types (string, number, boolean, null, undefined) All primitive value types and coercion
const s = "hello";
const n = 42;
const b = true;
const x = null;
(func $__module_init
  string.const "hello"
  global.set $s
  f64.const 42
  global.set $n
  i32.const 1           ;; true
  global.set $b)
Operators (arithmetic, comparison, logical, bitwise) All standard operators including ternary
const sum = a + b;
const eq = x === y;
const ok = a > 0 && b > 0;
const bits = n | 0xFF;
(func $example (param f64 f64)
    (result f64)
  local.get 0
  local.get 1
  f64.add               ;; a + b
)
;; x === y → f64.eq (or ref.eq)
;; n | 0xFF → i32.or
typeof / instanceof Runtime type checking operators
typeof x === "string";
obj instanceof Array;
;; typeof x === "string"
;; compiles to tag check on struct
local.get $x
ref.test (ref $String)
;; instanceof → ref.test
delete operator Remove object properties
const obj = { a: 1, b: 2 };
delete obj.b;
;; delete obj.b
local.get $obj
ref.null extern
struct.set $Obj $b
Comma operator Evaluate expressions left to right
const x = (1, 2, 3); // x === 3
f64.const 1
drop
f64.const 2
drop
f64.const 3
Labeled statements (break / continue) Named loop targets for break and continue
outer: for (let i = 0; i < 3; i++) {
  for (let j = 0; j < 3; j++) {
    if (j === 1) continue outer;
  }
}
(block $outer
  (loop $outer_loop
    (block $inner
      (loop $inner_loop
        ;; if j === 1
        br $outer_loop
      ))
    br $outer_loop
  ))
for-in Iterate over object property names
for (const key in obj) {
  console.log(key);
}
local.get $obj
call $Object.keys
local.set $keys
;; iterate keys array
arguments object (full) Legacy arguments — partial, rest params preferred
function legacy() {
  return arguments.length;
}
// prefer rest params: (...args)
The full arguments object is partially supported. Rest parameters (...args) are preferred and fully compiled.
eval() Dynamic code evaluation at runtime
eval("1 + 2"); // not supported
Requires a JS engine at runtime. Not possible in AOT compilation.
with statement Dynamic scope extension
with (obj) { x; } // not supported
Disallowed in strict mode. All modules run strict.
ES5
Variables (var, let, const) Block-scoped and function-scoped variable declarations
let count = 1;
const name = "hello";
var legacy = true;
(func $__module_init
  (local $count f64)
  (local $name externref)
  (local $legacy i32)
  f64.const 1
  local.set $count
  string.const "hello"
  local.set $name
  i32.const 1
  local.set $legacy)
Functions & closures Named functions, expressions, and lexical closures
function greet(name) {
  return "Hi " + name;
}

const add = (a, b) => a + b;
(func $greet (param (ref null $str))
    (result (ref null $str))
  string.const "Hi "
  local.get 0         ;; name
  string.concat
  return)
Control flow Branching, loops, and switch statements
if (x > 0) {
  handle();
} else {
  fallback();
}

for (let i = 0; i < 10; i++) {
  process(i);
}
(func $test (param f64)
    (result f64)
  local.get 0
  f64.const 0
  f64.gt
  (if (result f64)
    (then local.get 0 return)
    (else local.get 0 f64.neg return)))
try / catch / finally Exception handling with optional finally block
try {
  riskyOp();
} catch (e) {
  handle(e);
} finally {
  cleanup();
}
(func $safe (result f64)
  (try
    (do call $riskyOp)
    (catch 0
      local.set $e
      f64.const -1
      return)))
throw Throw custom and built-in error objects
throw new Error("something went wrong");
(func $__module_init
  i32.const 1
  string.const "something went wrong"
  struct.new $Error
  throw 0)
Objects Literals, property access, methods, and shorthand syntax
const obj = {
  name: "js2wasm",
  version: 1,
  greet() { return this.name; }
};
(type $obj (struct
  (field $__tag i32)
  (field $name externref)
  (field $version f64)))

(func $obj_greet (param (ref null $obj))
    (result externref)
  local.get 0
  struct.get $obj $name
  return)
Strings String methods, concatenation, and manipulation
"hello".toUpperCase();   // "HELLO"
"a,b,c".split(",");
"hello".slice(1, 3);      // "el"
(func $__module_init
  ;; "hello".toUpperCase()
  string.const "hello"
  call $string.toUpperCase ;; host
  ;; "hello".slice(1, 3)
  string.const "hello"
  i32.const 1  i32.const 3
  string.substring)
Numbers Math operations, parseInt, Number methods
Math.max(1, 2);
parseInt("42");
(3.14).toFixed(1);
(func $__module_init
  (local $a f64) (local $b f64)
  f64.const 1
  local.set $a
  f64.const 2
  local.set $b
  local.get $a  local.get $b
  f64.max
  drop)
host JSON Parse and stringify JSON data
const obj = JSON.parse('{"a": 1}');
const str = JSON.stringify({ a: 1 });
(func $__module_init
  global.get 0
  call $JSON.parse
  global.set $obj)
Error types Error, TypeError, RangeError, SyntaxError and more
throw new TypeError("expected string");
throw new RangeError("out of bounds");
(func $__module_init
  i32.const 2
  string.const "expected string"
  struct.new $TypeError
  throw 0)
host Arrays Array methods and iteration helpers
const doubled = [1, 2,
  3].map(x => x * 2);
const sum = arr.reduce((a,
  b) => a + b, 0);
const found = arr.find(x => x > 5);
(func $__module_init
  ;; [1, 2, 3].map(x =&gt; x...
  f64.const 1  f64.const 2  f64.const 3
  array.new_fixed 3
  ref.func $callback
  call $__arr_map
Most built-in methods work. Some iterator edge cases and sparse array handling incomplete.
host Regular expressions Pattern matching and string search
/hello/i.test("Hello World");
"abc123".match(/\d+/);
(func $__module_init
  global.get $re
  string.const "Hello World"
  call $regexp_test)
Basic patterns work. Named groups and lookbehind partially supported.
Property accessors (get / set) Getter and setter property definitions
const obj = {
  _v: 0,
  get value() { return this._v; },
  set value(v) { this._v = v; }
};
(func $get_value (param (ref null $obj))
    (result f64)
  local.get 0
  struct.get $obj $_v
)

(func $set_value (param (ref null $obj)) (param f64)
  local.get 0
  local.get 1
  struct.set $obj $_v)
Basic get/set works. Object.defineProperty partially supported.
Object.defineProperty (full) Full property descriptor configuration
Object.defineProperty(obj, "x", {
  enumerable: false,
  writable: false
});
Property descriptor system not yet fully emitted in Wasm structs.
ES2015
Arrow functions Concise function syntax with lexical this
const add = (a, b) => a + b;
const square = x => x * x;
const greet = () => "hello";
(func $add (param f64 f64)
    (result f64)
  local.get 0
  local.get 1
  f64.add)

(func $square (param f64)
    (result f64)
  local.get 0
  local.get 0
  f64.mul)
Template literals String interpolation with backtick syntax
const msg = `Hello ${name}, you are ${age}!`;
(func $__module_init
  string.const "Hello "
  local.get $name
  string.concat
  string.const "!"
  string.concat)
Destructuring Extract values from arrays and objects
const { name, age } = person;
const [first, ...rest] = items;
const { a: x = 0 } = opts;
;; typed object → direct struct ...
(func $__module_init
  local.get $obj
  struct.get $Obj $a     ;; a
  local.get $obj
  struct.get $Obj $b)    ;; b
Spread / rest operators Expand iterables and collect arguments
const merged = [...a, ...b];
const clone = { ...original };
function sum(...args) { }
(func $__module_init
  ;; [...arr, 4]
  f64.const 1  f64.const 2  f64.const 3
  f64.const 4
  array.new_fixed $f64arr 4)
Default parameters Fallback values for function parameters
function greet(name = "world") { }
(func $greet (param externref)
    (result externref)
  local.get 0
  ref.is_null
  (if (then
    global.get 1
    local.set 0))
  local.get 0
  return)
Computed property names Dynamic property keys in object literals
const key = "id";
const obj = { [key]: 42 };
(func $__module_init
  ;; { [key]: 42 }
  global.get $key          ;; "id"
  f64.const 42
  struct.new $obj)
for-of Iterate over iterable objects
for (const item of iterable) { }
(func $__module_init
  ;; for (const x of arr)
  local.get $arr
  struct.get $Vec $data
  struct.get $Vec $len
  (loop $for_of
    local.get $i
    array.get $data
    ;; ... process x
    br_if $for_of))
Generators (function*, yield) Pausable functions that produce sequences
function* range(n) {
  for (let i = 0; i < n; i++) {
    yield i;
  }
}
(func $range (param f64)
    (result (ref $Gen))
  ;; yield values into WasmGC array
  array.new_default $f64arr 0
  local.set $buf
  (loop $loop
    local.get $i
    local.get 0            ;; n
    i32.lt_s
    (if (then
      ;; yield i → push to buffer
      local.get $i
      ;; i++, br $loop
    )))
  local.get $buf
  struct.new $Generator)
Classes Class declarations with inheritance
class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    return this.name + " speaks";
  }
}

class Dog extends Animal { }
(type $Animal (struct
  (field $__tag i32)
  (field $name externref)))

(func $Animal_new (param externref)
    (result (ref null $Animal))
  struct.new $Animal
  local.get 0
  struct.set $Animal $name)
Constructor, methods, extends, super work. Dynamic prototype lookup is partial.
host Map / Set Key-value and unique-value collections
const m = new Map();
m.set("key", 42);

const s = new Set([1, 2, 3]);
s.has(2); // true
(func $__module_init
  call $Map_new
  global.set $m
  global.get $m
  string.const "key"
  f64.const 42
  call $Map_set)
Core operations work. Some iteration edge cases incomplete.
host Symbol Unique, immutable primitive identifiers
const id = Symbol("id");
obj[id] = 42;
(func $__module_init
  global.get $nextId
  i32.const 1
  i32.add
  global.set $nextId
  global.get $nextId
  global.set $id)
Creation and basic use work. Well-known symbols (Symbol.iterator) partial.
host TypedArray / ArrayBuffer Binary data buffers and typed views
const buf = new ArrayBuffer(16);
const view = new Int32Array(buf);
view[0] = 42;
(func $__module_init
  i32.const 16
  call $ArrayBuffer_new
  call $Int32Array_from
  global.set $view
  global.get $view
  i32.const 0
  f64.const 42
  call $TypedArray_set)
Int8 through Float64 arrays work. BigInt64Array not yet.
Modules (import / export) ES module system for code organization
import { foo } from "./mod";
export function bar() { return 1; }
(func $bar (export "bar")
    (result f64)
  f64.const 1
  return)
Static imports work. Dynamic import() not yet.
Proxy / Reflect Object behavior interception and reflection
new Proxy(target, handler); // not supported
Requires runtime trap dispatch. Not AOT-compilable.
host Promise .then / .catch / .finally Promise chaining and error handling
promise.then(v => v + 1); // not yet
Compiles but async callbacks do not execute. Promise.resolve/all/race work.
ES2017
host async / await Asynchronous functions with synchronous-style syntax
const mod = await import("./module"); // not yet
(func $fetchData (result f64)
  f64.const 42
  call $Promise.resolve
  call $__await
  return)
Object.entries / values Extract entries or values from objects
Object.entries({ a: 1, b: 2 });
// [["a", 1], ["b", 2]]

Object.values({ x: 10 });
// [10]
(func $__module_init
  f64.const 1  f64.const 2
  struct.new $obj
  extern.convert_any
  call $Object.entries)
host SharedArrayBuffer / Atomics Shared memory and atomic operations
new SharedArrayBuffer(1024); // not supported
Requires shared Wasm linear memory.
ES2018
Object spread / rest Object spread in literals and rest in destructuring
const clone = { ...original,
  extra: 1 };
const { a, ...rest } = obj;
(func $__module_init
  ;; { ...original, extra: 2 }
  local.get $original
  struct.get $Obj $x
  f64.const 2
  struct.new $Clone)
host Async iteration (for-await-of) Iterate over async data sources
for await (const chunk of stream) { }
(func $process (param externref)
  local.get 0
  call $__iterator
  local.set $iter
  (loop $for_await
    local.get $iter
    call $__iterator_next
    ;; ... process chunk
    br_if $for_await))
Basic patterns work. Some async generator edge cases incomplete.
ES2020
Optional chaining (?.) Safe property access on nullable values
const name = user?.profile?.name;
(func $test (param externref)
    (result externref)
  local.get 0
  ref.is_null
  (if (result externref)
    (then ref.null extern)
    (else local.get 0
          call $__extern_get_name)))
Nullish coalescing (??) Default values for null or undefined
const val = input ?? "default";
(func $test (param f64)
    (result f64)
  local.get 0        ;; x
  call $__is_nullish
  (if (result f64)
    (then f64.const 0)
    (else local.get 0)))
host globalThis Universal global object reference
globalThis.console;
(func $__module_init
  call $__get_globalThis
  global.set $g)
host BigInt Arbitrary precision integer arithmetic
const big = 9007199254740993n;
const sum = big + 1n;
(func $__module_init
  i64.const 9007199254740993
  global.set $big
  global.get $big
  i64.const 1
  i64.add
  global.set $sum)
Basic arithmetic works. BigInt typed arrays not yet.
host Dynamic import() Load modules at runtime on demand
const mod = await import("./module"); // not yet
Requires a runtime module loader.
ES2021
host WeakRef / FinalizationRegistry Weak references and GC callbacks
new WeakRef(obj); // not supported
GC-observable. Not available in standard WasmGC today.
ES2022
Class fields (public, private, static) Declarative field syntax in classes
class Counter {
  count = 0;
  #secret = 42;
  static instances = 0;
}
(type $Counter (struct
  (field $__tag i32)
  (field $count f64)))

(func $Counter_new
    (result (ref null $Counter))
  i32.const 0     ;; __tag
  f64.const 0     ;; count = 0
  struct.new $Counter)
Error.cause Chain errors with a cause property
throw new Error("fail", { cause: origErr });
(func $fail
  i32.const 1
  string.const "root"
  struct.new $Error        ;; cause
  i32.const 1
  string.const "fail"
  struct.new $ErrorCause
  throw 0)
Array.at / String.at Relative indexing with negative support
[1, 2, 3].at(-1);  // 3
"hello".at(0);      // "h"
(func $__module_init
  ;; [1,2,3].at(-1)
  local.get $arr
  i32.const -1
  local.get $len
  i32.add
  array.get $data)         ;; → 3
Top-level await Await at module scope without async wrapper
const data = await fetch(url);
export { data };
Requires module-level async execution model. Not yet supported.
ES2016
Array.prototype.includes Check if array contains a value
[1, 2, 3].includes(2); // true
;; linear scan over WasmGC array
local.get $arr
struct.get $Vec $data
(loop $scan
  array.get $data
  local.get $target
  f64.eq
  br_if $found
  br $scan)
Exponentiation operator (**) Power operator as infix syntax
const sq = 2 ** 10; // 1024
f64.const 2
f64.const 10
call $Math.pow
ES2019
Optional catch binding Omit the error parameter in catch
try { riskyOp(); }
catch { handleError(); }
(try
  (do call $riskyOp)
  (catch_all
    call $handleError))
host Array.prototype.flat / flatMap Flatten nested arrays
[[1, 2], [3]].flat();
// → [1, 2, 3]
Basic flat() works via host import. Deep flattening and flatMap partial.
host Object.fromEntries Create object from key-value pairs
const obj = Object.fromEntries(
  [["a", 1], ["b", 2]]
);
Uses host import to construct object from entries array.
ES2023
host Array.findLast / findLastIndex Search arrays from the end
[1, 2, 3,
  2].findLast(x => x === 2);
// → 2 (last match)
Iterates WasmGC array from end. Callback via host import.
host Change array by copy (toSorted, toReversed, toSpliced) Immutable array transformations
const sorted = arr.toSorted();
const rev = arr.toReversed();
Immutable array operations not yet implemented. Requires new array allocation + copy.
Hashbang (#!) comments Unix shebang line support
#!/usr/bin/env node
console.log("hello");
;; hashbang stripped at parse time
;; no Wasm output for comments
ES2024
host Promise.withResolvers Create Promise with external resolve/reject
const { promise, resolve, reject }
  = Promise.withResolvers();
Static Promise method not yet implemented. Requires host import.
host Resizable ArrayBuffer Growable and shrinkable buffers
const buf = new ArrayBuffer(8,
  { maxByteLength: 16 });
buf.resize(16);
Growable buffers not yet supported. Requires linear memory integration.
RegExp v flag Set notation in character classes
/[\p{Letter}--[a-z]]/v.test("A");
Unicode set notation in character classes. Requires updated regex host.
ES2025
host Set methods (union, intersection, difference) Built-in set operations
const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);
a.union(b); // {1,2,3,4}
ES2025 Set methods. Requires host import for Set operations.
host Iterator helpers (map, filter, take) Lazy iterator transformations
function* nums() { yield 1; yield 2; yield 3; }
nums().filter(x => x > 1)
      .take(1)
      .toArray();
Lazy iterator protocol. Requires iterator helper host imports.
RegExp duplicate named groups Same group name in alternatives
/(?<v>a)|(?<v>b)/.exec("b");
// v === "b"
Same named group in alternatives. Requires regex engine update.
Legacy / Deprecated
var hoisting Function-scoped variable declarations hoisted to top
console.log(x); // undefined
var x = 5;
;; var hoisted to function scope
(local $x f64)
f64.const 0
local.set $x
;; ... later
f64.const 5
local.set $x
arguments.callee Reference to currently executing function
function f() {
  return arguments.callee;
}
Forbidden in strict mode. All modules run strict.
__proto__ accessor Legacy prototype chain manipulation
obj.__proto__ = parent;
Dynamic prototype assignment not supported. Use Object.create() or class extends.
String.prototype.substr Deprecated substring extraction
"hello".substr(1, 3); // "ell"
// use .slice() or .substring()
Deprecated. Use .slice() which is fully supported.
Octal literals (0777) Legacy octal number syntax
const n = 0777;
const ok = 0o777;
Legacy octal forbidden in strict mode. ES2015 0o prefix works.
escape() / unescape() Deprecated string encoding globals
escape("hello world");
// use encodeURIComponent()
Deprecated globals. Use encodeURIComponent / decodeURIComponent.
Function.prototype.caller Access calling function reference
function f() {
  return f.caller;
}
Forbidden in strict mode. Not available in Wasm.
HTML string methods (.bold(), .anchor()) Deprecated HTML wrapper string methods
"text".bold();   // deprecated
"text".anchor("name");
Annex B legacy methods. Not implemented.
RegExp.$1 static properties Legacy RegExp match result globals
/(\d+)/.exec("abc123");
RegExp.$1; // "123" — deprecated
Legacy static properties. Use match result array instead.
Proposals
Temporal Modern date and time API
const now = Temporal.Now.instant();
const date = Temporal.PlainDate
  .from("2026-04-06");
Stage 3 proposal. Large API surface — not yet in scope.
Decorators Class and method metadata annotations
@logged
class MyClass {
  @bound method() { }
}
Stage 3 proposal. Requires compile-time decorator application.
Pattern matching Structural matching expressions
match (value) {
  when ({ x, y }): return x + y;
  when (String): return value.length;
  default: return 0;
}
Stage 1 proposal. Structural pattern matching not yet in scope.

Compiled ahead of time, no interpreter.

Write regular JavaScript. The compiler produces a .wasm binary that runs anywhere WebAssembly runs — browser, server, edge. No interpreter embedded, no garbage collector shipped. When deployed with explicit imports, the module boundary can reduce ambient access and limit supply-chain attack surface.

JavaScript (0.1 KB gzipped)

function fibonacci(n) {
  if (n <= 1) return n;
  return fibonacci(n - 1)
       + fibonacci(n - 2);
}

export function run() {
  return fibonacci(10);
}
app.js
compile

WebAssembly (0.2 KB gzipped)

(func $fibonacci (param f64) (result f64)
  local.get 0
  f64.const 1
  f64.le
  (if (result f64)
    (then local.get 0)
    (else
      local.get 0  f64.const 1  f64.sub
      call $fibonacci
      local.get 0  f64.const 2  f64.sub
      call $fibonacci
      f64.add)))

(func $run (export "run") (result f64)
  f64.const 10
  call $fibonacci)
app.wat

Using DOM and JS APIs

DOM access and browser APIs compile to typed host imports. The compiler generates the bindings — you just write normal JavaScript.

JavaScript (0.1 KB gzipped)

const el = document.createElement("div");
el.textContent = "Hello from Wasm";
el.style.color = "blue";
document.body.appendChild(el);
dom.js
compile

WebAssembly (0.3 KB gzipped)

(import "env" "global_document"
  (func $doc (result externref)))
(import "env" "__extern_method_call"
  (func $call (param externref externref)
    (result externref)))

(func $__module_init
  call $doc
  string.const "div"
  call $call              ;; createElement
  local.tee $el
  string.const "textContent"
  string.const "Hello from Wasm"
  call $__extern_set      ;; el.textContent = ...
  local.get $el
  string.const "color"
  string.const "blue"
  call $__style_set       ;; el.style.color = ...
  call $doc
  string.const "body"
  call $__extern_get      ;; document.body
  local.get $el
  call $call)             ;; .appendChild(el)
dom.wat

Import and run

import { js2 } from "js2wasm";

// Pure computation — runs standalone
const app = await js2.import("./app.js");
console.log(app.run()); // → 55

// DOM example — pass browser globals
const dom = await js2.import("./dom.js", { document });
// div is already appended to document.body
run.ts

js2.import() instantiates the cached compiled module or compiles on demand if needed, builds host imports, instantiates, and returns typed exports. Pass browser globals as dependencies for DOM access.


Project roadmap.

The roadmap is to first establish compatibility with existing code, then expand host support, optimize performance, and strengthen security.