fix-knip-unused-exports について
Knipの「Unused exports」警告を解消するための開発スキルです。問題を分類し、エクスポートの削除、barrelからの再エクスポート除去、コードの抽出などを案内し、パッケージ間の参照やlintルールも考慮します。
主な機能
- 01未使用エクスポートの特定と分類
- 02違反の種類に応じた修正方針の選択
- 03テスト専用コードの新規ファイルへの抽出
- 04barrel再エクスポートとパッケージサブパスの確認
- 05Knip、型、テストの検証
活用シーン
- 01`npm run knip`が報告する未使用エクスポートの修正
- 02テストからのみ参照される関数やクラスの整理
- 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-exportlint ルールが適用されている場合、ファイル名はそのエクスポート名に合わせる必要があります:myFunction.ts - 関数のエクスポートが2つ以上ある場合、ファイル名は自由です
d) 対応する名前のテストファイルはありますか?
bar.tsを残してbar.test.tsが存在する場合、プロジェクトでtest-imports-sourceルールが適用されていれば、テストファイルから引き続き./barの何かをインポートする必要がありますbar.tsを削除する場合(すべて切り出す場合)、通常、このルールは対応するソースファイルが存在するときにのみ適用されます
e) 循環依存のリスクはありますか?
- インポートの依存関係を図にします:新しいファイル → 元のファイル → 新しいファイルは循環しています
- 修正方法:共有する依存関係を新しいファイルまたは3つ目のファイルに移します
f) 定数をエクスポートしますか?
- プロジェクトで
constants-file-organizationlint ルールが適用されている場合、エクスポートする定数は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')、バレル再エクスポートを削除した後に、代替のインポートパスを用意する必要があります。
-
ソースパッケージの
package.jsonにサブパスエクスポートを追加します。{ "exports": { "./feature": "./src/feature/index.ts", "./feature/doSomething": "./src/feature/doSomething.ts" } } -
新しいサブパスからインポートするようにテストを更新します。
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以外のファイルから定数をエクスポートしないでください