こんにちは、気づけば入社してから3年目も終わろうとしている宮本です。先日は、NIFTY Tech Talk #9 「SvelteKit, Next.jsの導入事例紹介など 〜ニフティのフロントエンドの今とこれから〜」で担当している @nifty トップページのNext.js化について話してきました。当日ファシリテーターをしていた筑木くんの紹介記事がすでに掲載されているので、ぜひご覧ください!
紹介記事: SvelteKit, Next.jsの導入事例紹介など 〜ニフティのフロントエンドの今とこれから〜 NIFTY Tech Talk #9 を開催しました!
さて、今回の記事はここからが本題です。Tech Talk中で@niftyトップページではTypeDocを使いドキュメントを作成していると軽く触れました。フロントエンドアプリケーションのドキュメント生成といえばやはりStorybookが有名どころですが、今回はTypeDocをご紹介させていただこうと思います。
TypeDocってなに?
TypeDocは、TypeScriptで記述されたアプリケーション用のドキュメント生成ツールです。記述方法は簡単で、JSDocに非常によく似たTSDoc形式でコード内にコメントを書いていくだけ。あとはドキュメント生成コマンドを打てば、自動でドキュメントが作成されます。めっちゃ便利。
簡単な例ですが、次のようなドキュメントを作成することができます。こちらはcreate-next-appで自動生成したサンプルアプリケーションに、いくつかコンポーネントやカスタムフックを追加したアプリケーションです。
画像は型情報についてのドキュメントで、これを生成した元のコードは下のようになってます。TypeScript本来の型定義に加え、軽くコメントで補足している程度です。細かい記法についてはかなり量が多いので、公式ドキュメントをご覧ください。
1 2 3 4 5 6 7 8 9 10 11 |
export type UseCheck = { /** * チェック済みであるかを表す */ isChecked: boolean; /** * チェック状態を切り替える * @returns */ toggleCheck: () => void; }; |
TypeDocの設定
ここからは、アプリケーションにTypeDocを導入する方法を紹介します。create-next-appなどでサンプルアプリケーションを作成してこの手順を試してみると、どのようなドキュメントが生成されるのかなどもわかりやすいと思います。
1. インストール
1 |
yarn add -D typedoc |
2. 設定
TypeDocはドキュメントをビルドするタイミングで設定を入れることもできますが、typedoc.jsonというファイルを用意していると、毎回自動で設定を読み込んでくれるので手間がかかりません。
最初に、必須の設定のみ追加しておきます。schemaは入れておくとvscodeでの補助が効くので便利です。
- entryPoints:ドキュメントを生成する対象のコードが存在する最上位のディレクトリを指定します。
- entryPointStrategy:ドキュメントを作成する元になるファイルの指定方法を変更します。
デフォルト設定ではentryPointsに指定したディレクトリのindex.tsファイルを起点として指定しようとするため、そもそもindex.tsを用意していない場合は生成自体ができません。ディレクトリごとに必ずしもindex.tsを用意しないフロントエンドのために、全ファイルを起点にドキュメントを生成するようにします。
typedoc.json
1 2 3 4 5 |
{ "$schema": "https://typedoc.org/schema.json", "entryPoints": ["./src"], "entryPointStrategy": "expand", } |
3. ドキュメント生成
このコマンド一つでドキュメントを生成することができます。
1 |
yarn typedoc |
4. プラグイン
さて、上記までの手順でドキュメントを生成することはできるのですが、実際に生成されたドキュメントを見るとデフォルトでは結構見づらいです。特に辛いのが、このサイドバー……。スラッシュが入っていることからも分かる通り、entryPointsで指定したディレクトリからの相対pathをそのまま利用しています。
だいぶ手抜き気味なこのサンプルこそ9ファイルしか用意していないのでまだ良さそうに見えますが、実際のアプリケーションではもっとファイル数も多いと思います。その分がずらっと並ぶと考えると、かなり見づらくなってしまうでしょう。
うん、これは無理です、諦めよう……。と思いましたが、幸いTypeDocにはさまざまなプラグインがあります。
フロントエンドのドキュメントを書く場合は、以下のどちらかが使いやすいと思います。なお、今回ここで挙げた両方を適用すると、ファイルが階層を無視して統合されたりと使いづらくなってしまったのでご注意を。
typedoc-theme-hierarchy
typedoc-theme-hierarchyは、サイドバーに表示されるファイル名が実際のディレクトリと同じように階層化されて表示できるテーマプラグインです。テーマプラグインとは、生成されるドキュメントの見た目のみに手を入れるプラグインです。
注意点として、テーマプラグインはTypeDocが0.23にアップデートした際に後方互換無しで新しくなったため、古いテーマプラグインは使えない可能性があります。
導入方法はyarnでインストールし、設定ファイルで利用するテーマを選択します。
1 |
yarn add -D typedoc-theme-hierarchy@^3.0.0 |
typedoc.json
1 2 3 4 5 6 |
{ "$schema": "https://typedoc.org/schema.json", "entryPoints": ["./src"], "entryPointStrategy": "expand", "theme": "hierarchy" } |
hierarchyテーマを適用することで、画像右のような階層表示がTypeDocにも適用されるようになり、だいぶ見やすくなります。
typedoc-plugin-merge-modules
二つ目の方法は、typedoc-plugin-merge-modulesを利用する方法です。entryPointStrategyをexpandにした場合、デフォルトでは1ファイル=1モジュールとなります。それをこのプラグインを用いることで複数のファイルをTypeDoc上で一つのモジュールとして扱い、関連する機能ごとにまとめることができます。
yarnやnpmでインストールし、設定ファイルにmergeModulesMergeModeを追記します。
1 |
yarn add -D typedoc-plugin-merge-modules |
typedoc.json
1 2 3 4 5 6 |
{ "$schema": "https://typedoc.org/schema.json", "entryPoints": ["./src"], "entryPointStrategy": "expand", "mergeModulesMergeMode": "module" } |
mergeModulesMergeModeはモジュールをまとめ方を選択するオプションです。以下の3パターンがあります。
- project:全ファイルを1モジュールとして扱う
- ファイルや関数ごとに追記する必要がない
- アプリケーション全体で定義している関数の数が多いとわかりづらくなる
- module:明示的にファイルごとのモジュール名を指定し、同じモジュール名がつけられたファイル全てを合わせて1つのモジュールとして扱う
- 関連している機能をごとにまとめて1モジュールとして扱える
- ファイルごとにモジュール名を追記する必要がある
- module-category:同一のカテゴリタグに含まれるものを1モジュールとして扱う
- ドキュメントを記述する関数や変数ごとにカテゴリタグを追加する必要がある
個人的な感想としては、手軽さはprojectですが、コンポーネントやカスタムフックが多くなってくると、projectだとややドキュメントを追いづらいように感じました。一方でmoduleモードではファイルへの記述は増えるものの、機能の分割基準がはっきりしているのであれば、自由にモジュールの分割基準を決められるため、比較的管理しやすいように感じています。module-categoryモードもcategoryタグの使い方次第ですが、moduleモードとそう変わらないかもしれません。
下の画像の場合では、itemページに関わる以下のファイルを同一のitemモジュールとしてファイルにmoduleタグを追記し、まとめています。ごとに利用する関数やタイプがはっきりしている場合では、モジュール=機能という単位でドキュメントをまとめられるこの方法がわかりやすいかもしれません。
- types/item.ts
- component/item.tsx
- pages/item/[id].tsx
実際に生成してみて
まず初めにぶっちゃけてしまうと、フロントエンドのコンポーネントに特化したドキュメントとしてTypeDocを考えると、結構使いづらいかなと感じています……。というのも、UIドキュメントの代表格であるStorybookと異なり、直接コンポーネントの見た目や動作の確認などはできないためです。画像を添付することはできますが、変更があった際は画像を変更しなければどんどん古びてしまいます。そして、変更のあるたびに画像を作成して入れ替えて……というのは実際の手間としては些細なことですが、些細だからこそ凄まじく面倒です。フロントエンドのUIドキュメントという点ではやはりStoryBookの方が便利に感じました。
TypeDoc自体はフロントエンドアプリケーションというより、npmなどで公開されているような一般的なライブラリに適しています。出力結果に見た目というものが存在しない関数では、自動で型情報を取得してドキュメントとして表示してくれるだけでも結構ありがたいです。
一方で、コンポーネントそのものというよりは、細かいロジックを含むカスタムフックの振る舞いを記述するにはかなり向いていると思います。特に再利用されることの多いカスタムフックでは、しっかりドキュメントを書けばそれだけ後の開発が楽になると思います。また、コンポーネントではない関数について記述する分にも、こちらはTypeDoc本来の想定された使い方になるので十分使いやすいかと思います。
コンポーネントはStoryBook、コンポーネント以外はTypeDocに記述するようにして書き分けるというのが案外良いかもしれません。
最後に注意点ですが、exportされている関数・クラス等をもとにドキュメントを生成するという性質上、VueやSvelteのようなファイル内でコンポーネントを明示的にexportしないフレームワークでは、そもそもドキュメントの生成自体ができません。SvelteKitのサンプルアプリケーションからドキュメント生成を試してみましたが、一部のexportしているもののみドキュメントを生成し、肝心のコンポーネントについては何も出力されませんでした。この辺り、やはりそもそもフロントエンドアプリケーション向けに特化して作られたツールではないというあたりが大きい気がします。
ドキュメントを生成してみるという点では非常に楽に始めることができるため、一度自身のアプリケーションで生成してみて、使い勝手がどうか試してみるというのが良いかもしれません。