维护者操作手册
本页是常见改动的"配方书":每个任务给出从哪里下手、改哪些文件、注意什么不变量、怎么验证。它把前面各子系统页的知识压缩成可执行步骤。
全局工作规则(来自 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 解析器。
- 编辑
src/flags/schema.ts一处,声明 flag(kind、subcommand、consumer、必要的 forwardTo/terminal/internalShadow)。 - 跑
pnpm gen:flags(或pnpm format),生成的cmd/ttsc/flags_gen.go、linthost/flags_gen.go、website/.../flags.mdx自动更新——别手改它们。 - 在消费层用它:JS 经
getString/getBoolean(runTtsc.ts/runTtsx.ts),Go 经生成的 allow-list。 - 若 flag 该到达原生旁车,确认
filterHostArgs(cmd/ttsc/filter.go)不会在flag.Parse前吃掉它。 - 验证:
pnpm check:flags+ 加 e2e(tests/test-ttsc/src/features/)。
风险:漏改某层;schema 驱动正是为了避免,所以别在 runBuild 里手列 flag。
配方二:暴露一个缺失的 tsgo API(shim)
下手:shim 审计与同步。
- 在
go env GOMODCACHE/...typescript-go@<version>/internal/<pkg>/找符号,确认名/签名/是否导出。 - 按性质选机制:导出类型 →
surface.go别名(重跑gen_shims);生成器跳过的导出函数 →shim.go手写包装;未导出 →//go:linkname(extra-shim.json的ExtraFunctions)。 - 构建 shim 模块 +
packages/ttsc验证链接。 - 若是图遍历 API,加运行时完整性探针(
packages/lint/test/shim/),防运行时死胡同。 - 验证:
pnpm --filter ttsc shim:audit+ tarball 装进 typia 跑触碰新 API 的测试。
风险:枚举要 shim:audit -fix;遍历 API 的死胡同审计看不见。
配方三:加一条 lint 规则
下手:规则与注册表。
- 在
packages/lint/linthost/加rules_<家族>_<名>.go,实现Rule(需类型信息则加NeedsTypeChecker,是格式化则FormatRule),init()里Register。 - 加 fixture
tests/test-lint/src/cases/<rule-id>.ts(命名空间家族<家族>-<rule-id>.ts(x))。 - 同步更新
packages/lint/README.md与website/src/content/docs/lint/rules/<家族>.mdx(README 的 AGENT INSTRUCTIONS 注释定了 bullet 形状)。 - 测试覆盖:转换方向 + 负向 twin + 边界 + oracle 派生期望,不能只 happy path。
- 验证:
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 升级后 guardedEmitResolver 与 restoreOriginalDeclarationSymbols 最易炸;改文本 matchParen/callRegexFor 必须加负向 twin。
配方五:调插件发现/加载
下手:加载与发现。
发现规则中心在 resolvePluginEntries 与 discoverPackagePluginEntries;相对 vs 包说明符的 baseDir 差异(isRelativePluginSpecifier)是最易出错处。.ts 描述符走 ttsx 兜底(loadDescriptorViaTtsx)。export 条件走 resolvePluginExportCondition(ttsc 条件)。
风险:描述符在 ttsx/ESM 下 __dirname 是 undefined,source 从它派生会 mis-resolve(resolveGoPackageDir 专门命名这个失败)。
配方六:调缓存键(什么时候重建插件)
下手:go build 缓存。
改 computeCacheKey 就是改"何时重建"。漏掉影响二进制的输入 → 陈旧二进制;多加无关输入 → 缓存抖动。对照 GO_BUILD_ENV_KEYS 与 shouldOmitSourceFile。锁正确性靠原子 rename 不靠锁本身。
配方七:调 LSP 行为
下手:LSP 代理设计。
诊断合并守卫(generation/version/脏)在 prepareMergedPluginDiagnostics;code action 增强在 appendCodeActions;命令在 completeExecuteCommand;格式化活缓冲区在 completeFormattingRequest。脏文档贯穿一切——脏时不增强、不执行命令、不应用 edit。
风险:忘了脏守卫会把过期 edit/诊断应用到新内容;改 UTF-16 偏移(lspPositionToByteOffset)要处理星界平面字符。
配方八:bump typescript-go
见 发布与多平台 与 shim 审计与同步 的四步。bump 后优先回归 driver 的三个脆弱补丁(guardedEmitResolver、restoreOriginalDeclarationSymbols、emit 管线组装),再跑 typia/nestia transform 测试。
高风险文件清单
按"改它最容易引入回归"排序: