Dashboard and health score
A clone report answers one question. When you open an unfamiliar codebase, or check where a project stands before a refactoring, you want several at once: how big it is, how much of it is copied, where the complexity sits, and what nothing runs. --dashboard answers all four in one run and prints them on one screen, under the project's health score. --health prints that score alone, for a README badge or a CI gate.
--rust-diagnostics (5.3.2+).Run it
jscpd . --dashboard
jscpd src --dashboard --summary-top 10
jscpd src --dashboard --ignore-identifiers
The second line prints ten rows per list instead of five, the third counts renamed copies too. On fixtures/dashboard-demo of the jscpd repository, a five-file TypeScript service with a README:
Health B 74/100 █████████████████▊░░░░░░ 93 lines of code (XS)
duplication 74 ████████▉░░░ 6.5% in typescript (no text)
dead code 72 ████████▋░░░ 14.0%
complexity 76 █████████▏░░ 0.0% in complex files
── Project ─────────────────────────────────────────────────
7 files · 324 lines · 2.1K tokens · 3 formats
largest: markdown 163, typescript 93, bash 68 lines
Largest code files:
LINES TOKENS SIZE PATH
35 182 757 src/rates.ts
21 169 665 src/checks.ts
16 97 423 src/index.ts
11 109 409 src/labels.ts
10 113 430 src/legacy/manifest.ts
── Duplication ─────────────────────────────────────────────
1.85% duplicated lines · 1 clone (1 exact)
By format:
DUP% LINES CLONES FORMAT
6.5 6 1 typescript
── Complexity ──────────────────────────────────────────────
25 total · 5.0 mean per file
Most complex files:
CX LINES SIZE PATH
11 35 757 src/rates.ts
6 21 665 src/checks.ts
3 11 409 src/labels.ts
3 10 430 src/legacy/manifest.ts
2 16 423 src/index.ts
── Dead code ───────────────────────────────────────────────
13.98% unused lines · 2 findings in 5 files
1 unused-file · 1 unused-export
Largest findings:
LINES CATEGORY WHERE
10 unused-file src/legacy/manifest.ts
3 unused-export src/checks.ts:19 checkBatch
time: 10.149ms
Reading the output
| Section | What it reports |
|---|---|
| Health | One 0-100 score with a grade, from duplication, dead code and complexity, and under it each sub-score with what it was measured from; see The health score |
| Project | Files, lines and tokens analyzed, the largest formats by lines, and the largest code files. Only code is ranked there, so the README, the longest file of this project, stays out of the list |
| Duplication | The duplicated-lines percentage, the clones per kind (exact, renamed, similar) and a breakdown by format. Prose, data and markup formats are left out of the breakdown, since the health score does not count them either |
| Complexity | Total and mean complexity per file, and the most complex files. Complexity is one path per function plus one per branch, counted from the token stream; prose and data files score 0 |
| Dead code | For the languages the run could read: the share of lines nothing runs, the findings per category and the largest findings |
In this project src/checks.ts and src/labels.ts share one address block, one exact clone; src/rates.ts scores 11 with one function, three weight tiers, three case labels and two boolean operators; src/legacy/manifest.ts is reached by no import from src/index.ts, and checkBatch is exported but never imported. The two percentages of the duplication section differ on purpose: 1.85% counts every line the run read, 6.5% the TypeScript alone, and the health score reads the second.
The options of a normal run apply: --min-tokens, --ignore, --format, --ignore-identifiers, --max-gap-lines, --similarity and --kind shape the duplication section, and --entry, --dead-code-categories, --min-confidence and --rust-diagnostics shape the dead-code section. --summary-top N sets the rows per list (default 5).
The health badge alone
jscpd . --health
Health B 74/100 █████████████████▊░░░░░░ 93 lines of code (XS)
duplication 74 ████████▉░░░ 6.5% in typescript (no text)
dead code 72 ████████▋░░░ 14.0%
complexity 76 █████████▏░░ 0.0% in complex files
Each sub-score is followed by what it was measured from: the share of code lines that are duplicated, that nothing runs, and that sit in complex files. (no text) says that the project has prose files and that they were left out. A dimension that cannot be measured is named instead of scored as perfect:
Health B 75/100 ██████████████████▏░░░░░ 30 lines of code (XS)
duplication 76 █████████░░░ 0.0% in rust (no data)
complexity 75 █████████░░░ 0.0% in complex files
dead code n/a (no JavaScript, TypeScript, Python or compiler-checked Rust files)
Reporters
| Reporter | --health | --dashboard |
|---|---|---|
console | The badge | The screen above |
ai | One line: health 74 B (duplication 74, dead-code 72, complexity 76; 93 code lines) | |
json | jscpd-health.json: score, grade, size, and for each dimension its value, adjusted value, lines, half-life, weight and score | jscpd-dashboard.json: every section of the screen |
badge | jscpd-health-badge.svg | jscpd-health-badge.svg |
markdown, html | jscpd-health.md, jscpd-health.html: the score and the dimension table | jscpd-dashboard.md, jscpd-dashboard.html: every section of the screen |
The files go to --output. The Markdown report is ready for a PR comment:
jscpd . --health --reporters markdown
# Project health
**Score:** B (74/100) — 93 lines of code (XS)
| Dimension | Score | Measured |
|---|---:|---|
| duplication | 74 | 6.5% in typescript (no text) |
| dead code | 72 | 14.0% |
| complexity | 76 | 0.0% in complex files |
Clone lists and dead-code findings still come from the regular reporters, in a clone run or a --dead-code run.
Metrics from other tools
--health-input FILE (config key healthInput) adds what jscpd cannot measure. A metric is either a ready 0-100 score, or a value with the halfLife that turns it into one; "direction": "higher" scores the distance to max (default 100). The demo ships health-metrics.json with 81% test coverage and a clean security scan:
{
"metrics": [
{ "id": "coverage", "value": 81, "direction": "higher", "halfLife": 40 },
{ "id": "security", "score": 100 }
]
}
jscpd . --health --health-input health-metrics.json
Health B 78/100 ██████████████████▊░░░░░ 93 lines of code (XS)
duplication 74 ████████▉░░░ 6.5% in typescript (no text)
dead code 72 ████████▋░░░ 14.0%
complexity 76 █████████▏░░ 0.0% in complex files
coverage 72 ████████▋░░░ 81
security 100 ████████████
With halfLife: 40, 81% coverage is 19 short of 100 and scores 72. A CI job typically writes this file from its coverage and audit steps, then runs the command.
Tuning
The same object can live under health in .jscpd.json, together with the tuning of the built-in dimensions:
{
"health": {
"duplication": { "halfLife": 5, "weight": 2 },
"deadCode": { "weight": 0 },
"complexFile": 80,
"metrics": [{ "id": "security", "score": 100 }]
}
}
A weight of 0 leaves a dimension out. complexFile moves the complexity from which a file counts as complex (50 by default). An unknown key, or a metric that cannot be scored, is an error. Two scores are comparable only when they are built from the same dimensions with the same tuning, so keep the tuning in the config file where every run reads it.
Exit codes
A dashboard or health run is gated like a clone run: --threshold compares the duplication percentage, --exit-code is returned when clones were found, and --fail-on-empty fails a scan that analyzed nothing, so jscpd . --dashboard --threshold 3 works in CI. The baseline options are the exception: --baseline, --baseline-from-ref and --update-baseline need the clone report these runs do not write, so they warn and are ignored, and --fail-on-new-clones is refused:
Warning: --dashboard and --health ignore --baseline, --baseline-from-ref and --update-baseline
Error: --fail-on-new-clones needs a baseline, which --dashboard and --health do not build
A bad dead-code option, such as an unknown category, is refused as in a --dead-code run. Exit codes lists every case.
Cost
The dashboard runs a clone scan and a dead-code scan side by side. On a normal project that takes well under a second. On a very large tree, such as a 565 MB node_modules, it takes close to the two scans combined, because they share the CPU. --workers N is a budget for the whole run, the two scans take half of it each, and --workers 1 runs them one after the other.
When you only need one part
| Question | Command |
|---|---|
| How healthy is the project, in one number? | jscpd src --health |
| Which files are the most complex? | jscpd src --complexity (no clone detection) |
| Where is the duplication? | jscpd src --summary |
| Which clones are renamed copies? | jscpd src --ignore-identifiers --kind renamed |
| What does nothing run? | jscpd src --dead-code |
Related
- The health score explains the formula, its constants and a worked example on this demo.
- Dead code covers the findings, their confidence and entry points.
- Use jscpd in CI shows where a badge or a Markdown report goes in a pipeline.
- Reporters lists the reporters of a clone run.
Duplication over git history
Scan every commit in a range with --history and see whether duplication is going up or down, as a chart and a table in the terminal.
Dead code
Find the files, exports, symbols and imports nothing runs with jscpd --dead-code, in JavaScript, TypeScript, Python and compiler-checked Rust, each finding with a confidence.