Skip to content

Instantly share code, notes, and snippets.

@umeyuki
Created March 22, 2026 02:10
Show Gist options
  • Select an option

  • Save umeyuki/dcf633c8ef6c1521bb217aa2e5dfc4fb to your computer and use it in GitHub Desktop.

Select an option

Save umeyuki/dcf633c8ef6c1521bb217aa2e5dfc4fb to your computer and use it in GitHub Desktop.
Knip × SvelteKit Monorepo: 実践ガイド — pre-commit 700→0 warnings, 10,700+ lines deleted

Knip × SvelteKit Monorepo: 実践ガイド

背景

pnpmモノリポジトリ (SvelteKit + Hono + 共有パッケージ) で knip v5.71.0 を lefthook pre-commit に導入したところ、~700件の警告 (48 unused files, 277 unused exports, 349 unused types, 7 duplicate exports, 14 unused deps) が発生。

調査の結果、SvelteKit特有の問題実際のデッドコードが混在していることが判明した。

問題の分類

1. SvelteKit特有の誤検知 (解決困難)

カテゴリ 原因 影響
unused exports knipのSvelte compilerがregexベースで、<script>内のimport文のみ抽出。Svelte 5のrunes ($state, $derived) やtemplate内の参照を追跡できない 大量の誤検知
unused types TypeScript型のre-exportチェーンが途切れる。バレルexport経由の型が「未使用」と判定される 同上
barrel exports index.ts でre-exportしたコンポーネントが、消費側では直接importしている場合にbarrelが「未使用ファイル」と判定 ファイル誤検知

2. 設定不足 (修正可能)

問題 原因 修正
"No Svelte config file found" 警告 ルートの package.json にsvelteがあるためknipがルートでSvelteプラグインを起動 svelte: false をルートワークスペースに追加
ルートのpnpmfile.cjs検出 knipのデフォルトプロジェクトスキャン ignore: ['pnpmfile.cjs']
.skip ディレクトリのunresolved imports 無効化されたコードのimportが未解決 ignore: ['**/*.skip/**']

3. 実際のデッドコード (削除すべき)

大規模リファクタリング (@flops/uiパッケージ抽出、Svelte 5移行等) の残骸:

  • @flops/uiに移行済みのローカルコピー: PositionBadge, CardSelector, HandSelector, RangeMiniGrid
  • 実験的/デバッグコンポーネント: DebugPanel, ChipSourceDebugPanel, PwaInstallButton等
  • 未使用のコンポーネントチェーン: GameSettingsPanel → GameTypeSelector → ToggleSwitch/PlayerCountSelector
  • 空のbarrel export: showdown/index.ts, equity-standalone/index.ts
  • リファクタリングの旧名alias: createHoldemUIAdapter → createUIAdapter, isEngineGameState → isExtendedGameState等

最終的なknip設定

import type { KnipConfig } from 'knip';

export default {
  ignoreUnresolved: [/^\.\/\$types$/],
  ignore: ['pnpmfile.cjs', '**/*.skip/**'],
  ignoreBinaries: ['gh'],
  ignoreDependencies: ['power-assert', 'vitest'],
  workspaces: {
    '.': { entry: ['.claude/scripts/*.{js,bash}'], svelte: false },
    'packages/*': {},
    'apps/backend': { entry: ['src/workers/api.ts', 'scripts/*.ts'] },
    'apps/hand': {
      entry: ['scripts/*.{ts,mjs,js}'],
      project: [
        'src/**/*.{js,ts,svelte}',
        'tests/**/*.{js,ts,svelte}',
        'e2e/**/*.?(c|m)[jt]s?(x)',
        'scripts/*.{ts,mjs,js}',
      ],
      paths: {
        $lib: ['src/lib'],
        '$lib/*': ['src/lib/*'],
        $app: ['node_modules/@sveltejs/kit/src/runtime/app'],
        '$app/*': ['node_modules/@sveltejs/kit/src/runtime/app/*'],
      },
      svelte: { config: 'svelte.config.js' },
    },
    'apps/admin': {
      project: ['src/**/*.{js,ts,svelte}'],
      paths: {
        $lib: ['src/lib'],
        '$lib/*': ['src/lib/*'],
        $app: ['node_modules/@sveltejs/kit/src/runtime/app'],
        '$app/*': ['node_modules/@sveltejs/kit/src/runtime/app/*'],
      },
      svelte: { config: 'svelte.config.js' },
    },
  },
} satisfies KnipConfig;

lefthook設定

pre-commit:
  parallel: true
  jobs:
    - name: biome
      run: >-
        git diff --cached --name-only --diff-filter=ACM
        -- '*.ts' '*.js' '*.mjs' '*.cjs' '*.svelte'
        | xargs -r pnpm biome check --no-errors-on-unmatched
    - name: knip
      run: pnpm knip --no-progress --exclude exports,types

ポイント

  1. --exclude exports,types: SvelteKit環境では exports/types の誤検知が多すぎるため、pre-commitでは除外。files, dependencies, unresolved, duplicates のみチェック
  2. --diff-filter=ACM: 削除ファイルをbiomeに渡さない(IO errorになるため)
  3. xargs -r: ファイルがない場合にbiomeを実行しない(空引数防止)

学んだこと

knipのSvelte compiler

knipのビルトインSvelteコンパイラ (node_modules/knip/dist/compilers/svelte.js) は:

  • <script> タグの中身をregexで抽出
  • import 文のみを取り出して依存グラフに追加
  • テンプレート内のコンポーネント参照は追跡しない

カスタムコンパイラ (svelte/compiler を使用) も試したが、Svelte 5のコンパイラはimportを内部参照に変換するため、knipが追跡できなくなる。ビルトインコンパイラの方がマシ

knipのSvelteKitプラグイン

node_modules/knip/dist/plugins/svelte/index.js が自動で以下をentry pointとして追加:

  • src/routes/**/+{page,server,page.server,error,layout,layout.server}{,@*}.{js,ts,svelte}
  • src/hooks.{server,client}.{js,ts}
  • src/params/*.{js,ts}

手動でroute entriesを追加する必要はない

svelte: false の効果

ルートワークスペースに設定すると、knipのSvelteプラグインがルートで起動しなくなる。ただし @sveltejs/kit 自体のconsole.warn ("No Svelte config file found") は抑制されない(knipの警告カウントには影響なし)。

paths設定

$lib$app のパスエイリアスは knip の paths 設定で解決可能。SvelteKitプラグインが $app を自動提供するが、明示的に設定した方が確実。

数値成果

メトリクス Before After
Unused files 48 0
Unused deps 14 0
Unresolved imports 4 0
Duplicate exports 7 0
pre-commit warnings ~700 0
削除行数 - -10,700+
削除ファイル数 - 42

他プロジェクトへの適用チェックリスト

  1. ルートに svelte: false を設定
  2. 各ワークスペースの paths$lib, $app を設定
  3. svelte: { config: 'svelte.config.js' } を各SvelteKitアプリに設定
  4. ignore.skip ディレクトリや設定ファイルを除外
  5. ignoreBinaries でCLIツール (gh等) を除外
  6. lefthookの --exclude exports,types でpre-commitの誤検知を防止
  7. lefthookの --diff-filter=ACM で削除ファイルをbiomeに渡さない
  8. 定期的に pnpm knip (全チェック) を手動実行してデッドコードを棚卸し
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment