Skip to content

chore(deps): update dependency jscpd to v5 - #436

Open
renovate[bot] wants to merge 1 commit into
masterfrom
renovate/jscpd-5.x
Open

renovate[bot] wants to merge 1 commit into
masterfrom
renovate/jscpd-5.x

Conversation

@renovate

@renovate renovate Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

Package Change Age Confidence
jscpd (source) ^4.0.5^5.0.0 age confidence

Release Notes

kucherenko/jscpd (jscpd)

v5.3.1

Compare Source

New Features
  • --dashboard lists the largest code files. The Project section now ranks files by lines, with their tokens and size, next to the largest formats — the files worth splitting, beside the most complex ones further down. Only code is ranked: a lockfile, a changelog or a long HTML page is often the longest file in a repository and nothing anyone would refactor, so prose, data and markup files are left out, as they are from the complexity list. --summary-top sets the rows, and the json, markdown and html reporters carry the same list (project.largestFiles in jscpd-dashboard.json, an added key). See fixtures/dashboard-demo. (#​1086)

  • basta now detects which frameworks a project uses. A framework runs files that nothing imports: a router's pages, a runtime's plugin directories, handlers it finds by convention. basta used to know three such cases, hard-coded: Nuxt, Nitro and WXT. It now reads them from a list of about fifty definitions built into the binary, frameworks.yaml. The list covers Next.js, Nuxt, Nitro, WXT, Plasmo, Remix, React Router, SvelteKit, Astro, SolidStart, TanStack Start, Qwik City, Gatsby, RedwoodJS, Angular, NestJS, AdonisJS, Sails, @fastify/autoload, Strapi, Medusa, Ember, Quasar, React Native, Expo, Cloudflare Workers, Vercel, Netlify, Serverless, Trigger.dev, Docusaurus, VitePress, Eleventy, Storybook, Histoire, Jest, Vitest, Mocha, AVA, Playwright, Cypress, Cucumber, Prisma, Knex, Sequelize, TypeORM, Hardhat and Create React App.

    • A framework counts as present when basta finds any one of three things: its config file, its package among the package.json dependencies, or its section in package.json. basta checks every directory that holds scanned files, so each package of a monorepo is treated as its own project.
    • Several frameworks can apply at once, and each one keeps its own files alive. A Next.js application with Storybook, Vitest and Prisma counts as four.
    • The files and directories a framework loads are matched relative to the directory where the framework was found. basta also reads the framework's config for the settings that move directories: srcDir, entrypointsDir, appDirectory, sourceRoot in nest-cli.json, and imports: false.
    • The console report lists what was detected, for example Frameworks: next (apps/web), vitest. Both basta and jscpd --dead-code print it.
    • New basta flags: --list-frameworks prints the list. --framework <name> treats a framework as present, which helps when the scan starts below its package.json. --no-frameworks turns detection off. --frameworks-config <file> loads your own definitions, in YAML or JSON, in the same shape as the built-in list. basta also picks up basta.frameworks.{yaml,yml,json} from the working directory. Your definitions can add a framework basta does not know, or replace a built-in one that has the same name. If the file fails to load, basta stops with an error, because running without it would report as dead the very files the definitions protect.
    • A definition can also list the names its framework looks up in your code, under globals. The built-in list has getServerSideProps, generateMetadata and the route segment config for Next; loader, action and meta for Remix and React Router; load, actions, prerender and the hooks for SvelteKit; getStaticPaths for Astro; the Gatsby Node, browser and SSR APIs; the lifecycle methods of Angular and Nest; onRequest* for Cloudflare Pages Functions; handler for Netlify and Serverless functions; and more. basta treats a declaration with such a name as used. It never reports it, including under --include-entry-exports and as an unused member, and everything the declaration calls stays reachable.
    • A plain name in globals applies to every file of the project. The { names, files } form limits names to the files the framework reads them from, so loader counts for Remix under the app directory and is an ordinary name everywhere else. This also fixes a React Router route named from routes.ts. basta reaches that file through a string, so it is not an entry point, and its loader used to be reported as an unused export.
    • Nitro now follows srcDir and treats tasks/ as loaded by the framework.
    • See fixtures/dead-code-demo. (#​1087)
  • .jscpd.json can hold dead-code settings in a section of their own. The deadCode key (also spelled dead-code or basta) used to be a boolean that turned the mode on. It can now also be an object with these keys: enabled, categories, minConfidence, minLines, entry, ignore, includeTests, includeEntryExports, threshold, and the framework settings frameworks (definitions written inline, in the shape of frameworks.yaml), frameworksConfig, framework and noFrameworks.

    • The object only supplies settings. It turns the mode on only when it says "enabled": true, so you can keep clone settings and dead-code settings in one file and choose the run on the command line. "deadCode": true still works as the switch.
    • A flag wins over the section. The section wins over the older top-level keys (minConfidence, entry, deadCodeCategories, …), which still work.
    • minLines and threshold exist only inside the section, because the top-level keys with those names are about clones. The top-level minLines is the smallest clone to report and is never applied to a dead-code run. The top-level threshold is a limit for duplicated lines. A dead-code run still falls back to it when the section sets no threshold, as before.
    • A misspelled key inside the section is reported by name.
    • jscpd --dead-code and --dashboard now also pick up basta.frameworks.{yaml,yml,json} from the working directory.
    • The standalone basta binary reads the same section from the same file. It has a new -c, --config <file> flag. Without it, basta looks in the working directory for .jscpd.json, .config/jscpd.json or package.json. Both tools give the same result when you start them in the same directory.
    • See fixtures/dead-code-demo. (#​1087)
Fixes

We ran basta next to knip and fallow on 55 repositories: the GitHub trending lists for JavaScript, TypeScript, Vue, Svelte and Astro, plus the source code of Nuxt, Next.js, Svelte, Gatsby and Astro. The run found these problems in basta, all fixed in [#​1087](#​1087:

  • --dead-code was very slow on projects with many path aliases. basta tested every import against every alias in the project, and it did the slow check first: comparing the importer's path with the directory of the config that declared the alias, one path segment at a time. A monorepo with 150 packages, each listing a few hundred paths, took 198 s (ever-gauzy). Next.js took 83 s. basta now checks first whether the alias pattern matches the import at all, which fails on the first character for almost every alias. The same two projects now take 3.3 s and 4.4 s, and the findings are byte for byte the same.
  • A catch-all alias made every package import slow. With "*": ["./*", "../../node_modules/*"], an import of react went through the resolver from every file, and each miss built and hashed about twenty candidate paths. The module index can now tell in two lookups whether a path could name any module, under any extension or as a directory index, and the answer is exact. LibreChat went from 4.0 s to 0.86 s with identical findings.
  • Vite's '@': '/src' alias was read as the root of the file system. In a Vite alias target a leading / means the project root, as it does in a URL, and many projects write the alias that way. basta lost every @/ import in such a project and reported its components as unused files at 95% confidence. MoeKoeMusic went from 55 unused files to 0.
  • require(`./x`) with backticks was ignored. A template literal with nothing interpolated is as static as a quoted string, and Gatsby writes every string that way. Gatsby went from 584 unused files to 352.
  • An arrow function in a Svelte or Astro attribute broke parsing. In <script on:load={() => { … }} src=…>, basta took the > of => for the end of the tag and passed the rest of the attribute to the JavaScript parser as the component's script. The file failed to parse, and everything it imports was reported as unused. cobalt went from 37 unused files to 8.
  • Django migrations and management commands were reported as unused files. So were admin.py, apps.py, template tags, and the modules that settings name by dotted path. Django, Alembic and Scrapy are now in the framework list, detected by manage.py, alembic.ini and scrapy.cfg. AdventureLog went from 144 unused Python files to 6.
  • A file named only by a path string without an extension is no longer reported at full confidence. resolve(distDir, 'runtime/handlers/island') is how a framework registers a file it loads itself. It is not an import, and basta does not treat it as one. But a file whose own path ends the same way now gets a new reason, path-appears-in-string ("its path appears in a string literal"), which costs 40 points. That puts the file under the default threshold of 60, and it still shows up with --min-confidence 0. Nuxt went from 83 unused files to 46. The reason is a new possible value of reasons in the JSON report.
  • --dead-code read a WXT browser extension as almost entirely dead (#​1082): the framework's entrypoints/ directory — background, content scripts, popup pages — was not recognized as entry points, and its @/~srcDir, @@/~~ → root aliases live only in the generated .wxt/tsconfig.json, which no repository commits, so the whole tree dangled and cascaded (Tencent/BrowserSkill: 27.9% "unused", 115 unused files — now 0.4% and 5). A wxt.config.* now marks the entrypoints and auto-import directories as entries, honoring srcDir, entrypointsDir and imports: false, and declares the conventional aliases, the same way nuxt.config.* roots Nuxt's directories and svelte.config.* supplies $lib. (#​1083)
  • Markup, stylesheet and template files (HTML, XML, SVG, CSS, Handlebars, …) were assigned a complexity, counting words like if, for or a media query's and as branches — an HTML page could top the "Most complex files" list. These formats now have complexity 0, like prose and data files already did, and are therefore no longer counted as code by the health score: an all-markup project reports "no code files" instead of being scored on its markup. Component formats (Vue, Svelte, Astro) still count in full through their script blocks. The markup block of a component file (tokenized as html) is now also excluded from the duplication share, matching the css/scss blocks that already were. (#​1081) (#​1084)
Other

Published Packages

  • basta@0.2.0 on crates.io
  • cpd-core@0.1.16 on crates.io
  • cpd-finder@0.1.17 on crates.io
  • cpd-reporter@0.1.17 on crates.io
  • cpd-tokenizer@0.1.17 on crates.io
  • jscpd@5.3.1 on crates.io
  • cpd@5.3.1 on npm
  • jscpd@5.3.1 on npm
  • jscpd-darwin-arm64@5.3.1 on npm
  • jscpd-darwin-x64@5.3.1 on npm
  • jscpd-linux-x64-gnu@5.3.1 on npm
  • jscpd-linux-arm64-gnu@5.3.1 on npm
  • jscpd-linux-x64-musl@5.3.1 on npm
  • jscpd-linux-arm64-musl@5.3.1 on npm
  • jscpd-windows-x64-msvc@5.3.1 on npm
  • jscpd-windows-arm64-msvc@5.3.1 on npm
  • jscpd==5.3.1 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

v5.3.0

Compare Source

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

v5.2.1

Compare Source

New Features
  • --history: duplication trend over git historyjscpd src --history v5.0.0..HEAD scans every commit in the range in a detached worktree and prints a bar chart, a per-commit table with the change between points, the overall trend and how far --threshold could be tightened without failing the build. --history-since, --history-every N and --history-limit N narrow the range; the JSON reporter carries the points under a history key and the GitHub Action takes a history input. (#​1002, #​1050, #​1052)
  • Exit codes you can gate on, and --fail-on-empty — an unknown --format, a scan path that does not exist and a reporter that cannot write its file now print an error and exit 1 instead of passing with an empty report. --fail-on-empty (config key failOnEmpty, action input fail-on-empty) turns "analyzed no files" into a failure, so a mistyped path or an over-broad ignore cannot look like a clean run. (#​1047, #​1049)
  • PyPI: pip install jscpd — the release now publishes eight platform wheels built from the same prebuilt binaries as the npm and GitHub Release artifacts, so pip install jscpd and uvx jscpd get the Rust engine with no Python code and no Node.js runtime involved. The repository-hosted pre-commit hook installs from PyPI instead of npm, which removes Node.js from the pre-commit path. (#​1037, #​1039)
Bug Fixes
  • An open clone could be stretched past the file it started in — while growing a clone the detector accepted a continuation from any stored occurrence of the next window, so a third file that shared the same text but continued differently could extend a fragment beyond what its own file contains. The clone was then dropped or reported with mismatched ends (fixtures/haxe reported file1.hx [1:1 - 62:76] against file2.hx [1:1 - 62:2]). The match now asks first whether the clone's own anchor continues, and starts a new clone when it does not, so N-way copies no longer lose pairs. (#​1033, #​1034)
  • The XML report could be rejected by every parser — a clone containing a byte XML 1.0 cannot represent (an ANSI escape, a form feed) was written verbatim, and xmllint refused the file with PCDATA invalid Char value 27; ]]> inside a fragment closed the CDATA section early, and attribute values were escaped twice. Such characters are now replaced with U+FFFD, ]]> is split across two CDATA sections, and paths are escaped once. (#​375, #​1055)
  • --follow-symlinks renamed and double-counted linked files — a file reached through a symlink was reported by its resolved real path, which could be an absolute path outside the scan root, so the report and --ignore disagreed about its name; a file reachable through two paths counted as two sources, and a file symlink next to its target was reported as a clone of itself. Files now keep the path they were found at, and each real file is scanned once. (#​1059, #​1060)
Other
  • Symlinks are skipped by default in v5 — v4 followed them unless --noSymlinks was set; v5 needs --follow-symlinks (config key followSymlinks, and a v4 noSymlinks: false still maps to following). This was true in every 5.x release but undocumented, and it silently drops a corpus mounted through a symlink. Now in the README and the migration table. (#​1059)
  • CITATION.cff and a Citation section — GitHub's "Cite this repository" button and a BibTeX entry for the papers that use jscpd as their detector. The version and release date are kept in step by sync-version.mjs. (#​1051)
  • Docs: jscpd is language-aware — the README and the Rust docs now say that detection runs on language tokens, per-format comment and string syntax with the oxc parser for JavaScript/TypeScript, rather than on raw text. (#​1048)
  • Agent skills know about clone kinds, the summary and their noise — the bundled jscpd and dry-refactoring skills (npx skills add kucherenko/jscpd) document --summary, the Type-2 and Type-3 flags with their kind suffixes, and warn that normalized passes surface look-alike code, with conservative defaults and a triage step before refactoring. (#​1056, #​1057)
  • console-full prints the --history block like console does, and the test scaffolding behind the CLI, MCP, reporter and finder suites was deduplicated. (#​1053)
  • CI: the npm platform-package gate polls against a 5-minute deadline instead of a fixed sleep, so a slow registry no longer fails a release that would have succeeded. (#​1032)
Dependencies
  • Bump askama from 0.16.0 to 0.16.1 in /rust (#​1045)
  • Bump taiki-e/install-action from 2.87.3 to 2.87.8 in /.github/workflows (#​1046)
Thank You ❤️
  • @​mnahkies for correcting the ignore examples in the README — --ignore-pattern has no short flag and a bare node_modules does not match, since globs are matched against the whole path (#​1038)

Published Packages

  • cpd-core@0.1.13 on crates.io
  • cpd-finder@0.1.16 on crates.io
  • cpd-reporter@0.1.14 on crates.io
  • cpd-tokenizer@0.1.15 on crates.io
  • jscpd@5.2.1 on crates.io
  • cpd@5.2.1 on npm
  • jscpd@5.2.1 on npm
  • jscpd-darwin-arm64@5.2.1 on npm
  • jscpd-darwin-x64@5.2.1 on npm
  • jscpd-linux-x64-gnu@5.2.1 on npm
  • jscpd-linux-arm64-gnu@5.2.1 on npm
  • jscpd-linux-x64-musl@5.2.1 on npm
  • jscpd-linux-arm64-musl@5.2.1 on npm
  • jscpd-windows-x64-msvc@5.2.1 on npm
  • jscpd-windows-arm64-msvc@5.2.1 on npm
  • jscpd==5.2.1 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

v5.2.0

Compare Source

New Features
  • Type-2 clone detection: --ignore-identifiers, --ignore-literals, --ignore-annotations — three opt-in flags (config keys ignoreIdentifiers, ignoreLiterals, ignoreAnnotations, GitHub Action inputs of the same names) normalize token classes before hashing, so blocks that differ only in names, literal values or annotations are found. Identifiers hash as one class while keywords keep their value, strings and numbers stay distinct classes, and @Name(...) runs are dropped in Java, Kotlin, Scala, Groovy, Python, Dart, Swift, JavaScript and TypeScript (@interface declarations are kept). Every clone now carries a kind: exact or renamed. A run without the flags is unchanged apart from the additive "kind": "exact" JSON field. See fixtures/type2-demo. (#​998, #​1019)
  • Near-miss clone merging with --max-gap-lines N — a copy with a line inserted, removed or changed in the middle used to show up as two shorter clones. With --max-gap-lines N (config maxGapLines, Action input max-gap-lines, default 0 = off) clones of one file pair whose fragments follow each other in both files with at most N unmatched lines between them are merged into one clone of kind similar with a similarity value (matched tokens over the merged span). A merge whose similarity would fall below 0.5 is refused, duplicated-line statistics count only the matched lines, and a merge of renamed halves is reported as similar. See fixtures/type3-demo. (#​999, #​1020, #​1030)
  • Function-level similarity for JavaScript and TypeScript with --similarity RATIO — edits spread through a function rather than concentrated in one gap still escape a token window. --similarity (config similarity, Action input similarity, a number in (0, 1]; the default 1 means exact matches only, so nothing runs until you lower it) compares every function, method and arrow function by the bag of 4-grams over its syntax-tree node types, indexed with MinHash, and reports pairs at or above the ratio as similar clones spanning the whole functions. Names and literals do not take part: a renamed copy scores 1.0, one inserted line about 0.9, two inserted statements plus renames about 0.75. Every similar clone records its method (gap or ast) because the two scores are not on the same scale. The MCP check_duplication tool accepts the same similarity argument. (#​999, #​727, #​1021)
  • Clone kinds in every reporter — console prints Clone found (javascript, renamed) and Clone found (javascript, similar (gap) ~0.91), ai appends (renamed) / [~0.91 gap], JSON adds kind, similarity and method to each duplicate and renamedClones / similarClones to the statistics, XML adds the same attributes, HTML shows a badge, Xcode a suffix, and SARIF and Code Climate use the rules jscpd/renamed-code and jscpd/similar-code next to jscpd/duplicate-code. (#​1019, #​1021, #​1030)
  • Tips are skipped when stdout is not a terminal — the tips and sponsor lines are printed only on an interactive terminal; a pipe, a file, a CI log or an agent hook no longer receives them. JSCPD_NO_TIPS joins CI as an environment switch and --no-tips stays the explicit one; NO_COLOR only removes the colours. (#​1008, #​1029, thanks @​7487)
  • MCP: fully described tool definitions — the four tools now carry a title, read-only annotations, parameter descriptions with examples and defaults, and descriptions that say when to use each tool and what it returns; the server instructions describe the workflow across them. Tool names and schemas are unchanged. (#​1028)
Bug Fixes
  • Config-file ignorePattern entries without * or ? silently did nothing — such entries were treated as relative paths and joined onto the config directory, so "ignorePattern": ["Copyright 2026 Example Authors"] matched nothing while the same string via --ignore-pattern worked. Config entries are now applied verbatim, and an invalid regex prints a Warning: line instead of being dropped silently. See fixtures/ignore-demo. (#​997)
  • JavaScript/TypeScript files with a recoverable parse error could not match clean files — any parser diagnostic sent the file to the word-split fallback tokenizer, so a file containing, say, a redeclared function was tokenized differently from every well-formed file and never paired with one. Tokens now come from the lexer whenever the parser did not fail outright. Clone counts on codebases with such files change; that is the correction. (#​1023, #​1024)
  • Markdown inherited the C comment style — a /* (a glob like docs/**) or // (any URL) in prose opened a comment that swallowed the rest of the file, so two files sharing a paragraph after such a line were never reported. Markdown now has no comment syntax. (#​1026, thanks @​kwesolowski)
  • Vue template clones were reported with wrong ranges — the wrapper tags of the file and the template body were appended to the html token stream out of source order, so a clone across the seam took its endpoints from opposite ends of the file. The stream is now in source order, and the wrapper tags (<template>, <script>, <style> and their closing tags) are left out of it altogether, so a template clone is reported with the template's own line range and the script and style bodies are not counted as duplicated html. See fixtures/sfc-demo. (#​1031, thanks @​zero-stroke)
Other
  • Runnable demos under fixtures/ — every feature and fix above ships a demo directory (ignore-demo, type2-demo, type3-demo, parse-errors-demo, sfc-demo) whose README lists each command with its expected output, and the same files feed the smoke scan that runs on every pull request.
  • Docs: ignore patterns and inline markers--ignore-pattern / ignorePattern source-region filtering and the jscpd:ignore-start / jscpd:ignore-end markers are documented in the v5 reference, with license-header recipes and a note on the Rust regex syntax. (#​993, #​996, thanks @​w3lld1)
  • GitHub Action inputs ignore-identifiers, ignore-literals, ignore-annotations, max-gap-lines and similarity for the features above.
Dependencies
  • Add regex 1 to the jscpd crate for --ignore-pattern validation (#​997)
  • Bump taiki-e/install-action from 2.87.2 to 2.87.3 in /.github/workflows (#​995)
Thank You ❤️

Published Packages

  • cpd-core@0.1.12 on crates.io
  • cpd-finder@0.1.15 on crates.io
  • cpd-reporter@0.1.13 on crates.io
  • cpd-tokenizer@0.1.14 on crates.io
  • jscpd@5.2.0 on crates.io
  • cpd@5.2.0 on npm
  • jscpd@5.2.0 on npm
  • jscpd-darwin-arm64@5.2.0 on npm
  • jscpd-darwin-x64@5.2.0 on npm
  • jscpd-linux-x64-gnu@5.2.0 on npm
  • jscpd-linux-arm64-gnu@5.2.0 on npm
  • jscpd-linux-x64-musl@5.2.0 on npm
  • jscpd-linux-arm64-musl@5.2.0 on npm
  • jscpd-windows-x64-msvc@5.2.0 on npm
  • jscpd-windows-arm64-msvc@5.2.0 on npm

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

v5.1.2

Compare Source

New Features
  • Linux ARM64 musl prebuilt binaries — npm installs on Alpine and other musl-based ARM64 Linux systems now select a native binary from the new jscpd-linux-arm64-musl platform package, bringing the prebuilt platform count to 8. The GitHub release ships the matching jscpd-linux-arm64-musl.tar.gz asset. (#​988)
  • cargo binstall jscpd — the crate now carries cargo-binstall metadata pointing at the release tarballs for every supported target, so cargo binstall jscpd downloads a prebuilt binary instead of compiling the oxc parser stack from source. (#​988)
  • Docker image ghcr.io/kucherenko/jscpd — a multi-arch (amd64/arm64) distroless image built from the release binaries is published with every release, tagged latest, 5, 5.1 and the exact version, with SLSA provenance and an SBOM attached. Run it as docker run --rm -v "$PWD:/src" ghcr.io/kucherenko/jscpd; see docs/ci-and-hooks.md. (#​988)
Bug Fixes
  • jscpd --version and jscpd --help now say jscpd — both binaries are built from the same source and the command name was the literal cpd, so jscpd --version printed cpd 5.1.1 and the usage line read Usage: cpd. The name is now taken from the invoked executable (jscpd or cpd). (#​988)
  • Windows: drive-anchored --pattern values are treated as absolute — the Windows-only check for patterns like C:\src\**\*.ts compared the first character against : and \ after already requiring it to be a letter, so it could never match and such patterns were also given the relative **/ variant. The check is now a platform-independent helper with a unit test that runs everywhere. (#​988)
  • pre-commit hook passed v4-only flags.pre-commit-hooks.yaml still invoked --gitignore --exitCode '1', which the v5 CLI rejects, so repo: https://github.com/kucherenko/jscpd hooks failed on every run. The hook now passes --exit-code 1. (#​989)
  • Unsupported-platform error is actionable — when no prebuilt binary matches, the jscpd and cpd npm launchers now name the host (os/arch (libc)), list the supported platform keys and point to cargo install jscpd instead of printing a bare "Unsupported platform". (#​988)
Other
  • Repository split: master is v5-only — the TypeScript v4 engine (apps/, packages/, changesets, Node.js CI) moved to the long-lived master-v4 branch and releases from there under the latest-4 npm dist-tag. master keeps the Rust workspace, the shared fixtures/ corpus, the GitHub Action, Dockerfile and flake. README-v4.md describes the TypeScript version in one page; FORMATS.md is now generated from the Rust tokenizer (224 formats). (#​989, #​990)
  • Floating v5 tag for the GitHub Actionuses: kucherenko/jscpd@v5 follows the latest 5.x release; the release workflow moves the tag on every stable release. (#​988)
  • crates.io metadata — every crate now declares repository, documentation, keywords and categories; the jscpd crate excludes tests/ from the published package, ships an expanded README rendered on docs.rs, and npm packages carry a funding field. (#​988)
  • Signed release assets — in addition to SLSA provenance, each release archive and checksums.txt now has a Sigstore keyless signature (<asset>.sigstore.json) verifiable with cosign verify-blob; the release notes include the exact commands. (#​988)
  • CI — Windows joined the pull-request build matrix, a smoke test runs the release binary against the fixtures/ corpus on every push, and a nightly job runs cargo audit and cargo deny. (#​988, #​989)
Dependencies
  • Bump quick-xml to 0.42.0 in /rust (#​991)

Published Packages

  • cpd-core@0.1.11 on crates.io
  • cpd-finder@0.1.14 on crates.io
  • cpd-reporter@0.1.12 on crates.io
  • cpd-tokenizer@0.1.13 on crates.io
  • jscpd@5.1.2 on crates.io
  • cpd@5.1.2 on npm
  • jscpd@5.1.2 on npm
  • jscpd-darwin-arm64@5.1.2 on npm
  • jscpd-darwin-x64@5.1.2 on npm
  • jscpd-linux-x64-gnu@5.1.2 on npm
  • jscpd-linux-arm64-gnu@5.1.2 on npm
  • jscpd-linux-x64-musl@5.1.2 on npm
  • jscpd-linux-arm64-musl@5.1.2 on npm
  • jscpd-windows-x64-msvc@5.1.2 on npm
  • jscpd-windows-arm64-msvc@5.1.2 on npm

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

v5.1.1

Compare Source

Bug Fixes
  • jscpd on npm installed the 5.0.16 engine instead of 5.1.0 — the jscpd wrapper package published its optionalDependencies pinned to the 5.0.16 platform binaries, so npm i jscpd@5.1.0 resolved a native binary one release behind and jscpd --version reported cpd 5.0.16. Everything 5.1.0 fixed was therefore absent for jscpd users, including the Windows --baseline-from-ref fix. The cpd package was pinned correctly and is unaffected, as are the platform packages themselves — only the wrapper's pins were stale.

    The cause was in scripts/sync-version.mjs: the wrapper's version and its platform pins were updated together behind a single version !== npmVersion guard, so once anything set version before the script ran, the guard read "already up to date" and left the pins untouched. The two are now updated independently, and the script ends by verifying that every npm version and platform pin matches the release version, exiting non-zero if any disagree — the release workflow runs this script, so a repeat of this mismatch now fails the release instead of publishing. This is the same defect that produced the 5.0.13 republish; the earlier fix covered the cpd package but not the jscpd wrapper.

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: the oxc parser crates require 1.96.0, and ignore, globset and askama require 1.88. The value had been set when the Rust workspace was created and never revisited, and no CI job built at the declared MSRV, so the drift went unnoticed. CI now derives the toolchain from rust-version and checks against exactly that version.

Published Packages

  • cpd-core@0.1.10 on crates.io
  • cpd-finder@0.1.13 on crates.io
  • cpd-reporter@0.1.11 on crates.io
  • cpd-tokenizer@0.1.12 on crates.io
  • jscpd@5.1.1 on crates.io
  • cpd@5.1.1 on npm
  • jscpd@5.1.1 on npm
  • jscpd-darwin-arm64@5.1.1 on npm
  • jscpd-darwin-x64@5.1.1 on npm
  • jscpd-linux-x64-gnu@5.1.1 on npm
  • jscpd-linux-arm64-gnu@5.1.1 on npm
  • jscpd-linux-x64-musl@5.1.1 on npm
  • jscpd-windows-x64-msvc@5.1.1 on npm
  • jscpd-windows-arm64-msvc@5.1.1 on npm

v5.1.0

Compare Source

New Features
  • Windows on ARM support — npm installs now select a native aarch64-pc-windows-msvc binary from the jscpd-windows-arm64-msvc platform package on Windows ARM64.

  • Clone baseline (--baseline, --update-baseline, --fail-on-new-clones) — gate CI on new duplication only. A committed baseline file (e.g. .jscpd-baseline.json) records content-hash fingerprints of accepted clones (the same hash the SARIF reporter emits as partialFingerprints["jscpdCloneHash/v1"], with a multiplicity count per fingerprint); clones absent from it are reported as new, and --fail-on-new-clones[=N] exits 1 when more than N (default 0) new clones are found — independently of --threshold, so legacy duplication is tolerated while regressions fail the build. --update-baseline rewrites the file from the current run (creating it if missing) and prints added/removed fingerprint counts so baseline growth stays visible in CI logs and PR review. The baseline file is versioned, sorted one fingerprint per line for reviewable diffs and trivial merges, and configurable via the baseline / failOnNewClones config keys. New-clone info flows through the reporters: [NEW] markers and a "(N new)" found-count in console/console-full, per-clone isNew plus the newClones / newDuplicatedLines statistics in json, level error in sarif, and jscpd_new_clones / jscpd_new_duplicated_lines gauges in openmetrics. (#​944)

  • Ephemeral baseline from a git ref (--baseline-from-ref) — stateless variant of the clone baseline for PR gates without a committed file: cpd --baseline-from-ref origin/main --fail-on-new-clones . checks the base ref's tree out into a temporary detached git worktree (removed afterwards; shells out to git like blame does), scans it with the same detection configuration, and compares the current run against that in-memory fingerprint set — clones absent from the base ref are new. Costs a second scan of the corpus, where the committed --baseline file needs only one. When the ref is missing (shallow CI checkout) it fails with a clear hint to git fetch origin main or use fetch-depth: 0. Config key baselineFromRef; conflicts with --baseline / --update-baseline. (#​944)

  • OpenMetrics reporter (--reporters openmetrics) — writes jscpd-metrics.txt in the OpenMetrics text exposition format, ready to be declared as a GitLab CI artifacts:reports:metrics artifact so merge requests show duplication metric changes against the target branch. Exposes gauges for files/lines/tokens analyzed, clones found, duplicated lines/tokens with percentages (project total plus a format-labeled sample per format), and detection duration in seconds. (#​422)

  • CodeClimate / GitLab Code Quality reporter (--reporters codeclimate, alias gitlab) — writes gl-code-quality-report.json (the filename GitLab's docs use) in the CodeClimate issue format, restricted to the subset GitLab defines as its Code Quality report format, ready to be declared as an artifacts:reports:codequality artifact so duplicates appear as code quality issues in merge requests — unlike the SARIF reporter, which GitLab ingests as security vulnerability findings. Each clone yields an issue per fragment (each describing the other location, plus the CodeClimate other_locations field), with a deterministic fingerprint derived from the clone's content hash so GitLab can tell new issues from pre-existing ones across pipeline runs. Severity is minor, escalating to major for clones absent from a configured baseline or when the run exceeds --threshold. (#​958)

  • Config discovery in .config/ (dot-config convention) — auto-discovery now also checks .config/jscpd.json (and .config/.jscpd.json) per the dot-config convention, between the root .jscpd.json and the package.json jscpd key. A root .jscpd.json still wins, so existing setups are unaffected; paths inside the config resolve against the working directory, as with other auto-discovered sources. (#​979)

Bug Fixes
  • Unknown --format values warn instead of silently matching nothing — a typo like --format cs (instead of csharp) used to scan 0 files and exit 0, indistinguishable from a clean codebase in CI. The CLI now prints a stderr warning naming the unsupported value and pointing to --list; custom formats declared via --formats-exts stay accepted. (#​964)
  • Nix flake builds again — the flake pinned the hash of the mutable channel-rust-1.97.toml manifest, which broke with a fixed-output hash mismatch when Rust 1.97.1 was published. The toolchain is now pinned to the exact patch version (immutable manifest), so the hash can no longer drift. (#​976)
  • Windows: --baseline-from-ref no longer reports every clone as new — the format-suffix stripper treated the drive colon in Windows verbatim paths (\\?\C:\..., the form canonicalize returns) as a :format suffix and truncated the base scan's source ids to \\?\C, so every snippet read behind the fingerprint computation failed silently and the ephemeral baseline never matched. A colon followed by a path separator is now recognized as structural. Clone fingerprints are also line-ending agnostic now (CR stripped before hashing), so committed baselines survive CRLF/LF differences between platforms.
Other
  • Glama MCP listing — the repository now ships a glama.json maintainer manifest and a Dockerfile that runs the stdio MCP server (jscpd --mcp), used by Glama to build and score the server listing
  • Signed releases — release artifacts are signed with SLSA provenance, and piped downloads in workflows are pinned (OpenSSF Scorecard)
Dependencies
  • Bump Rust toolchain to 1.97.1 and oxc crates to 0.147 in /rust
  • Bump thiserror to 2.0.20, globset to 0.4.20, ignore to 0.4.33, log to 0.4.34 in /rust
Thank You ❤️

Published Packages

  • cpd-core@0.1.10 on crates.io
  • cpd-finder@0.1.12 on crates.io
  • cpd-reporter@0.1.10 on crates.io
  • cpd-tokenizer@0.1.11 on crates.io
  • jscpd@5.1.0 on crates.io
  • cpd@5.1.0 on npm
  • jscpd@5.1.0 on npm
  • jscpd-darwin-arm64@5.1.0 on npm
  • jscpd-darwin-x64@5.1.0 on npm
  • jscpd-linux-x64-gnu@5.1.0 on npm
  • jscpd-linux-arm64-gnu@5.1.0 on npm
  • jscpd-linux-x64-musl@5.1.0 on npm
  • jscpd-windows-x64-msvc@5.1.0 on npm
  • jscpd-windows-arm64-msvc@5.1.0 on npm

v5.0.16

Compare Source

New Features
  • MCP server over stdio (--mcp)cpd --mcp /path/to/project serves the Model Context Protocol on stdin/stdout; the project is scanned once at startup and kept in memory, so check_duplication snippet checks answer in milliseconds. Tools: check_duplication, get_file_clones, get_statistics, check_current_directory. (#​891)
  • Codebase summary (--summary) — opt-in refactoring-hotspot overview: top files and folders ranked by tokens, lines, size, or complexity, with each file's duplication share; --summary-top and --summary-by tune it. (#​934)
  • Isolated folder groups (--skip-isolated) — skip duplication between monorepo folders owned by different teams (--skip-isolated "packages/a|packages/b"); clones inside one folder or against shared code are still reported. Config file: "skipIsolated": [["packages/a", "packages/b"]]. (#​628, #​942)
Security
  • Supply-chain hardening (OpenSSF Scorecard) — GitHub Actions pinned to commit SHAs, least-privilege workflow tokens, SECURITY.md with private vulnerability reporting, protected master branch
Bug Fixes
  • GitHub "Latest" release badge stays on v5 — legacy v4 releases can no longer take the Latest badge from the v5 line
Thank You ❤️
  • @​hanzhangyu for proposing isolated folder groups for monorepos and contributing the original skipIsolated implementation (#​628), which this release ports to the Rust engine

v5.0.15

Compare Source

New Features
  • SARIF: size-based severity — new --sarif-error-tokens <N> flag (also sarifErrorTokens in .jscpd.json): clones with at least N tokens are reported at level error while smaller ones stay warning. When overall duplication exceeds --threshold, all SARIF results are emitted as error. ([#&#

Important

✂ PR body was truncated to here.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Enabled.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@w3nl

w3nl commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@renovate
renovate Bot force-pushed the renovate/jscpd-5.x branch 6 times, most recently from f207fe4 to c6664b7 Compare June 13, 2026 09:09
@renovate
renovate Bot force-pushed the renovate/jscpd-5.x branch 5 times, most recently from 25111c5 to c479db3 Compare June 23, 2026 20:38
@renovate
renovate Bot force-pushed the renovate/jscpd-5.x branch from c479db3 to ab1ce36 Compare June 27, 2026 01:31
@renovate
renovate Bot force-pushed the renovate/jscpd-5.x branch 3 times, most recently from 73bfd6b to 926d19a Compare July 14, 2026 10:38
@renovate
renovate Bot force-pushed the renovate/jscpd-5.x branch 3 times, most recently from acde38a to a5091d3 Compare July 27, 2026 13:58
@renovate
renovate Bot force-pushed the renovate/jscpd-5.x branch 2 times, most recently from 3254805 to dcb7300 Compare August 8, 2026 00:53
@renovate
renovate Bot force-pushed the renovate/jscpd-5.x branch 5 times, most recently from 5ccc96e to e385055 Compare August 18, 2026 17:43
@renovate
renovate Bot force-pushed the renovate/jscpd-5.x branch 2 times, most recently from dd575a0 to 7bab822 Compare August 25, 2026 23:37
@renovate
renovate Bot force-pushed the renovate/jscpd-5.x branch 2 times, most recently from 162ba7f to 37874c6 Compare August 31, 2026 17:59
@renovate
renovate Bot force-pushed the renovate/jscpd-5.x branch 4 times, most recently from fb1acaf to 34f440e Compare September 8, 2026 11:07
@renovate
renovate Bot force-pushed the renovate/jscpd-5.x branch from 34f440e to 8aa2c0c Compare September 15, 2026 19:05
@coderabbitai

coderabbitai Bot commented Sep 15, 2026

Copy link
Copy Markdown

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: a8340d9a-7a40-409f-9780-d1c54c7de295

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@renovate
renovate Bot force-pushed the renovate/jscpd-5.x branch 2 times, most recently from dbc6603 to 47197ff Compare September 19, 2026 02:49
@renovate
renovate Bot force-pushed the renovate/jscpd-5.x branch from 47197ff to 838b1d0 Compare September 21, 2026 23:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant