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. 01Dart の公開 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. すべての公開宣言に /// ドキュメントを付けます — 公開クラス、コンストラクター、メソッド、ゲッター、トップレベル関数、typedef、フィールドが対象です。public_member_api_docs は「見れば分かるメンバー」の例外がないエラーです。ドキュメントのない公開メンバーをすべて検出するため、ドキュメントが欠けているとビルドに失敗します。「念のため」シンボルを公開する変更はレビューで却下されます。代わりに _ を付けて非公開にすれば、ドキュメントも契約も必要ありません。
  2. /** */ ではなく、必ず /// を使います。 Dartdoc が認識するのは /// だけです。JavaDoc 形式のブロックは何も通知されず無視され、そのシンボルにはドキュメントがないように見えます。slash_for_doc_comments がこれを検出します。
  3. 最初の行は、ピリオドで終わる独立した一文とし、独立した段落にします。 ツールの API 一覧にはこの一文だけが表示されるため、それだけで意味が通じる必要があります。本文との間には空の /// 行を入れます。
  4. メソッドや関数のドキュメントは動詞句(3 人称)で始めます。 例:「Returns…」「Schedules…」「Loads…」「Marks…」。ブール値を返すゲッターや bool を返すメソッドは、「Whether…」で始めます。
  5. 名前をそのまま言い換えないでください。 String name に /// The name.、total() に /// Returns the total. と書くことは禁止です。名前以上に付け加えることがない公開メンバーは、_ を付けて非公開にするべきです(非公開メンバーにはドキュメントが不要なので、この矛盾は解消されます)。公開のままにする必要があるメンバーには、意味のある /// が必要です。公開かつドキュメントなしの状態にしてはいけません。単位、範囲、null 許容性、例外、サイドエフェクト、およびそのシンボルが保証する不変条件を加えてください。
  6. 識別子は [角括弧] で囲んで相互リンクし、dartdoc が解決できるようにします。 例:/// Throws [StateError] if [id] is unknown; see [copyWith]. リンク切れは comment_references が警告します。
  7. 本体内の // コメントは「何を」ではなく「なぜ」を説明します。 コードを見れば「何を」しているかは分かります。処理を説明するコメント(// loop over items など)はコードと食い違い、誤情報になります。理由、注意点、不変条件をコメントしてください。
  8. 保証を守る箇所で、不変条件を改めて示します。 1 行で保証を守っている箇所 — 順序、上限・下限の適用、公開前の永続化、標準単位への変換など — では、簡潔な // コメントでそのことを示します。そうすれば、それを弱める差分を見落としにくくなります。マジック定数も同様に、その数値の由来を記してください。
  9. ドキュメントはコードと同じ差分で変更します。 間違ったドキュメントは、ドキュメントがないよりも有害です。変更に触れるすべてのコメントが引き続き正しいことを確認し、PR チェックリストに含めてください。
  10. エクスポートする各バレルにライブラリドキュメントを 1 つ付けます。 公開エントリーポイント(例: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 や空行の上に /// を孤立させないでください。

適用箇所で不変条件を再提示する

シンボルがルールを適用する場合、そのルールを契約の一部として記述し、それを守る箇所の1行に本文内コメントで明記します。

/// 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 に対する /// Gets the id.。削除するか、単位・範囲・不変条件を追加します。
  • 1文の要約より前に段落を置く — 1文目だけで意味が通る必要があります。リスト表示で使用されるためです。
  • 本文で処理の仕組みを説明する — // loop over items、i++ // increment。理由を説明するか、削除します。
  • 出典のないマジックナンバー — 上限値として使われる 3 に、その数値の定義元を示すコメントがない状態です。
  • 古くなったドキュメント — リファクタリング前のパラメーターを説明しているものや、もはや不変条件を守っていないコードに残された不変条件コメントです。
  • 「念のため」のコメントアウトされたコード — git に記録されています。担当者や課題リンクのない // TODO — // TODO(name): reason と書くか、何も書かないようにします。
  • 公開メソッドがドキュメントなしのまま、非公開の細部を説明すること。
  • 空行や export の上にぶら下がった /// — dangling_library_doc_comments に違反します。
  • ファイルを肥大化させるバナーやアスキーアートの区切りコメント — 構造と命名を活用します。

完了の定義

  • 変更したコード内のすべての公開宣言に /// ドキュメントがあり、1行目は単独で意味が通るピリオドで終わる文です。
  • メソッド/関数のドキュメントは動詞で始まり、ブール値を返すゲッターは「Whether」で始まります。
  • 単位、範囲、null 許容性、例外/Result、副作用が、存在する箇所で説明されています。
  • 識別子は [角括弧] で相互リンクされており、壊れた comment_references はありません。
  • 適用される不変条件が適用箇所で再提示され、マジックナンバーには出典が記されています。
  • 名前を言い換えるだけのドキュメント、処理を説明する //、コメントアウトされたコード、担当者のない TODO、JavaDoc ブロックがありません。
  • エクスポートされる各バレルに library; ドキュメントがあり、ぶら下がったドキュメントコメントがありません。
  • 差分で変更したコメントはすべて、引き続き正確です。
  • dart analyze --fatal-infos --fatal-warnings がクリーンであり、パッケージの dart doc が警告なしで生成されます。

関連スキル

  • public_member_api_docs、dangling_library_doc_comments、comment_references をアナライザーのエラーとして設定する方法については、lint-and-style-config を参照してください。
  • これらのドキュメントで説明する役割を示すサフィックス付きの名前(Notifier/Repository/Service/Failure)については、naming-conventions を参照してください。
  • ドキュメントで保証するシールド型と全域関数については、dart3-idioms-and-coding-standards を参照してください。
  • メソッドのドキュメントに記載すべき Result/Failure の契約については、error-handling-typed-results を参照してください。

参考資料

コメント