アカベコマイリ

HEAR NOTHING SEE NOTHING SAY NOTHING

ESDoc を試す

July 01, 2015開発ES6, ESDoc, JavaScript

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

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

$ npm i -g esdoc

設定は esdoc.json jへ記述する。これは 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.jsondevDependencies へ記録する。

$ npm i -D esdoc

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

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

以下のコマンドで ESDoc を実行できるようになった。

$ npm run esdoc

ESDoc の解析対象

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

function TestFunc() {
}

class Util {
}

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

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

export function TestFunc() {
}

export class Util {
}

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

余談。

シングルトンはなるべく利用しないようにしているのだが、ちょっとしたツール系メソッドをまとめたユーティリティ系クラスに限定して許可している。このようなクラスは重要なデータへの参照は持たせずインスタンス生成の面倒さを回避することが目的。そのためクラス自体の export により複数インスタンスが作成されても問題にならない。

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

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

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

{
  "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 を再実行するだけで変更を反映可能なので運用も楽ちん。まさしく生きた資料である。

カバレッジ

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 上で会話する機会があった。

https://twitter.com/h13i32maru/status/596120479129145344

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

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

Copyright © 2009 - 2023 akabeko.me All Rights Reserved.