github kucherenko/jscpd v5.3.0
Release v5.3.0

latest release: basta-v0.1.2
5 hours ago

jscpd 5.3.0 adds a health score for your codebase, a full project dashboard, and a batch of fixes found during a pre-release review.

Highlights

🩺 --health — one score for the codebase. A single 0–100 number (with an A–E grade) built from three shares of the code: duplication, dead code and complexity. Small projects aren't unfairly punished — the score adjusts for project size — and a dimension jscpd can't measure (no JS/TS/Python to check for dead code, say) is left out and labeled n/a rather than silently scored as perfect. Plug in your own metrics — coverage, security scan results, whatever you track — with --health-input.

Health  B   74/100  █████████████████▊░░░░░░  93 lines of code (XS)
  duplication   75  █████████░░░  5.4% in typescript (no text)
  dead code     72  ████████▋░░░  14.0%
  complexity    76  █████████▏░░  0.0% in complex files

📊 --dashboard — the whole picture on one screen. Everything above, plus project size, a duplication breakdown by format, your most complex files, and dead-code findings by category — all in one run, one report. markdown and html reporters are new for both --dashboard and --health, alongside the existing console, json and badge (SVG) output.

--complexity — just the complexity half of --summary, without a clone scan. Handy when all you want is "what's the most complex file in here," fast.

🎯 --kind — filter clones by how they were found: exact, renamed, or similar (and, for similar, whether it was a near-miss gap merge or a structural match). Typos are caught rather than silently returning a clean report.

🧮 More accurate complexity counting. The --summary --summary-by complexity estimate now tracks real cyclomatic complexity much more closely — short-circuit operators (&&, ||) count properly across every language, branches are counted the way each language actually spells them (Rust match arms, Swift guard, Go select, and so on), and complexity is measured per function instead of per file. Validated against lizard across nine languages, with agreement on file ranking rising from 0.83 to 0.92.

🧟 --dead-code — find code nothing runs. A new engine, basta, builds your project's import graph from its real entry points and reports unused files, exports, and imports across JavaScript, TypeScript, Vue, Svelte, Astro and Python — including monorepo package names, path aliases, and framework conventions (Nuxt, SvelteKit, Astro components). Every finding comes with a confidence score, so you know how much to trust it. It's available inside jscpd as --dead-code, and also ships as its own standalone basta command.

See the full changelog for the details on all of the above.

Fixes

  • A single mistyped field in .jscpd.json no longer throws out the whole config — only the bad field is dropped, everything else still applies.
  • --dashboard/--health and their markdown/html output now escape untrusted values (file paths, custom format names, external metric IDs) before rendering them, closing off ways a crafted file name could break a table or inject content.
  • --dashboard/--health now drop the dead-code section gracefully when --format excludes every language it can analyze, instead of failing the whole report.
  • --complexity --fail-on-empty now writes its reports before failing, matching every other mode.
  • --min-confidence above 100 is now clamped (with a warning) everywhere it's read, not just in the standalone --dead-code mode.
  • Fixed a health-score bug where excluding markup duplication (HTML, CSS, templates, …) from scoring barely moved the number on real projects — it's now a proper exclusion on both sides of the calculation.
  • Windows report paths now use forward slashes consistently, matching every other platform's output.
  • Bumped basta's oxc parser crates to 0.150.

Thanks

Thanks to @Dev-next-gen for fixing how plain text, log and CSV files handle comments (#1065) 🙌

Published Packages

  • basta@0.1.1 on crates.io
  • cpd-core@0.1.15 on crates.io
  • cpd-finder@0.1.17 on crates.io
  • cpd-reporter@0.1.16 on crates.io
  • cpd-tokenizer@0.1.17 on crates.io
  • jscpd@5.3.0 on crates.io
  • cpd@5.3.0 on npm
  • jscpd@5.3.0 on npm
  • jscpd-darwin-arm64@5.3.0 on npm
  • jscpd-darwin-x64@5.3.0 on npm
  • jscpd-linux-x64-gnu@5.3.0 on npm
  • jscpd-linux-arm64-gnu@5.3.0 on npm
  • jscpd-linux-x64-musl@5.3.0 on npm
  • jscpd-linux-arm64-musl@5.3.0 on npm
  • jscpd-windows-x64-msvc@5.3.0 on npm
  • jscpd-windows-arm64-msvc@5.3.0 on npm
  • jscpd==5.3.0 on PyPI

Verify

Archives are signed with Sigstore (keyless, <asset>.sigstore.json)
and carry SLSA build provenance. Replace jscpd-linux-x64-gnu.tar.gz with your asset:

cosign verify-blob \
  --bundle jscpd-linux-x64-gnu.tar.gz.sigstore.json \
  --certificate-identity-regexp '^https://github\.com/kucherenko/jscpd/' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  jscpd-linux-x64-gnu.tar.gz
gh attestation verify jscpd-linux-x64-gnu.tar.gz --repo kucherenko/jscpd
sha256sum --check --ignore-missing checksums.txt

Don't miss a new jscpd release

NewReleases is sending notifications on new releases.