Getting Started

Migrating from v4 to v5

Guide for upgrading from jscpd v4 (TypeScript) to v5 (Rust).

jscpd v5 is a complete Rust rewrite that replaces the TypeScript engine with a native binary. This guide covers what changed, what's compatible, and how to run both versions side by side.

Why v5?

v4 (TypeScript)v5 (Rust)
SpeedBaseline24-37x faster
RuntimeRequires Node.jsSelf-contained binary
Install size~15 MB (node_modules)~5 MB (single binary)
JS/TS tokenizerPrismJSOXC parser
Git blameShells out to git CLIIn-process via gitoxide
Parallel detectionNoYes (--workers)

Detection Speed

TargetFilesSizev4 (TypeScript)v5 (Rust)Speedup
fixtures5481.5 MB1.03s0.03s34.3x
Svelte8,96338 MB15.80s0.43s36.9x
CopilotKit17,092159 MB82.89s3.44s24.1x

Benchmarked on macOS (Apple Silicon), 10 runs for fixtures/Svelte, 3 for CopilotKit.

Git Blame Speed

The --blame flag enriches clones with git author data. v4 shells out to git blame per file; v5 uses gitoxide for in-process blame.

Targetv4 --blamev5 --blameSpeedup
fixtures (548 files)3.57s0.13s27.5x

v4's blame mode is 3.5x slower than its own non-blame mode (3.57s vs 1.03s). v5's blame adds only ~0.10s (0.13s vs 0.03s).

Detailed Timing

fixtures (548 files, 1.5 MB)

Metricv4v5
Mean real time1.030s0.030s
Std dev0.042s0.000s
Mean user time1.174s0.085s
Mean sys time0.074s0.050s

Svelte (8,963 files, 38 MB)

Metricv4v5
Mean real time15.803s0.428s
Std dev1.010s0.021s
Mean user time16.075s0.553s
Mean sys time0.738s1.110s

CopilotKit (17,092 files, 159 MB)

Metricv4v5
Mean real time82.890s3.440s
Std dev4.086s0.699s
Mean user time100.020s7.323s
Mean sys time18.263s3.100s

Key Observations

  1. Startup overhead: v5's native binary has near-zero startup cost. v4's Node.js runtime adds ~1s even for tiny fixtures.
  2. Scaling: CopilotKit (159 MB) takes only 3.4s with v5 vs 83s with v4.
  3. CPU utilization: v5's higher user time relative to real time (e.g., CopilotKit: 7.3s user vs 3.4s real) shows effective multi-threading. v4 is single-threaded (user ≈ real).
  4. Consistency: v5 has tighter variance across all runs.

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 jscpd as a subprocess and parse JSON output
  • Rust crate: Use cpd-finder in Rust applications
  • v4 package: Install jscpd@4 for the Node.js API (see coexistence below)
  • Server: Use the jscpd-server REST API

--store leveldb Removed

The external store backend (LevelDB/Redis) is not supported in v5 — the Rust engine is fast enough that caching isn't needed. The --store and --store-path flags are accepted for compatibility but do nothing.

Reporter Name Change

  • v4's full reporter → v5's consoleFull

Output Filenames

Some reporter output filenames changed:

Reporterv4v5
HTMLhtml/index.htmljscpd-report.html
JSONjscpd-report.jsonjscpd-report.json (unchanged)
XMLjscpd-report.xmljscpd-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

v4v5Notes
--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(removed)Use --verbose instead
--verbose-v / --verboseShort flag -v now shows version; use --verbose for verbose output
--store leveldbaccepted, ignoredNo external store in v5
--store-pathaccepted, ignoredNo external store in v5
-w / --workersNew: control parallel detection threads
--no-colorsNew: disable ANSI color output
--skipComments--skip-commentsKebab-case (camelCase still works in config)
--skipLocal--skip-localKebab-case (camelCase still works in config)
--noTips--no-tipsKebab-case (camelCase still works in config)
--ignoreCase--ignore-caseKebab-case (camelCase still works in config)
--formatsExts--formats-extsKebab-case (camelCase still works in config)
--formatsNames--formats-namesKebab-case (camelCase still works in config)
--exitCode--exit-codeKebab-case (camelCase still works in config)

Note: CamelCase names still work in .jscpd.json config files. The kebab-case change only affects CLI flags.

@jscpd/* Packages

The v4 ecosystem packages are replaced by Rust crates:

v4 Packagev5 Replacement
jscpdjscpd@5 (npm, installs native binary) or cargo install jscpd
@jscpd/corecpd-core (Rust crate)
@jscpd/findercpd-finder (Rust crate)
@jscpd/tokenizercpd-tokenizer (Rust crate)
@jscpd/html-reporterBuilt into cpd-reporter (Rust crate)
@jscpd/badge-reporterBuilt into cpd-reporter (Rust crate)
@jscpd/sarif-reporterBuilt into cpd-reporter (Rust crate)
@jscpd/leveldb-storeRemoved (not needed)
jscpd-serverStill available (Node.js based, unchanged)

Compatible Features

These work the same in both v4 and v5:

  • CLI interface: Same command name (jscpd), same flags structure
  • .jscpd.json config: Fully compatible (uses jscpd as 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: --threshold and --exit-code work the same

Coexistence

You can run both v4 and v5 in the same project:

Install v5 globally, v4 as a project dependency

# Global: v5 (Rust binary)
npm install -g jscpd

# Project: v4 (Node.js API)
npm install --save-dev jscpd@4

Then use v5 from the CLI and v4 from Node.js scripts:

# CLI uses v5 (fast)
jscpd ./src --reporters json

# Node.js script uses v4
node scripts/detect.js
// scripts/detect.js — uses 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:

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

v5 ignores store and storePath fields if present (they're not needed with the Rust engine). Everything else maps directly.

Migration Checklist

  • Install v5: npm install -g jscpd@5 or cargo install jscpd
  • Replace --reporters full with --reporters consoleFull
  • Update HTML report output path from html/index.html to jscpd-report.html
  • Remove --store leveldb / --store-path flags (accepted but ignored)
  • Replace --noSymlinks with --follow-symlinks (inverted behavior)
  • Replace --gitignore with default behavior (now automatic)
  • Replace --debug with --verbose
  • Update Node.js API calls to use CLI, Rust crates, or jscpd-server
  • Pin jscpd@4 as a project dependency if you need the Node.js API