Back

Knip で未使用ファイルと依存関係を削除する

Knip で未使用ファイルと依存関係を削除する

すべての JavaScript および TypeScript プロジェクトは、時間とともに不要なものが蓄積していきます。ちょっとした実験のためにインストールした依存関係、リファクタリング後に残ったユーティリティファイル、誰もインポートしなくなったエクスポート関数などです。個々は無害に見えますが、まとめて見ると、ビルドを遅くし、node_modules を肥大化させ、セキュリティ上のノイズを生み出し、コードベースのナビゲーションを困難にします。

従来のアプローチは手動監査でした。パッケージ名を grep し、ファイル参照を検索し、package.json を手作業でクロスチェックする。これは機能しますが、スケールしません。ここで登場するのが Knip です。

重要なポイント

  • 未使用の依存関係やデッドコードは、ビルド時間を増やし、バンドルを肥大化させ、セキュリティやライセンス上のノイズを生み出します。
  • Knip はプロジェクトレベルのリンターとしてリポジトリ全体を解析し、未使用のファイル、エクスポート、依存関係、未登録のインポートをレポートします。
  • --fix フラグで自動クリーンアップが適用され、--allow-remove-files を併用すると未使用ファイルの削除まで実行できます。
  • CI で Knip を実行すれば、デッドコードの蓄積を防ぐことができ、ESLint やフォーマッターを補完します。

なぜ未使用の依存関係とデッドコードを修正する価値があるのか

package.json 内の未使用パッケージは、単なる見た目のノイズではありません。次のような影響があります。

  • インストール時間と node_modules のディスク使用量を増やす
  • バンドラーが適切に tree-shake しなければ、本番バンドルに混入する可能性がある
  • Dependabot などのツールで誤ったセキュリティアラートを発生させる
  • 制限的なライセンスが技術的にプロジェクトに適用される可能性がある
  • 同じ問題を抱える推移的依存関係を引き連れてくる

デッドコードにも同様のコストがあります。未使用のエクスポートや参照されていないファイルは、コードベースが実際に何をしているのかを理解しにくくし、それらを処理せざるを得ないリンターや型チェッカーの動作を遅くします。

Knip の違い

depcheckts-prune などのツールはこの問題の一部に対処していましたが、どちらも 2025 年にアーカイブされました。Knip は、依存関係、エクスポート、ファイル解析を 1 つの活発にメンテナンスされているツールに統合した、モダンな代替として登場しました。

Knip は、プロジェクトレベルのリンターと考えるとよいでしょう。ESLint が個々のファイルをチェックするのに対し、Knip はリポジトリ全体、つまりエントリポイント、インポートグラフ、エクスポート、package.json をまとめて解析します。未使用のファイル、未使用のエクスポート、未使用の依存関係、そしてインポートされているが package.json に記載されていないパッケージをレポートします。

Knip は npm、pnpm、Yarn、Bun に対応しており、実行には Node.js 20.19+ または Bun が必要です。

5 分で始める

次のコマンドでプロジェクトに Knip を初期化します。

npm init @knip/config

これにより、適切なデフォルト設定を含む knip.json 設定ファイルが作成されます。続いて解析を実行します。

npx knip

Knip は問題の種類ごとにグループ化されたレポートを出力します。未使用ファイル、未使用エクスポート、未使用依存関係、未登録依存関係などです。古いプロジェクトでの初回実行では、リストが長くなることがあります。これは想定どおりです。

修正を安全に適用する

レポートを確認して内容が正確であると判断したら、Knip は確認可能な自動修正を適用できます。

npx knip --fix

これにより、未使用エクスポートから export キーワードを削除し、package.json をクリーンアップし、再エクスポートや TypeScript の enum メンバーも処理します。未使用ファイルも削除するには次のようにします。

npx knip --fix --allow-remove-files

コミット前に必ず Git で変更内容を確認してください。 Knip は設計上保守的ですが、自動修正は実際のファイルを変更します。ステージング前に git diff をさっと確認するのは良い習慣です。

変更を小さく、レビューしやすく保つために、特定の問題タイプのみを対象とすることもできます。

npx knip --fix-type dependencies
npx knip --fix-type exports,types

Knip に少し手助けが必要なとき

Knip は完璧なブラックボックスではありません。いくつかの状況では手動設定が必要です。

  • 動的インポート(import(someVariable))は静的に解決できない
  • フレームワーク規約(Next.js のページファイル、Vite の仮想モジュールなど)はプラグイン設定や無視ルールが必要になる場合がある
  • 生成ファイルは通常、解析から除外すべき
  • モノレポは標準で問題なく動作するが、ワークスペース間の参照には明示的な設定が必要になることがある

誤検知が出る場合は、解析セクション全体を無効化するのではなく、knip.jsonignore または ignoreDependencies エントリを追加してください。

クリーンアップを習慣にする

Knip の最も効果的な使い方は、一度きりの監査ではなく、定期的に実行することです。リンターや型チェッカーと並べて CI パイプラインに追加しましょう。

npx knip --reporter compact

すべてのプルリクエストでクリーンなレポートが得られれば、そもそもデッドコードが蓄積するのを防げます。Knip を ESLint(ファイル内の未使用変数用)とお好みのフォーマッターと組み合わせれば、手作業の調査なしで JavaScript および TypeScript プロジェクトを無駄なく保つ、堅実で自動化されたメンテナンスループが完成します。

まとめ

Knip は、未使用ファイル、エクスポート、依存関係を見つけ出すという面倒な作業を、再現可能で自動化されたステップに変えます。クリーンアップを年に一度の大掃除ではなく、通常のワークフローの一部として扱うことで、プロジェクトを無駄なく、ビルドを高速に、依存関係を最小限に保てます。まずは npx knip を 1 回実行し、結果を CI に組み込み、これまで手作業で行っていた調査をツールに任せましょう。

よくある質問

はい。Knip はデフォルトでは問題をレポートするだけで、--fix フラグを渡さない限り変更を加えません。まず実行してレポートを確認し、その後 --fix-type で一度に 1 つのカテゴリを対象にして段階的に修正を適用してください。特に --allow-remove-files を使用する際は、コミット前に必ず Git diff を確認してください。

Knip は両ツールの責務を 1 つの活発にメンテナンスされたパッケージに統合しています。depcheck は依存関係に、ts-prune は未使用の TypeScript エクスポートに焦点を当てていますが、Knip はワークスペース全体でファイル、エクスポート、依存関係をまとめて解析し、モダンなプロジェクトに対してより正確な結果を生成します。

誤検知は通常、Knip が静的に解決できない動的インポート、フレームワーク規約、または生成ファイルから発生します。お使いのフレームワーク用の Knip プラグインが存在するか確認し、残りのケースについては knip.json に ignore または ignoreDependencies エントリを追加してください。カバレッジを維持するため、解析セクション全体を無効化することは避けてください。

はい、そしてそこで最も価値を発揮します。CI パイプラインに npx knip --reporter compact を追加すると、すべてのプルリクエストで新しいデッドコードがチェックされます。ESLint やフォーマッターと組み合わせることで、リリース間で未使用ファイルや依存関係が蓄積するのを防ぐ自動メンテナンスループが完成します。

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay