Vercel Labs' scriptc Compiles TypeScript to Native Binaries, Skipping Node, V8, and Any JavaScript Engine
Vercel Labs' experimental scriptc compiler turns ordinary TypeScript into small native binaries with no embedded Node or JavaScript engine, now at version 0.1.4 with more than 5,000 GitHub stars.
Overview
Vercel Labs has built scriptc, an experimental compiler that turns ordinary TypeScript and JavaScript into small native executables without embedding Node, V8, or any JavaScript engine in the resulting binary. The project’s GitHub repository describes it as a “TypeScript-to-Native Compiler,” licensed under Apache 2.0, and its current release is version 0.1.4. InfoQ reported that the repository was established on July 22, 2026, and the project has attracted more than 5,000 stars on GitHub since then.
What We Know
According to the project’s introduction docs, scriptc “compiles ordinary TypeScript into small, fast native executables — no Node, no V8, no JavaScript engine in the binary,” and requires “no changes to your code: no annotations, no dialect, no special standard library.” The compiler uses the actual TypeScript compiler for parsing and type checking, then lowers the result into a typed intermediate representation before emitting C, LLVM IR, assembly, object files, native executables, or WebAssembly, per the README. It targets macOS, Linux, Windows, and WebAssembly via WASI Preview 1, and requires Node.js 24 or newer to run the compiler itself — though the executables it produces do not require Node, the README states.
Every construct in a program lands in one of three tiers, the introduction docs explain: compiled statically by default; run dynamically when the --dynamic flag is passed, which embeds the quickjs-ng JavaScript engine (about 620KB) to execute npm packages’ shipped JavaScript and any-typed code; or rejected outright at compile time “with a specific error code, a code frame, and usually a rewrite hint.” The docs add that a value crossing from the embedded engine back into statically compiled code is validated at runtime, so “a lying type throws a catchable TypeError instead of corrupting memory.”
On performance, the project’s own homepage copy states that “a hello-world binary is ~320KB, starts in about 4ms, and links against nothing but libSystem,” adding that “Node needs a ~120MB runtime and ~35ms to print the same line.”
The how-it-works documentation frames scriptc’s correctness claim as running program semantics “against Node’s and they matched,” rather than an independent reimplementation of the JavaScript specification. Every program in its test corpus runs under both Node and a compiled binary, with stdout, stderr, and exit codes required to match byte-for-byte; the same documentation says memory in compiled programs is reference-counted rather than garbage-collected, so that “an acyclic value is freed the moment its last reference drops,” while reference cycles are swept at deterministic collection points instead of by a concurrent garbage collector.
Where scriptc’s behavior cannot match Node exactly, the project documents the divergence rather than leaving it undocumented. Strings are stored as UTF-8 internally, Object.keys, Object.values, Object.entries, and JSON.stringify report a record’s declaration order rather than per-object insertion order, and process.argv[0] returns the literal string "scriptc", with argv[1] holding the compiled binary’s own path. The current 0.1.4 release, per the project’s changelog, expanded static support for Node’s module system, built-in functions used as values, child-process IPC, and HTTP servers that enforce Node’s Host header rules.
What We Don’t Know
The project labels itself “experimental” throughout its own documentation, and its public materials do not lay out a timeline for a stable 1.0 release or describe how widely it has been adopted in production beyond its GitHub star count. It is also not established how the project’s differential-testing approach against Node will hold up as more of the npm ecosystem — most of which still ships plain JavaScript rather than statically analyzable TypeScript — is run through the compiler’s dynamic tier.
Analysis
scriptc’s documentation frames its correctness bar as byte-for-byte behavioral parity with Node, rather than a from-scratch reimplementation of JavaScript semantics. By keeping the real TypeScript compiler in the frontend and falling back to an embedded JavaScript engine only for code that cannot be proven static, the project is explicitly trading some performance in dynamic-heavy programs for a smaller, documented gap between what runs on Node today and what a compiled binary would do instead.