コード分析ツール

August 16, 2026 · View on GitHub

プロジェクトでは3つのコード分析ツールを使用して、コード品質とアーキテクチャの健全性を維持している。

Knip(デッドコード検出)

未使用のファイル、エクスポート、依存関係を検出する静的解析ツール。

検出対象

  • 未使用ファイル(どこからもimportされていないモジュール)
  • 未使用エクスポート(exportされているが使われていないシンボル)
  • 未使用依存関係(package.jsonに記載されているが使われていないパッケージ)

設定

設定ファイル: knip.json

モノレポの各ワークスペースごとにエントリーポイントと除外設定を定義:

  • app: Next.jsの規約ファイル(page.tsx, layout.tsx, route.ts, instrumentation.ts等)をエントリーポイントとして自動認識。i18n/request.ts等の内部ファイルはignore対象
  • packages/ui: ui/, forms/, display/, layouts/, providers/, hooks/, utils/配下のファイルがエントリーポイント。Storybookファイル・テストファイルは除外
  • packages/core: 全.tsファイルがエントリーポイント(テストファイルは除外)
  • packages/database: Prisma関連の依存関係をignore

ignoreExportsUsedInFile: trueにより、同一ファイル内で使用されているexportは未使用扱いしない。

コマンド

pnpm knip    # デッドコード検出を実行

jscpd(コード重複分析)

コピー&ペーストされた重複コードを検出するツール。

設定

設定ファイル: .jscpd.json

項目
重複率閾値10%
対象パターン**/*.{ts,tsx}
分析パスapp/src, packages/ui, packages/core

除外対象:

  • node_modules, .next, dist, .storybook-static, coverage
  • テストファイル(*.test.ts
  • 生成ファイル(app/src/generated/**

コマンド

pnpm jscpd          # コンソールに重複レポートを出力
pnpm jscpd:json     # JSON形式のレポートを ./jscpd-report/ に出力
pnpm jscpd:summary  # サマリーを jscpd-summary.txt に出力

CI連携

  • PR時: jscpd.yaml — PRごとに重複率を分析し、結果をPRコメントとして投稿。閾値超過時は警告を表示。レポートはArtifactとして90日間保存
  • 月次レポート: update-reports.yaml — 毎月1日にjscpd-summary.txtを自動更新するPRを作成

dependency-cruiser(依存関係分析)

モジュール間の依存関係を分析し、アーキテクチャルールへの違反を検出するツール。

設定

設定ファイル: .dependency-cruiser.cjs

ドメイン境界ルール(severity: error)

packages/core内の4ドメイン間の相互importを禁止:

ルール名fromto(禁止)
boundary-core-articlespackages/core/articles/books, notes, images
boundary-core-bookspackages/core/books/articles, notes, images
boundary-core-notespackages/core/notes/articles, books, images
boundary-core-imagespackages/core/images/articles, books, notes

各ドメインはshared-kernel経由でのみ共通機能を利用できる。

汎用ルール

ルール名severity検出内容
no-circularwarn循環依存
no-orphanswarn孤立モジュール(どこからも参照されていないファイル)
no-deprecated-corewarn非推奨のNode.jsコアモジュールの使用
not-to-deprecatedwarn非推奨npmパッケージへの依存
no-non-package-jsonerrorpackage.jsonに未記載のnpmパッケージへの依存
not-to-unresolvableerror解決不能なモジュールへの依存
no-duplicate-dep-typeswarndependencies/devDependencies両方に存在するパッケージ
not-to-specerrorテストファイルへの依存(プロダクションコードから)
not-to-dev-deperrordevDependenciesへのプロダクションコードからの依存

コマンド

pnpm deps:check      # 依存関係ルールのチェック(全ルール適用)
pnpm deps:circular   # 循環依存の検出(テキスト出力)
pnpm deps:graph      # 依存関係グラフをmermaidとして出力(dependency-graph.md)

deps:graphはmermaid形式で出力するためGraphviz不要。生成物(dependency-graph.md)はアーキテクチャ図としてコミット管理し、GitHub・VS Code上でネイティブ描画する。月次で自動再生成されるため(CI連携を参照)、ローカルでは構成変更時にpnpm deps:graphで更新する。

グラフは「Clean Architectureの層境界・cross-domain禁止が守られているか」を一目で確認する用途のため、層×ドメイン単位まで粗く集約している:

  • --collapse '<regex>'で集約。core/application-services/infrastructuresはドメイン(articles/books/images/notes等)ごとのノードを残し(cross-domain依存の可視化)、app/common/components/loadersや各パッケージ(ui/database等)は1ノードに集約する。ドメイン内部のファイル詳細は描画しない。
  • --include-only '^(app/src|packages)/...'でグラフ出力のみを絞り込み(ルール検査には不適用)、node_modules等の外部依存とvitest設定・*.stories.*などのノイズノードを除外する。

CI連携

Lint/Format: oxlint主体 + コンテンツESLint、oxfmt(2026-08)

TypeScript/JavaScript は oxlint に一本化した状態を維持し、oxlint が扱わない YAML/JSON/Markdown のみ ESLint で検査する。oxlint 設定は .oxlintrc.json(JSONC)、コンテンツ lint は eslint.config.js、フォーマットは .oxfmtrc.jsonpnpm lint / pnpm lint:fix は両リンターを順に実行する。

役割分担:

  • oxlint: 旧 ESLint の全ルール。typescript-eslint strict+stylistic type-checked(type-aware, tsgolint)、@next/*→native nextjs@vitest/*→native vitesteslint-plugin-regexp/eslint-plugin-storybook(JS plugin)、Prisma raw-SQL ガード(eslint-js/no-restricted-syntax via oxlint-plugin-eslint)。@eslint-react の明示ルールは native へ: use-state→react/hook-use-state、jsx-no-useless-fragment→react/jsx-no-useless-fragment、dom-no-dangerously-set-innerhtml→react/no-danger、no-array-index-key→react/no-array-index-key。set-state-in-effect は eslint-plugin-react-hooks(alias react-hooks-js)。
  • ESLint: eslint-plugin-ymleslint-plugin-jsonc@eslint/markdown で YAML/JSON/JSONC/JSON5/Markdown を検査。ロックファイルと生成物は除外。
  • dependency-cruiser: Clean Architecture 層境界(eslint-plugin-boundaries から移植。oxlint の JS plugin は settings の boundaries/* キーを受け付けず動作不可のため)。.dependency-cruiser.cjsboundary-* ルール(allow-list を deny-list に翻訳、29 本)。
  • oxfmt: format 全般(Prettier互換、タブ・行幅80)+ import 整理(sortImports)+ Tailwind class 並べ替え(sortTailwindcss、cn/clsx/tv)。旧 Biome の format + organizeImports + useSortedClasses を置換。
  • Stylelint: CSS 構文、無効な宣言の組み合わせ、対象ブラウザで未対応の機能、最低限のプロパティ順を検査する。Tailwind CSS v4 のディレクティブと関数は明示的に許可する。
  • Markdownlint: 手書き Markdown の構造を検査する。既存違反ファイルは .markdownlint-cli2.jsonc の baseline に列挙し、修正完了時に該当 entry を削除する。生成 docs、CHANGELOG、agent skills は対象外。
  • Secretlint: recommended preset でコミット済みファイルの credential、token、秘密鍵、接続文字列を検査する。ドキュメント上のダミー値は理由付きの局所 disable のみ許可する。

補助 lint のコマンド:

pnpm lint:css          # CSS 検査
pnpm lint:css:fix      # CSS の安全な自動修正
pnpm lint:md           # Markdown 検査(baseline を除く)
pnpm lint:md:fix       # Markdown の安全な自動修正
pnpm lint:secret       # secret 検査(出力は mask)

CI の supplemental-lint job で3種すべてを検査する。ローカルでは Husky + lint-staged が staged file に対して formatter と安全な fix を直列実行し、最後に Secretlint を実行する。pre-commit の oxlint は高速化と Next.js type generation への非依存化のため非 type-aware とし、完全な oxlint --type-aware は CI で担保する。

  • 旧 Biome の base lint は oxlint へ吸収(categories.correctness="error" + style ルール移植: noParameterAssign→no-param-reassign、useSelfClosingElements→react/self-closing-comp、useNumberNamespace→unicorn/prefer-number-properties、noUselessElse→no-else-return ほか)。noExplicitAnytypescript/no-explicit-any="error" に統一。

新規有効化(旧コメントアウト分): jsx-a11y(oxlint native, error。当初 warn で導入し 2026-05 に error へ昇格)。

移行できなかったもの(oxlint に等価なし):

  • @eslint-react の react-x 固有ルール(type-aware で JS plugin 不可): no-unused-props, naming-convention-ref-name, no-context-provider, ほか strict-type-checked 群。本コードベースでは warning 8 件相当の損失(error 損失は 0)。
  • eslint-plugin-tailwindcss: Tailwind v4(tailwind.config.js 無し)と非互換のため不採用。class 並べ替えは oxfmt sortTailwindcss(Tailwind v4 native)が担当。
  • Biome 専用ルール noUnusedTemplateLiteraluseSingleVarDeclarator(oxlint に one-var 無し)は等価がなく削除(軽微なスタイル系のみ)。