transform 派发
本页讲插件被构建之后,ttsc 如何把工作派发给原生进程:check 阶段怎么顺序跑、transform 阶段怎么选"共享宿主"、--plugins-json 与 TTSC_LINKED_PLUGINS_JSON 怎么协作。涉及 src/compiler/internal/transformProjectInMemory.ts、src/compiler/internal/runBuild.ts、src/compiler/internal/sharedHostHelpers.ts。
两个派发路径
ttsc 有两个会派发原生插件的入口,机制相似但产物不同:
两者都遵循同一阶段顺序:先 check,后 transform。
派发决策树
check 阶段:顺序、短路
runNativeChecks(transform 路径,transformProjectInMemory.ts:204)/ runNativeCheckPlugins(构建路径,runBuild.ts:764)顺序跑每个 check 插件,首个非零退出即返回,并用 appendBuildOutput 聚合诊断与输出。每个 check 插件用 createNativeCheckArgs 构造参数(check/fix/format 子命令 + --tsconfig= + --plugins-json= + --cwd=)。
check 阶段是 @ttsc/lint 的运行点:它作为 check 插件先于任何 transform 跑,把 lint 违规当作 emit 前的失败条件。
共享宿主选择(核心机制)
当存在多个 transform 插件时,它们必须共享一个 emit 进程(一次 emit pass 只能有一个二进制拥有 Program)。sharedHostHelpers.ts 三个函数管这件事:
assertSharedHostCompatibility(sharedHostHelpers.ts:23):
- 若所有 transform 插件解析到同一二进制(去重后 ≤1),通过。
- 否则去掉 linked transform(它们会被链接进别的宿主),看剩下的 owner 二进制是否 ≤1;是则通过。
- 否则报错。两个调用方措辞不同(
pass参数区分):emit 路径报 "multiple compiler native backends cannot share one emit pass",源到源路径报 "cannot share one source-to-source pass",并提示 "compose transform libraries through one aggregate native host"。
selectSharedHostPlugin(sharedHostHelpers.ts:57):选第一个非 linked 的插件作为拥有进程的宿主;linked transform 源骑在用 driver.LoadProgram 的宿主里,所以有 executable transform 时它胜出,否则退到第一个(即 fallback driver host)。
两套 manifest 协作
派发一个 transform 宿主时同时传两样东西:
--plugins-json(CLI 参数):序列化每个插件的{ config, name, stage }(serializeNativePlugins,只传协议需要的字段保持参数短)。原生宿主据此知道有哪些插件、各自 config、阶段。TTSC_LINKED_PLUGINS_JSON(环境变量):只在 transform 阶段、且有链接源时设。nativePluginEnv(runBuild.ts:126/transformProjectInMemory.ts:297)调linkedTransformPlugins收集链接插件并序列化进去。driver 在loadLinkedPluginState读它,按注册顺序配对(见 driver: 链接插件)。
所以一个有 typia(executable transform)+ 多个链接 transform 的项目里:typia 二进制拥有进程,--plugins-json 告诉它全部插件配置,TTSC_LINKED_PLUGINS_JSON 告诉它哪些链接源已编进自己进程、按什么顺序跑。
源到源 vs 构建的产物差异
源到源(transformProjectInMemory)
无插件时走 transformProjectWithNativeHost(transformProjectInMemory.ts:70):构建 cmd/ttsc 原生编译宿主(buildNativeCompiler),spawn api-transform,解析 { typescript, diagnostics, dependencies? } 信封。有 transform 插件时 spawn 共享宿主的 transform 子命令;无 transform 插件但 check 通过时退回 api-transform。
parseNativeTransformOutput(transformProjectInMemory.ts:333)严格校验信封:typescript 必须是 Record<string,string>,否则当协议错误抛出并带 stderr 上下文。可选 dependencies(每文件咨询过的源列表)作为 watch 元数据被转发,格式不对的条目被丢弃而非失败(它是 advisory 而非输出)。
构建(runBuild)
构建路径在 emit 阶段更复杂(见 运行时流程 流程二)。关键分支(runBuild.ts:163):
- 有 transform 编译器 →
buildWithNativeCompilerPlugins(spawn 共享宿主build子命令,由它的 driver emit)。 - 无 transform 编译器 → 视
reportsTypeScriptDiagnostics与终止 flag 决定是否先跑 tsgo--noEmit守卫,再 tsgo 构建。
format 模式的短路在这里很关键(见运行时流程页流程三):lint 旁车重写源后,绝不再跑 tsgo --noEmit 或 transform 编译器。
tsgo flag 转发
ttsc 没识别的 tsgo flag 经 --tsgo-args=<JSON> 转发给原生旁车(createNativeTsgoArgs,runBuild.ts:687)。nativeTsgoPassthroughArgs 会先剥掉 --diagnostics/--extendedDiagnostics(这些是 ttsc 自己的计时 flag,单独经 --diagnostics 转发给声明了 diagnosticsTiming 能力的宿主)。编码成单个 token,让旁车的未知 flag 过滤器保持它完整。
不变量与失败模式
维护者提示
- check 失败短路是契约:check 阶段任一插件非零退出就停,不 emit。改派发顺序前确认这点。
nativePluginEnv在构建与转换两路径镜像,改环境握手要两处同步改。- 线程 flag(
--singleThreaded/--checkers)只转发给声明threadingArgs能力的 check 宿主(createNativeCheckThreadingArgs,runBuild.ts:618),别无条件转发——会让旧第三方宿主退出 2(#113 的教训)。