用scriptc编译monorepo:多包工作空间输出原生二进制的实战指南
【免费下载链接】scriptcTypeScript-to-Native Compiler项目地址: https://gitcode.com/GitHub_Trending/sc/scriptc
scriptc 是一款把 TypeScript / JavaScript 直接编译成原生二进制的编译器——产物里没有 Node、没有 V8、没有任何 JavaScript 引擎。本文以 scriptc 项目自身的 monorepo 为蓝本,手把手带你在多包工作空间(pnpm workspace)中逐包编译,并用一条命令交叉输出 Linux / Windows / WebAssembly 等多平台原生二进制。🚀
一、为什么是 scriptc:静态优先,动态兜底
与传统"打包 + 捆绑 Node"的方案不同,scriptc 对每个语法结构做三层判定,并如实告诉你:
| 层级 | 含义 | 说明 |
|---|---|---|
| 静态编译 | 编译为原生代码 | 默认模式,产物与 Node 运行结果逐字节一致 |
| 动态运行 | 内嵌 quickjs-ng 引擎 | 加--dynamic后,npm 依赖与any类型代码在嵌入式引擎中执行 |
| 明确拒绝 | 编译期报错 | 带SC错误码与修复提示,绝不静默误编译 |
这一"可看见的静态性"正是它在 monorepo 场景下的价值:每个包都能单独回答"我的代码能编译成原生二进制吗?"
二、一键安装:30 秒准备好编译环境
编译器需要 Node.js ≥ 24,而它产出的二进制完全不需要 Node:
$ npm install -g scriptc验证安装并编译第一个二进制:
$ scriptc run hello.ts # 编译并运行 $ scriptc build hello.ts -o hello # 输出独立可执行文件如果要从源码使用(本文所有文件路径都基于仓库结构),克隆后构建工作空间即可:
$ git clone https://gitcode.com/GitHub_Trending/sc/scriptc $ cd scriptc && pnpm install && pnpm -r build $ pnpm scriptc build hello.ts -o hello三、看懂 monorepo 的多包工作空间布局
scriptc 仓库本身就是一个典型的多包工作空间,其根目录的 pnpm-workspace.yaml 定义了两个包组:packages/*与internal/*。根 package.json 中的build脚本pnpm -r --filter "./packages/*" run build负责递归构建全部业务包。
各包分工如下:
| 包路径 | 包名 | 职责 |
|---|---|---|
| packages/cli/ | scriptc | 命令行入口,含build/run/coverage等命令实现 |
| packages/compiler/ | @scriptc/compiler | 前端、类型化 IR、LLVM 与 C 双后端 |
| packages/runtime/ | @scriptc/runtime | 无 Node 依赖的原生运行时源码(C) |
packages/llvm-<平台>/、packages/runtime-<平台>/ | 各平台二进制包 | 预编译的 LLVM 辅助工具与运行时 pack |
| internal/compatibility/ | @internal/compatibility | Node API 兼容性追踪 |
CLI 命令的核心分发逻辑在 main.ts 中:build、run、coverage三选一,加上cache warm共四个子命令。理解这个结构,你就理解了多包 monorepo 里"谁生产、谁消费"的边界。
四、逐包编译:用 scriptc build 输出每个包的原生产物
对 monorepo 中最关键的 CLI 包,编译一条命令:
$ scriptc build packages/cli/src/main.ts -o dist/cli-bin产物规则很克制,适合 CI:
- 默认输出为平台可执行文件,不指定
-o时落到输入文件旁的.scriptc/目录; --emit=ir|c|llvm只产出源级中间产物(*.ir.json、*.c、*.ll),只需要 Node,不需要任何 C 编译器,非常适合先在 CI 廉价层跑类型与降级检查;- 各产物类型会累积在
.scriptc/中,重复构建只更新对应文件。
一个实用技巧:对每个包分别执行--emit=llvm,可以在不链接的情况下快速验证"整个工作空间的代码都在 LLVM 静态层内",出问题再切到完整构建。
五、多包之间的 npm 依赖怎么办
monorepo 里包 A 依赖包 B 是常态。scriptc 提供了两档策略:
策略 1:--dynamic动态嵌入
$ scriptc build packages/app/src/main.ts --dynamic -o appnpm 依赖的 JS 会在构建时嵌入二进制,运行时不再读取node_modules,产物可以放到任何目录直接执行。引擎约 620KB,仅在显式开启时才存在——二进制绝不会"悄悄"变大。
策略 2:--npm-static静态编译(实验性)
$ scriptc build main.ts --npm-static auto -o app将符合条件的 npm 包直接编译为静态程序模块,而不是交给嵌入式引擎。preflight 拒绝的包会自动回退到动态层,并在覆盖率报告中注明原因。
六、交叉编译:一次构建,输出多平台原生二进制
这是 monorepo 发版最需要的能力。通过环境变量SCRIPTC_CC=zigcc+SCRIPTC_TARGET=<目标三元组>,同一份源码可以产出 Linux、Windows、WASI 三种原生产物:
$ SCRIPTC_CC=zigcc SCRIPTC_TARGET=aarch64-linux-gnu.2.36 \ scriptc build main.ts -o app-linux $ SCRIPTC_CC=zigcc SCRIPTC_TARGET=x86_64-windows-gnu \ scriptc build main.ts -o app.exe $ SCRIPTC_CC=zigcc SCRIPTC_TARGET=wasm32-wasi \ scriptc build main.ts -o app.wasm仓库自带的 bench-builds.mjs 就是用来批量对比多目标构建耗时的脚本,可作为多平台 CI 矩阵的参考。
七、上线前体检:用 coverage 定位不可编译点
在把每个包推上原生流水线之前,先跑一次零成本的静态覆盖分析:
$ scriptc coverage packages/cli/src/main.ts报告会给出语句级的判定:多少可静态编译、哪些站点需要--dynamic、哪些被拒绝以及对应的SC错误码。配合 docs/src/app/introduction/page.mdx 中对三层模型的说明,你可以把每个包的"动态残留"当作技术债来清零。
八、进阶:库模式与外部链接
如果你希望把某个包编成可被外部 C 工程链接的原生库,而非独立可执行文件,使用--lib库模式(命令入口见 main.ts 的库模式分支):
$ scriptc build --lib --profile tests/library-mode/buffers/profile.jsonprofile JSON 声明入口模块与导出通道,产物是一个自包含的静态归档。测试语料可参考 tests/library-mode/。
另一条路是--emit=obj+--print=native-link-info:产出可重定位目标文件并打印一份版本化 JSON 链接配方(目标、入口、精确的运行时 pack、系统库、FFI 输入),供外部 C 驱动或链接器消费,完整示例见 examples/native-object/。
九、构建提速:缓存预热与环境变量
多包工作空间重复构建时,善用持久化缓存:
$ scriptc cache warm runtime tls dynamic常用环境变量(详见 CLI 参考文档):
SCRIPTC_CACHE_DIR/SCRIPTC_NO_CACHE:控制缓存根目录与开关;SCRIPTC_CACHE_MAX_MB:缓存容量上限,默认 4096MB;SCRIPTC_LINKER:指定 macOS arm64 平台链接驱动;SCRIPTC_TARGET:跨编译目标三元组。
CI 中预热缓存可显著缩短矩阵构建时间;仓库自身的沙箱测试脚本 sandbox-test.mjs 展示了在隔离环境中安装工具链并构建工作空间的完整流程。
十、小结:monorepo × 原生二进制的完整工作流
把本文方法串起来,就是一个可复制的多包发版流水线:
- 布局:
packages/*业务包 +internal/*工具包,根 workspace 统一编排; - 体检:每包
scriptc coverage,清零静态拒绝项; - 编译:
scriptc build -o <平台产物>,npm 依赖按--dynamic/--npm-static二选一; - 发版:
SCRIPTC_TARGET矩阵交叉编译 Linux / Windows / WASM; - 加速:
cache warm+ 缓存环境变量压短 CI 时长。
最终你得到的是无 Node、无 node_modules、启动毫秒级的一组自包含原生二进制——这才是 monorepo 输出"一个仓库、多平台原生产物"的正确打开方式。💪
【免费下载链接】scriptcTypeScript-to-Native Compiler项目地址: https://gitcode.com/GitHub_Trending/sc/scriptc
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考