TypeDoc v20 でファイル分割されたモジュールをいい感じにまとめて表示する

# tl;dr

[typedoc-plugin-merge-modules](https://github.com/krisztianb/typedoc-plugin-merge-modules) ってプラグインにモジュールをいい感じでまとめる [Pull Request](https://github.com/krisztianb/typedoc-plugin-merge-modules/pull/2) を送ったら採用されたので使ってください。[利用方法のサンプルレポジトリ](https://github.com/beijaflor/typedoc-plugin-merge-modules-example)も用意しました

```sh
yarn add --dev typedoc typedoc-plugin-merge-modules
yarn typedoc
```
```js:typedoc.js
var { sync } = require("glob");
const { resolve } = require("path");

module.exports = {
  entryPoints: sync(resolve("src/**/*{.js,.ts}")),
  out: "docs/",
  categorizeByGroup: true,
  mergeModulesMergeMode: "module", // ←　これを追加してください
};
```

# 標準のモジュール名解決

TypeDoc のモジュール名解決は全自動でやってくれるんですが、かなり原始的で、ファイルごとにモジュールとして認識されてしまいます。これは、フォルダ内に １関数1ファイルで実装して `index.ts` で一括 re-export とかしている場合に非常に不便です

```ts:src/module/function!.ts
export function module2functionA(): void {
  console.log("do nothing");
}
```
```ts:src/module/functionB.ts
export function moduleFunctionB(): void {
  console.log("do nothing");
}
```
```ts:src/module/index.ts
/**
 * module with re-export index with multiple files with single function export
 * @packageDocumentation
 * @module module
 */
export * from "./functionA";
export * from "./functionB";
```

↓↓↓

```text:typedoc.output
module
module/functionA
module/functionB
```

- [この設定のブランチ](https://github.com/beijaflor/typedoc-plugin-merge-modules-example/tree/v20)
- [TypeDoc での出力結果](https://github.com/beijaflor/typedoc-plugin-merge-modules-example/raw/v20-with-plugin/resources/v20.png)

# `@module` ディレクティブ

TypeDoc には [`@module` というディレクティブ](https://typedoc.org/guides/doccomments/#%40module) が定義されており、該当ファイルのモジュール名を明示的に指定できます
しかし、残念なことにモジュール名の重複は想定しておらず、以下のように同じモジュール名が並びリンク先がひとつになり、情報が欠けてしまいます

```text:typedoc.output
module
module
module
```

- [この設定のブランチ](https://github.com/beijaflor/typedoc-plugin-merge-modules-example/tree/v20-bad)
- [TypeDoc での出力結果](https://github.com/beijaflor/typedoc-plugin-merge-modules-example/raw/v20-with-plugin/resources/v20-bad.png)


# プラグインを求めて

v19 系では [`typedoc-plugin-external-module-name`](https://github.com/christopherthielen/typedoc-plugin-external-module-name) というプラグインがいい感じにしてくれたようなのですが、 2021-06-08 時点では v20　に対応しておらず、すなわちそれは TypeScript 4 にも未対応ということで哀しい気持ちになってしまいます

> FWIW - this plugin is likely partially obsolete with 0.20.
> [AssertionError: "Tried to access Context.program when not converting a source file." · Issue #587 · christopherthielen/typedoc-plugin-external-module-name](https://github.com/christopherthielen/typedoc-plugin-external-module-name/issues/587)

そもそも `External Module Name` プラグインはディレクトリ構造とモジュール名の対応付けを行うためのプラグインで、その機能が v20 で標準に組み込まれたため、メンテナンスするモチベーションがなくなるのはわかります。でも違う、ぼくが欲しいのはモジュールの統合機能であってそっちだけでちゃんと動いて欲しかった

v19 で同じソースコードを出力するとそれなりにいい感じになります

- [この設定のブランチ](https://github.com/beijaflor/typedoc-plugin-merge-modules-example/tree/v19)
- [TypeDoc での出力結果](https://github.com/beijaflor/typedoc-plugin-merge-modules-example/raw/v20-with-plugin/resources/v19.png)

# 無いなら作ればいい

他にいいプラグインはないかと探してみたんですがそれっぽいのは見つからず、唯一見つけたのが [typedoc-plugin-merge-modules](https://github.com/krisztianb/typedoc-plugin-merge-modules) で、このプラグインは元々、モジュール分割を完全にフラットにする、というものでした

雑にソースコードを読んだ感じいけるのではないか、、、ということで、サクッとオプションを追加して PR を投げてみたところ、すんなりとマージされて公開されました :clap: :clap_tone1: :clap_tone4: 

プラグインをインストールすると `typedoc` コマンド実行時に自動で読み込まれるようになります

```
yarn add --dev typedoc-plugin-merge-modules
```

標準ではすべての `export` がフラットになるため、 `mergeModulesMergeMode` オプションに `"module"` を設定します

```js:typedoc.js
var { sync } = require("glob");
const { resolve } = require("path");

module.exports = {
  entryPoints: sync(resolve("src/**/*{.js,.ts}")),
  out: "docs/",
  categorizeByGroup: true,
  mergeModulesMergeMode: "module", // ←　これを追加してください
};
```

これで `@module` ディレクティブで指定されたモジュールの重複をまとめてひとつにしてくれます :clap: :clap_tone1: :clap_tone4:

- [この設定のブランチ](https://github.com/beijaflor/typedoc-plugin-merge-modules-example/tree/v20-with-plugin)
- [TypeDoc での出力結果](https://github.com/beijaflor/typedoc-plugin-merge-modules-example/raw/v20-with-plugin/resources/v20-with-plugin.png)

# サンプルレポジトリについて

https://github.com/beijaflor/typedoc-plugin-merge-modules-example

よくある構成ごとにパターンを用意しています

モジュール名 | 構成
--- | ---
module1 | module/index.ts の中に複数の export
module2 | module/index.ts は re-export 。関数の実態は別ファイル
module3 | フォルダ分けせずに module.ts の中に複数の export
module4 | module2 のパターンにサブモジュールを追加 <sup>[※](#module4-note)</sup>

> <aside id="module4-note">※　module3/index.ts で module4/submodule を re-export しているがドキュメント上は影響なし</aside>

各設定はブランチで分けています

ブランチ名 | 内容
--- | ---
[v20-with-plugin](https://github.com/beijaflor/typedoc-plugin-merge-modules-example/tree/v20-with-plugin)（デフォルト） | `typedoc-plugin-merge-modules` を導入したもの
[v20](https://github.com/beijaflor/typedoc-plugin-merge-modules-example/tree/v20) | v20 の標準
[20-bad](https://github.com/beijaflor/typedoc-plugin-merge-modules-example/tree/v20-bad) | v20 に `@module` ディレクティブを指定したもの
[v19](https://github.com/beijaflor/typedoc-plugin-merge-modules-example/tree/v19) | v19 に `typedoc-plugin-external-module-name` を導入したもの