Configuration
Configuration File
Create a .jscpd.json file in your project root to configure jscpd:
{
"threshold": 0,
"reporters": ["html", "console"],
"ignore": ["**/__snapshots__/**", "**/node_modules/**"],
"absolute": true,
"minLines": 5,
"minTokens": 50
}
CLI Options
| Option | Description | Default |
|---|---|---|
-V, --version | Output the version number | - |
-l, --min-lines | Minimum size of duplication in code lines | 5 |
-k, --min-tokens | Minimum size of duplication in code tokens | 50 |
-x, --max-lines | Maximum size of source in lines | 1000 |
-z, --max-size | Maximum size of source in bytes (e.g. 1kb, 1mb, 120kb) | 100kb |
-t, --threshold | Threshold for duplication, in case duplications >= threshold jscpd will exit with error | null |
-c, --config | Path to config file (default is .jscpd.json in scanned <path>) | null |
-i, --ignore | Glob pattern for files to exclude from duplication detection | null |
--ignore-pattern | Ignore code blocks matching the regexp patterns | null |
-r, --reporters | Reporters or list of reporters separated with comma to use | time,console |
-o, --output | Path for reports | ./report/ |
-m, --mode | Mode of quality of search: strict, mild, weak | mild |
-f, --format | Format or formats separated by comma to detect (e.g. php,javascript,python) | all formats |
-p, --pattern | Glob pattern to file search (e.g. **/*.txt) | - |
-b, --blame | Blame authors of duplications (get information about authors from git) | false |
-s, --silent | Do not write detection progress and result to console | false |
-w, --workers | Number of worker threads for parallel detection (default: auto, uses all CPU cores) | auto |
-a, --absolute | Use absolute path in reports | false |
--follow-symlinks | Follow symbolic links | false |
--ignore-case | Ignore case of symbols in code (experimental) | false |
--no-gitignore | Ignore files from .gitignore file | false |
--formats-exts | List of formats with file extensions (e.g. javascript:es,es6;dart:dt) | null |
--formats-names | List of formats with specific filenames (e.g. makefile:Makefile,GNUmakefile;docker:Dockerfile) | null |
--cross-formats | Groups of related formats to compare together (e.g. javascript,typescript;css,scss or the js-ts preset) | null |
-v, --verbose | Show full information during detection process | false |
--no-colors | Disable ANSI color output | false |
--list | Show list of total supported formats | false |
--skip-local | Skip duplicates in local folders, just detect cross folders duplications | false |
--skip-isolated | Skip clones between different folders of the same isolation group: comma-separated groups of pipe-separated folders (e.g. packages/a|packages/b,libs/a|libs/b) | null |
--summary | Append a codebase summary: top files and folders by tokens, lines, size, complexity, with duplication share | false |
--summary-top | Number of entries in each summary top list | 10 |
--summary-by | Summary ranking metric: tokens, lines, size, complexity | tokens |
--sarif-error-tokens | Report SARIF results as error for clones with at least this many tokens (smaller clones stay warning) | null |
--baseline | Path to a clone baseline file (e.g. .jscpd-baseline.json): clones whose fingerprint is absent from it are reported as new | null |
--update-baseline | Rewrite the baseline file from the current run, creating it if missing, and print added/removed fingerprint counts (requires --baseline) | false |
--fail-on-new-clones | Exit with code 1 when more than N new clones are found (--fail-on-new-clones alone means N=0; requires --baseline or --baseline-from-ref) | null |
--baseline-from-ref | Compare against an ephemeral baseline built from a git ref's tree (e.g. origin/main): the base ref is scanned with the same configuration and clones absent from it are reported as new. Conflicts with --baseline | null |
--mcp | Serve the Model Context Protocol over stdio: scan <path> once, then expose duplication tools to MCP clients | false |
--min-duplicated-lines | Minimum percentage of duplication to report (0-100) | 0 |
--exit-code | Exit code to use when code duplications are detected | 0 |
--no-tips | Do not print tips and promotional messages after detection | false |
--skip-comments | Ignore comments during detection (alias for --mode weak) | false |
--debug | Print merged config (CLI + config file) as JSON and exit without running detection | false |
-h, --help | Display help for command | - |
Examples
Basic scan
jscpd ./src
With pattern matching
jscpd --pattern "**/*.ts" ./src
Multiple reporters
jscpd --reporters html,json,console ./src
Custom threshold
Fail if duplication exceeds 5%:
jscpd --threshold 5 ./src
Gate on new duplication only
Record the currently accepted duplication once, commit the baseline file, and fail CI only when new clones appear:
# record / refresh the accepted state (commit .jscpd-baseline.json)
jscpd --baseline .jscpd-baseline.json --update-baseline ./src
# CI: tolerate everything in the baseline, fail on anything new
jscpd --baseline .jscpd-baseline.json --fail-on-new-clones ./src
Clone fingerprints are content hashes of the duplicated fragments (with a per-fingerprint multiplicity count), so unrelated edits, moved lines, and renamed files don't trigger the gate — only genuinely new or grown duplication does. In config-file form the keys are baseline and failOnNewClones.
Prefer not to commit a baseline file? Compare against a git ref instead — jscpd scans the base ref's tree (via a temporary git worktree) and uses it as the baseline:
jscpd --baseline-from-ref origin/main --fail-on-new-clones ./src
This scans the corpus twice (base tree + working tree) and needs the base ref present locally — in shallow CI checkouts, fetch it first (git fetch origin main, or fetch-depth: 0 with actions/checkout). Config key: baselineFromRef.
Ignore specific patterns
jscpd --ignore "**/*.test.ts,**/__mocks__/**" ./src
Skip comments (weak mode)
Use --skip-comments as a shorthand for --mode weak:
jscpd --skip-comments ./src
Map filenames to formats
For extensionless files like Makefile and Dockerfile:
jscpd --formats-names "makefile:Makefile,GNUmakefile;docker:Dockerfile" ./src
Detect clones across related formats
Compare JavaScript and TypeScript files in one pool (see Cross-Format Detection):
jscpd --cross-formats "js-ts" ./src
Parallel detection with workers
Control the number of threads for detection:
jscpd --workers 4 ./src
Suppress tips in CI
Tips are automatically suppressed when --silent is active. To suppress manually:
jscpd --no-tips ./src
Show full information during detection
jscpd --verbose ./src
Cross-folder detection only
jscpd --skip-local /path/to/folder1/ /path/to/folder2/
Monorepo: isolate team-owned folders
Clones between packages/team-a and packages/team-b are skipped; duplication inside each package or against shared code is still reported. In the config file use nested arrays: "skipIsolated": [["packages/team-a", "packages/team-b"]].
jscpd --skip-isolated "packages/team-a|packages/team-b" .
Codebase summary: where to refactor first
Appends top files and folders ranked by tokens (or lines, size, complexity via --summary-by), each with its duplication share. Config keys: summary, summaryTop, summaryBy.
jscpd --summary ./fixtures
Real output from jscpd's own test fixtures:
Summary (by tokens; 321 files, 129 folders analyzed)
Top files:
TOKENS LINES SIZE CX DUP% PATH
7688 233 10.3K 2 96.1 clike/file2.java
7376 221 9.8K 2 100.0 clike/file1.java
3020 416 12.3K 26 90.6 rust/file1.rs
2108 301 8.7K 17 100.0 rust/file2.rs
1584 262 7.7K 25 99.6 vue/file1.vue
1539 213 7.7K 36 100.0 markdown/file2.md
1517 376 11.8K 24 94.1 javascript/file2.cts
1517 376 11.8K 24 26.6 javascript/file2.mts
1517 376 11.8K 24 0.0 javascript/file2.ts
1505 193 7.5K 36 100.0 markdown/file1.md
Top folders:
FILES TOKENS LINES SIZE CX PATH
16 22639 1426 50.6K 3 clike
18 11680 2522 72.7K 12 javascript
2 5128 717 21.0K 21 rust
4 3807 602 19.7K 21 markdown
5 3543 615 17.6K 12 vue
4 2953 253 9.3K 3 objective-c
2 2512 574 15.4K 7 pug
4 2272 609 14.4K 10 perl
2 2270 338 11.3K 19 d
2 2076 393 14.0K 12 haskell
Every row carries all metrics regardless of the ranking: TOKENS, LINES, SIZE, CX (a token-based cyclomatic-complexity estimate — a ranking signal, not an exact metric), and DUP% (share of the file's lines covered by detected clones). A large file with high DUP% — like clike/file2.java above at 96.1% — is your best refactoring target. Folders aggregate their direct children only.
Ignore case in code
jscpd --ignore-case ./src
Don't follow symlinks
By default, symlinks are not followed. Use --follow-symlinks to follow them:
jscpd --follow-symlinks ./src
Ignore blocks by regexp pattern
jscpd --ignore-pattern "import.*from\s*'.*'" ./src
Config Discovery Order
jscpd looks for configuration in this order (first match wins):
--config <path>— explicit config file.jscpd.jsonin the working directory.config/jscpd.json(or.config/.jscpd.json) — the dot-config convention, since v5.1.0- The
jscpdkey inpackage.json
A root .jscpd.json always wins over the .config/ location, so moving your config into .config/ is opt-in and safe. Paths inside an auto-discovered config resolve against the working directory.
Package.json Configuration
You can also configure jscpd in your package.json:
{
"jscpd": {
"threshold": 0,
"reporters": ["html", "console"],
"ignore": ["**/node_modules/**"],
"absolute": true,
"gitignore": true
}
}
Modes
The --mode option controls detection quality:
Modes decide what an exact clone may differ in. For clones that differ in identifier names or literal values, see Types of Code Clones.
strict— use all types of symbols as token, skip only blocks marked as ignoredmild— skip blocks marked as ignored and new lines and empty symbols (default)weak— skip blocks marked as ignored and new lines and empty symbols and comments
Cross-Format Detection
jscpd v5+ supports cross-format detection for multi-block file types:
- Vue SFC (
.vue) —<script>,<template>,<style>blocks tokenized separately - Svelte (
.svelte) — script and style blocks tokenized independently - Astro (
.astro) — frontmatter and template sections tokenized independently - Markdown (
.md) — fenced code blocks tokenized by language
This means a <script> block in a .vue file can be matched against a .ts file, and a fenced Python code block in Markdown can match a .py file.
Cross-Format Groups (--cross-formats)
Since v5.0.14, --cross-formats defines groups of related formats that share one comparison pool, so a block duplicated between a .js and a .ts file is reported as a clone. By default each format is only compared against itself.
# One group: compare JavaScript and TypeScript together
jscpd --cross-formats "javascript,typescript" ./src
# Preset covering javascript, jsx, typescript, tsx
jscpd --cross-formats "js-ts" ./src
# Multiple groups, separated by ";"
jscpd --cross-formats "javascript,typescript;css,scss" ./src
Rules and behavior:
- Groups are comma-separated format names; multiple groups are separated by
;. A group needs at least two formats, and groups sharing a format are merged into one pool. - When a group mixes TypeScript with JavaScript, TS files are compared with erasable type syntax stripped, so
function f(a: number): voidmatchesfunction f(a). Reported positions still reference the original source. - In per-format statistics, a cross-format clone is attributed to one member format of the group.
In .jscpd.json or package.json the key is crossFormats (alias cross-formats) and accepts a string, an array of strings, or an array of arrays:
{
"crossFormats": [["javascript", "typescript"], ["css", "scss"]]
}
Breaking Change for Vue Users
Vue files are no longer tokenized as markup. Each block uses its resolved sub-format (javascript, typescript, css, scss, etc.). Any tooling that relied on .vue clones being reported under markup must be updated.
Shebang Detection
jscpd can detect duplications in script files that have no file extension by reading the #! shebang line:
| Interpreter | Detected as |
|---|---|
bash, sh, zsh, fish, dash, ksh | shell |
python, python3, python2 | python |
node, nodejs | javascript |
ruby | ruby |
perl | perl |
php | php |
lua | lua |
tclsh, wish | tcl |
Rscript | r |
groovy | groovy |
swift | swift |
kotlin | kotlin |
Both direct (#!/usr/bin/bash) and env-mediated (#!/usr/bin/env python3) shebangs are supported. Version suffixes are stripped automatically (python3.11 → python).
Ignored Blocks
Mark blocks in code as ignored:
/* jscpd:ignore-start */
import lodash from 'lodash';
import React from 'react';
import {User} from './models';
import {UserService} from './services';
/* jscpd:ignore-end */
<!--
// jscpd:ignore-start
-->
<meta data-react-helmet="true" name="theme-color" content="#cb3837"/>
<link data-react-helmet="true" rel="stylesheet" href="https://static.npmjs.com/103af5b8a2b3c971cba419755f3a67bc.css"/>
<!--
// jscpd:ignore-end
-->