TypeScriptでpackage privateを実現する

:::note
## 結論 🧑‍🏫
### 業務に導入する場合
コミュニティの規模を考えて、[dependency-cruiser](https://github.com/sverweij/dependency-cruiser)で実現する方が妥当だと思いました。
[npmtrends](https://npmtrends.com/dependency-cruiser-vs-eslint-plugin-import-access-vs-eslint-plugin-strict-dependencies)
| ライブラリ | 2024/3/10週 Download数 |
| ---- | ---- |
| dependency-cruiser | 310,506 |
| eslint-plugin-strict-dependencies | 18,340 |
| eslint-plugin-import-access | 18,715 |

### 個人開発
~~[eslint-plugin-strict-dependencies](https://github.com/knowledge-work/eslint-plugin-strict-dependencies)が以下の理由で妥当だと思いました。~~
[eslint-plugin-import-access](https://github.com/uhyo/eslint-plugin-import-access)が以下の理由で妥当だと思いました。
- エディタ上で不正なimportをした場合にすぐ検知される
- 設定が簡単（`defaultImportability: 'package'`）

:::

# 1. 背景 🖼️
最近の業務ではfeaturesディレクトリで開発しています。
TypeScirptではexport宣言した変数や関数は全てのファイルからimportできてしまいます。

これにpackage-privateの機能を組み合わせれば外部のディレクトリからimportを制限できたり、モジュール管理がしやすくなると思い調査しました。

# 2. package privateを実現する方法
## 2.1. [dependency-cruiser](https://github.com/sverweij/dependency-cruiser) 🚢

以前こちらの「4.2 package private のような設計を作る」で紹介しましたのでご覧ください。

https://qiita.com/yamatai12/items/fce4104e273981f78807#42-package-private-%E3%81%AE%E3%82%88%E3%81%86%E3%81%AA%E8%A8%AD%E8%A8%88%E3%82%92%E4%BD%9C%E3%82%8B

### 使い方
例：_から始まるファイルは同階層に置かれたファイルからのみimport可能

fromがimport(require)する側で、toがexportする側です。
pathがルールを適用するファイルパスで、pathNotが適用されないファイルパスです。

```.dependency-cruiser.js
module.exports = {
  forbidden: [
    {
      name: `'_'から始まるファイルは同階層に置かれたファイルからのみimport可能`,
      severity: 'error',
      from: { path: ['(.*)\\/.*\\.ts$'], pathNot: ['.*\\.spec\\.ts$'] },
      to: {
        path: ['.*\\/_\\w+.ts$'],
        pathNot: ['$1\\/_\\w+.ts$'],
      },
    },
]
```

## 2.2. [eslint-plugin-import-access](https://github.com/uhyo/eslint-plugin-import-access) 🚨

[uhyo](https://github.com/uhyo)さんが作られたライブラリです。

### 使い方
export宣言されているものに[JSDoc](https://jsdoc.app/) で```@package```とアノテートする

```ts
/**
 * @package
 */
export const fooPackageVariable = "I am package-private export";
```

こちらで使い方を取り上げてますので良かったらご覧ください。

https://qiita.com/yamatai12/items/b91f785ecd3091833c01

# 2.3. [eslint-plugin-strict-dependencies](https://github.com/knowledge-work/eslint-plugin-strict-dependencies) 🚨

ナレッジワークさんが作られたライブラリです

こちらで使い方を取り上げてますので良かったらご覧ください。

https://qiita.com/yamatai12/items/90d61e36ee0013556937

### 使い方
src/domains/sample 配下のpackage privateを実現する例です。
```ts
'strict-dependencies/strict-dependencies': [
      'error',
      [
        {
          module: 'src/domains/sample',
          allowReferenceFrom: ['src/domains/sample'],
        },
      ],
    ],
```

# 3. 3つのライブラリを比較する 🤔

業務に導入する場合と個人開発で分けました。

## 業務に導入する場合
dependency-cruiserが妥当と思いました。

勿論以下のデメリットはあります。
* 設定ファイルに慣れる必要があります
* コマンド実行するまでは意図しない依存関係を検知できないです

### 理由
コミュニティの規模です。

https://npmtrends.com/dependency-cruiser-vs-eslint-plugin-import-access-vs-eslint-plugin-strict-dependencies

| ライブラリ | 2024/3/10週 Download数 |
| ---- | ---- |
| dependency-cruiser | 310,506 |
| eslint-plugin-strict-dependencies | 18,340 |
| eslint-plugin-import-access | 18,715 |

## 個人開発
~~[eslint-plugin-strict-dependencies](https://github.com/knowledge-work/eslint-plugin-strict-dependencies)が以下の理由で妥当だと思いました。~~
[eslint-plugin-import-access](https://github.com/uhyo/eslint-plugin-import-access)が以下の理由で妥当だと考えました。

### 理由
- エディタ上で不正なimportをした場合にすぐ検知されます
  [dependency-cruiser](https://github.com/sverweij/dependency-cruiser)だとコマンドで実行してから検知されます
  ここまでは[eslint-plugin-import-access](https://github.com/uhyo/eslint-plugin-import-access)も同様のメリットです

- 設定が簡単
  - eslintの設定ファイルでディレクトリごとの依存関係を丸々指定できます
- [eslint-plugin-import-access](https://github.com/uhyo/eslint-plugin-import-access)は~~package-privateに特化しています~~
    ~~しかし、ディレクトリごとカプセル化したい場合にはやや手間かなと思いました~~
    ~~各ファイル、各export宣言の箇所にコメントをつけることを忘れずにする必要があります~~
    `defaultImportability: 'package'`により、package-privateをデフォルトで適用してくれるので設定がとても楽です
    
今後の[eslint-plugin-strict-dependencies](https://github.com/knowledge-work/eslint-plugin-strict-dependencies)、[eslint-plugin-import-access](https://github.com/uhyo/eslint-plugin-import-access)の更なる普及を期待します！

# 4. 参考 ✨
- [EsLintのルールを幾つ知っていますか？](https://qiita.com/yamatai12/items/ebde3b4e92c870227712#6-eslint-plugin-import-access)
- [eslint-plugin-import-accessではじめるディレクトリ単位カプセル化](https://zenn.dev/uhyo/articles/eslint-plugin-import-access)
- [TypeScriptプロジェクトでディレクトリ単位のカプセル化をする](https://tech.iimon.co.jp/entry/2023/12/07/140000)
- [React+TSプロジェクトで便利だったLint/Format設定紹介](https://zenn.dev/knowledgework/articles/0994f518015c04#%E4%BE%9D%E5%AD%98%E9%96%A2%E4%BF%82%E3%81%AE%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%EF%BC%88strict-dependencies%EF%BC%89)
- [eslint why does my plugin show Definition for rule was not found?](https://stackoverflow.com/questions/56404119/eslint-why-does-my-plugin-show-definition-for-rule-was-not-found)
- [Reactのディレクトリ構造パターン例](https://www.nitaking.dev/react-directory-structure-2021/)
- [【Marp】ページの表示を (現在ページ)／(総ページ数) にしたい](https://briboo-pc.hatenablog.jp/entry/2023/10/28/%E3%80%90Marp%E3%80%91%E3%83%9A%E3%83%BC%E3%82%B8%E3%81%AE%E8%A1%A8%E7%A4%BA%E3%82%92_%28%E7%8F%BE%E5%9C%A8%E3%83%9A%E3%83%BC%E3%82%B8%29%EF%BC%8F%28%E7%B7%8F%E3%83%9A%E3%83%BC%E3%82%B8%E6%95%B0%29_)

# 5. 最後に 💪
- package-privateを実現すれば、依存関係を整理しやすくしてデグレリスク解消や、開発効率の向上につながるのでおすすめです
- コメントで助言いただけると助かります😂

本記事を読んで頂き、ありがとうございました。
いいねいただけると記事執筆の励みになりますので、参考になったと思われた方は是非よろしくお願い致します🙏