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.jsonno longer throws out the whole config — only the bad field is dropped, everything else still applies. --dashboard/--healthand 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/--healthnow drop the dead-code section gracefully when--formatexcludes every language it can analyze, instead of failing the whole report.--complexity --fail-on-emptynow writes its reports before failing, matching every other mode.--min-confidenceabove 100 is now clamped (with a warning) everywhere it's read, not just in the standalone--dead-codemode.- 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
oxcparser 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.1on crates.iocpd-core@0.1.15on crates.iocpd-finder@0.1.17on crates.iocpd-reporter@0.1.16on crates.iocpd-tokenizer@0.1.17on crates.iojscpd@5.3.0on crates.iocpd@5.3.0on npmjscpd@5.3.0on npmjscpd-darwin-arm64@5.3.0on npmjscpd-darwin-x64@5.3.0on npmjscpd-linux-x64-gnu@5.3.0on npmjscpd-linux-arm64-gnu@5.3.0on npmjscpd-linux-x64-musl@5.3.0on npmjscpd-linux-arm64-musl@5.3.0on npmjscpd-windows-x64-msvc@5.3.0on npmjscpd-windows-arm64-msvc@5.3.0on npmjscpd==5.3.0on 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