mcpField
F
fix-knip-unused-exports

fix-knip-unused-exports

github.com/factory-ai開発117140 回インストール4 日前に更新
リポジトリ

fix-knip-unused-exports について

Knipの「Unused exports」警告を解消するための開発スキルです。問題を分類し、エクスポートの削除、barrelからの再エクスポート除去、コードの抽出などを案内し、パッケージ間の参照やlintルールも考慮します。

主な機能

  1. 01未使用エクスポートの特定と分類
  2. 02違反の種類に応じた修正方針の選択
  3. 03テスト専用コードの新規ファイルへの抽出
  4. 04barrel再エクスポートとパッケージサブパスの確認
  5. 05Knip、型、テストの検証

活用シーン

  1. 01`npm run knip`が報告する未使用エクスポートの修正
  2. 02テストからのみ参照される関数やクラスの整理
  3. 03未使用の`index.ts`再エクスポートの削除

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/factory-ai/factory-plugins --skill fix-knip-unused-exports

インストール後、Claude Code で/fix-knip-unused-exportsを入力して読み込まれたか確認します。

詳細

Knip の未使用エクスポートを修正する

Knip の「Unused exports」違反を修正します。違反にはいくつかのカテゴリがあり、それぞれ修正方法が異なります。

使用するタイミング

  • npm run knip で「Unused exports」が報告されたとき

使用しないタイミング

  • 別のファイルにあるテスト以外の本番コードがそのエクスポートを使用している場合 — ほかに問題があります

ワークフロー

1. 違反を特定する
npm run knip

出力例:

Unused exports (3)
::error file=packages/foo/src/bar.ts,line=42,title=Unused exports::myFunction
2. 各違反を分類する

フラグが付いたエクスポートごとに、(パッケージ内だけでなく)リポジトリ全体を grep します。

rg "myFunction"

該当するカテゴリを特定します。

カテゴリ呼び出し元修正方法
テスト専用エクスポート同じファイルとテストファイルでのみ使用新しいファイルに切り出す
未使用のバレル再エクスポートindex.ts から再エクスポートされているが、本番コードは相対パスや別のサブパス経由でインポートしているバレルから再エクスポートを削除する
同じファイル内でのみ使用されるエクスポート同じファイル内でのみ使用され、テストやほかのファイルでは使用されていないexport キーワードを削除する
デッドコードどこからも呼び出されていないエクスポートを削除する
本番コードからの参照あり別のファイルにあるテスト以外のコードで使用されているKnip の問題ではありません — さらに調査する

重要:grep するときは、テストファイルを除外して本番コードからの参照を特定します。

rg "myFunction" --glob '!**/*.test.*'

修正:テスト専用エクスポート(新しいファイルに切り出す)

テストからアクセスするためだけにエクスポートされているものの、同じファイル内でも使用されている関数を対象とします。

切り出しの計画

コードを書く前に、次の質問に答えます。

a) 新しいファイルに移すものは何ですか?

  • フラグが付いたエクスポート関数/クラス/定数
  • 依存先となるすべてのプライベートヘルパー関数
  • 依存先となるすべてのプライベート定数/型

b) 残す関数と共有するヘルパーはありますか?

  • ある場合、そのヘルパーを新しいファイルからエクスポートし、元のファイルでインポートする必要があります
  • そのため、新しいファイルには2つ以上のエクスポートが含まれます(どのファイル名とエクスポート名を一致させる lint ルールでも問題ありません)

c) 新しいファイルには、エクスポートされる関数がちょうど1つだけ含まれますか?

  • プロジェクトで filename-match-export lint ルールが適用されている場合、ファイル名はそのエクスポート名に合わせる必要があります:myFunction.ts
  • 関数のエクスポートが2つ以上ある場合、ファイル名は自由です

d) 対応する名前のテストファイルはありますか?

  • bar.ts を残して bar.test.ts が存在する場合、プロジェクトで test-imports-source ルールが適用されていれば、テストファイルから引き続き ./bar の何かをインポートする必要があります
  • bar.ts を削除する場合(すべて切り出す場合)、通常、このルールは対応するソースファイルが存在するときにのみ適用されます

e) 循環依存のリスクはありますか?

  • インポートの依存関係を図にします:新しいファイル → 元のファイル → 新しいファイルは循環しています
  • 修正方法:共有する依存関係を新しいファイルまたは3つ目のファイルに移します

f) 定数をエクスポートしますか?

  • プロジェクトで constants-file-organization lint ルールが適用されている場合、エクスポートする定数は constants.ts という名前のファイルに置く必要があります
  • 切り出す関数が、元のファイルに残るほかの関数も使用する定数に依存している場合、その定数を新しいファイルからエクスポートしないでください。代わりに関数を呼び出し(たとえば、BUDGET[effort] を getBudget(effort) に置き換えます)、別個の constants.ts が必要にならないようにします
切り出しの実行

同じディレクトリに新しいファイルを作成します。

// myFunction.ts (new file)
import { SomeType } from '../types';

function privateHelper(): void { /* ... */ }

export function myFunction(): SomeType {
  return privateHelper();
}

元のファイルを更新し、新しいファイルからインポートします。

// bar.ts (original file, updated)
import { myFunction } from './myFunction';

function otherFunction() {
  const result = myFunction(); // Now imports from new file
}

テストファイルを更新し、新しいファイルからインポートします。

// bar.test.ts (updated)
import { myFunction } from './myFunction';
// If bar.ts still exists, you may need to also import something from './bar'
// to satisfy any test-imports-source rule
連鎖する違反に注意する

切り出し後に、再度 npm run knip を実行します。関数 A が関数 B とともに新しいファイルに切り出され、A が B を呼び出しているものの、B が外部からテストでしか使用されていない場合、Knip は B にもフラグを付けます。A のファイルから B への本番コードでのインポートが成立するように、B も独自のファイルに切り出す必要があります。

例:throwMappedError を最初に mapResponseFailure とともに error-mappers.ts に切り出したとします。throwMappedError がそのファイル内(mapResponseFailure から)でしか呼び出されない場合、引き続きフラグが付けられます。修正方法:throwMappedError.ts に切り出し、error-mappers.ts からのインポートを本番コードでの実際の参照にします。

修正:使用されていないバレル再エクスポート(index.ts から削除)

バレルの index.ts が何かを再エクスポートしていても、本番コードがバレル経由でそれをインポートしていない場合です。次のような場合に発生します。

  • 同じパッケージ内の本番コードが、バレルではなく相対インポート(例:import { x } from './source')を使用している
  • 他のパッケージの本番コードが、バレルではなくサブパスから直接インポートしている(例:@scope/pkg/feature/handlers)
  • 再エクスポートが見込みで追加されたものの、使用されていない
識別方法

テストファイルを除外して grep します。ヒットするのが次のものだけであれば、

  • バレルの index.ts 自体
  • 同じパッケージ内で相対インポートを使用しているソースファイル
  • テストファイル

そのバレル再エクスポートは使用されていません。index.ts から削除するだけです。

パッケージ間のテストインポート

別のパッケージのテストがバレル経由でシンボルをインポートしている場合(例:import { x } from '@scope/pkg/feature')、バレル再エクスポートを削除した後に、代替のインポートパスを用意する必要があります。

  1. ソースパッケージの package.json にサブパスエクスポートを追加します。

    {
      "exports": {
        "./feature": "./src/feature/index.ts",
        "./feature/doSomething": "./src/feature/doSomething.ts"
      }
    }
    
  2. 新しいサブパスからインポートするようにテストを更新します。

    import { doSomething } from '@scope/pkg/feature/doSomething';
    

このパターンは、モノレポで一般的に使われるサブパスエクスポートの規約に従っています。

修正:内部でのみ使用されるエクスポート(エクスポートを解除)

エクスポートが同じファイル内でのみ使用され、他のどこからも(テストからも)インポートされていない場合は、export キーワードを削除するだけです。

// Before
export const MySchema = z.object({ ... });

// After
const MySchema = z.object({ ... });

これは、同じファイル内の他のスキーマを構築するためだけに使用される Zod スキーマでよくあるパターンです。

検証

影響を受けるパッケージで、次のチェックをすべて実行します。

# Knip passes (the whole point)
npm run knip

# Types still compile
npm run typecheck

# Tests still pass
npm run test

# Lint passes (catches filename-match-export, test-imports-source, constants-file-organization, etc.)
npm run lint

パッケージ間インポートがある場合は、インポート元のパッケージも検証します。

関連する Lint ルール

多くの TypeScript モノレポでは、knip に加えてカスタム Lint ルールを適用しています。以下の修正は、プロジェクトで使用しているルールに合わせて調整してください。

filename-match-export(または類似のルール)

ファイルにエクスポートされた関数(React コンポーネントを除く)がちょうど 1 つある場合、ファイル名は関数名と一致している必要があります。

  • loadConfig.ts の export function loadConfig — 合格
  • helpers.ts の export function loadConfig — 不合格
  • helpers.ts にエクスポートが 2 つある場合 — 複数のエクスポートがあるため、このルールは適用されません
test-imports-source(または類似のルール)

foo.test.ts と foo.ts の両方が存在する場合、テストは ./foo からインポートする必要があります。

  • import { x } from './foo' のようなインポートは、このルールに準拠します
  • 通常、index.ts が foo.ts から再エクスポートしていれば、'.' または './index' からのインポートも許可されます
  • foo.ts が削除されている場合、このルールは適用されません
constants-file-organization(または類似のルール)

エクスポートする定数は、constants.ts という名前のファイルで定義する必要があります。

  • 共有定数に依存する関数を抽出する場合、その定数を関数のファイルからエクスポートしないでください
  • 代わりに、定数への直接アクセスを関数呼び出しに置き換えます(例:BUDGET[effort] を getBudget(effort) に変更)
  • または、定数を constants.ts ファイルに移動します
Knip によるエクスポートの追跡方法
  • Knip はテストファイル(**/*.test.*、**/*.spec.*)を無視します
  • knip.json の ignoreIssues は、指定したファイルに対する警告を抑制しますが、ソースのエクスポートを「使用済み」にはしません
  • ignoreIssues が設定された index.ts からのバレル再エクスポート(export { x } from './source')は、ソースのエクスポートの使用としてカウントされません
  • テスト対象外かつ無視対象外のプロジェクトファイルからの実際のインポートのみが、使用としてカウントされます
  • includeEntryExports: true が設定されている場合、エントリーポイントファイルからのエクスポートもチェックされるため、マイグレーションやスクリプトなどのエントリーポイント形式のファイルには、明示的に ignoreIssues を設定する必要がある場合があります
パッケージのサブパスエクスポート

パッケージ間テストで使用されていたバレル再エクスポートを削除する場合は、package.json にサブパスエクスポートを追加します。

{
  "exports": {
    "./feature": "./src/feature/index.ts",
    "./feature/doSomething": "./src/feature/doSomething.ts"
  }
}

してはいけないこと

  • 本当にエントリーポイントのスクリプト(マイグレーション、CLI)でない限り、knip.json の ignoreIssues にファイルを追加しないでください
  • エクスポートを減らすためにすべての関数を1つのファイルにまとめないでください。knip の観点では、同じファイル内でエクスポートを使用しても使用実績としてカウントされません
  • テストで必要な場合は、export キーワードを削除しないでください。テストが失敗します
  • 新しいファイルと元のファイルの間に循環インポートを作成しないでください
  • プロジェクトで constants-file-organization の lint ルールを適用している場合は、constants.ts 以外のファイルから定数をエクスポートしないでください

コメント