The config file
This page lists the keys jscpd reads from .jscpd.json, their types, and the forms the file can take. Every key is a flag of the command line spelled in camelCase: --min-tokens 70 becomes "minTokens": 70, --no-gitignore becomes "noGitignore": true, and a flag given on the command line wins over the file. jscpd reads the file from the directory it runs in, never from the paths it scans.
Where jscpd looks
jscpd takes the first of these that exists and reads no other:
- The file named with
-cor--config, from anywhere. A file that is missing or is not valid JSON stops the run with exit code 1. .jscpd.jsonin the working directory..config/jscpd.json, then.config/.jscpd.json, in the working directory (5.1.0+).- The
jscpdkey ofpackage.jsonin the working directory.
The run prints Using config from <file> when it found one. The scanned path plays no part: jscpd packages/api from the repository root reads the root's file and ignores packages/api/.jscpd.json, so a scan from another directory needs -c. A found file that does not parse is reported as a warning and skipped, and the search goes on to the next source.
Relative paths in path, baseline, healthInput and the dead-code rustDiagnostics resolve against the directory of the config file, which is the working directory for a found file. output is relative to the working directory, and the globs in ignore and pattern are matched against the paths below each scanned root.
The file
A .jscpd.json most projects could start from:
{
"threshold": 3,
"minTokens": 50,
"minLines": 5,
"mode": "mild",
"format": ["javascript", "typescript", "python"],
"ignore": ["**/node_modules/**", "**/dist/**", "**/*.min.js"],
"ignorePattern": ["(?s)\\A/\\*.*?\\*/"],
"reporters": ["console", "html"],
"output": "report",
"absolute": false
}
The tables below give each key with its type and what it decides. The defaults are on the CLI page, which also marks the flags that have no key: --workers, --update-baseline, --semantic-download, --list, --debug and the switches that choose another run (--dashboard, --health, --complexity, --compare, --mcp, --lsp) stay on the command line.
What to scan
| Key | Type | What it decides |
|---|---|---|
path | array of strings | The paths to scan when the command line names none, relative to the config file's directory. |
pattern | string | A glob the files must match, such as **/*.ts or **/*.{js,ts}. |
ignore | array of strings | Globs for files and folders to leave out, such as **/node_modules/**. |
format | array of strings | The formats to scan, by name from jscpd --list; formats and a single string are accepted too. |
formatsExts | string or object | Extra file extensions for a format: "javascript:es,es6;dart:dt" or {"javascript": ["es", "es6"]}. |
formatsNames | string or object | File names for a format: "makefile:Makefile,GNUmakefile;docker:Dockerfile" or the object form. |
crossFormats | string, array of strings or array of arrays | Groups of formats compared in one pool; see cross-format detection. |
noGitignore | boolean | true scans the files that .gitignore lists. |
followSymlinks | boolean | true follows symbolic links; v4's noSymlinks is translated. |
maxSize | string | Files larger than this are skipped: "500kb", "2mb" or a number of bytes. |
maxLines | number | Files with more lines than this are skipped; there is no limit unless you set one. |
skipLocal | boolean | Skip clones whose two fragments are under the same scanned root, which leaves the clones between roots. |
skipIsolated | array of arrays of strings | Isolation groups; clones between two folders of one group are skipped: [["packages/a", "packages/b"]]. See monorepos. |
What counts as a clone
| Key | Type | What it decides |
|---|---|---|
minTokens | number | The smallest clone, in tokens. |
minLines | number | The smallest clone, in lines. |
mode | string | mild, weak or strict; see Detection modes. |
ignorePattern | array of strings | Regular expressions whose matches are removed from every file before detection; see Ignoring code. |
ignoreCase | boolean | Compare tokens without regard to case (experimental). |
ignoreIdentifiers | boolean | Treat every identifier as the same one, which finds renamed clones. |
ignoreLiterals | boolean | Treat every string and number literal as the same one. |
ignoreAnnotations | boolean | Skip @Name annotations and decorators in the languages that have them. |
maxGapLines | number | Merge two clones of one file pair that at most this many unmatched lines separate into one near-miss clone; 0 is off. |
similarity | number in (0, 1] | Report JavaScript and TypeScript function pairs whose syntax trees are at least this similar; 1 is off. |
kind | array of strings | Report only these kinds: exact, renamed, similar, gap, ast, semantic. |
semantic | boolean or object | true turns the semantic pass on; an object holds its settings (enabled, scope, model, threshold, sameThreshold, url, provider and more), see semantic clones. An API key never goes in the file: jscpd refuses a section that holds one and reads JSCPD_SEMANTIC_API_KEY instead. |
Reports
| Key | Type | What it decides |
|---|---|---|
reporters | array of strings | The reporters to run, by name from the reporters page. |
output | string | The directory file reporters write to. |
absolute | boolean | Absolute paths in reports. |
blame | boolean | Add git blame data to every clone. |
sarifErrorTokens | number | SARIF results of at least this many tokens are errors; the rest stay warnings. |
noColors | boolean | No ANSI colors in the console. |
silent | boolean | No progress and no result on the console. |
noTips | boolean | No tips after detection. |
summary | boolean | Append the codebase summary: top files and folders by tokens, lines, size and complexity. |
summaryTop | number | Rows in each summary list. |
summaryBy | string | The summary's ranking: tokens, lines, size or complexity. |
Gates
| Key | Type | What it decides |
|---|---|---|
threshold | number | The duplication percentage above which the run exits with code 1; a run that lands exactly on it passes. |
exitCode | number | The exit code when at least one clone was found. |
baseline | string | The baseline file; clones absent from it are new. See baseline. |
failOnNewClones | number | Exit with code 1 when more than this many new clones are found; needs baseline or baselineFromRef. |
failOnEmpty | boolean | Exit with code 1 when the scan analyzes no files. |
baselineFromRef | string | A git ref to build the baseline from, such as origin/main. |
Other runs of the same binary
| Key | Type | What it decides |
|---|---|---|
history, historySince | string | The commit range or the start date of a history run. |
historyEvery, historyLimit | number | How the history series is sampled. |
deadCode | boolean or object | true makes the run a dead code run; an object is its section and turns the run on only with "enabled": true. dead-code and basta are aliases. |
deadCodeCategories | array of strings | The dead-code findings to report. |
minConfidence | number | Drop dead-code findings below this confidence, 0 to 100. |
entry | array of strings | Globs of extra dead-code entry points. |
includeTests, includeEntryExports | boolean | Report dead code in test files, and the exports of entry files. |
health | object | Tuning and external metrics of the health score; it does not switch --health on. |
healthInput | string | A JSON file with metrics from other tools for the health score. |
lsp | object | What --lsp runs for this project; see editors. |
Kebab-case spellings of the same keys (min-tokens, ignore-pattern, cross-formats and so on) are accepted, so a v4 file works unchanged: noSymlinks is translated into followSymlinks, the v4 keys gitignore, debug, verbose, config and xslHref are ignored without a word, and keys that v5 removed (store, cache, reportersOptions and others) get a hint. A "//" key is ignored as well, which is the way to leave a comment in JSON.
A key jscpd does not know is reported as unknown field 'name' and the run goes on. A key with a value of the wrong type, say a string where an array is expected, is reported and dropped, and the other keys of the file still apply.
package.json form
The same keys under a jscpd key, with the same rules. jscpd reads them only when no .jscpd.json and no .config/jscpd.json is in the working directory.
{
"name": "my-app",
"jscpd": {
"threshold": 3,
"ignore": ["**/node_modules/**", "**/dist/**"],
"reporters": ["console", "html"]
}
}
Detection modes
mode decides which tokens the tokenizer keeps before matching:
| Mode | Left out before matching |
|---|---|
mild | Whitespace. This is the default. |
weak | Whitespace and comments. --skip-comments on the command line is the same thing. |
strict | Nothing: every token the tokenizer produces counts, whitespace and comments included. |
Regions between jscpd:ignore-start and jscpd:ignore-end and the matches of ignorePattern are left out in every mode. JavaScript and TypeScript comments never produce tokens, so weak changes nothing for them; it matters for the formats of the generic tokenizer, C#, Java, Go, Python and most others, where a repeated license header alone can be a clone. Modes decide what an exact clone may differ in; clones that differ in names or values need ignoreIdentifiers and ignoreLiterals, see Types of code clones.
Ignoring code
Four mechanisms, from whole files down to one region of one file. fixtures/ignore-demo in the jscpd repository has one directory per mechanism, and the outputs below come from it.
Files
ignore takes globs for files and folders to leave out; on the command line, --ignore takes them comma-separated. In the glob directory, src/checkout.js is copied into vendor/ and generated/:
jscpd --ignore "**/vendor/**,**/generated/**" .
No duplicates found.
Found 0 clones.
In the file the same setting is "ignore": ["**/vendor/**", "**/generated/**"].
Inside a git repository, jscpd also skips what .gitignore and .git/info/exclude skip, so build output and vendored code are usually out already; .ignore files work in any directory. noGitignore: true (--no-gitignore) turns the .gitignore part off. Dotfiles are scanned like any other file.
A region of every file
ignorePattern takes regular expressions. jscpd matches each one against the raw text of every file before tokenization and leaves out the tokens that overlap a match; the positions reported for the rest do not shift. The syntax is that of the Rust regex crate, which has no look-around and no backreferences, and a pattern that does not compile is skipped with a warning. The regex directory holds two C# files that share nothing except a 13-line license comment, which the generic tokenizer turns into tokens:
{
"ignorePattern": ["(?s)\\A/\\*.*?\\*/"]
}
jscpd .
Using config from .jscpd.json
No duplicates found.
Found 0 clones.
JSON escaping doubles the backslashes: the expression jscpd runs is (?s)\A/\*.*?\*/, a block comment at the very start of the file. --ignore-pattern on the command line splits its value on commas, so a pattern that contains one, such as {1,3}, belongs in the file.
A region of one file
Put jscpd:ignore-start and jscpd:ignore-end in comments that are valid for the language. Everything between them is left out of detection, in every mode. The markers directory marks a generated block in two Python modules:
"""Stock report schema."""
# jscpd:ignore-start
# --- generated by schemagen 2.4, do not edit ---
FIELDS = (
("id", "integer", False),
("sku", "string", False),
("warehouse", "string", False),
("quantity", "integer", False),
("reserved", "integer", True),
("updated_at", "timestamp", False),
)
FIELD_NAMES = tuple(name for name, _, _ in FIELDS)
NULLABLE = frozenset(name for name, _, nullable in FIELDS if nullable)
# --- end generated ---
# jscpd:ignore-end
In JavaScript the markers are // jscpd:ignore-start and // jscpd:ignore-end, in HTML and Markdown <!-- jscpd:ignore-start --> and <!-- jscpd:ignore-end -->. The language server inserts them for you with its "Ignore this clone" action.
Formats
format names the formats to scan; without it jscpd scans every format it knows. formatsExts maps extra extensions to a format and formatsNames maps file names such as Makefile or Dockerfile to one. Supported formats lists the names, the extensions and the shebang lines that select each format, and cross-format detection explains crossFormats and the files that embed several languages.
Checking the merged config
--debug prints the configuration a run would use, file and flags merged, and exits without scanning:
jscpd --debug
Using config from .jscpd.json
{
"paths": [
"."
],
"min_tokens": 50,
"min_lines": 5,
"max_lines": null,
"max_gap_lines": 0,
"similarity": 1.0,
"semantic": null,
"kind": [],
"mode": "mild",
"formats": [],
"ignore": [],
"ignore_patterns": [
"(?s)\\A/\\*.*?\\*/"
],
"reporters": [
"console"
],
"output_dir": "report",
The output goes on with every other setting; this is its start in the regex directory above. The names in it are the internal ones and differ from the file's keys: minTokens shows as min_tokens, output as output_dir, ignorePattern as ignore_patterns and format as formats. A key you set that is missing here was misspelled, and the warning above the JSON names it.
Recipes
jscpd --pattern "src/**/*.ts" .scans only the files a glob matches.jscpd --formats-names "makefile:Makefile,GNUmakefile;docker:Dockerfile" .gives files without an extension a format.jscpd --max-size 5mb .raises the file size above which files are skipped.jscpd --ignore-case .compares tokens without regard to case (experimental).jscpd --follow-symlinks .scans through symbolic links; a file reachable by several paths counts once.
See also
- CLI options: every flag with its config key and default.
- Configuration basics: the five or six keys to set first.
- Exit codes: what
threshold,exitCode,failOnNewClonesandfailOnEmptydo to the exit status.