mcpField
J
jscpd

jscpd

github.com/kucherenko测试与质量6.5k2.6k 次安装今天更新
代码仓库

关于 jscpd

面向开发者和代码审查流程的 jscpd 技能,介绍如何运行代码复制检测器并解读报告。内容涵盖精确、重命名和相似代码克隆,以及死代码、复杂度和项目健康度分析选项。

主要功能

  1. 01检测 220 多种语言的代码重复
  2. 02输出面向 Agent 的精简报告
  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 Reporter 输出格式

ai reporter 会生成简洁、节省 token 的输出,专为 Agent 使用而设计:

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(共享路径只显示一次)
  • 同一目录:shared/prefix/ file-a.ts:10-25 ~ file-b.ts:42-57(提取共同前缀)
  • 不同路径:path/a.ts:10-25 ~ path/b.ts:42-57

后缀表示克隆的类型;没有后缀表示完全相同的副本:

  • (renamed):两个代码块的差异仅在标识符名称、字面值或注释(Type-2)。仅在使用 --ignore-identifiers、--ignore-literals 或 --ignore-annotations 时出现。
  • [~0.91 gap]:两个完全相同的克隆代码块之间合并了最多 --max-gap-lines 行不匹配的代码行(Type-3)。数字表示匹配的 token 数占合并跨度的比例。
  • [~0.85 ast]:两个函数的规范化语法树中,至少有 --similarity 的子树相同(Type-3)。数字表示相同部分的占比。被调用函数和方法的名称以及运算符会计入,局部名称和字面值则不会;因此,使用其他常量的改名副本得分为 1,而调用其他方法的副本得分较低。两侧的名称仍须一一对应:如果副本交换了两个名称的作用,得分会较低。

选项

选项描述
--reporters ai使用针对 AI 优化的报告器(为 Agent 提供精简的克隆列表)
--reporters html生成 HTML 报告
--reporters json输出 JSON 报告
--min-tokens N判定重复代码所需的最少 token 数(默认值: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 行未匹配代码的克隆合并为一个 similar 克隆(Type-3,默认值:0 = 关闭)
--similarity [RATIO]报告函数对:其标准化语法树至少有 RATIO 的子树相同,RATIO 范围为 (0, 1];此类克隆标记为 similar(Type-3,未提供值时为 0.8)。支持 JavaScript、TypeScript、Python、Java、Kotlin、Scala、C#、Go、Rust、C、C++、PHP、Ruby、Swift 和 Clojure;不包括测试文件
--min-nodes N函数用于 --similarity 检测时,标准化语法树所需的最少节点数(默认值:20)
--summary附加代码库摘要:按 token 数、行数、大小、复杂度列出排名靠前的文件和文件夹,并显示重复代码占比
--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)
--dashboard一屏展示:健康徽章、项目大小和最大的代码文件、重复代码、复杂度、死代码(JS/TS/Python);-r json 会写入 jscpd-dashboard.json
--health仅显示项目健康徽章:根据重复代码、死代码和复杂度给出 0-100 分及等级(-r ai 输出一行,-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 只报告精确克隆:令牌序列完全相同(空白、布局以及根据模式不同而忽略的注释不计)。另外两类可选检测会扩大范围。请在默认扫描后将它们作为单独的扫描运行,因为它们会发现更多、更长的克隆,并改变“克隆”的含义。

**这些扫描本来就容易产生噪声。**精确克隆几乎总是真正的复制。重命名或相似克隆是一个候选项:这些标志会故意忽略那些(名称、值、一两条语句)常常令两个代码块含义不同的因素。请预期会出现以下误报:

  • 本就应该相似的样板代码:DTO 和模型、配置表、类似枚举的映射、路由或处理器注册、构建器
  • 测试文件:describe/it 块、固定数据和设置代码会有意重复相同的结构
  • 生成的代码、迁移代码、序列化器、协议绑定
  • 语言惯用写法:两个 reduce 循环或两个 switch 语句结构相同,但逻辑不同
  • 较小的代码块:使用 --ignore-identifiers 时,50 个令牌的大部分都是占位符,因此应提高 --min-tokens

以下规则有助于控制噪声:

  • 先处理完精确克隆,再运行这些扫描;每次只运行一种类型,以便每个命中结果都能对应到一个标志。
  • 从保守设置开始:单独使用 --ignore-identifiers(只有在已知问题是常量时才添加 --ignore-literals)、设置 --max-gap-lines 1 或 2、--similarity 0.85 或更高,并在标识符扫描中将 --min-tokens 设为 70 或更高。只有严格扫描结果为空时才扩大范围。
  • 将每行 (renamed) 或 [~…] 视为有待阅读的线索,而不是需要修复的问题。除非团队已经审查过这些扫描在代码库中的报告结果,否则不要用这些扫描结果设置 CI 门禁(--threshold、--fail-on-new-clones)。
  • 如果没有说明是哪些标志产生了结果,绝不要在规范化扫描后声称“发现 N 处重复”。
类型 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。
类型 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.0,在十条语句的函数中插入一条语句约为 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。
kind 的显示位置
  • console:Clone found (javascript, renamed)、Clone found (javascript, similar (gap) ~0.91)、Clone found (javascript, similar (ast) ~0.75)。
  • json:"kind": "exact" | "renamed" | "similar";对于 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 reporter 时,摘要会为每个条目输出一行:

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
...

解读方式:

  • Top files 按 --summary-by 指标排序,但每行都会包含所有指标:tokens/lines/size/cx/dup%。
  • cx 是根据 token 流估算的、与语言无关的圈复杂度(1 + if/while/&& 等决策点 token);请将其视为排序信号,而非精确指标。
  • dup% 表示文件中被检测到的克隆覆盖的行所占比例。dup% 较高的大文件是最佳重构目标。该值反映当前运行检测到的克隆,因此启用 Type-2/Type-3 标志后会相应升高。
  • Folders 会将文件按其直接父目录聚合(不累计祖先目录的总数)。
  • 在 console reporter 中,摘要以对齐的表格呈现;在 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) 可匹配。报告的位置仍对应原始源代码。
  • 每个分组至少需要两种格式;共享某种格式的分组会合并到同一个池中。
  • 在各格式的统计数据中,跨格式克隆会归属于该分组的一种成员格式。
  • 在配置文件中,键为 crossFormats(或 cross-formats),接受字符串("javascript,typescript;css,scss")、字符串数组(["javascript,typescript", "css,scss"])或数组的数组([["javascript","typescript"],["css","scss"]])。

比较两个代码库(--compare)

--compare SOURCE TARGET 使用 --semantic 嵌入模型配对两个文件夹中的函数,并列出双方各有哪些函数在另一方有对应项:用于跟踪代码移植到另一种语言或框架的进度,或检查同一应用的两种实现是否一致。它需要恰好两个互不重叠的路径,只需下载一次模型(--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 将测试和代码分为两个区块进行度量,并且只会将测试与测试配对。此处 --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

评论

支持 Markdown