Skip to main content
Scramjet’s JavaScript rewriter is implemented in Rust and compiled to WebAssembly for maximum performance. This architecture enables AST-level transformations with near-native speed.

Architecture overview

The WASM rewriter is a Cargo workspace with multiple components:

Key dependencies

Scramjet uses OXC, a blazingly fast JavaScript parser written in Rust, achieving performance comparable to native compilers.

WASM module structure

Initialization

The WASM module is loaded and initialized in src/shared/rewriters/wasm.ts:
The WASM module must be loaded before any JavaScript rewriting occurs. Scramjet automatically handles this in service workers via asyncSetWasm().

Service worker WASM loading

In service workers, WASM is fetched dynamically:

Rewriter class

The WASM Rewriter class exposes a JavaScript-friendly API:

TypeScript bindings

The Rust code generates TypeScript definitions:

Rewriter configuration

The Rewriter constructor accepts a configuration object:

Shared functions

The rewriter calls back into JavaScript for certain operations:
  • URL rewriting: rewriteUrl(url, meta)
  • CSS rewriting: rewriteCss(css, meta) (for <style> tags)
  • JS rewriting: rewriteJs(js, url, meta) (for nested scripts)
  • HTML injection: getHtmlInjectCode(cookieStore, foundHead)
While implementing everything in Rust would be faster, JavaScript callbacks provide:
  1. Flexibility: Configuration (codec, flags) can be changed without recompiling WASM
  2. Code sharing: URL/CSS rewriting logic is shared between client and worker contexts
  3. Smaller binary: Avoiding duplicate implementations reduces WASM size
  4. Maintainability: Complex logic (HTML parsing) stays in TypeScript where it’s easier to debug

AST transformation

The Rust rewriter performs AST-level transformations using OXC:

Visitor pattern

The visitor walks the AST and collects transformations:

Building the WASM module

Scramjet includes a build script for the WASM module:

Build configuration

For production builds, Scramjet uses aggressive optimizations:
The production WASM binary is approximately 500KB gzipped, which is loaded once and cached by the browser.

Sourcemap generation

The rewriter generates sourcemaps for debugging:
Sourcemaps enable:
  • Accurate stack traces in DevTools
  • Breakpoint debugging in original code
  • Proper error line numbers

Error handling

Parse errors

The rewriter collects parse errors without failing:

Panic recovery

If OXC panics, the rewriter catches it:
In TypeScript:

Performance optimizations

Rewriter pooling

Scramjet maintains a pool of rewriter instances:

Usage

Always release the rewriter back to the pool using the release() function to avoid memory leaks and rewriter exhaustion.

Testing the rewriter

Scramjet includes a native test runner:
This allows testing the rewriter without compiling to WASM, which is useful for:
  • Debugging Rust code
  • Running benchmarks
  • Integration tests

Debugging tips

Enable rewriter logs

Inspect WASM binary

Compare input/output