mcpField
D
dartdoc-conventions

dartdoc-conventions

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

关于 dartdoc-conventions

面向 Dart 项目的文档编写规范,规定公共 API 与代码注释的写法,帮助开发者维护清晰且可由分析器检查的 API 契约。

主要功能

  1. 01为每个公共声明添加 `///`
  2. 02首行使用独立摘要句
  3. 03说明行为、约束和异常
  4. 04为文档标识符添加交叉链接
  5. 05在实现注释中说明不变量
  6. 06检查导出库文档和分析器规则

适用场景

  1. 01新增 Dart 公共 API
  2. 02审查或更新代码注释
  3. 03准备可由 `dart doc` 生成文档的包
  4. 04配置文档 lint 检查

安装

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

$npx skills add https://github.com/zakariaf/flutter-skills --skill dartdoc-conventions

安装后在 Claude Code 中输入/dartdoc-conventions确认已加载。

详细介绍

Dartdoc 约定——公共 API 是一份契约

公共代码被阅读的次数远多于编写的次数。没有前导 _ 的符号就是调用方所依赖的契约,因此它必须带有读者无需打开函数体就能理解的 /// 文档注释。文档注释使用 ///,并遵循 Effective Dart: Documentation。添加或更改公共声明,或编写函数体内的 // 注释时,均适用本约定。

不容妥协的规则

  1. 每个公共声明都必须有 /// 文档注释——包括公共类、构造函数、方法、getter、顶层函数、typedef 和字段。public_member_api_docs 是错误,没有“显而易见的成员”例外:它会标记每个没有文档注释的公共成员,因此缺少文档注释会导致构建失败。仅仅“以防万一”就将符号设为公共会被评审拒绝——应改为 _ 私有,这样就不需要文档注释,也不会形成契约。
  2. 只能用 ///,绝不用 /** */。 Dartdoc 只识别 ///。JavaDoc 块会被静默忽略,符号看起来就像没有文档注释一样。slash_for_doc_comments 会标记这种写法。
  3. 首行必须是独立成段、以句号结尾的完整句子。 工具在 API 列表中只显示这一句,因此它必须能够独立呈现;用空白的 /// 行将它与正文隔开。
  4. 方法和函数的文档注释以动词短语开头(第三人称),例如:“Returns…”、“Schedules…”、“Loads…”、“Marks…”。布尔型 getter 或返回 bool 的方法以 “Whether…” 开头。
  5. 绝不复述名称。 在 String name 上写 /// The name.,或在 total() 上写 /// Returns the total.——都不允许。如果公共成员除了名称之外没有其他信息可补充,这就说明应该将它改为 _ 私有(私有成员无需文档注释,这样矛盾也就消失了)。必须保持公共的成员仍然需要真正的 /// 文档注释——绝不能让它保持公共却没有文档注释;应补充有意义的信息:单位、范围、可空性、异常和副作用,以及该符号所强制执行的不变量。
  6. 用 [方括号] 链接标识符,以便 dartdoc 解析它们:/// Throws [StateError] if [id] is unknown; see [copyWith].。comment_references 会对失效链接发出警告。
  7. 函数体内的 // 只解释原因,绝不解释内容。 代码已经说明了内容。叙述代码的注释(// loop over items)会过时并产生错误信息。注释应说明原因、易错之处或不变量。
  8. 在不变量得到强制执行的位置重申它。 如果某行代码维护了某项保证——例如顺序、限制范围、先持久化再发布,或转换为标准单位——就用简短的 // 说明,这样任何削弱保证的差异都会格外醒目。魔法常量也一样:注明数字的来源。
  9. 文档注释应与代码在同一个差异中更改。 错误的文档注释比没有文档注释更糟。你所更改的每条注释都必须仍然正确——将此项加入 PR 检查清单。
  10. 每个导出入口文件都应有一条库文档注释。 公共入口文件(例如 my_package.dart)应在 library; 指令上方添加 /// 库文档注释。dangling_library_doc_comments 是错误——没有附着到声明的前导 /// 必须是真正的库文档注释,不能成为空行或 export 上方的孤立注释。

为值类型编写文档

/// A note the user has authored and can later edit or archive.
///
/// Immutable; derive changes with [copyWith]. [updatedAt] is UTC and never
/// precedes [createdAt].
class Note {
  /// Creates a note; [updatedAt] must not precede [createdAt].
  const Note({
    required this.id,
    required this.title,
    required this.createdAt,
    required this.updatedAt,
  });

  /// Stable unique identifier, assigned once at creation.
  final String id;

  /// Human-readable heading shown in lists; may be empty, never null.
  final String title;

  /// Creation instant, in **UTC**.
  final DateTime createdAt;

  /// Last-edit instant, in **UTC**; equals [createdAt] until first edited.
  final DateTime updatedAt;

  /// Returns a copy with the given fields replaced.
  Note copyWith({String? title, DateTime? updatedAt}) => Note(
        id: id,
        title: title ?? this.title,
        createdAt: createdAt,
        updatedAt: updatedAt ?? this.updatedAt,
      );
}

每个公共字段都必须带有 /// 文档注释,包括 title——在强制执行 public_member_api_docs 的情况下,字段“复述了名称”并不意味着可以省略文档注释,而是提醒你应补充实际信息(或将字段改为 _ 私有)。如果类型本身未说明单位和约束,应明确记录:货币的最小单位、0–23 小时、范围是否包含端点、null 的含义,以及“必须 > 0”。

记录行为——异常、异步和副作用

/// Schedules a reminder for [task] at [when].
///
/// [when] must be in the future; a past instant is clamped to now. Persists the
/// scheduled row before arming the OS notification, so a crash mid-call cannot
/// leave a notification the store never recorded.
///
/// Throws [PermissionDeniedException] if the OS denied notification permission.
Future<void> scheduleReminder(Task task, DateTime when) async {
  // persist-before-arm: the store is the source of truth, the OS mirrors it
  await _store.saveScheduled(task.id, when);
  await _plugin.schedule(task.id, when);
}

说明异步行为、方法会写入或修改的内容,以及它可能抛出的每一种异常。如果函数是全函数(对每个输入都会返回,绝不抛出异常),请明确说明——这是调用方依赖的一项实际保证。

库文档注释和导出入口文件

/// Pure-Dart domain core: value types, typed failures, and total functions.
///
/// No Flutter, Riverpod, or `dart:io` import — this purity is what lets the
/// core be unit-tested without a widget harness and reused across platforms.
library;

export 'src/note.dart';
export 'src/task.dart';

/// 会附着到 library; 指令上。绝不能让 /// 孤立在 export 或空行上方。

在强制执行的位置重申不变量

如果某个符号强制执行一条规则,请将该规则写入其契约,并在函数体内维护该规则的那一行标注出来:

/// Failure modes of a checkout. Switched exhaustively by the caller, so a new
/// case is a compile error at every call site.
sealed class CheckoutFailure {
  /// Const base constructor, so subclasses can be `const`.
  const CheckoutFailure();
}

/// The account balance was below the order total; no charge was made.
final class InsufficientFunds extends CheckoutFailure {
  /// Creates an [InsufficientFunds] failure.
  const InsufficientFunds();
}

/// Read-only view of the current order; mutated only through this notifier.
class OrderNotifier extends Notifier<Order> {
  @override
  Order build() => Order.empty();

  /// Adds [item] and recomputes the total.
  ///
  /// Persists via the repository before emitting, so a mid-write crash cannot
  /// surface a line the store never saw.
  Future<void> addItem(Item item) async {
    // persist-before-emit: emitted state is always a state the store holds
    await _repository.append(item);
    state = state.withItem(item);
  }
}

反模式

  • /** JavaDoc-style */——dartdoc 会忽略它;该符号会被视为未编写文档,并导致 public_member_api_docs 检查失败。
  • 重复说明名称——在 String get id 上写 /// 获取 id。。删除它,或补充单位、范围或不变量。
  • 在一句话摘要前写一段文字——第一句话必须能在列表视图中独立成立。
  • 在函数体内叙述操作机制——// 遍历项目、i++ // 递增。解释原因,否则删除。
  • 没有来源说明的魔法常量——上限值写成裸 3,却没有注释指出该数字的定义位置。
  • 过时的文档——描述重构前的参数,或将不变量注释放在已不再遵守该约束的代码上。
  • 保留注释掉的代码以备不时之需——git 会记住。没有负责人或问题链接的 // TODO——写成 // TODO(name): reason,否则删除。
  • 记录私有细节,却让公共方法没有文档。
  • 空行或 export 上方悬空的 ///——会导致 dangling_library_doc_comments 检查失败。
  • 横幅式/ASCII 艺术分隔注释——只会让文件变得臃肿;依靠结构和命名即可。

完成标准

  • 所有改动代码中的公共声明都有 /// 文档;首行是以句号结尾的完整独立句子。
  • 方法/函数文档以动词开头;布尔 getter 以 “Whether” 开头。
  • 在适用处记录单位、范围、可空性、抛出异常/Result 及副作用。
  • 使用 [方括号] 添加标识符的交叉链接;没有失效的 comment_references。
  • 在强制执行不变量的位置重申这些不变量;为魔法常量注明来源。
  • 不重复说明名称,不写叙述操作的 //,不保留注释掉的代码,不写无负责人的 TODO,也不使用 JavaDoc 代码块。
  • 每个导出的 barrel 都有 library; 文档;没有悬空的文档注释。
  • diff 改动的每条注释仍然准确。
  • dart analyze --fatal-infos --fatal-warnings 检查通过;dart doc 为各软件包生成文档时没有警告。

相关技能

  • 请参阅 lint-and-style-config,了解如何将 public_member_api_docs、dangling_library_doc_comments 和 comment_references 配置为 analyzer 错误。
  • 请参阅 naming-conventions,了解这些文档所描述的角色后缀命名(Notifier/Repository/Service/Failure)。
  • 请参阅 dart3-idioms-and-coding-standards,了解文档承诺的密封类型和全函数保证。
  • 请参阅 error-handling-typed-results,了解方法文档必须说明的 Result/Failure 契约。

参考资料

评论

支持 Markdown