🥑

Biome で特定のディレクトリ内のモジュールの import を制限する

2025/01/30に公開

特定のディレクトリからモジュールの import を制限したい

TypeScriptのプロジェクトで、アーキテクチャや依存関係の整合性を保つために、あるディレクトリから別のディレクトリへの import を制限したいことがあります。

具体的には下記のようなケース

  • foo ディレクトリ配下のコードでは、bar ディレクトリ配下のモジュールを使ってはいけない
  • ただし、bar ディレクトリ配下から、foo ディレクトリ配下のモジュールは使ってもよい

ESLint での制限

ESLint であれば no-restricted-imports という便利なルールがあり、下記のようにpatternsの指定で "bar/*" を一括で制限できます。

[
    {
        files: ["foo/**/*.{ts,tsx}"],
        rules: { "no-restricted-imports": ["error", { patterns: ["bar/*"] }] },
    }
]

こうすることで、foo ディレクトリ内で bar/* を import している箇所があればエラーとなります。

しかし、Biome の noRestrictedImports では、限定的な機能しか提供しておらず、 patterns の指定をサポートしていません。

biome search を使った疑似的なチェック

Biome v2 以降では、プラグイン機能の追加が予定されています。v2はまだリリースされていませんが、現在リリースされている v1.9 ではプラグイン追加にさきがけて、ASTを解析できる GritQL の検索機能を使った biome search が実装されました。(2025年1月現在の最新は v1.9.4)

https://biomejs.dev/blog/biome-v1-9/#search-command

biome search で該当の import を探す

入力例

biome search '`"@bar/$modules"`' ./src/foo

出力例

./src/foo/index.tsx:4:41 search ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  4 │ import { Baz } from "@bar/baz";

Searched 4 files in 1122µs. Found 1 match.

foo ディレクトリ内で bar を import している箇所のファイルパスと該当行が表示されているので、これを使って疑似的なチェックを行います。
ちなみに、該当する箇所が見つからない場合は Found 0 matches. と表示されます。

npm script で実行する

手動で都度実行するのは面倒なので、CI で実行しやすいように npm script に追加してみます。

package.json の scripts に追加

{
  "scripts": {
    "lint:imports": "OUTPUT=$(biome search '`\"@bar/$modules\"`' ./src/foo) && echo \"$OUTPUT\" && (echo \"$OUTPUT\" | grep -q 'Found 0 matches.' && exit 0 || (echo 'ERROR: Forbidden imports detected in ./src/foo!' && exit 1))"
  }
}

処理の流れは下記の通り

  • biome search の結果をシェル変数 OUTPUT に保存
  • Found 0 matches. が含まれるかを grep -q でチェック
  • 含まれていれば違反なし ⇒ exit 0
  • 含まれなければ違反あり ⇒ exit 1(エラーで終了)

実際に npm run lint:imports を実行すると、foo ディレクトリから bar/* を import していればエラーとなり、見つからなければ正常終了します。
CI でもこのコマンドを使って制限できそうですね。

まとめ

一応、Biome でも特定のディレクトリ内のモジュールの import を制限する方法はありそうでした。
ただし、ESLint の no-restricted-imports に比べると手間がかかりますし、柔軟性も正確性も低いです。
現在の実装では、コメントの場合も誤検出されてしまいますし、行単位の ignore も難しいです。
多くの場合では ESLint の no-restricted-imports を使うほうが確実だと思います。
将来的に Biome でも正式に対応できると願いながらの一時的な回避策として、ご参考まで!

参考

https://biomejs.dev/linter/rules/no-restricted-imports/
https://biomejs.dev/reference/gritql/
https://eslint.org/docs/latest/rules/no-restricted-imports

GitHubで編集を提案

Discussion