命令行工具
vercel-labs/scriptc avatar
vercel-labs/scriptc

scriptc:用真正的 TypeScript 编译器把 Node 代码变成原生可执行文件

TypeScript 到 Native 编译器。没有注释,没有方言,与在 Node 上运行的 TypeScript 相同,由真正的 TypeScript 编译器进行类型检查并编译为原生。

4,834 个 Star125 个 ForkTypeScriptApache-2.0

秒懂

它是什么?
scriptc 是一个实验性的 TypeScript 到原生编译器,它直接复用 TypeScript 编译器做类型检查,不引入方言或注解,能把同一份代码编译成 C、LLVM IR、汇编、对象文件、原生可执行文件和 WASI 模块。本文基于 README 和仓库信息,分析它的机制、用法、边界和适用场景。
适合谁用?
scriptc 适合那些希望把现有 TypeScript 代码(尤其是 CLI 工具或小型服务)直接编译成无需 Node 运行时的原生可执行文件的开发者,尤其是 macOS 15+ arm64 用户,因为该平台支持最完整的工具链路径。不适合需要网络套接字、子进程、信号处理或文件系统监听的 WASI 目标,也不适合依赖大量动态特性(如任意 npm 包或 any 类型)且无法接受性能损失的场景。
能商用吗?
可以。Apache-2.0 是宽松许可证:你可以使用、修改并销售基于它的软件,只需保留版权和许可证声明。
还在维护吗?
在维护。仓库最近一次提交在 1 天前。
用什么语言写的?
主要是 TypeScript(依据 GitHub 的语言统计)。

以上回答依据项目的 GitHub 数据(最近同步于 2026年9月14日)和我们的分析,不构成法律意见。

开源项目深度解析

它解决的是 Node 原生分发的痛点

scriptc 的目标很直接:让你用标准 TypeScript 写代码,然后编译成原生可执行文件,最终产物不依赖 Node 运行时。这对 CLI 工具、内部服务或需要分发给没有 Node 环境的用户的场景很有吸引力。它不像其他方案那样要求你学习新语法或添加类型注解,而是直接接受你在 Node 里运行的那份 TypeScript。README 里反复强调“no annotations, no dialect”,意思是类型检查完全交给真正的 TypeScript 编译器,scriptc 只负责把类型检查后的 AST 翻译成中间表示。

编译管线:从 IR 到可执行文件的五级产物

scriptc 的编译过程分成多个可选的产物层级。你可以在不调用任何外部工具的情况下,用 --emit=ir、--emit=c 或 --emit=llvm 生成类型化的 IR、可读的 C 代码或文本 LLVM IR,这些只需要 Node 24 以上。在 macOS 15+ arm64 上,--emit=asm 和 --emit=obj 会使用 scriptc 自带的平台辅助程序,不需要单独的编译器、归档器或链接器。最终生成可执行文件时,仍然需要一个平台链接器驱动程序和 SDK,但 clang 只充当链接器,不编译程序或运行时的 C 代码。这种分层设计让开发者可以停在任意一级:比如只检查 IR,或者把 C 代码拿去自己交叉编译。

静态编译与动态回退:coverage 命令告诉你边界

scriptc 的核心机制是静态编译:它会把 TypeScript 代码直接翻译成原生代码,并内置一个小的原生运行时,不包含 Node 或 JavaScript 引擎。但并非所有代码都能静态编译。README 给出了 scriptc coverage 命令,它会分析程序中有多少语句可以静态编译,并为每个动态或不受支持的站点输出一个编码诊断。例如示例中的 hello.ts 显示“fully static”。对于无法静态编译的代码,比如使用了 npm 包或 any 类型,你需要显式传入 --dynamic 参数,这会嵌入 quickjs-ng 引擎来执行那些动态部分。这意味着同一个可执行文件里同时存在原生代码和解释执行的 JavaScript,性能会有差异,但功能上能跑通。

实际用法:从 hello world 到 npm 包

安装很简单:npm install -g scriptc。然后写一个 hello.ts,用 scriptc run hello.ts 直接运行,或者 scriptc build hello.ts -o hello 生成可执行文件。构建时可以指定 --emit=ir、--emit=c、--emit=llvm、--emit=asm 或 --emit=obj,产物会放在 .scriptc/ 目录下。对于 Node 内置模块,比如 node:http,scriptc 声称支持将其编译到原生运行时,README 里给出了一个 createServer 的例子,编译后运行 ./server 就能监听端口。对于 npm 包,你需要使用 --dynamic 参数,它会把包的 JavaScript 嵌入可执行文件,运行时不再读取 node_modules。例如,用 picocolors 包时,编译后直接运行就能输出带颜色的文本。

WASI 目标:跨平台但有明确的能力边界

scriptc 支持通过 Zig 工具链编译到 WASI Preview 1 模块。你需要设置 SCRIPTC_CC=zigcc 和 SCRIPTC_TARGET=wasm32-wasi,然后构建出 .wasm 文件。README 声称 WASI 目标支持与原生目标相同的语言层级,包括 async/await、promises、generators、timers 和文件系统 API。但有一个硬性边界:网络套接字、fetch、子进程、OS 信号和文件系统监视在 WASI Preview 1 中不存在,这些 API 会在链接前报 SC3002 错误。此外,sanitizer 构建、原生 FFI 和库模式归档在 WASI 目标下也是不支持的。如果你需要这些能力,WASI 就不是合适的目标。

原生对象导出:实验性但设计谨慎

scriptc 的 --emit=obj 会生成一个可重定位的程序对象,而不是独立的库。这个对象带有未定义的 scr_* 运行时引用和一个 scr_runtime_abi_v1 标记。README 明确警告,外部对象消费是实验性的,建议使用 --print=native-link-info 来输出一个版本化的 JSON 配方,其中包含目标架构、main 入口、精确的 @scriptc/runtime 源码包、所需系统库、FFI 输入和 ABI 标记。这个配方不会引用隐藏的 scriptc 缓存路径,这意味着你可以把它交给其他构建系统。但要注意,当前辅助程序只支持 macOS 15+ arm64,并且部署目标固定为 arm64-apple-macosx14.0.0。如果你想在 Linux 或 Windows 上做类似的事,目前没有对应的辅助程序。

限制与维护成本:实验阶段的现实

scriptc 明确标注为实验性,版本号 v0.0.35 说明 API 和平台支持还在快速变化。安装要求 Node.js 24 或更新版本,这对一些企业环境可能是个门槛。运行时打包在可执行文件里,所以最终产物不依赖 Node,但构建过程本身需要 Node。维护方面,项目使用 pnpm 工作区,正常的构建不需要本地 LLVM,但如果你想重新生成 macOS arm64 的原生工件,需要安装 CMake、Ninja 和 Homebrew 的 llvm@22,然后运行两个特定包的 build:native 命令。测试套件依赖 Vercel Sandbox 和 OIDC 令牌,这意味着贡献者需要 Vercel 项目访问权限,普通用户无法轻易跑完整测试。许可证是 Apache-2.0,允许商用和修改,但需要保留版权声明。

与同类方案的差异:不是另一种“TS 到 C”翻译器

常见的 TypeScript 到原生方案要么要求你使用受限的 TypeScript 子集,要么引入自己的类型系统。scriptc 的不同之处在于它直接使用 TypeScript 编译器做类型检查,这意味着它不会接受任何非标准语法。你可以把现有代码原样编译,只要它不依赖动态特性。另一个区别是它提供了从 IR 到 C 到 LLVM 到汇编的完整产物链,这让你可以在不同阶段介入。相比之下,像 Bun 这样的运行时选择直接嵌入 JavaScript 引擎,而不是编译到原生。scriptc 的静态路径更接近 AOT 编译,而 --dynamic 模式则退回到解释执行。这种双模式设计是它的特色,但也意味着你需要理解哪些代码走哪条路径,否则性能预期会落空。

编辑结论

scriptc 适合那些希望把现有 TypeScript 代码(尤其是 CLI 工具或小型服务)直接编译成无需 Node 运行时的原生可执行文件的开发者,尤其是 macOS 15+ arm64 用户,因为该平台支持最完整的工具链路径。不适合需要网络套接字、子进程、信号处理或文件系统监听的 WASI 目标,也不适合依赖大量动态特性(如任意 npm 包或 any 类型)且无法接受性能损失的场景。在采用前,应先运行 scriptc coverage 检查代码的静态覆盖率,确认所有动态站点都能被接受或通过 --dynamic 处理;同时要验证目标平台的链接器、SDK 或 Zig 工具链是否可用,因为不同 emit 级别对工具链的要求差异很大。scriptc 仍处于实验阶段,版本号 v0.0.35 表明 API 和平台支持可能频繁变化,生产环境使用前需锁定版本并跟踪每次发布的变更。

官方来源

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
社区笔记

社区笔记