Migrating from v4 to v5
jscpd v5 is a complete Rust rewrite that replaces the TypeScript engine with a self-contained native binary. This guide covers what changed, what's compatible, and how to stay on v4 if you need to.
The v4 (TypeScript) source and documentation live on the master-v4 branch of the jscpd repository; this site documents v5, plus a v4 overview page.
For benchmark comparisons against other tools (jscpd-rs, Duplo, Simian, PMD CPD, and more), see Detection Speed.
What Changed
| v4 (TypeScript) | v5 (Rust) | |
|---|---|---|
| Engine | Node.js | Native Rust binary |
| Runtime | Requires Node.js 18+ | None — self-contained binary |
| Install size | ~15 MB (node_modules) | ~5 MB (single binary) |
| JS/TS tokenizer | PrismJS | OXC parser |
| Parallel detection | No | Yes (--workers) |
| Programmatic API | Node.js (import { jscpd } from 'jscpd') | Rust crates (cpd-finder, cpd-core) |
| MCP server | Separate jscpd-server package | Built in: cpd --mcp |
| External store | LevelDB / Redis via --store | Not needed; flag accepted and ignored |
Breaking Changes
Node.js API Removed
The v4 programmatic API is not available in v5:
// ❌ v4 only — not available in v5
import { jscpd } from 'jscpd';
const clones = await jscpd(['', '', './src', '-r', 'json']);
Alternatives:
- CLI: Use
jscpdas a subprocess and parse JSON output (--reporters json) - Rust crate: Use
cpd-finderin Rust applications - MCP: Use
cpd --mcpto expose detection to AI assistants - Stay on v4: Keep
jscpd@4as a project dependency (see Staying on v4 below)
--store leveldb Removed
The external store backend (LevelDB/Redis) is not supported in v5 — the Rust engine does not need a token cache. The --store and --store-path flags are accepted for compatibility but do nothing.
Reporter Name Change
- v4's
fullreporter → v5'sconsoleFull
Output Filenames
Some reporter output filenames changed:
| Reporter | v4 | v5 |
|---|---|---|
| HTML | html/index.html | jscpd-report.html |
| JSON | jscpd-report.json | jscpd-report.json (unchanged) |
| XML | jscpd-report.xml | jscpd-report.xml (unchanged) |
Token Counts
Token counts may differ by 1-2% from v4 due to the OXC tokenizer handling JS/TS/JSX/TSX differently than PrismJS.
CLI Flag Changes
| v4 | v5 | Notes |
|---|---|---|
--noSymlinks | (removed) | Symlinks are not followed by default; use --follow-symlinks to follow |
--gitignore | (removed) | .gitignore is respected by default; use --no-gitignore to disable |
-g | (removed) | Use --no-gitignore to invert |
-n | (removed) | Symlinks are not followed by default |
-d / --debug | --debug | Short -d removed. Prints the merged config (CLI + config file) as JSON and exits without running detection |
--verbose | -v / --verbose | Short flag -v now shows version; use --verbose for verbose output |
--store leveldb | accepted, ignored | No external store in v5 |
--store-path | accepted, ignored | No external store in v5 |
| — | -w / --workers | New: control parallel detection threads |
| — | --no-colors | New: disable ANSI color output |
--skipComments | --skip-comments | Kebab-case (camelCase still works in config) |
--skipLocal | --skip-local | Kebab-case (camelCase still works in config) |
--noTips | --no-tips | Kebab-case (camelCase still works in config) |
--ignoreCase | --ignore-case | Kebab-case (camelCase still works in config) |
--formatsExts | --formats-exts | Kebab-case (camelCase still works in config) |
--formatsNames | --formats-names | Kebab-case (camelCase still works in config) |
--exitCode | --exit-code | Kebab-case (camelCase still works in config) |
Note: CamelCase names still work in
.jscpd.jsonconfig files. The kebab-case change only affects CLI flags.
@jscpd/* Packages
The v4 ecosystem packages are replaced by Rust crates:
| v4 Package | v5 Replacement |
|---|---|
jscpd | jscpd@5 (npm, installs native binary) or cargo install jscpd |
@jscpd/core | cpd-core (Rust crate) |
@jscpd/finder | cpd-finder (Rust crate) |
@jscpd/tokenizer | cpd-tokenizer (Rust crate) |
@jscpd/html-reporter | Built into cpd-reporter (Rust crate) |
@jscpd/badge-reporter | Built into cpd-reporter (Rust crate) |
@jscpd/sarif-reporter | Built into cpd-reporter (Rust crate) |
@jscpd/leveldb-store | Removed (not needed) |
jscpd-server | Built-in MCP server: cpd --mcp (the Node.js server remains on master-v4) |
Compatible Features
These work the same in both v4 and v5:
- CLI interface: Same command name (
jscpd), same flags structure .jscpd.jsonconfig: Fully compatible (usesjscpdas the config key)- Detection modes:
strict,mild,weak— identical behavior - Cross-format detection: Vue SFC, Svelte, Astro, Markdown — same support
- Shebang detection: Same behavior
- Reporters:
console,json,xml,csv,html,markdown,sarif,ai,badge,threshold,silent— all present in both versions --ignore-pattern: Same inline ignore blocks (jscpd:ignore-start/jscpd:ignore-end)- Exit codes:
--thresholdand--exit-codework the same
Staying on v4
v4 is still published on npm under the latest-4 dist-tag and maintained on the master-v4 branch, with its own README and package docs (summarised on the v4 page). If you depend on the Node.js API, the jscpd-server package, or the LevelDB/Redis store, you can keep using it:
# Global install
npm install -g jscpd@4
# Or pin it as a project dependency
npm install --save-dev jscpd@4
Both versions can coexist — for example v5 on the command line and v4 inside Node.js scripts:
# Global: v5 (Rust binary)
npm install -g jscpd
# Project: v4 (Node.js API)
npm install --save-dev jscpd@4
// scripts/detect.js — uses the v4 Node.js API
import { jscpd } from 'jscpd';
const clones = await jscpd(['', '', './src', '-r', 'json']);
console.log(clones);
CI/CD: Pin a specific version
# GitHub Actions — v5
- name: Install jscpd
run: npm install -g jscpd@5
- name: Check duplications
run: jscpd --threshold 5 ./src
# GitHub Actions — v4 (if you need the Node.js API)
- name: Install jscpd
run: npm install -g jscpd@4
- name: Check duplications
run: jscpd --threshold 5 ./src
Configuration Compatibility
.jscpd.json files work with both v4 and v5 without changes:
{
"threshold": 0,
"reporters": ["html", "console"],
"ignore": ["**/node_modules/**"],
"absolute": true,
"minLines": 5,
"minTokens": 50
}
v5 ignores store and storePath fields if present. Everything else maps directly.
Migration Checklist
- Install v5:
npm install -g jscpd@5orcargo install jscpd - Replace
--reporters fullwith--reporters consoleFull - Update HTML report output path from
html/index.htmltojscpd-report.html - Remove
--store leveldb/--store-pathflags (accepted but ignored) - Replace
--noSymlinkswith--follow-symlinks(inverted behavior) - Replace
--gitignorewith default behavior (now automatic) - Replace
-dwith--debug(prints the merged config and exits) - Update Node.js API calls to use the CLI (JSON output), the Rust crates, or
cpd --mcp - Pin
jscpd@4as a project dependency if you need the Node.js API (see Staying on v4)