Getting Started

Configuration

Configure jscpd for your project.

Configuration File

Create a .jscpd.json file in your project root to configure jscpd:

.jscpd.json
{
  "threshold": 0,
  "reporters": ["html", "console"],
  "ignore": ["**/__snapshots__/**", "**/node_modules/**"],
  "absolute": true,
  "minLines": 5,
  "minTokens": 50
}

CLI Options

OptionDescriptionDefault
-V, --versionOutput the version number-
-l, --min-linesMinimum size of duplication in code lines5
-k, --min-tokensMinimum size of duplication in code tokens50
-x, --max-linesMaximum size of source in lines1000
-z, --max-sizeMaximum size of source in bytes (e.g. 1kb, 1mb, 120kb)100kb
-t, --thresholdThreshold for duplication, in case duplications >= threshold jscpd will exit with errornull
-c, --configPath to config file (default is .jscpd.json in scanned <path>)null
-i, --ignoreGlob pattern for files to exclude from duplication detectionnull
--ignore-patternIgnore code blocks matching the regexp patternsnull
-r, --reportersReporters or list of reporters separated with comma to usetime,console
-o, --outputPath for reports./report/
-m, --modeMode of quality of search: strict, mild, weakmild
-f, --formatFormat or formats separated by comma to detect (e.g. php,javascript,python)all formats
-p, --patternGlob pattern to file search (e.g. **/*.txt)-
-b, --blameBlame authors of duplications (get information about authors from git)false
-s, --silentDo not write detection progress and result to consolefalse
-w, --workersNumber of worker threads for parallel detection (default: auto, uses all CPU cores)auto
-a, --absoluteUse absolute path in reportsfalse
--follow-symlinksFollow symbolic linksfalse
--ignore-caseIgnore case of symbols in code (experimental)false
--no-gitignoreIgnore files from .gitignore filefalse
--formats-extsList of formats with file extensions (e.g. javascript:es,es6;dart:dt)null
--formats-namesList of formats with specific filenames (e.g. makefile:Makefile,GNUmakefile;docker:Dockerfile)null
--cross-formatsGroups of related formats to compare together (e.g. javascript,typescript;css,scss or the js-ts preset)null
-v, --verboseShow full information during detection processfalse
--no-colorsDisable ANSI color outputfalse
--listShow list of total supported formatsfalse
--skip-localSkip duplicates in local folders, just detect cross folders duplicationsfalse
--skip-isolatedSkip 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
--summaryAppend a codebase summary: top files and folders by tokens, lines, size, complexity, with duplication sharefalse
--summary-topNumber of entries in each summary top list10
--summary-bySummary ranking metric: tokens, lines, size, complexitytokens
--sarif-error-tokensReport SARIF results as error for clones with at least this many tokens (smaller clones stay warning)null
--baselinePath to a clone baseline file (e.g. .jscpd-baseline.json): clones whose fingerprint is absent from it are reported as newnull
--update-baselineRewrite the baseline file from the current run, creating it if missing, and print added/removed fingerprint counts (requires --baseline)false
--fail-on-new-clonesExit 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-refCompare 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 --baselinenull
--mcpServe the Model Context Protocol over stdio: scan <path> once, then expose duplication tools to MCP clientsfalse
--min-duplicated-linesMinimum percentage of duplication to report (0-100)0
--exit-codeExit code to use when code duplications are detected0
--no-tipsDo not print tips and promotional messages after detectionfalse
--skip-commentsIgnore comments during detection (alias for --mode weak)false
--debugPrint merged config (CLI + config file) as JSON and exit without running detectionfalse
-h, --helpDisplay 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

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

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):

  1. --config <path> — explicit config file
  2. .jscpd.json in the working directory
  3. .config/jscpd.json (or .config/.jscpd.json) — the dot-config convention, since v5.1.0
  4. The jscpd key in package.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:

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 ignored
  • mild — 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): void matches function 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:

.jscpd.json
{
  "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:

InterpreterDetected as
bash, sh, zsh, fish, dash, kshshell
python, python3, python2python
node, nodejsjavascript
rubyruby
perlperl
phpphp
lualua
tclsh, wishtcl
Rscriptr
groovygroovy
swiftswift
kotlinkotlin

Both direct (#!/usr/bin/bash) and env-mediated (#!/usr/bin/env python3) shebangs are supported. Version suffixes are stripped automatically (python3.11python).

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