关于 jscpd
面向开发者和代码审查流程的 jscpd 技能,介绍如何运行代码复制检测器并解读报告。内容涵盖精确、重命名和相似代码克隆,以及死代码、复杂度和项目健康度分析选项。
主要功能
- 01检测 220 多种语言的代码重复
- 02输出面向 Agent 的精简报告
- 03识别精确及相似克隆
- 04分析死代码与复杂度
- 05生成项目健康度概览
适用场景
- 01查找代码库中的重复片段
- 02筛查重命名或近似代码块
- 03定位潜在重构热点
- 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 会将文件按其直接父目录聚合(不累计祖先目录的总数)。
- 在
consolereporter 中,摘要以对齐的表格呈现;在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