scriptc 源码级编译新特性:--provenance-sources 如何把 npm 依赖从"运行"升级为"编译"
【免费下载链接】scriptcTypeScript-to-Native Compiler项目地址: https://gitcode.com/GitHub_Trending/sc/scriptc
scriptc 是一个 TypeScript 到原生编译器的实验项目,它的--provenance-sources实验特性更进一步:借助 npm 来源证明(provenance attestation)锁定依赖包的原始提交,把发布包里的 JavaScript 替换成真正的 TypeScript 源码参与静态编译——依赖不再是"在嵌入式引擎里跑",而是"被编译进你的二进制"。
一、为什么需要它:npm 依赖一直是静态编译的"盲区"
在 scriptc 中,你写的代码会被静态编译成原生机器码,但 npm 依赖发布出来的 JavaScript 没有类型、常被压缩,原本只能交给内嵌的动态引擎执行。--provenance-sources想解决的问题就是:
能不能拿到依赖包的发布时源码,像编译自己代码一样编译它?
关键前提是 npm 的来源证明机制:包作者发布时签发的 SLSA provenance 声明会记录"这个 dist 是从哪个仓库、哪个 commit 构建的"。有了这个可信锚点,编译器就能精确地取回那个提交的源码树。🔐
二、工作原理:五步流水线,全程可回退
整条流水线在 provenance-core.ts 中实现,逻辑清晰且任何一步失败都不会导致构建失败——该包自动退回动态岛路径,只在报告中留下说明:
| 步骤 | 做什么 | 结果 |
|---|---|---|
| 1. 预扫描 | 在程序加载前轻量解析入口文件的 import 闭包,收集所有裸 npm 说明符 | 得到待处理包列表 |
| 2. 取证明 | 向 npm registry 请求该包对应版本的 provenance attestation | 得到{repo, commit} |
| 3. 拉源码 | 按 commit 拉取源码树,内容寻址缓存在~/.cache/scriptc/provenance/<commit>,同一 commit 只拉一次 | 本地源码快照 |
| 4. 定位包 | 支持 monorepo:按packages/<name>等约定布局 + 有限深度扫描定位包目录 | 包源码目录 |
| 5. 映射入口 | 把exports的 dist 目标(如dist/esm/index.js)启发式重写到源码孪生文件(src/index.ts) | 编译器拿到真正的 .ts |
映射成功后,这些源码会以 tsconfigpaths的形式注入前端(见 provenance-registry.ts),让类型检查器、预检解析器、模块图三处"咽喉点"看到同一份源码——从此它们就是普通的项目模块。
几个值得注意的工程设计:
- 硬上限保护:单次编译最多为 16 个包做源码映射(
MAX_PACKAGES),超出者退回动态岛并写明原因; - 离线钩子:设置
SCRIPTC_PROVENANCE_MANIFEST环境变量可预置包到源码目录的映射,完全跳过网络,测试夹具就走这条路(见 provenance.test.ts 的头部注释); - 诚实的版本提示:若源码树的
package.json版本与安装版本不一致(发布时才升版的工具链),报告里会明确标注,用"行为差异测试"作为真实性校验。
三、如何上手:一条命令开启实验特性
安装后(需要 Node 24 或更新版本),构建时加上标志即可:
$ scriptc build tool.ts --provenance-sources -o tool该标志在 CLI 中的定义为"从来源证明锁定的源码、在证明的 commit 处以静态程序模块编译 npm 依赖;没有可用证明的包保持引擎路径(一条说明,而非失败)",完整说明见 CLI 文档 与 npm Dependencies 文档。
⚠️ 两点提醒:
- 它是实验特性,成熟度与
--npm-static同级:静态覆盖率高但部分,无法静态化的位置会被延迟(defer),报告会逐条点名; - 原型阶段尚未验证 sigstore bundle 签名(证明按"服务端所给即信任"处理),也不做 dist 与源码的构建复现校验——行为差异比对是目前诚实的检验手段(缺口清单见 provenance-core.ts 头部注释)。
四、它验证了什么:字节级一致的"双轨"测试
项目的测试 tests/harness/provenance.test.ts 定义了这条特性的核心契约,非常有说服力:
- 源码静态二进制(加
--provenance-sources,不加--dynamic)与 - 发布 dist 的动态岛二进制(加
--dynamic)
二者运行输出必须字节一致,并且都要与 Node 直接运行该包的结果一致。注意第一个二进制能不加--dynamic构建成功,本身就证明了"依赖真的被静态编译进了程序,而不是偷偷进岛"。测试还覆盖了子路径导入映射(greeter/echo→src/echo.ts)、@__PURE__死代码消除、以及"无法映射的包只降级不报错"的回退契约。
五、它带来什么、还不能做什么
能得到的🚀
- 依赖代码获得真实类型、真实语句,进入静态前端的完整能力边界;
- 编译后二进制更小、启动更快,不再为此依赖加载约 620KB 的动态引擎;
scriptc coverage报告给出逐包归属:每个包究竟是源码静态、还是进了岛,一目了然;- 失败永远是"说明"而非"报错",构建体验平滑。
目前的边界(官方明确标注的原型缺口):
- 仅支持 GitHub 仓库的源码拉取(经 codeload),其他平台的仓库暂不可用;
- 传递依赖的版本从当前项目的安装树解析,而非依赖包自己的 lockfile;
- 入口映射是启发式(
dist/lib/build→src重写),特殊布局可能映射不到。
六、写在最后
--provenance-sources代表了 TypeScript 原生编译器的一个有想象力的方向:供应链的可信元数据 + 编译器,把"依赖只能被解释执行"的默认假设打破。它还在实验阶段,但设计上的"永远可回退、报告永远诚实"已经体现了成熟工程的气质。如果你的项目依赖大量 TypeScript 编写的 npm 包,且这些包发布了来源证明,不妨现在就试试这条标志,再用 coverage 报告看看你的依赖里有多少"源码级可编译"的比例。🔍
相关源码路径速查:
- 流水线实现:packages/compiler/src/frontend/provenance-core.ts
- 注册表与三个咽喉点:packages/compiler/src/frontend/provenance-registry.ts
- CLI 入口接线:packages/cli/src/bootstrap.ts
- 功能文档:docs/src/app/dependencies/page.mdx
- 特性测试:tests/harness/provenance.test.ts
【免费下载链接】scriptcTypeScript-to-Native Compiler项目地址: https://gitcode.com/GitHub_Trending/sc/scriptc
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考