driver 子系统

packages/ttsc/driver 是 ttsc Go 侧的编译器外观层(facade)。它是整个引擎里唯一按设计允许直接 import shim/* 的包(见 driver/host.go:1 的包注释);下游所有代码消费一个与 shim 无关的小接口 *driver.Program

理解 driver 就理解了"ttsc 如何在 tsgo 的 Program/Checker 之上叠加自己的行为"。

职责边界

driver 拥有

  • 用 tsgo 的 shim/compilerProgram、租用单个 Checkerprogram.go)。
  • 解析 tsconfig(含 extends、CLI flag 覆盖层)(program.go::ParseTSConfig)。
  • 把诊断翻译成与 shim 无关的 Diagnostic 结构,并提供富文本/纯文本渲染(program.go)。
  • 两条 emit 路径:文本级重写(rewrite.go)与 AST 集成的 emit transformer(emit_plugin.go)。
  • 链接插件的注册、配对与生命周期 hook(plugins.go)。
  • 源码预置(source preamble)注入(program.go::sourcePreambleFS)。
  • 增量类型检查的常驻 Sessionsession.go)。
  • sourcemap 前导调整(sourcemap_preamble.gooverlay.go)。

driver 不拥有:决定加载哪些插件(JS 的活)、解析 npm 依赖、写 emit 文件到用户项目树(公共 API 走内存捕获)。

文件一览

文件作用
host.goDefaultFS / DefaultHost:把 OS 文件系统包上 tsgo 的 bundled libs
program.goProgram 外观、LoadProgramParseTSConfigDiagnostics、单 checker 钉定
rewrite.go文本级 emit 重写:定位插件调用点并 splice 替换 JS
emit_plugin.goAST 集成 emit:EmitWithPluginTransformers,替代文本 splice
plugins.go链接插件注册表、PluginContext、三类 hook 接口
session.go增量类型检查常驻宿主(TtscService 后端之一)
overlay.goOverlayFS:内存编辑覆盖在真实文件系统之上
sourcemap_preamble.gopreamble 行偏移导致的 sourcemap 坐标修正
lsp.godriver 侧 LSP 辅助

子页

  • Program 与 CheckerLoadProgram 全流程、单 checker 约束、诊断过滤、tsgo flag 覆盖。
  • Emit 与重写:两条 emit 路径的设计对比、并行 emit 的串行化、文本 splice 的脆弱面。
  • 链接插件SourcePreamblePlugin / ProgramPlugin / EmitTransformPlugin 三类 hook 与按顺序配对。
  • 增量 Session:为什么 transform 不能复用热程序、type-check 复用怎么做。

核心数据结构:Program

type Program struct {
  TSProgram      *shimcompiler.Program
  ParsedConfig   *tsoptions.ParsedCommandLine
  Checker        *shimchecker.Checker
  checkerRelease func()
  Host           shimcompiler.CompilerHost
  FS             vfs.FS
  SourcePreamble string
  plugins        linkedPluginState
  pluginsApplied bool
}

Programprogram.go:186)是下游唯一看到的类型。它持有 tsgo 的真实 Program 与一个租来的 Checker,外加链接插件状态。Close() 释放 checker 池租约(checkerRelease)。

下游代码(cmd/ttscinternal/graphutility)只通过 Program 的方法(SourceFiles()Diagnostics()EmitAll()ApplyLinkedPlugins() 等)工作,看不到任何 shim 类型。

设计取舍速览

取舍选择原因
checker 池大小钉到 1(forceSingleCheckerttsc 的串行 transform/rewrite 阶段必须用同一个 checker;多 checker 会让跨文件循环类型解析成 any
parse / emit 并行保留 tsgo 的并行单 checker 只影响类型解析,不影响 parse/emit
emit WriteFile一把 wfMu 全局串行让插件输出重写器永远看到单写入者,避免并发 map 崩溃
重写机制文本 splice(旧)→ AST 集成(新)文本 splice 脆弱;AST 集成让 tsgo 的 module-transform 自己别名注入的 import
诊断模型shim 无关的 Diagnostic + raw/lint 锚点让纯文本、tsgo、lint 三类诊断走同一渲染管线

最容易在 tsgo 升级时炸的地方

driver 是 typescript-go 知识的唯一汇聚点,因此也是升级最脆弱处:

  • emit_plugin.go 手工组装 tsgo 的 emit 管线(GetSourceFilesToEmitGetScriptTransformersPrintFileWithSourceMap);任何 tsgo emit 内部重构都可能在这里断。
  • guardedEmitResolveremit_plugin.go:47)包住 const-enum 内联器的 GetConstantValue,对插件构造的合成节点做 panic 恢复——这是对 tsgo checker 行为的脆弱假设。
  • restoreOriginalDeclarationSymbolsemit_plugin.go:112)手工把 binder symbol 从原节点拷到插件重建的合成节点上,绕过 emit resolver 的 nil panic。

升级 tsgo 时优先回归这三处。详见 Emit 与重写

接下来