Files
Chris Tate ee9f0d64eb feat: ship the self-hosted native scriptc CLI (#585)
* feat: ship the self-hosted native scriptc CLI

- Run the shared compiler and CLI natively, including library builds and compile-time evaluation.
- Install native platform packages and ship relocatable standalone distributions.
- Preserve program semantics and reuse validated build artifacts for fast rebuilds.

* fix: complete native CLI integration and validation

- Preserve library diagnostics and run recursive compiler work on the main stack.
- Restore packaged helper permissions and exercise native npm and Wasm library builds.
- Recover interrupted Sandbox status probes within the original command deadline.
2026-09-30 02:17:56 -05:00
..
2026-07-22 18:08:04 -05:00
2026-07-22 18:08:04 -05:00

scriptc

Compile ordinary TypeScript and JavaScript to small, fast native executables or WASI WebAssembly modules — no Node, no V8, no JavaScript engine in the artifact.

$ cat fib.ts
function fib(n: number): number {
  return n < 2 ? n : fib(n - 1) + fib(n - 2);
}
console.log(fib(30));

$ scriptc run fib.ts
832040

$ scriptc build fib.ts -o fib && ./fib
832040

Install

$ npm install -g scriptc

The installed compiler runs natively on supported macOS, Linux, and Windows hosts. Compilation, compile-time evaluation, and native execution do not require Node. --emit=ir|llvm|asm|obj uses the bundled TypeScript checker and LLVM helper without an external compiler, archiver, linker, or SDK. Executable builds additionally need a platform linker driver and SDK/sysroot; precompiled runtime packs supply the C runtime. Set SCRIPTC_LINKER to choose that driver. Runtime development with --sanitize additionally needs a C compiler. Node.js 24 or newer is needed for npm installation and scriptc run of WASI modules.

Builds use a bounded persistent cache. Unchanged source can reuse the validated frontend result and LLVM program objects. Library identity getters occupy a separate LLVM module, so an identity change can reuse the large program object. Runtime objects come from the installed pack. Executable cache entries verify their native dependencies; FFI builds relink against current external inputs. Set SCRIPTC_NO_CACHE=1 to bypass the cache or SCRIPTC_CACHE_DIR to select its location. An existing POSIX override must already be private.

Commands

  • scriptc build <file.ts> — compile to a native executable or selected target artifact
  • scriptc run <file.ts> — compile and run
  • scriptc coverage <file.ts> — what compiles statically, and why the rest doesn't

scriptc build app.ts --emit=ir|llvm|asm|obj selects serialized typed IR, textual LLVM IR, target assembly, or a relocatable program object as the one primary artifact. --emit=exe is the default. Assembly/object emission uses the matching helper on supported macOS, Linux, and Windows hosts (and produces WASI artifacts when that target is selected). Objects retain undefined scr_* runtime references plus the scr_runtime_abi_v4 compatibility marker; they are not library archives. External consumption is experimental and requires the exact matching runtime. scriptc build app.ts --print=native-link-info -o app.o prints the versioned JSON target/runtime/link recipe without performing a link. --emit=asm|obj --sanitize is rejected until ASan pipeline parity is available.

For embedder-hosted modules that are not installed npm packages, coverage can map an exact bare specifier to a local declaration with repeatable --external-types <specifier=file.d.ts> options. This is analysis-only: the types unblock application measurement, while runtime module uses remain reported as blockers.

No annotations, no dialect, no special stdlib: the same TypeScript you run on Node, type-checked by the real TypeScript compiler. Programs outside the static tier can opt into --dynamic, which embeds a small JavaScript engine (~620KB) for the parts that can't be static; everything else fails the build with a specific error code and usually a rewrite hint.

WebAssembly is available as a production LLVM target: SCRIPTC_TARGET=wasm32-wasi scriptc build app.ts. It emits a WASI Preview 1 .wasm module, and scriptc run supplies a WASI host. The complete executable language tier—including async, generators, timers, and --dynamic—is supported. APIs needing capabilities WASI P1 does not expose (network sockets/fetch, child processes, OS signals, and filesystem watching), sanitizer builds, and native FFI are rejected with SC3002. With --lib --profile, the target produces a callable Wasm reactor with named host imports and per-instance memory. See WebAssembly Modules for the embedding contract.

Native code can be called through an explicit, link-time C ABI manifest: declare the function signature in TypeScript, bind it to a C symbol, and build with --ffi <manifest.json>. See the Native FFI guide.

Docs: scriptc.dev