☰
scriptc三档编译模型全解析:静态编译、--dynamic与明确拒绝的设计哲学
2026/9/29 1:15:27 网站建设 项目流程

scriptc三档编译模型全解析:静态编译、--dynamic与明确拒绝的设计哲学

【免费下载链接】scriptcTypeScript-to-Native Compiler项目地址: https://gitcode.com/GitHub_Trending/sc/scriptc

scriptc是一款将 TypeScript/JavaScript 直接编译为原生可执行文件的编译器,产物中不含 Node、不含 V8、不含任何 JavaScript 引擎。它最有辨识度的设计是「三档编译模型」:每个语法结构要么静态编译为原生代码,要么通过--dynamic标志进入内嵌的quickjs-ng动态引擎执行,要么被明确拒绝并给出带错误码的诊断。没有任何代码会被悄悄误编译——这套「看得见静态性」的契约,正是 scriptc 与普通 TS 转原生工具最大的区别。

为什么需要三档:一个编译器的三种承诺

大多数「TS 转原生」方案要么把整个 JS 引擎塞进二进制(体积大、启动慢),要么对无法静态处理的代码默默降级(行为不可预期)。scriptc 选择了第三条路:

档位触发条件产物特征
🟢 静态编译默认模式纯原生代码,无引擎,体积约 320KB 量级
🟡 动态执行显式加--dynamic内嵌 quickjs-ng 引擎(约 620KB),运行 npm 依赖与any类型代码
🔴 明确拒绝既不能静态、也未开--dynamic编译期报错:SC 错误码 + 代码框 + 改写提示

关键在于「档位就是承诺」:留在第一档的程序,其 stdout、stderr 与退出码和同一文件在 Node 下运行逐字节一致;第二档的每个跨回静态代码的值都会在运行时校验,类型说谎会得到可捕获的TypeError而不是内存损坏。官方介绍页对这一模型的完整描述见 introduction/page.mdx。

第一档:静态编译 —— 默认且唯一的模式

不加任何标志时,scriptc 走的是纯静态路径。覆盖范围相当宽:

  • 语言层面:单继承类与动态分派、闭包、泛型函数声明(单态化)、可辨识联合 + 类型收窄、async/await(与 JS 精确一致的调度)、解构/展开/可选参数、迭代器、模板字符串等;
  • 标准库:UTF-16 语义的字符串、bigint、Map/Set(JS 精确顺序)、JSON(带运行时校验的转换)、Math、Buffer与 TypedArray、Error层次结构;
  • Node API:fs(同步 + promises)、path、child_process、crypto、url、timers,乃至完整的服务端栈net/http/https/tls/dgram/dns——真实的 HTTP 服务器可以直接编译成交付给客户的单文件二进制。

类型检查由真正的 TypeScript 编译器完成(es2025lib + 可选@types/node),你的tsconfig.json决定严格程度。架构细节(tsc 前端 → 类型化 IR → LLVM IR → 平台链接)在 how-it-works/page.mdx 中有完整图解。

第二档:--dynamic —— 把 JS 引擎变成一座"动态岛"

npm 依赖发布的是无类型、面向 V8 的 JavaScript,这是动态前沿。scriptc 的答案是动态岛(dynamic island):

$ npm install picocolors $ scriptc build cli.ts --dynamic -o cli $ ./cli hello from scriptc

它的工作方式值得新手注意:

  1. 依赖按 Node 自身的解析规则从node_modules解析,.d.ts作为类型表面供你检查;
  2. 包的 JavaScript 在构建期嵌入二进制——运行时不读node_modules,产物可在同平台任意机器上直接运行;
  3. 静态值进入岛内按拷贝传递,动态值回到静态一侧时逐个校验,类型不符即抛可捕获的TypeError。

一个容易误解的点:--dynamic不会「偷偷膨胀」普通构建。静态依然是默认值——没有这个标志,动态站点就是逐条的编译错误;即使开了标志但从未触达动态代码,产物与纯静态构建生成相同的代码。岛屿机制的编译端实现可参考 lower-island.ts。

第三档:明确拒绝 —— 宁可在编译期喊停

scriptc 的立场是「诚实是产品」。任何无法落地的代码都会在编译期被拒绝,附错误码、代码框和通常还有改写提示,常见几类:

  • SC2011:无--dynamic时出现any—— 提示改用unknown+ 受检转换,或显式开启引擎;
  • SC2013:导入需要动态引擎的 npm 包但当前构建未包含它;
  • SC2020:触达「类型检查可见、但无降级实现」的标准库表面(如部分regex/SymbolAPI),提示会给出受支持的替代写法;
  • SC3002:WASI 目标下缺失能力的 API(网络套接字、子进程、OS 信号等),在链接前直接拒绝。

被拒绝 ≠ 项目失败,而是把「运行时玄学」提前为「编译期清单」。完整的拒绝面与「按设计偏离 Node 的行为」编号清单见 limitations/page.mdx。

用 scriptc coverage 亲眼看见三档分布

判断某个程序落在哪一档,不需要猜——scriptc coverage会逐语句给出带编码的诊断:

$ scriptc coverage cli.ts statements analyzed 4 compile statically 3 (75%) runs with --dynamic 2 sites (embeds a JS engine, ~620KB — static stays the default) ×1 importing 'picocolors' requires the embedded dynamic engine ... SC2013 ×1 values from the 'picocolors' package run in the embedded dynamic engine SC2013

加--dynamic参数则回答另一个问题:如果开启动态引擎,这个构建会编译什么、还剩什么被挡住。覆盖率报告的实现入口在 coverage/report.ts。

新手三步上手:从静态到动态

  1. 安装(需要 Node.js 24+):npm install -g scriptc

  2. 静态构建并运行:

    $ scriptc build hello.ts -o hello $ ./hello hello, world
  3. 遇到依赖时加--dynamic,用 coverage 复核边界:

    $ scriptc build tool.ts --dynamic -o tool $ scriptc coverage tool.ts --dynamic

    所有命令与选项(--emit=ir|c|llvm|asm|obj|exe、--backend、--strip等)的完整参考见 cli/page.mdx。

设计哲学小结:为什么「拒绝」是特性

三档模型背后是一条清晰的原则链:

能静态就静态 → 不能静态就显式付费(引擎体积)→ 两者都不行就编译期喊停。

静态档位保证零引擎、毫秒级启动、逐字节与 Node 一致的行为;动态档位保证「真实世界依赖」无需改造即可嵌入;拒绝档位保证永远不存在沉默的误编译——每一处没编译的东西,都以编号、可复查的形式摆在台面上。对一个把「正确性当方法论」的编译器来说,明确拒绝不是能力的缺失,而是可信度的来源。

延伸资料

  • 项目总览:README.md
  • 三档模型与静态覆盖范围:docs/src/app/introduction/page.mdx
  • 编译管线与运行时架构:docs/src/app/how-it-works/page.mdx
  • npm 依赖与动态岛细节:docs/src/app/dependencies/page.mdx
  • 拒绝清单与设计性偏离:docs/src/app/limitations/page.mdx
  • 覆盖率分析器源码:packages/compiler/src/coverage/

【免费下载链接】scriptcTypeScript-to-Native Compiler项目地址: https://gitcode.com/GitHub_Trending/sc/scriptc

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询