読者です 読者をやめる 読者になる 読者になる

ESDocというJavaScript向けのAPIドキュメントツールを作りました

こんにちは丸山@h13i32maruです。

昨日、ESDocというツールをリリースしました。GW中になんとかリリースできて一息ついているところです。今回はそのESDocというツールについて紹介します。あと最後に雑談と宣伝があります。

ESDocとは?

ESDocとはJavaScript(ES6)向けのAPIドキュメントツールです。JavaScript界隈ではJSDocがデファクトスタンダードであり、ESDocもJSDocに触発されて作りました。なのでタグの使い方はなるべく互換性を持たせています。とはいえ不要だなと思うタグもかなりあったのでそれらは実装していません。

ESDocの特徴(主にJSDocに比べて)としてはこんな感じです。

  • 詳細なドキュメントを生成する
  • ドキュメントカバレッジを計測する
  • テストコードとドキュメントを関連付ける
  • ES6のclass, import/exportを使ったスタイルのコードを対象とする

デモはこちらです

以降ではいくつかの機能を紹介していきます。

ES6 Class

ES6にはクラス構文が入ったので、それをサポートしています。というかクラス構文で書かれているコードを前提としています。クラス構文が入ったおかげで、クラス、メソッド、コンストラクタ、継承関係が明確にわかるようになりました。これはタグでアノテーションを書かなくてもドキュメントを生成できるということです。JSDocでは@classや@extendsなどを書く必要がありましたが*1、ESDocでは不要です。

そこでESDocではクラス構文を使って以下の内容を自動的にドキュメント化しています。

  • 自身の先祖クラスを表示
  • 自身を直接継承/間接継承しているクラスを表示
  • 先祖クラスから継承したメソッド、メンバの一覧を表示
  • オーバーライド元のメソッドを表示

あとクラスの継承関係や包含関係をグラフにできたら便利かもしれません。

ES6 Module

ES6にはモジュール(import/export)構文が入ったので、それをサポートしています。クラス構文同様、モジュールを使ってコードが書かれていることを前提としています。ES6のモジュールはファイルベースのものなので、1ファイル = 1モジュールとして扱っています。JSDocでは@module、@exportタグを使ってモジュールを指定していましたが、ESDocでは不要です。

ただし、このモジュール構文には一つ注意すべき点があります。それはexportの仕方によってimportの方法が変わるということです。具体的には

  • export default class Foo {}で公開されたものはimport Foo from './Foo.js
  • export class Bar{}で公開されたものはimport {Bar} from './Bar.js'

となります。これはモジュールの利用者側からすると不便なので、ESDocでは各クラスや関数にはimportの方法を表示するようにしています。

ドキュメントのカバレッジ

テストはカバレッジを測定できるようになっていて、テストを書くモチベーションになったりどこのテストが薄いかなどがわかりますよね。ドキュメントでもそういう体験が欲しかったのでドキュメントのカバレッジを測定できるようにしました。

ESDocでは処理するクラスや関数などはモジュール(ファイル)のトップレベルに宣言されているものだけとしています。これを元にして、すべての処理対象のうちどれだけドキュメントが書かれているのかでカバレッジを測定しています。また各モジュールごとのカバレッジも測定するので、ドキュメントの漏れも見つけやすくなっています。例えばESDoc自体のカバレッジはこんなかんじです。

あと余談ですが、Inch CIというドキュメントのCIサービスがあるのを見つけました。対応しているのはRuby, JS, Elixirだそうです。JSは裏でJSDocを使っているので現在はまだES6には対応していないみたいです。

テストコードとドキュメント

テストコードもそのソフトウェアを使う上で有益な情報です。なのでテストコードとドキュメントの関連づけを行うことにしました。具体的にはテストコードに専用のタグ(@test)を書くことで、テストとその対象を関連づけてドキュメント上で参照できるようにしています。

/** @test {MyClass} */
describe('MyClass is super useful class.', ()=>{

  /** @test {MyClass#sayMyName} */
  it('say my name', ()=>{
    let foo = new MyClass('Alice');
    assert.equal(foo.sayMyName(), 'My name is Alice');
  })
});

ただし、現在対応してるのはMochaを使ってかかれたテストのみです(assertionライブラリは問わない)。それと、この機能を作るにあたり@hokacchaさんにアドバイスを頂きました。ありがとうございました!

検索

ドキュメントにとって検索機能は必須だと思います。ESDocではJSだけで検索できる機能を実装しています。どうやっているかというと、ドキュメント生成時に検索で使うインデックス(JSON)を作ります。そしてユーザが検索機能を使うときにはそのインデックスから検索して、検索結果とリンクを表示するようにしています。 ただし、今の実装はかなりナイーブなのであまりにも大きなドキュメントでは速度に問題があるかもしれません。500個程度の対象を検索するには特に問題なく動いているので、とりあえずはマシンパワーとブラウザに任せておこうかなと思っています。

型の推測

ESDocではタグが書かれていなくてもある程度はドキュメントを生成することができます。それはES6でクラス構文などのいろいろな構文が入ったおかげです。その中の一つであるデフォルト引数の構文を使ってメソッドの仮引数の型を推測するようになっています。とはいってもすごく単純なのでたいしたことはしていません。単純にデフォルト引数の値を見てそれがプリミティブならその型を使うというだけです。戻り値もなるべく推測しています。これはメソッド内にreturn文がある場合、その値を見てそれがプリミティブならその型を使うという仮引数と同じ方法です。

まあ推測とは言えないレベルですが実験的に作ってみた機能なので、とりあえずこれでよしとしています。

カスタマイズ

出力されるドキュメントをカスタマイズしたくなる場合があると思います。特にデザインなど。そういった場合のためにESDocではスタイルシートとJavaScriptをHTMLに読み込む機能を提供しています。この機能を使うことである程度ドキュメントをカスタマイズすることができます。JSDocではドキュメントをカスタマイズしようとすると自分でプラグインを書くことになってかなりハードルがあがります。ESDocでも同じようにプラグインの機能を提供するつもりですが、そもそもプラグインを書かなくても有益なドキュメントを生成すべきだとは思います。


以上ESDocの機能を幾つか紹介しました。ES6のclassとimport/exportを使って書いていれば、タグを書いてなくてもとりあえずそれぽいドキュメントを生成できるので、興味がある方はぜひ一度試してみてください。

https://esdoc.org




ここからは雑談というかESDocを開発していたときに考えていたこととかです。

なぜつくろうとおもったのか?

最初はJSDocを使っていたのですが、生成されるHTMLが貧弱だったので自分で出力部分のプラグインを書き始めました。JSDocのタグの多さに疲れながらもある程度完成させることができました(JSDoc Cloudy)。しかしJSDocはES6のコードを(現時点では)正しく処理できなかったので、ES6のコードをコメントの構造を維持したままJSDocが理解できるES5のコードにトランスパイルするプラグインも作り始めえてこれもある程度完成しました(JSDoc Knight)。

しかし、ここまでくるとJSDocがどうやってドキュメントを扱っているの分かるようになり、ASTをつかってJSのコードを操作する方法もわかってきました。ということで、自分で作りなおしたほうが自分好みのものが作れそうとおもったので、一から作ることにして、完成したのがESDocです。

一応断っておくと、JSDoc本体にコントリビュートしようともおもっていました。しかし、ES5で書かれているコードを触るのが辛い体になっていたのと、JSDocは内部でNode.jsとMozilla Rihnoの二種類に対応するために複雑になってる感じでした。それとJSDoc自体にはJSDocがあまりかかれていませんでした。つまりドキュメントツールにドキュメントが書かれていない == テストツールがテストされていないのと同じくらい印象が良くなかったのです*2。というわけでJSDocにコントリビュートするのは諦めました。でもJSDocはあれだけ色んな種類のタグを持っていて柔軟にドキュメントをかけるし、開発もものすごいスピードなので素直にすごいと思っています。

そういえばJSDocについて調べていたら少し古いですが、こんな記事をみかけました。YUIDocは知っていたけど他にもいくつかあったんですね。

JSDoc vs YUIDoc vs Doxx vs Docco – Choosing a JavaScript documentation generator

どういうドキュメントが必要なのか?

僕はドキュメントを書くのは好きな方ですが、それでも得意とは言えません。これからドキュメントツールをつくろうとしてる者がドキュメントとは何たるかをしらないのはあれなので、ESDocを作りながらドキュメントについて調べていました。特に参考にしたものが下記のリンクです。

これらの記事を読みつつドキュメントについてぼんやり考えていました。今のところ「ソフトウェアのAPIドキュメント」に求められる価値は「ライブラリの実装を読まなくてもエンドユーザ(開発者)がそのライブラリを使いこなせること」かなという結論にいたりました。そのためにはなるべくソースコードから有益な情報を抜き出して、参照しやすい形で出力するというのがツール側の仕事だと思っています。さらにライブラリの実装者は本当に必要なドキュメントだけを書けば済むようになるはずです。それがこのESDocで達成できてるかはまだ良くわかりませんが。

ドキュメント界隈

というような感じで開発していたのですが、ドキュメントの話をしてる人ってあまり見かけませんでした。プログラムの話はよく見かけるし有名な人もいます。テストの話もまあまあよく見かけるしこれも有名な人(@t_wadaさん!)がいます。じゃあドキュメントは?となったときに話はあまり見かけないし、有名な人もわかりません。どういうところをウォッチしてたらいいんでしょうか??

UML?そういえばありました。UMLについては僕はあまり詳しくなくて、クラス図、シーケンス図、ユースケース図くらいしか書いたことはありません。確かにこれはこれでドキュメントとしての一つの解になっているとは思います。ですが、僕が求めているドキュメントとなんか違う気がします。仕様書やUMLはどちらかというとソフトウェアを設計/実装する時に必要なものだと思っていて、僕が興味あるのはソフトウェアを利用するときに開発者が参照するもっと軽量なもの(APIドキュメント、チュートリアル、README.mdなど)です。

ちなみになんでこんなにドキュメント、ドキュメントといっているかというと、僕は「ソフトウェア = プログラム + テスト + ドキュメント」と思っていて、プログラムとテストはいろいろ参考になるものがありますが、ドキュメントはどしたらいいんだろう?とずっと思っていたからです。

ポッドキャストの宣伝

ESDocやドキュメントの話には全然関係ないですが、CodeLunch.fmというポッドキャストをやっています。Rebuld.fmにあこがれて2013年末から始めました。話している内容はWeb系の話もあれば、言語実装の話もあるし、ハードウェアの話もしています。よかったら聞いてみてください。

*1:JSDocv3.3.0ではES6が正式にサポートされる予定なので不要になるかも

*2:ESDocは質はさておきカバレッジ90%です