scriptc README

The project's README from github.com/vercel-labs/scriptc. As one markdown file: README.md.

scriptc

scriptc compiles TypeScript and JavaScript to typed IR, readable C, textual LLVM IR, native assembly and objects, native executables, and WebAssembly modules. It uses the TypeScript compiler for parsing and type checking. Source outputs require only Node. On macOS 15+ arm64, ordinary LLVM-tier executables use scriptc's bundled helper and precompiled runtime pack; clang is only the platform linker driver and does not compile program or runtime C.

Static builds include a small native runtime, but no Node or JavaScript engine. Code that cannot compile statically is reported as a diagnostic. For npm packages and any-typed code, --dynamic embeds quickjs-ng explicitly.

scriptc is experimental and targets macOS, Linux, Windows, and WebAssembly via WASI Preview 1.

Installation

The compiler requires Node.js 24 or newer. --emit=ir|c|llvm needs only Node. --emit=asm|obj uses the matching optional platform helper installed with scriptc on supported macOS, Linux, and Windows hosts (and for WASI), but needs no compiler, archiver, linker, or SDK. Ordinary LLVM-tier executable builds need a platform linker driver and SDK/sysroot, but use the bundled helper plus precompiled runtime pack rather than compiling generated or runtime C. Set SCRIPTC_LINKER to choose that driver. Explicit C builds, LLVM fallbacks, --sanitize, and the deprecated SCRIPTC_CC=clang|zigcc compatibility route additionally need a C compiler. The executables it produces do not require Node.

$ npm install -g scriptc

Build a program

Create hello.ts:

const who = process.argv.length > 2 ? process.argv[2] : "world";
console.log(`hello, ${who}`);

Compile and run it in one step:

$ scriptc run hello.ts
hello, world

Or write a standalone executable:

$ scriptc build hello.ts -o hello
$ ./hello ctate
hello, ctate

Or stop at a source-level compiler artifact without invoking clang, an archiver, or a linker:

$ scriptc build hello.ts --emit=ir >/dev/null
$ ls .scriptc/
hello.ir.json
$ scriptc build hello.ts --emit=c >/dev/null
$ ls .scriptc/
hello.c
$ scriptc build hello.ts --emit=llvm >/dev/null
$ ls .scriptc/
hello.ll
$ scriptc build hello.ts --emit=asm >/dev/null
$ ls .scriptc/
hello.s
$ scriptc build hello.ts --emit=obj >/dev/null
$ ls .scriptc/
hello.o

--emit=obj writes a relocatable program object, not a standalone library. It has undefined scr_* runtime references and a required scr_runtime_abi_v1 marker; scriptc build --lib --profile ... remains the self-contained archive interface. The helper runs on macOS 15+ arm64 and emits artifacts with an arm64-apple-macosx14.0.0 deployment target. Sanitized assembly/object emission is rejected until the helper's AddressSanitizer pipeline matches the executable path.

External object consumption is experimental. Use --print=native-link-info to emit the object and print a versioned JSON recipe containing its target, main entry, exact @scriptc/runtime source pack, required system libraries, FFI inputs, and ABI marker. The recipe never uses hidden scriptc cache paths. See examples/native-object for C-driver and direct Apple-linker builds.

Use Node APIs

Supported Node APIs compile to the native runtime. For example, server.ts:

import { createServer } from "node:http";

const server = createServer((req, res) => {
  res.setHeader("content-type", "application/json");
  res.end(JSON.stringify({ path: req.url }));
});

server.listen(8080, () => {
  console.log("listening on http://localhost:8080");
});
$ scriptc build server.ts -o server
$ ./server
listening on http://localhost:8080

Check static coverage

scriptc coverage shows how much of a program can compile statically and gives a coded diagnostic for every dynamic or unsupported site.

$ scriptc coverage hello.ts

  statements analyzed   2
  compile statically    2  (100%)

  fully static — this program has no dynamic remainder.

Build WebAssembly

WASI and other cross-target builds require Zig. Its bundled WASI libc produces a portable WASI Preview 1 module through the production LLVM backend:

$ SCRIPTC_CC=zigcc SCRIPTC_TARGET=wasm32-wasi scriptc build hello.ts --no-keep-c -o hello.wasm >/dev/null
$ file hello.wasm
hello.wasm: WebAssembly (wasm) binary module version 0x1 (MVP)
$ SCRIPTC_CC=zigcc SCRIPTC_TARGET=wasm32-wasi scriptc run hello.ts
hello, world

The WASI target supports the same executable language tiers as the native targets, including async/await, promises, generators, timers, stdin/readline events, callback and promise filesystem APIs, and --dynamic. APIs that require capabilities absent from portable WASI Preview 1—network sockets/fetch, child processes, OS signals, and filesystem watching—fail before linking with SC3002; sanitizer builds, native FFI, and library-mode archive builds are target diagnostics too. See platform support for the precise boundary.

Use npm packages

Pass --dynamic to embed an npm package's JavaScript in the executable. The result does not read node_modules at runtime.

import pc from "picocolors";

console.log(pc.green("hello from scriptc"));
$ npm install picocolors
$ scriptc build cli.ts --dynamic -o cli
$ ./cli
hello from scriptc

Documentation

See the quickstart and CLI reference for the complete workflow. The docs also describe npm dependencies, native FFI, platform support, and the current limitations.

Development

$ pnpm install && pnpm -r build
$ vercel link && vercel env pull  # writes a project-scoped VERCEL_OIDC_TOKEN
$ pnpm test:sandbox

The normal workspace build needs no local LLVM installation. To rebuild a native helper/runtime pack, install CMake, Ninja, and the pinned LLVM 22 development package on that target host, then run the matching @scriptc/llvm-<platform> and @scriptc/runtime-<platform> build:native scripts. The macOS full test suite also uses those generated artifacts.

pnpm test:sandbox loads .env.local, preflights Vercel authentication and project access, and uses the managed vercel/sandbox/universal image by default. It installs the repository-pinned Node, pnpm, and LLVM toolchain plus ScriptC dependencies in each disposable Sandbox before building the uploaded worktree. Set SCRIPTC_SANDBOX_IMAGE to a fully qualified VCR reference only to use the optional prebuilt image from pnpm test:sandbox:image. The prebuilt image keeps the roughly four-minute fast path; cold managed-image runs take longer because they install the pinned toolchain in each Sandbox.

VERCEL_OIDC_TOKEN is preferred. For access-token authentication, set VERCEL_TOKEN, VERCEL_TEAM_ID, and VERCEL_PROJECT_ID; team and project are never inferred from SCRIPTC_SANDBOX_IMAGE. The legacy VCR command used by pnpm test:sandbox:image cannot authenticate with an OIDC JWT, so image builds use VERCEL_TOKEN when available or the existing Vercel CLI login; OIDC claims still select the VCR team and project. The test corpus runs each program under Node and as a compiled native binary, then compares stdout, stderr, and exit codes byte for byte. The full gate also runs the corpus with AddressSanitizer and the runtime reference-count audit.

scriptc (vercel-labs/scriptc)

Searching Database…

6.2K ·GitHub
这是什么?scriptc 是一个实验性的编译器,可以把 TypeScript 和 JavaScript 转换为带类型的 IR(中间表示)、可读的 C 代码、LLVM IR、原生汇编或目标文件、原生可执行文件,或 WebAssembly 模块。生成的独立二进制文件运行时不需要 Node.js。
它是像 tsc 那样的转译器,还是真的能生成原生二进制文件?它不止于转译成 JS。scriptc 只借用 TypeScript 编译器做解析和类型检查,然后生成原生可执行文件,并附带一个小型内置运行时(无需 Node 或 JS 引擎),或者通过 WASI 生成 WebAssembly 模块。
我该如何用它编译程序?编写一个 .ts 文件,然后运行 scriptc,并使用 --emit=ir|c|llvm|asm|obj 获取中间产物,或者直接构建独立可执行文件;`scriptc build --lib --profile ...` 会创建一个自包含的归档文件。
除了 Node 之外,我还需要安装什么?所有功能都必须先安装 Node.js 24+。--emit=ir|c|llvm 只需要 Node;--emit=asm|obj 使用 scriptc 内置的平台辅助工具,无需额外的编译器;完整的可执行文件需要平台链接器驱动程序或 SDK(通过 SCRIPTCLINKER 设置);WebAssembly 和 WASI 构建还需要 Zig。
它开箱即用就支持所有 TypeScript 或 JavaScript 特性吗?不支持。无法静态编译的代码会被标记为诊断信息;带有 any 类型的代码或 npm 包需要使用 --dynamic 标志,它会嵌入 quickjs-ng 引擎。WASI 构建还会拒绝任何需要套接字、子进程、操作系统信号或文件系统监视的代码。
它支持哪些平台和目标?scriptc 支持为 macOS、Linux 和 Windows 进行原生构建,并通过 WASI Preview 1 生成 WebAssembly。内置的 LLVM 级别辅助工具目前只能在 macOS 15+ arm64 上运行。
--dynamic 标志有什么作用?它会在输出中嵌入 quickjs-ng JavaScript 引擎,这样编译器就可以在原生二进制文件内部回退到解释执行带有 any 类型的代码,或 npm 包的 JavaScript 代码,而运行时无需读取 node_modules。
如何检查我的代码有多少部分可以静态编译?运行 scriptc coverage。它会报告程序中有多少部分可以静态编译,并为每个动态或不支持的调用点输出一条带编号的诊断信息。
--print=native-link-info 是做什么用的?它会输出编译后的目标文件,并打印一份带版本号的 JSON 配方,描述目标平台、主入口、确切的运行时包版本、所需的系统库、FFI 输入和 ABI 标记,适用于自定义链接配置。
为什么 WebAssembly 输出需要 Zig,而不是用内置辅助工具?跨目标的 WASI 和 WebAssembly 构建通过 Zig 内置的 WASI libc 和 LLVM 后端,生成可移植的 WASI Preview 1 模块。这与原生 macOS、Linux、Windows 辅助工具所用的工具链路径不同。
Size
78 MB
Stars
6.2K
Age
2mo
Commits · 90 Days