Skip to main content
Version: 3.x

Duplicated code

Detect copy-pasted code across a project or monorepo - repeated code blocks and JSX elements that can be extracted into shared implementations.

It reports whole repeated code blocks, not repeated lines, because a block is already something you can extract: a duplicated function body becomes a shared function, a duplicated JSX element becomes a component. See How duplicated code detection works for the reasoning and for what comparison ignores.

The check reports counts. To see the actual snippets, run rev-dep duplicated-code - the failure output prints the exact command that reproduces the detection.

Adopting it on an existing project

Most projects have duplication before they start checking for it. A snapshot acknowledges what exists today, so the check reports only what changes from here.

rev-dep config init already writes snapshotPath for you, without creating the file - the first run reports what exists and tells you the baseline is missing. To set it up by hand:

{
"workspaces": [
{
"path": ".",
"duplicatedCodeDetection": {
"enabled": true,
"snapshotPath": "duplicated-code.json"
}
}
]
}
rev-dep config run --update-snapshot

Commit the file. From then on the check reports the delta: new duplication fails, acknowledged duplication passes.

When you resolve some of the acknowledged duplication, run rev-dep config run --update-snapshot again and commit the updated baseline, so it reflects the new state.

Until the baseline exists the check still reports the duplication it finds, and fails - a configured baseline that is not there is a broken setup, not a clean one. Without snapshotPath at all, any duplication fails.

snapshotPath is resolved against the workspace the detection sits on, and two detections resolving to the same file is a config error.

The snapshot is versioned by its schemaVersion field and has a published JSON schema. A file written at any other schema version is rejected rather than half-trusted, since the settings it records are applied to the run - re-create it with --update-snapshot.

Where to put it in a monorepo

On the root workspace, which is what rev-dep config init generates. Duplication is a property of the repository: a block pasted from one package into another is one finding, not one per package, and the root workspace already scans every package below it.

Per-package detections are for the case where a package must be held to a different standard - stricter floors on a library, say - and each one then needs its own snapshotPath.

Comprehensive Configuration Example

{
"workspaces": [
{
"path": ".",
"duplicatedCodeDetection": {
"enabled": true,
"blindIdentifiers": true,
"minTokens": 50,
"minLines": 3,
"minDepth": 2,
"minStatements": 2,
"minDuplicates": 2,
"skipObjects": true,
"ignoreFiles": ["**/*.test.ts"],
"snapshotPath": "duplicated-code.json"
}
}
]
}

A workspace may configure several detections as an array - an exact comparison and a looser structural one, say. Each is reported on its own terms, and each needs its own snapshotPath.

Options

OptionDefaultEffect
enabled-Enable the detection.
blindIdentifiersfalseNames are wildcards, so a renamed copy still counts.
blindStringsfalseString and template contents are wildcards.
blindNumbersfalseNumeric literals are wildcards.
minTokens50Smallest duplication to report, in tokens.
minLines3Smallest duplication to report, in lines.
minDepth0Smallest nesting depth, counting the block itself. 2 requires at least one nested level.
minStatements0Smallest statement count. Applies only to statement blocks.
minDuplicates2How many copies a chunk needs. Raise to 3 to tolerate a single copy-paste.
skipObjectsfalseDo not report duplications that are only object literals.
ignoreFiles[]Glob patterns to leave out of the analysis.
snapshotPath-Path to a committed baseline, relative to the workspace.

Keywords are never blinded, so if never matches while however much is ignored.

minTokens is measured in tokens, not characters, because a token count does not change when a blind* option is applied - one number means the same amount of code whatever is being ignored.

minDepth and minStatements measure complexity rather than size, which is what separates a duplicated three-key config object from duplicated logic. No size floor can: the object's keys and string values may be long. minStatements does not apply to object literals or JSX elements, which are expressions and have no statements to be short of - use minDepth for those.

Interaction with ignoreFiles

ignoreFiles is applied before anything is counted, not to the finished report. A chunk appearing in three files with one of them ignored is a two-copy duplicate, so with minDuplicates: 3 it is not reported at all.

The config's top-level ignoreFiles applies as well, and is recorded in the snapshot so the reproduction command sees the same files.

Also referred as

Duplicated Code Detection is also known as:

  • Copy-paste detection
  • Code clone detection
  • Duplicate code detection
  • Repeated code blocks
  • CPD
  • jscpd
  • duplicate code javascript
  • duplicate code typescript
  • find duplicate code in monorepo
  • DRY violations