ESDoc を試す

2015年7月1日 0 開発 , ,

ES6 ( ECMAScript 2015 ) に対応しているコード ドキュメント生成ツール、ESDoc を試す。

まずは ESdoc をインストール。グローバルで esdoc コマンドを使いたいなら以下のようにする。

$ npm i -g esdoc

設定は esdoc.json に記述する。これは package.json と同じディレクトリに保存するとよいだろう。単純な解析なら対象となる JavaScript とドキュメントの出力ディレクトリを指定するだけでよい。

{
  "source": "./src/js",
  "destination": "./esdoc"
}

設定が完了したら、そのディレクトリ内で以下のコマンドを実行。

$ esdoc -c ./esdoc.json

すると esdoc ディレクトリ内に解析結果となる HTML ファイルなどが出力される。

ESDoc をプロジェクトのローカルで使用する

最近 npm はなるべくプロジェクトのローカルにインストールしている。グローバルだと npm が更新されたときに依存しているプロジェクトすべてが影響を受ける。互換性の問題が起きたりしたら一大事だ。

プロジェクトのローカルならそのような心配は無用である。

また、package.json で npm のバージョンを管理することで必要とする環境を明示できる。ファイルを Git リポジトリなどで管理していれば、環境の共有や復元も容易だ。

というわけで、ESDoc もローカルにインストールする。package.json の置かれたディレクトリで以下のコマンドを実行する。ESDoc は開発用なので -D オプションをつけて package.json の devDependencies に記録する。

$ npm i -D esdoc

次に package.json へ ESDoc 用スクリプトを定義。

{
  "scripts": {
    "esdoc": "esdoc -c esdoc.json"
  },
}

以下のコマンドで ESDoc を実行できる。

$ npm run esdoc

ESDoc の解析対象

ESDoc の解析対象はスクリプトから export しているものに限定されるようだ。よって以下のような定義だと TestFunc や Util は除外される。シングルトンのために export したインスタンスも同様。

function TestFunc() {
}

class Util {
}

// シングルトンとして公開
export default new Util();

もしこれらを含めたければ、それぞれを export する。

export function TestFunc() {
}

export class Util {
}

// シングルトンとして公開
export default new Util();

以下、余談。

シングルトンはなるべく利用しないようにしているのだが、ちょっとしたツール系メソッドをまとめたユーティリティ系クラスに限定して許可している。

このようなクラスは重要なデータへの参照は持たせず、インスタンス生成の面倒さを回避することを目的にシングルトンとしているため、クラス自体の export により複数インスタンスが作成されても問題にならない。

逆に問題となるようなものは、そもそもシングルトンをやめる方向で設計を見なおしたほうがよいだろう。

コードとユニット テストを関連付ける

ESDoc の特徴として、解析対象とするクラスやメソッドとユニット テストの関連付けがある。これを実施するためには esdoc.json に test プロパティを設定する。

{
  "source": "./src/js",
  "destination": "./esdoc",
  "test": {
    "type": "mocha",
    "source": "./test"
  }
}

type がユニット テストの書式、source はユニット テストのソース コード置き場となる。type は ESDoc v0.1.2 の時点で mocha のみ対応とのこと。mocha 標準だとプロジェクト ルート直下の test ディレクトリにユニット テストを格納するので、今回もこの構成で source にパスを指定。

次にユニット テスト側へ ESDoc の設定をおこなう。テストは ES6 コードをテストする で書いた power-assertespower-babel で構成。

ESDoc への関連付けはコメントとして記述する。@test に続けて {Type} を書く。ある Type に属する method なら {Type#method} となる。

import Assert from 'power-assert';
import Util   from '../src/js/model/Util.js';

/** @test {Util} */
describe( 'Util', () => {
  // 秒を時間文字列に変換
  /** @test {Util#secondsToString} */
  describe( 'secondsToString', () => {
    it( 'm:ss', () => {
      Assert( Util.secondsToString( 5 ) === '0:05' );
    } );

    it( 'mm:ss', () => {
      Assert( Util.secondsToString( 917 ) === '15:17' );
    } );

    it( 'h:mm:ss', () => {
      Assert( Util.secondsToString( 3704 ) === '1:01:44' );
    } );

    it( 'hh:mm:ss', () => {
      Assert( Util.secondsToString( 41018 ) === '11:23:38' );
    } );
  } );
} );

この状態で ESDoc を実行するとユニット テスト部分も解析され、コメントに記述した関連付け指定にもとづいて、テスト対象のページにユニット テストへのリンクが追加される。

ユニット テストのリンク

リンク先に移動すると、ユニット テストの当該部分を表示してくれる。

テスト部分

JavaDoc 系だと、サンプル コードを pre タグや @code で記述していたが、ESDoc の本機能は @code より有用な代替となるだろう。ユニット テストはそれ自体がサンプルであり、しかも動作保証されている。

テスト自体が更新されたとしてもコメントを直す必要はなく、ESDoc を再実行するだけで変更を反映できるので運用も楽ちん。まさしく生きた資料である。

カバレッジ

ESDoc はコード ドキュメントの網羅率をカバレッジとして出力してくれる。コード ドキュメントとして扱われる対象はクラスや関数だけでなく、コンストラクタで宣言したメンバー変数も含まれるようだ。

ESDoc のカバレッジ

パーセンテージがあると 100% にしたくなる。ドキュメントの抜けている場所は Identifier ページに移動して、各項目のコメント部分を見れば分かる。ここが空欄なら抜けになる。

欲を言えば ESDoc を実行した時のコンソール出力で、抜けのあるファイルと行数を示してくれると嬉しい。

不完全な構文によるエラー

ESDoc を利用していて、以下のエラーに遭遇した。なお、私の環境依存なパスは $ で置き換えてある。

$ npm run esdoc

> audio-player@1.0.2 esdoc $/examples-nw/audio-player
> esdoc -c esdoc.json

identifiers.html
index.html
$/examples-nw/audio-player/node_modules/esdoc/out/src/Publisher/Builder/ClassDocBuilder.js:77
            throw _iteratorError;
                  ^
TypeError: Cannot read property 'indexOf' of undefined
    at ClassDocBuilder._buildSignatureHTML ($/examples-nw/audio-player/node_modules/esdoc/out/src/Publisher/Builder/DocBuilder.js:1034:26)
    at $/examples-nw/audio-player/node_modules/esdoc/out/src/Publisher/Builder/DocBuilder.js:443:38
    at IceCap.loop ($/examples-nw/audio-player/node_modules/esdoc/node_modules/ice-cap/out/src/IceCap.js:319:15)
    at ClassDocBuilder._buildSummaryDoc ($/examples-nw/audio-player/node_modules/esdoc/out/src/Publisher/Builder/DocBuilder.js:440:11)
    at ClassDocBuilder._buildSummaryHTML ($/examples-nw/audio-player/node_modules/esdoc/out/src/Publisher/Builder/DocBuilder.js:399:29)
    at ClassDocBuilder._buildClassDoc ($/examples-nw/audio-player/node_modules/esdoc/out/src/Publisher/Builder/ClassDocBuilder.js:149:38)
    at ClassDocBuilder.exec ($/examples-nw/audio-player/node_modules/esdoc/out/src/Publisher/Builder/ClassDocBuilder.js:62:36)
    at publish ($/examples-nw/audio-player/node_modules/esdoc/out/src/Publisher/publish.js:121:59)
    at Function.generate ($/examples-nw/audio-player/node_modules/esdoc/out/src/ESDoc.js:185:7)
    at ESDocCLI.exec ($/examples-nw/audio-player/node_modules/esdoc/out/src/ESDocCLI.js:88:28)

調査したところ @param で引数名が存在しない箇所が原因だった。例えば以下の書き方だとエラーになり、

export default class AudioPlayer {
  /**
   * 音声の再生を停止します。
   *
   * @param {Boolean}
   */
  stop( pause ) {
  }
}

引数名を指定するば直る。

export default class AudioPlayer {
  /**
   * 音声の再生を停止します。
   *
   * @param {Boolean} pause 一時停止する場合は true。それ以外は停止。
   */
  stop( pause ) {
  }
}

構文としては引数の型と名前があればよく、コメントは省略できる。ESDoc を使うならコメントも書くべきだとは思うけど。

エラー内容から ESDoc の不具合を疑ってしまったが、これは不完全な構文が原因なので利用側の問題といえる。もし ESDoc 側を改善するとしたら、解析時に構文エラーとして検出してくれると助かる。

まとめ

JavaScript、それも ES6 以降のコードをドキュメント化できたらいいな、という動機で試してみたら、カバレッジ計測やユニット テストの関連付けまで付いてきた。しかもそれらが実に有用。大満足である。

特にユニット テストは単なるテストにとどまらず、関連付けを前提とすることで資料性を高めることができる。チーム開発でユニット テストを導入する際の訴求力にもなるだろう。

ところで以前、ESDoc 作者の方と Twitter 上で会話する機会があった。

ESDoc という名称 と JSDoc の構文互換から、JSDoc の Wrapper + 機能拡張のスーパーセットだと予想していたのだけど、JSDoc の全タグをサポートしているわけではなくサブセットとのこと。私は基本的なタグしか使わないので、この仕様でも特に問題ない。

最後に ESDoc によるコード ドキュメント生成を試したサンプル プロジェクトを公開しておく。


REPLY

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です