维护者操作手册

本页是常见改动的"配方书":每个任务给出从哪里下手、改哪些文件、注意什么不变量、怎么验证。它把前面各子系统页的知识压缩成可执行步骤。

全局工作规则(来自 development skill)

每个改动前记住 .codex/skills/development/SKILL.md 的硬规则:

  • 匹配现有约定:加文件/函数/测试前先看邻近 peer,镜像其命名、位置、风格。
  • 尊重包边界:别把消费者特定行为 hardcode 进编译器宿主。
  • JS 描述符,Go 逻辑:JS transform 函数不是公共契约。
  • 提交前 pnpm format 并 stage 结果(含 gen:flags 与 gofmt)。
  • 代码行为变了就同步更新 website/src/content/docs/ 对应页。

四条禁止:无 monkey-patch/hardcode、无"只为过测试"逻辑、无强推坏设计、无打地鼠。

配方一:加一个 CLI flag

下手:flag 解析器

  1. 编辑 src/flags/schema.ts 一处,声明 flag(kind、subcommand、consumer、必要的 forwardTo/terminal/internalShadow)。
  2. pnpm gen:flags(或 pnpm format),生成的 cmd/ttsc/flags_gen.golinthost/flags_gen.gowebsite/.../flags.mdx 自动更新——别手改它们
  3. 在消费层用它:JS 经 getString/getBooleanrunTtsc.ts/runTtsx.ts),Go 经生成的 allow-list。
  4. 若 flag 该到达原生旁车,确认 filterHostArgscmd/ttsc/filter.go)不会在 flag.Parse 前吃掉它。
  5. 验证:pnpm check:flags + 加 e2e(tests/test-ttsc/src/features/)。

风险:漏改某层;schema 驱动正是为了避免,所以别在 runBuild 里手列 flag。

配方二:暴露一个缺失的 tsgo API(shim)

下手:shim 审计与同步

  1. go env GOMODCACHE/...typescript-go@<version>/internal/<pkg>/ 找符号,确认名/签名/是否导出。
  2. 按性质选机制:导出类型 → surface.go 别名(重跑 gen_shims);生成器跳过的导出函数 → shim.go 手写包装;未导出 → //go:linknameextra-shim.jsonExtraFunctions)。
  3. 构建 shim 模块 + packages/ttsc 验证链接。
  4. 若是图遍历 API,加运行时完整性探针(packages/lint/test/shim/),防运行时死胡同。
  5. 验证:pnpm --filter ttsc shim:audit + tarball 装进 typia 跑触碰新 API 的测试。

风险:枚举要 shim:audit -fix;遍历 API 的死胡同审计看不见。

配方三:加一条 lint 规则

下手:规则与注册表

  1. packages/lint/linthost/rules_<家族>_<名>.go,实现 Rule(需类型信息则加 NeedsTypeChecker,是格式化则 FormatRule),init()Register
  2. 加 fixture tests/test-lint/src/cases/<rule-id>.ts(命名空间家族 <家族>-<rule-id>.ts(x))。
  3. 同步更新 packages/lint/README.mdwebsite/src/content/docs/lint/rules/<家族>.mdx(README 的 AGENT INSTRUCTIONS 注释定了 bullet 形状)。
  4. 测试覆盖:转换方向 + 负向 twin + 边界 + oracle 派生期望,不能只 happy path。
  5. 验证:node scripts/test-go-lint.cjs + pnpm --filter "./tests/test-lint" start

风险:类型感知规则把整个引擎钉到串行;只喂规范输出证明的是幂等不是正确。

配方四:加一个插件 emit 行为

下手:Emit 与重写

优先走 AST 路径EmitTransformPlugin + EmitWithPluginTransformers),别扩文本 splice。实现 PluginTransform(SourceFile→SourceFile),用 ec.Factory 构造节点、ec.SetOriginal 关联。需要重新设父链时用 SetParentInChildrenUnset,需要恢复 symbol 时参考 restoreOriginalDeclarationSymbols

风险:tsgo 升级后 guardedEmitResolverrestoreOriginalDeclarationSymbols 最易炸;改文本 matchParen/callRegexFor 必须加负向 twin。

配方五:调插件发现/加载

下手:加载与发现

发现规则中心在 resolvePluginEntriesdiscoverPackagePluginEntries;相对 vs 包说明符的 baseDir 差异(isRelativePluginSpecifier)是最易出错处。.ts 描述符走 ttsx 兜底(loadDescriptorViaTtsx)。export 条件走 resolvePluginExportConditionttsc 条件)。

风险:描述符在 ttsx/ESM 下 __dirname 是 undefined,source 从它派生会 mis-resolve(resolveGoPackageDir 专门命名这个失败)。

配方六:调缓存键(什么时候重建插件)

下手:go build 缓存

computeCacheKey 就是改"何时重建"。漏掉影响二进制的输入 → 陈旧二进制;多加无关输入 → 缓存抖动。对照 GO_BUILD_ENV_KEYSshouldOmitSourceFile。锁正确性靠原子 rename 不靠锁本身。

配方七:调 LSP 行为

下手:LSP 代理设计

诊断合并守卫(generation/version/脏)在 prepareMergedPluginDiagnostics;code action 增强在 appendCodeActions;命令在 completeExecuteCommand;格式化活缓冲区在 completeFormattingRequest。脏文档贯穿一切——脏时不增强、不执行命令、不应用 edit。

风险:忘了脏守卫会把过期 edit/诊断应用到新内容;改 UTF-16 偏移(lspPositionToByteOffset)要处理星界平面字符。

配方八:bump typescript-go

发布与多平台shim 审计与同步 的四步。bump 后优先回归 driver 的三个脆弱补丁(guardedEmitResolverrestoreOriginalDeclarationSymbols、emit 管线组装),再跑 typia/nestia transform 测试。

高风险文件清单

按"改它最容易引入回归"排序:

文件为什么高风险
driver/emit_plugin.go手工组装 tsgo emit 管线,升级最易断
driver/rewrite.go文本 splice 正则 + mini-scanner,过度匹配难发现
driver/program.go::forceSingleChecker改它影响类型解析正确性
buildSourcePlugin.ts::computeCacheKey漏/多输入都坏缓存
loadProjectPlugins.ts 发现逻辑baseDir 解析微妙
lsp_proxy.go 脏守卫漏守卫 = 过期 edit/诊断
flags/schema.ts单一真相源,影响五层

troubleshooting 速查

症状先看
找不到 tsgo/平台二进制依赖与进程拓扑 调试表
插件没构建/没生效buildSourcePluginloadProjectPlugins--plugins-json
链接插件没注册TTSC_LINKED_PLUGINS_JSONRegisterPlugin 顺序
shim 报缺失shim:auditshim 审计
LSP 诊断没出现脏守卫、TTSC_LSP_PLUGINS_JSONNativePluginSource
缓存炸/抖动computeCacheKey 输入、shouldOmitSourceFile

接下来