mcpField
J
jscpd

jscpd

github.com/kucherenkoテスト・品質6.5k2.6k 回インストール今日更新
リポジトリ

jscpd について

開発者やコードレビュー向けに、コード重複検出ツール jscpd の実行方法とレポートの読み方を解説するスキルです。完全一致・名前変更・類似クローンのほか、未使用コード、複雑度、プロジェクト健全性の分析オプションも扱います。

主な機能

  1. 01220 以上の言語で重複を検出
  2. 02エージェント向けの簡潔なレポート
  3. 03完全一致・類似クローンの検出
  4. 04未使用コードと複雑度の分析
  5. 05プロジェクト健全性の要約

活用シーン

  1. 01リポジトリ内の重複コードの発見
  2. 02名前変更や類似コードの確認
  3. 03リファクタリング候補の特定
  4. 04未使用コードと複雑度の集計

インストール

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

$npx skills add https://github.com/kucherenko/jscpd --skill jscpd

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

詳細

jscpd

220 以上の言語に対応した、プログラミングソースコードのコピー&ペースト検出ツールです。このスキルを使って jscpd を実行し、出力の内容を確認できます。

クイックスタート

# Run with ai reporter (compact output optimized for agents)
npx jscpd --reporters ai <path>

# With ignore patterns
npx jscpd --reporters ai --ignore "**/node_modules/**,**/dist/**" <path>

# Scope to specific formats
npx jscpd --reporters ai --format "javascript,typescript" <path>

# Second pass, noisier: copies that only differ in names and values (Type-2, "renamed").
# Review each hit before acting on it.
npx jscpd --reporters ai --ignore-identifiers --min-tokens 70 <path>

# Third pass, noisier still: copies with a few edited lines or the same function
# structure (Type-3, "similar"). Keep the settings tight.
npx jscpd --reporters ai --max-gap-lines 1 --similarity <path>

# Where to refactor first: clone list plus a hotspot summary
npx jscpd --reporters ai --summary <path>

# Most complex files only, without clone detection
npx jscpd --complexity --reporters ai <path>

AI レポーターの出力形式

ai レポーターは、エージェントが利用しやすいように、コンパクトでトークン効率の高い形式で出力します。

Clones:
src/ foo.ts:10-25 ~ bar.ts:42-57
src/utils/helpers.ts:100-120 ~ src/utils/other.ts:5-25
src/cart/ basket.js:1-9 ~ cart.js:1-9 (renamed)
src/api/ save-account.js:1-12 ~ save-user.js:1-11 [~0.91 gap]
src/billing/ orders.ts:1-14 ~ refunds.ts:1-15 [~0.85 ast]
---
5 clones · 4.2% duplication

各行はクローンのペアを表します。

  • 同じファイル:path/file.ts 10-25 ~ 45-60(共通パスを1回だけ表示)
  • 同じディレクトリ:shared/prefix/ file-a.ts:10-25 ~ file-b.ts:42-57(共通のプレフィックスをまとめて表示)
  • 異なるパス:path/a.ts:10-25 ~ path/b.ts:42-57

接尾辞はクローンの種類を示します。接尾辞がない場合は完全一致です。

  • (renamed):2つのブロックは、識別子名、リテラル値、またはアノテーションのみが異なります(Type-2)。--ignore-identifiers、--ignore-literals、または --ignore-annotations を指定した場合にのみ表示されます。
  • [~0.91 gap]:最大 --max-gap-lines 行の一致しない行を挟んで、2つの完全一致クローンがマージされています(Type-3)。数値は、マージされた範囲内で一致するトークンの割合です。
  • [~0.85 ast]:正規化された構文ツリーの部分木が、--similarity 以上の割合で一致する2つの関数です(Type-3)。数値はその一致率です。呼び出された関数やメソッドの名前、演算子は一致率に含まれますが、ローカル名やリテラル値は含まれません。そのため、定数が異なる名前変更されたコピーは1となり、別のメソッドを呼び出すコピーはより低い値になります。一方の名前は、もう一方の側の1つの名前に対応している必要があります。2つの名前の役割を入れ替えたコピーは、より低い値になります。

オプション

オプション説明
--reporters aiAI に最適化されたレポーターを使用します(エージェント向けの簡潔なクローン一覧)
--reporters htmlHTML レポートを生成します
--reporters jsonJSON レポートを出力します
--min-tokens N重複と判定する最小トークン数(デフォルト:50)
--min-lines N重複と判定する最小行数(デフォルト:5)
--threshold N重複率が N を超えた場合にエラーで終了します
--ignore "glob"除外するパターン(カンマ区切り)
--format "list"対象を特定の言語に限定します(例:typescript,javascript)
--cross-formats "groups"関連する形式間のクローンを検出します(例:javascript,typescript または js-ts プリセット)
--ignore-identifiersすべての識別子を同一として扱い、変数名、関数名、型名だけが異なるブロックを一致させます(Type-2、renamed として報告)。一方の名前はもう一方の一つの名前に対応する必要があるため、データフローが異なる類似コードは対象外です。また、同じテキスト内のフィールドとローカル変数は別々の名前として扱われるため、this.url = url は this.url = link と引き続き一致します(--ignore-identifiers=blind は任意の名前を一致させます)
--ignore-literalsすべての文字列リテラルを同一として、すべての数値リテラルを同一として扱います(Type-2)
--ignore-annotations@ がアノテーションを表す言語では、一致判定の前に @Name / @Name(...) アノテーションとデコレーターを除外します(Type-2)
--max-gap-lines N同じファイルペア内で N 行以下の未一致行を挟むクローンを 1 つの similar クローンに統合します(Type-3、デフォルト:0 = 無効)
--similarity [RATIO]正規化された構文木の部分木を少なくとも RATIO の割合で共有する関数ペアを、similar クローン(Type-3)として報告します。RATIO は (0, 1] で指定し、省略時は 0.8 です。対応言語:JavaScript、TypeScript、Python、Java、Kotlin、Scala、C#、Go、Rust、C、C++、PHP、Ruby、Swift、Clojure。テストファイルは対象外です
--min-nodes N--similarity の判定対象となる関数の正規化構文木に必要な最小ノード数(デフォルト:20)
--summaryトークン数、行数、サイズ、複雑度の上位ファイル/フォルダーと重複の割合を示すコードベースの概要を追加します
--summary-top N概要の各上位リストに表示する項目数(デフォルト:10)
--summary-by metric概要の並べ替え基準:tokens、lines、size、complexity(デフォルト:tokens)
--kind list指定したクローン種別のみを報告します:exact、renamed、similar、gap、ast(検出器は有効化されません)
--complexityクローン検出を行わず、複雑度の表のみを表示します(レポーター:console、ai、json)
--dead-code未使用のファイル、エクスポート、モジュール内限定の宣言、インポートを検出します — JavaScript、TypeScript、JSX、TSX、Vue、Svelte、Astro、Python。各検出結果には 0~100 の信頼度スコアが付与されます
--dead-code-categories list--dead-code/--dashboard の対象カテゴリを限定します:unused-file、unused-export、unused-symbol、unused-import
--min-confidence N--dead-code/--dashboard の検出結果に適用する信頼度の下限(0~100、デフォルト:60)
--entry "glob"package.json/pyproject.toml/フレームワークの規約から推定されるものに加え、デッドコード解析用の追加エントリーポイントを指定します
--include-testsデッドコード解析で、テストのみでの使用も「使用中」としてカウントします(デフォルトでは除外)
--include-entry-exportsエントリーファイル自身の未使用エクスポートも検出します(デフォルトでは除外:エクスポートは公開 API です)
--dashboard1 画面に健全性バッジ、プロジェクトの規模と最大のコードファイル、重複、複雑度、デッドコードを表示します(JS/TS/Python)。-r json は jscpd-dashboard.json を書き出します
--healthプロジェクトの健全性バッジのみを表示します:重複、デッドコード、複雑度に基づく 0~100 のスコアと評価(-r ai は 1 行、-r json、-r badge)
--health-input FILE他のツールのメトリクス(カバレッジ、テスト、セキュリティ)を健全性スコアに追加します
--pattern "glob"ファイル選択用の Glob パターン
--no-gitignore.gitignore を無視します(デフォルトでは適用されます)
--output "path"レポートの出力先ディレクトリ
--silentコンソールへの出力を抑制します(ファイルレポーターと --output の使用時に便利です)
--listサポートされているすべての形式を一覧表示して終了します
--no-tips出力内のヒントを無効にします(stdout が TTY でない場合、または CI もしくは JSCPD_NO_TIPS が設定されている場合は自動的にスキップされます)
--config "path".jscpd.json 設定ファイルへのパス

クローンの種類:完全一致、名前変更、類似

デフォルトでは、jscpd は完全一致のクローンのみを報告します。つまり、トークン列が同一です(空白、レイアウト、およびモードによってはコメントは考慮されません)。オプトインで利用できる 2 つのファミリーを使うと、検出範囲が広がります。より多くの、より長いクローンが見つかり、「クローン」の意味も変わるため、デフォルトのスキャンの後に別々のパスとして実行してください。

これらのパスは、意図的にノイズが多くなるよう設計されています。 完全一致のクローンは、ほとんどの場合、実際のコピーです。名前変更または類似のクローンは 候補 です。これらのフラグは、2 つのブロックの意味を異なるものにしていることが多い要素(名前、値、1~2 個の文)を意図的に無視します。次のようなケースでは、誤検出が想定されます。

  • 似た見た目になることが想定されているボイラープレート:DTO とモデル、設定テーブル、列挙型のようなマップ、ルートやハンドラーの登録、ビルダー
  • テストファイル:describe/it ブロック、フィクスチャ、セットアップコードは、意図的に同じ形を繰り返します
  • 生成コード、マイグレーション、シリアライザー、プロトコルバインディング
  • 言語の慣用表現:構造は共通していてもロジックは異なる、2 つの reduce ループや 2 つの switch 文
  • 小さなブロック:--ignore-identifiers を使う場合、50 トークンのブロックはほとんどがプレースホルダーになるため、--min-tokens を引き上げてください

ノイズを管理しやすくするためのルール:

  • 完全一致のクローンに対処した後、フラグごとの検出結果がわかるよう、一度に 1 つのファミリーを実行してください。
  • まずは保守的な設定から始めてください:--ignore-identifiers のみ(定数が既知の問題である場合に限り --ignore-literals を追加)、--max-gap-lines 1 または 2、--similarity 0.85 以上、識別子パスでは --min-tokens 70 以上にします。絞り込んだ実行で何も見つからなかった場合にのみ、範囲を広げてください。
  • (renamed) または [~…] の行は、修正すべき欠陥ではなく、内容を確認するための手がかりとして扱ってください。コードベースで何が報告されるかをチームが確認していない限り、これらのパスで CI(--threshold、--fail-on-new-clones)を失敗させないでください。
  • 正規化した実行で見つかったものについて、どのフラグで検出されたかを明示せずに「重複が N 件見つかった」と主張しないでください。
Type-2:名前変更クローン(--ignore-identifiers、--ignore-literals、--ignore-annotations)

変数名を変更したり、定数を変えたりしたコピーです。

npx jscpd --reporters ai --ignore-identifiers <path>                      # function a(x) {…} matches function b(y) {…}
npx jscpd --reporters ai --ignore-identifiers --ignore-literals <path>    # …and 10 matches 25, 'dev' matches 'prod'
npx jscpd --reporters ai --ignore-annotations <path>                      # @Override / @Deprecated no longer split a clone
Clones:
basket.js:1-9 ~ cart.js:1-9 (renamed)
limits-dev.js:1-13 ~ limits-prod.js:1-13 (renamed)
---
  • キーワードの意味は維持されます(return が retry に一致することはありません)。そのため、構造も一致している必要があります。
  • --ignore-annotations が機能するのは Java、Kotlin、Scala、Groovy、Python、Dart、Swift、JavaScript、TypeScript のみです。Ruby、Perl、T-SQL、Razor、CSS では @ が別の意味を持つため、無視されません。
  • レポート内の位置は、引き続き元のソースを指します。
  • 設定キー:ignoreIdentifiers、ignoreLiterals、ignoreAnnotations。
Type-3:ニアミスクローン(--max-gap-lines、--similarity)

数行を編集したコピー、または同じ形に書き換えた関数です。

npx jscpd --reporters ai --max-gap-lines 2 <path>      # a copy with up to 2 inserted/changed lines becomes one clone
npx jscpd --reporters ai --similarity <path>           # functions whose normalized syntax trees share ≥82% of their subtrees
Clones:
save-account.js:1-12 ~ save-user.js:1-11 [~0.91 gap]
orders.ts:1-14 ~ refunds.ts:1-15 [~0.85 ast]
---
  • --max-gap-lines N は、完全一致パスですでに検出されたクローンのみを結合します。そのため、新たな一致を作り出すのではなく、断片化を解消します。すべての言語で機能します。間隔に含まれるトークン数が、両側に共通するトークン数を超える場合は、類似度が 0.5 未満になるため、結合は行われません。
  • --similarity はすべての関数の構文木を正規化し(ローカル名とリテラルはマーカーに置き換えられ、各名前は反対側の 1 つの名前に対応します。呼び出し先の名前と演算子はそのままです)、2 つの木に共通する部分木の割合をスコア化します。そのため、名前を変更したコピーは 1.0、10 個の文を含む関数に文を 1 つ挿入した場合は約 0.85 となり、他のメソッドを呼び出すコピーはスコアが低くなります。JavaScript、TypeScript、Python、Java、Kotlin、Scala、C#、Go、Rust、C、C++、PHP、Ruby、Swift、Clojure、および Markdown 内のそれらのコードブロック、Vue、Svelte、Astro ファイルに適用されます。それ以外の形式では何もせずに終了します。別の関数内にネストされた関数はその一部として扱われ、テストファイルは対象外です。デフォルトの 0.8 から始め、手がかりを探す場合にのみ 0.7 まで下げてください。低いスコアを信じる前に、両方の関数を読んでください。
  • 両方に該当する場合は、similar が renamed より優先されます(結合されたクローンは、正規化しても同一ではなくなります)。
  • 設定キー:maxGapLines、similarity、minNodes。
種類が表示される場所
  • console:Clone found (javascript, renamed)、Clone found (javascript, similar (gap) ~0.91)、Clone found (javascript, similar (ast) ~0.75)。
  • json:"kind": "exact" | "renamed" | "similar"。類似クローンの場合は、さらに"similarity"と"method": "gap" | "ast"が含まれます。また、ASTによるクローンの場合は、正規化された各ツリーのサイズがfirstFileとsecondFileの"nodes"として含まれます。
  • sarif:ルールはjscpd/duplicate-code、jscpd/renamed-code、jscpd/similar-code(ギャップをまたいでマージされたクローン)、jscpd/similar-function(--similarityでペアにした関数とクラス)、およびjscpd/semantic-code(--semantic)です。Code Climateでは、同じ値がcheck_nameに使用されます。
  • デフォルトの実行ではexactクローンのみが報告され、これらの機能による出力への影響はありません。
  • 正規化を行う実行では、完全一致の実行とは異なるクローンフィンガープリントが生成されます。設定ごとに別の--baselineファイルを使用してください。

コードベースの概要(--summary)

--summaryを指定すると、リファクタリングの優先候補の概要が実行結果に追加されます。個々のクローンを詳しく調べる前に、どこからリファクタリングを始めるかの判断にお使いください。

# Compact clone list + compact summary, optimized for agents
npx jscpd --reporters ai --summary --no-tips <path>

# Rank by complexity instead of tokens, top 5 lists
npx jscpd --reporters ai --summary --summary-by complexity --summary-top 5 <path>

aiレポーターでは、各項目が1行で表示されます。

Summary by tokens (321 files, 129 folders):
files (tokens/lines/size/cx/dup%):
src/files.ts 2052/363/11662/80/0.0%
long-line/theme-branded.js 498/15/2.5K/10/93.3%
...
folders (files/tokens/lines/size):
src/core 8/5264/843/27136
...

読み方:

  • 上位ファイルは--summary-byの指標に基づいて順位付けされますが、各行にはすべての指標(tokens/lines/size/cx/dup%)が含まれます。
  • cxは、トークンストリームから算出する言語に依存しない循環的複雑度の推定値です(1に、if、while、&&などの判定ポイントとなるトークンを加えた値)。正確な指標ではなく、順位付けの目安としてお使いください。
  • **dup%**は、検出されたクローンが占めるファイルの行の割合です。dup%が高い大きなファイルは、リファクタリングの最適な候補です。現在の実行で検出されたクローンを反映するため、Type-2/Type-3フラグを使用すると値が上昇します。
  • フォルダーでは、ファイルを直接の親ディレクトリごとに集計します(祖先ディレクトリの累積合計はありません)。
  • consoleレポーターでは、概要が列を揃えた表として表示されます。jsonレポートでは、追加のsummaryキーとして表示されます(フラグをオフにすると含まれません)。

設定ファイルでは、"summary": true、"summaryTop": 10、"summaryBy": "tokens"に相当します。

フォーマットをまたいだクローン検出

デフォルトでは、各フォーマットは同じフォーマットとのみ比較されます。--cross-formatsでは、関連するフォーマットを比較グループにまとめ、同じ比較対象に含めます。これにより、.jsファイルと.tsファイルの間で重複しているコードブロックがクローンとして報告されます。

# One group: compare JavaScript and TypeScript files together
npx jscpd --reporters ai --cross-formats "javascript,typescript" <path>

# Preset covering javascript, jsx, typescript, tsx
npx jscpd --reporters ai --cross-formats "js-ts" <path>

# Multiple groups are separated by ";"
npx jscpd --reporters ai --cross-formats "javascript,typescript;css,scss" <path>

注意:

  • グループにTypeScriptとJavaScriptの両方を含める場合、TSファイルは消去可能な型構文を取り除いて比較されます。そのため、function f(a: number): voidはfunction f(a)と一致します。報告される位置は元のソースを参照します。
  • グループには2つ以上のフォーマットが必要です。同じフォーマットを共有するグループは、1つの比較対象に統合されます。
  • フォーマット別の統計では、フォーマットをまたぐクローンはグループ内のいずれか1つのフォーマットに割り当てられます。
  • 設定ファイルでは、キーはcrossFormats(またはcross-formats)です。文字列("javascript,typescript;css,scss")、文字列の配列(["javascript,typescript", "css,scss"])、または配列の配列([["javascript","typescript"],["css","scss"]])を指定できます。

2 つのコードベースを比較する(--compare)

--compare SOURCE TARGET は、--semantic 埋め込みモデルを使って 2 つのフォルダーの関数を対応付け、各側のどの関数に相手側の対応関数があるかを一覧表示します。別の言語やフレームワークへの移植の進捗、または 1 つのアプリの 2 つの実装間の機能の同等性を確認できます。重複しないパスを正確に 2 つ指定する必要があり、モデルは一度だけ必要です(--semantic-download)。クローン検出は実行しません。

npx jscpd --compare python-lib/ rust-lib/                                  # progress of a port and what is left
npx jscpd --compare ios/ android/ -r console-full                          # parity, with every pair and its similarity
npx jscpd --compare python-lib/ rust-lib/ -r json -o .jscpd-compare --silent  # jscpd-compare.json for an agent

関数はまずコードに基づいて対応付けられます(それぞれが相手側にとって最も近い一致である場合)。次に、大文字と小文字およびアンダースコアを無視した名前が一致し、コードも十分に類似している場合は、名前に基づいて対応付けられます。各ペアには類似度とモデルのスケールに基づくレベル(high、medium、low)が付与されます。デフォルトのレポートでは、ペアは相手側の名前ごとに個別に一覧表示されます。jscpd はテストとコードを 2 つのブロックで測定し、テストはテストとのみ対応付けます。ここでの --min-tokens のデフォルト値は 30 です。レポーター:console、console-full、json、markdown。比較の仕組みと確認方法については compare-codebases スキルを、コードの移植方法については code-migration を参照してください。

設定ファイル

プロジェクトのルートに .jscpd.json を作成します。

{
  "threshold": 0,
  "reporters": ["ai"],
  "ignore": ["**/node_modules/**", "**/dist/**", "**/*.min.*"],
  "format": ["typescript", "javascript"],
  "minLines": 5,
  "minTokens": 50,
  "ignoreIdentifiers": false,
  "ignoreLiterals": false,
  "maxGapLines": 0,
  "similarity": 1,
  "summary": false,
  "output": "./reports/jscpd"
}

重複コードのリファクタリング

クローンを検出したら、クローンの種類ごとの戦略を使ったガイド付きワークフローで重複をなくすには、dry-refactoring スキルを使用します。

→ dry-refactoring — 重複を取り除くための段階的なリファクタリング戦略とワークフロー。次のコマンドでインストールします。

npx skills add https://github.com/kucherenko/jscpd --skill dry-refactoring

コードベース全体の健全性を改善する

より広範な「このコードベースを整理する」作業では、重複、デッドコード、最大または最も複雑なファイルの順に対応し、--health/--dashboard で優先順位を付けて最後に再測定します。これには codebase-refactoring を使用します。

npx skills add https://github.com/kucherenko/jscpd --skill codebase-refactoring

コメント