mcpField
C
codegen-and-toolchain

codegen-and-toolchain

github.com/zakariaf开发63149 次安装5 周前更新
代码仓库

关于 codegen-and-toolchain

codegen-and-toolchain 是面向使用 build_runner 或 gen-l10n 的 Flutter/Dart 项目的开发技能。它规定代码生成、格式化、分析与测试的可复现顺序,并说明生成范围、文件策略和工作区配置。

主要功能

  1. 01确定性代码生成流程
  2. 02使用 generate_for 限定生成范围
  3. 03生成文件提交与 CI 策略
  4. 04分析器与覆盖率排除项
  5. 05SDK 固定与工作区指南

适用场景

  1. 01注解变更后重新生成代码
  2. 02按包范围配置 build.yaml
  3. 03选择生成文件的提交策略
  4. 04设置多包工作区

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/zakariaf/flutter-skills --skill codegen-and-toolchain

安装后在 Claude Code 中输入/codegen-and-toolchain确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

examples/
build.yaml
gitattributes.example
references/
codegen-scoping.md
commit-vs-gitignore.md
toolchain-and-workspace.md
scripts/
check-codegen-hygiene.sh
regen.sh

详细介绍

代码生成与工具链

一次确定性的代码生成流程是所有生成文件的唯一事实来源; 先运行它,严格限定范围,并一次性决定其输出是否纳入 Git。 适用于使用 build_runner 或 gen-l10n 的项目。

阅读与当前任务相关的参考文档:

  • references/codegen-scoping.md — 生成器清单、generate_for: 范围限定表,以及错误到修复的恢复映射。
  • references/commit-vs-gitignore.md — 两种提交/加入 Git 忽略规则的策略、 各自的权衡,以及每种策略所需的确切 CI 门禁。
  • references/toolchain-and-workspace.md — SDK 固定、分析器/覆盖率 排除规则,以及多包工作区分层(有边界、可选启用)。

提交 PR 前运行 scripts/regen.sh(代码生成 → 格式化 → 分析),并运行 scripts/check-codegen-hygiene.sh,以验证排除规则与提交策略的一致性。

不可妥协的规则

  1. 代码生成必须先于 flutter analyze 运行。 全新克隆后,生成文件可能 不存在;编辑注解后,生成文件也可能已过时。分析这样的代码树会产生误导性的 “缺少 part 文件”和“未定义类 _$Foo”错误。先重新生成,再分析——绝不要在调用处追查症状。
  2. 始终传入 --delete-conflicting-outputs。 不传此参数时,build_runner 会拒绝覆盖已重命名/删除源文件所对应的输出,并报出一大串“输出冲突”信息后失败。 这不是需要调查的 bug。
  3. 绝不要手动编辑生成文件。 下一次构建会悄无声息地还原文件,并一并抹掉你的修改。 修改带注解的源文件,然后重新运行代码生成。
  4. 在每个包的 build.yaml 中,用 generate_for: 限定每个构建器的范围。 一个目录中的更改不得触发整个代码树重新生成。每个 glob 都应指向拥有该注解的最窄目录。
  5. 一次性决定提交还是加入 Git 忽略规则,并配套相应的 CI 门禁。 加入 Git 忽略规则 → CI 先重新生成,再进行分析。提交 → CI 重新生成后, 运行 git diff --exit-code 作为新鲜度门禁。两种方式都有效;没有对应门禁的策略无效(参见 references/commit-vs-gitignore.md)。
  6. 将生成文件的 glob 同步添加到分析器和覆盖率排除规则中。 对机器输出运行 lint 会浪费审查精力;lcov 中缺少 glob 则会虚增覆盖率。排除实际 生成的后缀——应核实,而非猜测。
  7. 固定 SDK;本地版本必须与 CI 一致。 CI 使用的 Flutter/Dart 版本必须与本地固定的版本一致 (使用 FVM 时为 .fvmrc,或使用仓库规定的工具)。版本偏差会导致“在我的机器上能运行”的格式化/代码生成反复变动。
  8. watch 仅用于本地编辑循环。 CI 和分析前的步骤始终使用一次性 build 命令。

标准命令

从仓库根目录运行:

dart run build_runner build --delete-conflicting-outputs

此命令会在一次流程中重新生成所有构建器的输出——drift_dev、freezed、json_serializable、 riverpod_generator、mockito。gen-l10n 通过 Flutter 工具运行,但处理方式相同(先重新生成,采用相同的提交策略):

flutter gen-l10n   # if not auto-triggered by `flutter pub get` via l10n.yaml

全新克隆/拉取之后/编辑任何注解之后,固定顺序如下:

build_runner build --delete-conflicting-outputs   →   format   →   analyze   →   test

绝不要把代码生成调整到分析之后。在活跃编辑期间,只有此时才使用 监视器在保存时重新构建受影响的输出:

dart run build_runner watch --delete-conflicting-outputs

使用 build.yaml 限定构建器范围

为每个包含代码生成的包(或单个应用包)分别提供一个 build.yaml,并通过 generate_for: 将每个构建器限定在拥有其注解的最窄目录 中。未限定范围的根配置会扩展到整个代码树中的每个注解,因此编辑一个 Task 模型会重新生成无关的 Order 和 Account 输出。复制 examples/build.yaml; references/codegen-scoping.md 提供完整的生成器到后缀对应表和各构建器的 glob。

Commit 与 gitignore:二选一,并配置相应的门禁

两种策略都合理;它们面临的风险不同(参见规则 5)。gitignore 可以保持目录树整洁,但需要可用的工具链才能构建。提交生成文件即使未来工具链损坏也能继续使用,还能让 Schema 差异可供审查,但需要配置 .gitattributes 差异折叠(examples/gitattributes.example),并设置强制的新鲜度门禁——重新生成,然后运行 git diff --exit-code -- '*.g.dart' '*.freezed.dart' '*.drift.dart'。没有门禁的策略不算策略。两种策略、各自的权衡以及所需的具体门禁见:references/commit-vs-gitignore.md。

Analyzer 和覆盖率排除项

无论提交还是 gitignore,每一种实际生成的后缀都必须同时从 analyzer 和 lcov 覆盖率过滤器中排除——先确认配置实际生成的后缀,不要猜。遗漏 analyzer glob 会让审查浪费在机器生成的输出上;遗漏(或锚定位置错误的)lcov glob 会把未经测试的代码计入覆盖率。保存了生成时代兼容数据类的迁移测试基准目录也需要加入 analyzer 排除项——如果自动修复改动了已导出的 Schema 快照,就会破坏迁移验证(参见 run-migration)。规范的排除代码块和正确锚定的 lcov 模式见 references/toolchain-and-workspace.md。

多包场景(工作区)

仅适用于确实已不止一个包可满足需求的仓库。单应用项目不需要这些设置。

  • 使用 Dart 原生 pub 工作区关联各成员:在每个成员的 pubspec 中设置 resolution: workspace,并在根目录中添加 workspace: 列表,从而生成一个根 pubspec.lock。切勿添加每个包各自的锁文件。
  • 如果叠加使用 Melos,只将它用于脚本编排(melos run gen/format/analyze/test),绝不用它进行路径关联——关联工作由 pub 完成。将 gen 脚本限制为依赖 build_runner 的包。
  • 在工作区根目录运行一次 build_runner,让每个成员在同一轮中重新生成;在某个包目录内运行会导致其他包过期。

反模式

  • 重新生成前先运行分析——所有后续错误都是假象;应修正顺序,而不是代码。
  • 省略 --delete-conflicting-outputs——会让常规重命名变成构建障碍。
  • 手动编辑 .g.dart/.drift.dart 来消除错误——下次构建时会被还原;真正的修复应在带注解的源文件中进行。
  • 提交生成代码却没有新鲜度门禁——输出会悄悄偏离源文件,审查者却会相信谎言。
  • 使用一个不加范围限制的根级 build.yaml——每次微小编辑都会重新生成整个目录树,拖慢每次构建。
  • 覆盖率或 analyzer glob 匹配了错误的后缀——在 analyzer 中静默失效,在 lcov 2.x 中明确报错,并悄悄抬高覆盖率。
  • CI Flutter 版本 ≠ 本地固定版本——导致无人能解释、反复出现的格式或代码生成变更。
  • 生成的迁移测试留有空数据列表——会无条件通过([] == []),却无法证明任何事情;应填入数据或删除(参见 run-migration)。

完成定义

  • 在干净的目录树中运行 dart run build_runner build --delete-conflicting-outputs 成功,然后 flutter analyze 无错误。
  • 每个承载代码生成的包都有包含限定范围 generate_for: glob 的 build.yaml。
  • 已决定使用 commit 还是 gitignore,并配置了相应的 CI 门禁(新鲜度差异检查,或先运行代码生成)。
  • 实际生成的后缀已同时从 analysis_options.yaml 和 lcov 覆盖率过滤器中排除。
  • SDK 已固定,且 CI 工具链版本与之匹配。
  • 没有手动编辑生成文件;CI 中没有 watch。

相关技能

  • 查看 run-codegen,了解操作步骤简单、自由度低的“运行此确切命令”循环及其故障排查表。
  • 查看 run-migration,了解 Drift 代码生成流程所产出的架构快照与迁移测试惯例。
  • 查看 persistence-drift,了解这些生成器所服务的 Drift/DAO 数据层。
  • 查看 lint-and-style-config,了解此处镜像的分析器规则集与排除项。
  • 查看 ci-pipeline-and-gates,了解代码生成和新鲜度检查门槛在流水线中的位置。
  • 查看 dependency-hygiene,了解锁文件与 SDK 固定机制。

参考资料

评论

支持 Markdown