Changelog
Overview
For the full, up-to-date changelog, see the v5 (Rust) changelog in the jscpd GitHub repository. The v4 (TypeScript) history lives on the master-v4 branch.
v5.2.0 (Latest)
Released 2026-09-08
New Features
- Type-2 clone detection —
--ignore-identifiers,--ignore-literalsand--ignore-annotations(config keysignoreIdentifiers,ignoreLiterals,ignoreAnnotations, Action inputs of the same names) find blocks that differ only in names, literal values or annotations. Every clone now carries akind,exactorrenamed; a run without the flags is unchanged apart from the additive"kind": "exact"JSON field. (#998, #1019) - Near-miss clone merging with
--max-gap-lines N— clones of one file pair separated by at mostNunmatched lines in both files are merged into one clone of kindsimilarwith asimilarityscore, so a copy with a line inserted in the middle is reported once instead of as two halves. Default0keeps the old behaviour. (#999, #1020, #1030) - Function-level similarity for JavaScript and TypeScript with
--similarity RATIO— compares whole functions by syntax-tree structure and reports pairs at or above the ratio assimilarclones; names and literals do not take part, so a renamed copy scores1.0and a couple of inserted statements about0.75. The default1means exact matches only. The MCPcheck_duplicationtool takes the same argument. See MCP Server. (#999, #1021) - Clone kinds in every reporter — console,
ai, JSON, XML, HTML, Xcode, SARIF (rulesjscpd/renamed-codeandjscpd/similar-code) and CodeClimate all show whether a clone is exact, renamed or similar, and how a similar one was found (gaporast). (#1019, #1021, #1030) - Tips are skipped when stdout is not a terminal — pipes, files, CI logs and agent hooks no longer receive the tips and sponsor lines;
JSCPD_NO_TIPSjoinsCIas an environment switch and--no-tipsstays. (#1008, #1029) - MCP: fully described tool definitions — titles, read-only annotations, parameter examples and descriptions that say when to use each tool; names and schemas are unchanged. See MCP Server. (#1028)
Bug Fixes
- Config-file
ignorePatternentries without*or?silently did nothing — they were treated as relative paths; they are now applied verbatim, and an invalid regex prints a warning. See Configuration. (#997) - JavaScript/TypeScript files with a recoverable parse error could not match clean files — any parser diagnostic sent the file to the fallback tokenizer; tokens now come from the lexer whenever the parser did not fail outright. (#1023, #1024)
- Markdown inherited the C comment style — a
/*or//in prose swallowed the rest of the file; Markdown now has no comment syntax. (#1026) - Vue template clones were reported with wrong ranges — the html token stream is now in source order and the wrapper tags are left out of it, so a template clone gets the template's own line range. (#1031)
Other
- Runnable demos under
fixtures/— every feature and fix above ships a demo directory with a README listing each command and its expected output. - Docs: ignore patterns and inline markers are documented in the v5 reference. (#993, #996)
Thank You ❤️
- @7487 for skipping the tips on a non-terminal stdout (#1029)
- @zero-stroke for the Vue template clone ranges (#1031)
- @kwesolowski for the Markdown comment-style fix (#1026)
- @w3lld1 for documenting ignore patterns and inline markers (#996)
v5.1.2
Released 2026-09-03
New Features
- Linux ARM64 musl prebuilt binaries — npm installs on Alpine and other musl-based ARM64 Linux systems now get a native binary from the new
jscpd-linux-arm64-muslpackage, bringing the prebuilt platform count to 8. See Platform Binaries. (#988) - Docker image
ghcr.io/kucherenko/jscpd— a multi-arch (amd64/arm64) distroless image built from the release binaries ships with every release, taggedlatest,5,5.1and the exact version, with SLSA provenance and an SBOM. See Using Docker. (#988) cargo-binstallmetadata — the crate now pointscargo binstall jscpdat the release tarballs. The 5.1.2 metadata still makes binstall fall back to compiling from source because the crate ships two binaries from one archive file; a follow-up release fixes the layout. (#988)
Bug Fixes
jscpd --versionand--helpnow sayjscpd— the command name was the literalcpd, sojscpd --versionprintedcpd 5.1.1. The name now follows the invoked executable. (#988)- Windows: drive-anchored
--patternvalues are treated as absolute — patterns likeC:\src\**\*.tswere never recognized as absolute because of an impossible character check. (#988) - pre-commit hook passed v4-only flags — the hook definition still used
--gitignore --exitCode, which the v5 CLI rejects; it now passes--exit-code 1. (#989) - Unsupported-platform error is actionable — the npm launchers now name the host (
os/arch (libc)), list the supported platforms and point tocargo install jscpd. (#988)
Other
- Repository split:
masteris v5-only — the TypeScript v4 engine moved to themaster-v4branch and releases from there under thelatest-4npm dist-tag. See jscpd v4. (#989, #990) - Floating
v5tag for the GitHub Action —uses: kucherenko/jscpd@v5follows the latest 5.x release. (#988) - Signed release assets — every release archive carries a Sigstore keyless signature (
<asset>.sigstore.json) in addition to SLSA provenance. (#988) - crates.io metadata and CI — all crates declare repository, docs, keywords and categories; Windows joined the PR build matrix; a smoke test runs the release binary against the fixtures corpus; nightly
cargo audit/cargo deny. (#988)
v5.1.1
Released 2026-08-31
Bug Fixes
jscpdon npm installed the 5.0.16 engine instead of 5.1.0 — thejscpdwrapper package published itsoptionalDependenciespinned to the5.0.16platform binaries, sonpm i [email protected]resolved a native binary one release behind andjscpd --versionreportedcpd 5.0.16; everything 5.1.0 fixed was therefore absent forjscpdusers, including the Windows--baseline-from-reffix. Thecpdpackage and the platform packages were unaffected. The release script now updates the wrapper's version and its platform pins independently and fails the release if any pin disagrees with the release version.
Other
- Declared MSRV corrected to 1.96 — the workspace advertised
rust-version = "1.87"on crates.io, a floor the crate could not build on (theoxcparser crates require 1.96). CI now builds at exactly the declared MSRV so it cannot drift again.
v5.1.0
New Features
- Clone baseline (
--baseline,--update-baseline,--fail-on-new-clones) — gate CI on new duplication only: a committed baseline file records fingerprints of accepted clones, and only clones absent from it fail the build, so legacy duplication is tolerated while regressions are caught. See Gate on New Duplication. (#944) - Ephemeral baseline from a git ref (
--baseline-from-ref) — stateless PR gating without a committed file: scans the base ref in a temporary worktree and reports clones that don't exist there. (#944) - OpenMetrics reporter (
--reporters openmetrics) — duplication gauges in the OpenMetrics text format, ready for GitLab CIartifacts:reports:metrics. See OpenMetrics Reporter. (#422) - CodeClimate / GitLab Code Quality reporter (
--reporters codeclimate, aliasgitlab) —gl-code-quality-report.jsonfor GitLab'sartifacts:reports:codequality, so duplicates appear as code quality issues in merge requests. See CodeClimate Reporter. (#958) - Windows on ARM support — npm installs pick a native
aarch64-pc-windows-msvcbinary on Windows ARM64. (#963) - Config discovery in
.config/— jscpd also finds its config at.config/jscpd.jsonper the dot-config convention; a root.jscpd.jsonstill wins. (#979)
Bug Fixes
- Unknown
--formatvalues warn instead of silently scanning 0 files —--format cs(or any typo) no longer looks identical to a clean scan in CI. (#964) - Nix flake builds again — the Rust toolchain is pinned to an exact patch version, so the manifest hash can no longer drift. (#976)
- Windows:
--baseline-from-refno longer reports every clone as new — Windows verbatim paths were truncated by the format-suffix stripper, silently breaking snippet reads behind the baseline fingerprints; fingerprints are also line-ending agnostic now.
Thank You ❤️
- @luchsamapparat for contributing Windows on ARM support (#963)
- @dmromanov for proposing the OpenMetrics reporter (#422)
- @beanaroo for proposing the GitLab / CodeClimate Code Quality report format (#958)
- @MRDGH2821 for proposing config discovery from the
.config/subfolder (#979) - @zbcoding for reporting the silent unknown-
--formatbehavior (#964) - @eaves-dropper for reporting the Nix build failure (#976)
v5.0.16
New Features
- MCP server over stdio (
--mcp) —cpd --mcp /path/to/projectserves the Model Context Protocol on stdin/stdout, the transport MCP clients like Claude Desktop, Claude Code, and Cursor spawn and manage themselves — no port, no network policy. The project is scanned once at startup and kept in memory, so snippet checks answer in milliseconds. Tools:check_duplication,get_file_clones,get_statistics,check_current_directory. See MCP Server. (#891) - Codebase summary (
--summary) — opt-in refactoring-hotspot overview appended to the run output: top files and folders ranked by tokens, lines, size, or a token-based complexity estimate, with each file's duplication share.--summary-top <n>sets the list length,--summary-by tokens|lines|size|complexitypicks the metric. Renders inconsole/console-full, compactly in theaireporter, and as an additivesummarykey in the JSON report. (#934) - Isolated folder groups (
--skip-isolated) — skip duplication between monorepo folders owned by different teams:--skip-isolated "packages/team-a|packages/team-b"drops clones spanning two different folders of the same group, while duplication inside one folder or against shared code is still reported. Config file:"skipIsolated": [["packages/a", "packages/b"]]. (#628)
Security
- Supply-chain hardening (OpenSSF Scorecard) — GitHub Actions pinned to commit SHAs, least-privilege workflow tokens,
SECURITY.mdwith private vulnerability reporting, protectedmasterbranch
Thank You ❤️
- @hanzhangyu for proposing isolated folder groups for monorepos and contributing the original
skipIsolatedimplementation (#628)
v5.0.15
New Features
- SARIF: size-based severity — new
--sarif-error-tokens <N>flag: clones with at least N tokens are reported at levelerrorwhile smaller ones staywarning; when duplication exceeds--threshold, all results becomeerror. (#908) - SARIF: clone fingerprints — each result carries a
partialFingerprintsentry (jscpdCloneHash/v1) for cross-run result identity in GitHub code scanning. (#909) - SARIF: related-location messages & richer rule metadata — counterpart locations get linked messages, and the
jscpd/duplicate-coderule has full metadata for SARIF viewers. (#911, #914)
Bug Fixes
- Scan-root-relative report paths — report paths are relative to the scanned directory again (as in 4.x); fixes empty snippets and unresolvable paths when scanning from outside the target directory. (#872, #892)
- Report version stamping — SARIF and HTML report versions now match
--version. (#915) - Multi-root blame attribution and git root discovery fixes
Thank You ❤️
- @chrisc-onaorg for the SARIF fingerprints, related-location messages, and rule metadata
- @darronz for the scan-root-relative paths fix
- @nvuillam for proposing size-based SARIF severity
v5.0.14
New Features
- Cross-format clone detection —
--cross-formatsdefines groups of related formats sharing one comparison pool, e.g.--cross-formats "javascript,typescript"or thejs-tspreset (javascript,jsx,typescript,tsx). When a group mixes TypeScript with JavaScript, TS files are compared with erasable type syntax stripped, sofunction f(a: number): voidmatchesfunction f(a). Also configurable ascrossFormatsin.jscpd.json/package.json. See Configuration → Cross-Format Groups. (#810)
Bug Fixes
- Prose-only Markdown files are now analyzed —
.mdfiles without fenced code blocks were silently skipped; duplicated prose is now detected as clones, while embedded code fences keep being detected under their own sub-format pools. (#883)
v5.0.6 – v5.0.13 highlights
- v5.0.11 — Razor (
.razor) tokenizer support - v5.0.10 — scan-root-relative paths in all reporters;
--skip-localnow matches v4 semantics - v5.0.9 — GitHub Action for jscpd v5; fixed platform binary resolution for nested installs
- v5.0.8 — fixed mmap exhaustion on very large repos; fixed
--patternmatching with absolute scan roots - v5.0.7 — fixed stack overflow on deeply-nested JS/TS;
--max-sizedefaults to1mb - v5.0.6 — v4 config backward compatibility (
path,pattern,ignore,ignorePattern);jscpdnpm wrapper package; memory-mapped I/O and SIMD line counting - v5.0.12 / v5.0.13 — dependency updates and npm packaging fixes
Full details for every release are on GitHub Releases and in the Rust changelog.
v5.0.4
The Rust Rewrite
jscpd v5 is a complete Rust rewrite — a new Rust engine shipped as a self-contained native binary with zero Node.js dependency, bringing parallel detection, near-zero startup cost, and a large performance improvement over the TypeScript engine.
See the Migration Guide for what changed and how to upgrade.
New Features
- Native binary — no Node.js runtime required. Install via
npm install -g jscpd,cargo install jscpd, or direct download. - 13 reporters — console, console-full, json, xml, csv, html, markdown, sarif, ai, badge, xcode, threshold, silent.
- OXC-based JS/TS tokenization — JavaScript/TypeScript/JSX/TSX tokenization now uses the OXC parser instead of PrismJS for better accuracy and speed.
- Parallel detection —
--workers Ncontrols thread count. Detection runs in parallel across format groups. - CLI alignment — new
--absolute,--ignore-case,--formats-exts,--formats-namesflags; fixed--threshold; improved--max-size. - Side-by-side blame — console-full reporter shows side-by-side blame comparison with
--blame. - Dual npm package —
jscpd@5andcpdboth install the same Rust binary via platform-specific optional dependencies.
Breaking Changes from v4
- No Node.js API —
import { jscpd } from 'jscpd'is not available in v5. Use the CLI (with JSON output) or the Rust crates instead. - No
--store leveldb— external store backends are not supported in v5 (detection is fast enough without caching). - Reporter name change — v4's
fullreporter is nowconsole-fullin v5. - Output filenames — HTML reporter outputs
jscpd-report.htmlat the output directory root. - Token counts — may differ by 1-2% due to the Rust tokenizer using OXC for JS/TS.
- Format count — 223 formats (vs. v4's 224).
v5.0.3
- Full Rust CLI feature parity with TypeScript jscpd
- Cross-platform binary distribution via npm platform packages
- CLI short-form aliases matching TypeScript jscpd conventions
- Time reporter for execution timing
- ReportContext struct for extensible reporter signatures
v5.0.2
- Fixed Vue SFC tokenization (each block dispatched to its own sub-format)
- Fixed entire-file duplicates silently dropped by RabinKarp store flush logic
- Fixed ReDoS hang on Lisp/Elisp files
- Fixed crash on malformed package.json when reading config
v5.0.1
- Initial Rust workspace with cpd-core, cpd-tokenizer, cpd-finder, cpd-reporter, and jscpd crates
- Cross-format detection for Vue SFC, Svelte, Astro, and Markdown
- Shebang detection for extensionless scripts
v5.0.0
- First stable Rust release — replaces the TypeScript CLI with a native binary
- Reporter trait signature changed to use ReportContext instead of Statistics directly
v4.2.5
- JSON reporter token count fix, gitignore parent-directory walk, Commander v15, Vitest 4.1.0
v4.2.3
- fix(finder): resolve relative ignore patterns against scan dirs (#611)
- Dependency bumps across all packages
v4.2.2
- fix(tokenizer): resolve quadratic bash tokenization hang
v4.2.1
- fix(tokenizer): fix tokenization issue for cross-format detection
v4.2.0
New Features
- Custom tokenizer backend —
@jscpd/tokenizernow uses a self-contained reprism-based engine. ~11.5% faster tokenization on real projects (avg 1126 ms → 997 ms on a 548-file, 223-format scan). - Cross-format detection — Vue SFC (
.vue), Svelte (.svelte), Astro (.astro), and Markdown files are tokenized per-block/per-section, enabling duplicate detection between embedded blocks and standalone source files. - 223 supported formats — Apex, CFML/ColdFusion, GDScript, Svelte, Astro, and 70+ additional languages (up from 152). Run
jscpd --listto see the full list. - Shebang detection — extensionless executable scripts are auto-detected via their
#!shebang line. --store-path— specify a custom directory for the LevelDB token cache, eliminating collisions when multiple jscpd processes run concurrently.--skipComments— shorthand for--mode weak. Strips comments before tokenization.--formats-names— map specific filenames (e.g.Makefile,Dockerfile) to a detection format.
Bug Fixes
- Entire-file duplicates silently dropped (#728) — fixed RabinKarp end-of-file flush bug in
@jscpd/core. - ReDoS hang on Lisp/Elisp files (#737) — replaced catastrophic backtracking with a linear regex in
@jscpd/tokenizer. - Process crash on malformed
package.json(#739) — now emits a warning and continues. - Vue SFC cross-file detection broken — blocks now use resolved sub-format namespace.
- Vue SFC incorrect column numbers — fixed in
@jscpd/tokenizer. - 50 dependency security vulnerabilities remediated.
v4.1.1
- Update hash function, improve performance and keep browser support.
v4.1.0
- AI reporter (
--reporters ai) — compact, token-efficient output for LLMs. --noTipsflag — suppress usage-tip messages after detection.- Execution timer — timing summary shown after detection completes.
- Tokenizer speed — lazy Prism grammar loading, O(1) extension lookup, native MD5 hashing.
- New languages: Apex and CFML (ColdFusion).
- Test coverage raised to 98%+.
Earlier releases (v1.x – v4.0.x)
See the root CHANGELOG on the master-v4 branch for the full history of jscpd versions 1.0.0 through 4.0.9, covering the tool's origins, the TypeScript rewrite, LevelDB store, pluggable reporters, monorepo extraction, and all the improvements made from 2018 through 2024.