scriptc:把 TypeScript 编译成原生程序和 WebAssembly
一个把 TypeScript 编译成原生程序或 WASI 模块的实验性工具,产物运行时不依赖 Node。
GitHub vercel-labs/scriptc 更新 2026-09-28 分支 main 星标 5.4K 分叉 141
TypeScript C LLVM WebAssembly WASI Preview 1 Node.js 24+

🧭 决策指南

适合,如果你

  • 你要把 `hello.ts` 或 Node.js `node:http` 服务编译成不依赖 Node 的可执行文件。
    README 的“Build a program”和“Use Node APIs”章节展示了 `scriptc build`、`./hello` 和 `node:http`。
  • 你需要把 TypeScript 输出为 `.scriptc/` 中的 IR、C、LLVM IR、汇编或对象文件。
    README 的“Build a program”章节列出了 `--emit=ir|c|llvm|asm|obj` 及对应文件。
  • 你需要将 npm 包如 `picocolors` 嵌入可执行文件,而不是运行时读取 `node_modules`。
    README 的“Use npm packages”章节要求使用 `--dynamic`,并明确写出运行时不读取 `node_modules`。
  • 你的目标是 WASI Preview 1,并且可以安装 Zig、使用 `SCRIPTC_CC=zigcc`。
    README 的“Build WebAssembly”章节给出了 `SCRIPTC_TARGET=wasm32-wasi` 和 Zig 命令。

不适合,如果你

  • 你的 WASI 程序依赖网络 sockets、fetch、child processes、OS signals 或 filesystem watching。
    README 的“Build WebAssembly”章节说明这些能力在 WASI Preview 1 中会以 `SC3002` 在链接前失败。
  • 你需要把 `--emit=obj` 直接当作独立库使用。
    README 明确说明 `--emit=obj` 是 relocatable program object,不是 standalone library,并含未定义 `scr_*` 引用。
  • 你的项目要求成熟稳定的生产级编译器,而不是 experimental 的 v0.1.7 项目。
    README 写明 scriptc is experimental;发布信息显示最新版本为 v0.1.7。
  • 你只能使用 Node.js 23 或更早版本。
    README 的“Installation”章节要求 Node.js 24 or newer。

前置条件

  • Node.js 24 or newer。
  • `--emit=ir|c|llvm` 只需要 Node。
  • `--emit=asm|obj` 使用 scriptc 在受支持 macOS、Linux、Windows 主机上安装的 matching optional platform helper。
  • 普通 LLVM-tier executable build 需要 platform linker driver 和 SDK/sysroot。
  • WASI 和其他 cross-target build 需要 Zig,并确保 `zig` 位于 `PATH`。
  • `--dynamic` 用于 npm packages 和 `any`-typed code,并显式嵌入 `quickjs-ng`。

第一步命令(README 原文)

$ npm install -g scriptc

要注意

  • `SCRIPTC_CC=zigcc` 是调用 Zig `cc` 子命令的选择器,不是独立可执行文件。
    README 的“Build WebAssembly”章节对 `SCRIPTC_CC=zigcc` 有明确说明。
  • macOS 15+ arm64 的 helper 产物使用 `arm64-apple-macosx14.0.0` deployment target。
    README 的“Build a program”章节说明了 helper 运行平台和 deployment target。
  • WASI 的 sanitizer、native FFI 和 library-mode archive builds 会成为 target diagnostics。
    README 的“Build WebAssembly”章节列出了这些 WASI 限制。
  • 外部 object consumption 仍是 experimental,需要 `--print=native-link-info` 生成带 ABI marker 的 JSON recipe。
    README 的“Build a program”章节明确称 external object consumption 为 experimental。

材料未说明

  • README 未给出不同 TypeScript 项目或 Node.js API 的完整支持清单。
  • README 未提供 scriptc 与 Node.js、TypeScript 编译器或其他 native compiler 的性能基准。
  • README 未说明 `--dynamic` 嵌入 `quickjs-ng` 后的产物体积和运行时开销。
  • README 未给出 macOS、Linux、Windows 各版本的完整兼容矩阵。
  • README 未说明 v0.1.7 之后的 API、CLI 参数和兼容性承诺。
  • README 未说明 native FFI 的具体 ABI、平台边界和可用示例范围。

💡 深度解析

6
不适合 我想把 TypeScript 代码编译成可被 C 驱动或 Apple linker 消费的对象文件;我能否把 `--emit=obj` 当作独立库直接链接?
适合读者: 正在构建 C 驱动或直接 Apple linker 集成的系统开发者,需要消费 scriptc 的 `--emit=obj` 目标文件,并接受 native FFI 和 external object consumption 仍属实验性

不适合把 --emit=obj 当作独立库直接链接;README 明确说它是 relocatable program object,不是 standalone library。

  • 该对象含有未定义的 scr_* runtime 引用,并要求 scr_runtime_abi_v2 marker,因此还需要匹配的 runtime 和 ABI 配置。
  • 外部对象消费仍是 experimental;README 要求使用 --print=native-link-info 生成 versioned JSON recipe。
  • recipe 会记录 target、main entry、准确的 @scriptc/runtime source pack、系统库、FFI inputs 和 ABI marker,不能凭隐藏缓存路径拼链接命令。
  • 如果真正需要自包含 archive,应使用 scriptc build --lib --profile ...;但 WASI 目标又把 library-mode archive 列为诊断项。

因此,C-driver 或 direct Apple-linker 集成可行,但必须按 recipe 完整组装,而不是把 .o 当普通库。

  • Build a program:`--emit=obj` writes a relocatable program object, not a standalone library
  • Build a program:It has undefined `scr_*` runtime references and a required `scr_runtime_abi_v2` marker
  • Build a program:Use `--print=native-link-info` to emit ... a versioned JSON recipe
  • Build a program:`scriptc build --lib --profile ...` remains the self-contained archive interface
材料未说明:README 未给出你的 C 驱动、FFI 输入和目标系统库所需的完整实际链接命令。;README 未说明不同 scriptc 版本之间 native-link-info recipe 的向后兼容承诺。
适合 我的 TypeScript 工具目标是 `wasm32-wasi`,构建机可以安装 Zig,功能只需要 stdin/readline、Promise、定时器和文件系统;scriptc 是否适合?
适合读者: 需要把 TypeScript CLI 部署到 WASI Preview 1 的工具开发者,构建环境可以安装 Zig,但程序不能使用网络、子进程、OS signals 或文件监听

适合,前提是程序严格遵守 WASI Preview 1 的能力边界。

  • README 要求 WASI 和其他 cross-target builds 使用 Zig,并通过 bundled WASI libc 生成 WASI Preview 1 module。
  • README 明确列出 async/await、promises、generators、timers、stdin/readline events,以及 callback 和 promise filesystem APIs 为支持能力。
  • 网络 sockets/fetch、child processes、OS signals 和 filesystem watching 会在链接前以 SC3002 失败;你的约束正好避开这些限制。
  • 构建命令需要 SCRIPTC_CC=zigcc、SCRIPTC_TARGET=wasm32-wasi,并要求 zig 在 PATH 中;zigcc 不是独立可执行文件。

如果工具还需要 native FFI、sanitize 或 library-mode archive,WASI 目标同样会拒绝这些能力。

  • Build WebAssembly:WASI and other cross-target builds require Zig
  • Build WebAssembly:The WASI target supports ... async/await, promises, generators, timers, stdin/readline events, callback and promise filesystem APIs
  • Build WebAssembly:network sockets/fetch, child processes, OS signals, and filesystem watching fail ... with `SC3002`
  • Build WebAssembly:`$ SCRIPTC_CC=zigcc SCRIPTC_TARGET=wasm32-wasi scriptc build hello.ts --no-keep-c -o hello.wasm >/dev/null`
$ SCRIPTC_CC=zigcc SCRIPTC_TARGET=wasm32-wasi scriptc build hello.ts --no-keep-c -o hello.wasm >/dev/null
材料未说明:README 未说明目标 WASI runtime、宿主实现及不同 WASI 运行器之间的兼容差异。;README 未给出该工具使用文件系统时所需的具体预开放目录配置。
适合 我的 TypeScript 项目包含 any 和 npm 依赖,我想先知道哪些语句能静态编译、哪些位置必须动态执行;scriptc 能提供逐位置判断吗?
适合读者: 需要先判断现有 TypeScript 项目能否静态编译的维护者,项目可能包含 any、npm 依赖和动态 JavaScript 行为,希望获得逐位置诊断而不是直接猜测

适合,coverage 就是为这个决策设计的诊断入口。

  • README 说明 scriptc coverage 会显示程序能静态编译的比例,并为每个 dynamic 或 unsupported site 给出 coded diagnostic。
  • 示例对 hello.ts 输出 statements analyzed、compile statically 和 fully static,能先确认代码是否存在动态剩余。
  • README 同时明确:npm packages 和 any-typed code 通常需要 --dynamic,因此 coverage 结果可以帮助你判断是调整源码、接受动态运行时,还是放弃原生路径。
  • 该命令不会自动证明完整 Node 兼容性;项目仍处于 experimental 阶段,且诊断范围受目标平台和 runtime 支持影响。

先用 coverage 获取代码位置和诊断,再决定纯静态或嵌入 quickjs-ng 的构建方式,是 README 明确支持的工作流。

  • Check static coverage:`scriptc coverage` shows how much of a program can compile statically
  • Check static coverage:gives a coded diagnostic for every dynamic or unsupported site
  • README:For npm packages and `any`-typed code, `--dynamic` embeds quickjs-ng explicitly
  • README:scriptc is experimental
$ scriptc coverage hello.ts
材料未说明:README 未说明 coverage 对大型项目的运行时间、项目配置文件支持和 monorepo 解析边界。;README 未给出每种诊断代码对应的完整修复建议或自动化迁移工具。
适合 我有一个类型明确的 TypeScript CLI,构建机使用 Node.js 24+,目标是 macOS 15+ arm64;我能否用 scriptc 生成一个部署时不依赖 Node 的单文件程序?
适合读者: 维护 TypeScript CLI、构建环境要求 Node.js 24+、希望把 macOS 15+ arm64 上的工具交付为不依赖 Node 的独立可执行文件的开发者

适合,因为 README 明确支持把 TypeScript 编译为独立原生可执行文件,而且生成的 executable 不需要 Node。

  • 安装阶段要求 Node.js 24 或更高版本,但这是编译器的要求,不是最终程序的运行要求。
  • macOS 15+ arm64 的普通 LLVM-tier executable 使用 bundled helper 和 precompiled runtime pack;clang 只作为平台 linker driver,不负责编译生成的程序或 runtime C。
  • 静态构建包含较小的 native runtime,不包含 Node 或 JavaScript engine;类型明确、没有动态剩余的 CLI 最符合这条路径。
  • README 已给出 scriptc build hello.ts -o hello 和 ./hello 的独立程序流程。

不过 Node API 覆盖和动态特性仍有限,不能把它当作完整 Node.js 替代品。

  • Installation:The compiler requires Node.js 24 or newer
  • Installation:The executables it produces do not require Node
  • README:On macOS 15+ arm64, ordinary LLVM-tier executables use scriptc's bundled helper and precompiled runtime pack
  • Build a program:`$ scriptc build hello.ts -o hello`
$ scriptc build hello.ts -o hello
材料未说明:README 未说明该 CLI 的完整依赖树、动态反射使用情况以及最终可执行文件的体积。;README 未给出 macOS 15+ arm64 之外目标架构的同等 helper 和部署保证。
视情况 我的 TypeScript CLI 使用 picocolors 这类 npm 包,代码中也可能有 any;我能否在不让最终程序读取 node_modules 的前提下用 scriptc 打包?
适合读者: 维护依赖 picocolors 等 npm 包的 TypeScript CLI、希望发布时不携带 node_modules、但可以接受嵌入 QuickJS 动态运行时的开发者

视情况,但如果接受 QuickJS 运行时,README 给出了明确的兼容路径。

  • README 指出 npm packages 和 any-typed code 应使用 --dynamic;该模式会显式嵌入 quickjs-ng。
  • 生成物运行时不读取 node_modules,因此适合希望交付单个 CLI 的场景。
  • 代价是它不再是纯静态原生程序:JavaScript 执行能力来自嵌入的 QuickJS,运行时体积、性能和行为兼容性不能按静态路径理解。
  • 项目仍处于 experimental 阶段,且 README 没有承诺所有 npm 包、原生扩展或 Node 专属行为都能工作。

README 的示例就是安装 picocolors 后执行 scriptc build cli.ts --dynamic -o cli,因此这类 CLI 可以优先沿用该路径。

  • README:For npm packages and `any`-typed code, `--dynamic` embeds quickjs-ng explicitly
  • Use npm packages:The result does not read `node_modules` at runtime
  • Use npm packages:`$ scriptc build cli.ts --dynamic -o cli`
  • README:scriptc is experimental
$ scriptc build cli.ts --dynamic -o cli
材料未说明:README 未说明 quickjs-ng 在该 CLI 上的具体体积、启动时间和性能开销。;README 未列出 picocolors 之外每个 npm 包的兼容性矩阵,也未说明原生 npm 扩展的支持范围。
视情况 我维护一个使用 `node:http`、Promise 和文件系统 API 的 TypeScript 服务;在 Linux 或 macOS 上,我能否把它编译成不依赖 Node 的原生服务?
适合读者: 维护使用 node:http、文件系统和异步 API 的 TypeScript 服务,希望在 Linux 或 macOS 上减少 Node 部署依赖的服务开发者

视情况,README 已证明部分 Node API 可走 native runtime,但是否适合取决于服务实际使用的 API 边界。

  • Use Node APIs 示例直接用 node:http 创建 HTTP 服务,并通过 scriptc build server.ts -o server 生成可执行文件。
  • 项目洞察列出已支持的文件系统、异步、Promise、生成器、定时器和 stdin/readline 能力,但支持范围取决于目标平台。
  • 静态构建不带 Node 或 JavaScript engine;未实现或无法静态编译的代码会产生诊断。
  • 普通 LLVM-tier executable 仍需要平台 linker driver 和 SDK/sysroot;预编译 runtime pack 并不消除这些构建条件。

因此,受控的 HTTP 服务可能合适,但依赖完整 Node 生态、动态加载或未覆盖 API 的服务不应直接迁移。

  • Use Node APIs:`import { createServer } from "node:http";`
  • Use Node APIs:`$ scriptc build server.ts -o server`
  • Installation:Ordinary LLVM-tier executable builds need a platform linker driver and SDK/sysroot
  • README:Code that cannot compile statically is reported as a diagnostic
$ scriptc build server.ts -o server
材料未说明:README 未给出 Linux 各发行版、架构及具体 Node API 的完整兼容矩阵。;README 未说明该服务在并发量、长连接和错误处理场景下的性能与行为差异。

✨ 核心亮点

  • 支持 IR、C、LLVM IR、汇编和原生可执行文件
  • 静态产物运行时不需要 Node 或 JavaScript 引擎
  • 可用 Zig 构建 WASI Preview 1 模块
  • 项目明确标注为 experimental,当前 v0.1.7

🔧 工程化

  • 用 TypeScript 编译器解析并检查类型,输出 typed IR、C、LLVM IR 与原生产物。
  • `--dynamic` 可把 npm 包 JavaScript 和 `quickjs-ng` 嵌入可执行文件。
  • `scriptc coverage` 为动态或不支持代码生成逐点诊断。

⚠️ 风险

  • README 明确称 scriptc 为 experimental,项目仅有 4 位贡献者和 5 个版本。
  • `any`、npm 包和无法静态编译的代码需要 `--dynamic` 或会产生诊断。
  • WASI Preview 1 不支持网络、子进程、OS 信号和文件监听。
  • `--emit=obj` 不是独立库,含未定义 `scr_*` 运行时引用。

👥 适合谁?

  • 需要把 TypeScript CLI 或 Node.js API 编译成无 Node 运行时程序的开发者。
  • 目标为 macOS、Linux、Windows 或 WASI Preview 1 的原生编译实验者。
  • 需要 `scriptc coverage` 检查静态覆盖率和动态代码边界的 TypeScript 团队。