🧊

gajus/eslint-plugin-jsdocのコードを読む Part 1

OxcにJSDocのサポートを入れるべく、このへんをよく調査してる今日この頃。聞いてはいたけど、噂に違わぬ魔境であった。

というわけで、せっかく調査したのでそのメモを残しておく。

一口にJSDocといっても

ぱっと思いつくのはアノテーションからドキュメントを作れるアレではあるけど、昨今はもっといろんな使われ方をしてる。

/**
 * JSDoc example.
 * @param {string} x X!
 * @returns {string}
 */
const dummy = (x) => {
  return `${x}!`;
};

そしてみんな似た目的(大まかに)のために、微妙に違うことをやってる。

共通のSpecがあるわけでも、共通の実装があるわけでもないってのがポイント。

育ってきた環境が違うから〜って感じで、まぁそれはそれでいいとしても、どれもいろんな方向性で読むのが大変なソースコードに仕上がってる。

eslint-plugin-jsdocのルール

gajus/eslint-plugin-jsdoc https://github.com/gajus/eslint-plugin-jsdoc

最初に概観を知っておきたく、どういうルールがあるのかもざっと見ておく。

どうやらほぼすべてのルールがiterateJsdoc()という高階関数を介してたので、特殊そうなオプション指定についてもメモしておく。

数が多いので、とりあえずrecommendedなルールのみ。

  • check-access: checkPrivate, iterateAllJsdocs
    • @accessに正しい値が入ってるか、@privateなどが併用されてないか重複してないか
  • check-alignment: iterateAllJsdocs
    • コメントブロックの*の位置が揃っているか
  • check-param-names
    • @paramで定義した引数が、ちゃんと実装にあるか
  • check-property-names: iterateAllJsdocs
    • @propertyで存在しないプロパティを指してないか(fooなしでfoo.barって書いてるとか)
  • check-tag-names: iterateAllJsdocs
    • @xxxのタグ名が既定の値になってるかどうか
  • check-types: iterateAllJsdocs
    • {string}のように型を指定する部分の指定が、規定の文字列になっているか(Stringとしていないか)
  • check-values: iterateAllJsdocs
    • 特定のタグに対して、正しいフォーマット(versionsemver準拠かどうかなど)が指定されてるか
  • empty-tags: checkInternal, checkPrivate, iterateAllJsdocs
    • 特定のタグに対して、空値になってるかどうか
  • implements-on-classes: contextDefaults
    • @implementsが正しい条件下で使われているか
  • multiline-blocks: iterateAllJsdocs
    • コメントブロックのスタイル(複数行 or 行など)を強制できる
  • no-multi-asterisks: iterateAllJsdocs
    • コメントブロック中の各行において、*が複数登場しないようにできる
  • no-undefined-types: iterateAllJsdocs
    • {}で指定された型がスコープ内に存在し、undefinedになってないか
  • require-jsdoc
    • JSDocの記述を強制する
    • ⚠️ このルールだけはiterateJsdoc()ではなく、独自でVisitorを用意
  • require-param: contextDefaults, noTracking
    • 関数の引数がすべて@paramで定義されているか
  • require-param-description: contextDefaults
    • @paramに説明文がついているか
  • require-param-name: contextDefaults
    • @paramに名前がついているか
  • require-param-type: contextDefaults
    • @paramに型の指定がついているか
  • require-property: iterateAllJsdocs
    • @typedef@namespaceでオブジェクトが定義されるとき、@propertyがセットになっているかどうか
  • require-property-description: iterateAllJsdocs
    • @propertyに説明文がついているか
  • require-property-name: iterateAllJsdocs
    • @propertyに名前がついているか
  • require-property-type: iterateAllJsdocs
    • @propertyに型の指定がついているか
  • require-returns: contextDefaults
    • @returnsが定義されているか
  • require-returns-check
    • @returnsが定義されているとき、returnが書かれているか
  • require-returns-description: contextDefaults
    • undefined|voidを含まない@returnsに説明文がついてるか
  • require-returns-type: contextDefaults
    • @returnsに方の指定がついているか
  • require-yields: contextDefaults
    • ジェネレータ関数に@yieldsが定義されているか
  • require-yields-check
    • @yieldsが定義されているとき、yieldが書かれているか
  • tag-lines: iterateAllJsdocs
    • JSDocコメント内の空行の置き方を強制できる
  • valid-types: iterateAllJsdocs
    • {}の型指定がシンタックスエラーになっていないか

細かく役割が分かれたシンプルなルールもあれば、とても重厚な実装のあるルールもある。役割が被ってない?何が違うの?っていうのもある。

recommendedではないものは一旦追わずに置いておく・・・。

  • check-examples
  • check-indentation
  • check-line-alignment
  • check-syntax
  • informative-docs
  • match-description
  • no-bad-blocks
  • no-blank-block-descriptions
  • no-defaults
  • no-missing-syntax
  • no-restricted-syntax
  • no-types
  • require-asterisk-prefix
  • require-description
  • require-description-complete-sentence
  • require-example
  • require-file-overview
  • require-hyphen-before-param-description
  • require-throws
  • sort-tags

実装のOverview

という前提を踏まえつつ、コードを読んでいく必要がある。

続く

入口からしてろくなテストケースもないとこから察するに、読むのに難儀しそうであるなあ。

https://github.com/gajus/eslint-plugin-jsdoc/blob/37df54dc8535eaed65b4dadaca2dc072e4c7bc4e/test/iterateJsdoc.js

続く。