@doppel-ts/core
v0.1.2
Published
Detect duplicate and similar React components in your codebase.
Readme
doppel-ts
Detect duplicate and similar React components in your codebase.
doppel-ts statically analyzes your React project, measures structural similarity between components, and reports near-duplicates that are candidates for consolidation. It is fully deterministic — no AI inside — and outputs rich structured data that AI agents can consume downstream.
Features
- Props + JSX similarity — Compares component interfaces (Props types) and render structure (JSX trees) as primary similarity signals
- Two-phase algorithm — Fast feature-vector filtering narrows O(N²) pairs, then precise Tree Edit Distance comparison on candidates
- Rust-powered core — Similarity engine built in Rust (via napi-rs) for high performance
- Rich JSON output — Structured data including Props details, normalized JSX trees, and diff breakdowns — designed for AI agent consumption
- Configurable thresholds — Multi-level severity (high / medium) with customizable weights per similarity dimension
- Suppress false positives — Exclude intentionally similar pairs via config or
// doppel-ignorecomments
Install
npm install -D doppel-tsQuick Start
# Scan components directory
npx doppel-ts src/components
# Multiple directories
npx doppel-ts src/components src/ui
# With custom threshold
npx doppel-ts src/components --threshold 0.8
# JSON output (for CI or AI consumption)
npx doppel-ts src/components --format json
# Rich JSON with Props, JSX tree, and diff
npx doppel-ts src/components --format json --detail
# Detailed breakdown in terminal
npx doppel-ts src/components --detailOutput Example
doppel-ts v1.0.0 — scanning 142 components...
HIGH (≥90%)
PrimaryButton ↔ SubmitButton 92% src/components/PrimaryButton.tsx ↔ src/components/SubmitButton.tsx
UserCard ↔ ProfileCard 91% src/components/UserCard.tsx ↔ src/components/ProfileCard.tsx
MEDIUM (≥70%)
SearchInput ↔ FilterInput 78% src/components/SearchInput.tsx ↔ src/components/FilterInput.tsx
Found 3 similar pairs (2 high, 1 medium) across 142 components.Configuration
Create doppel.config.ts in your project root:
import { defineConfig } from "doppel-ts";
export default defineConfig({
include: ["src/components/**/*.tsx"],
exclude: ["**/*.test.tsx", "**/*.stories.tsx"],
threshold: {
high: 0.9,
medium: 0.7,
},
weights: {
props: 0.5,
jsx: 0.35,
style: 0.1,
behavior: 0.05,
},
suppress: [
["BaseButton", "IconButton"],
["*Layout*", "*Container*"],
],
});Configuration Priority
CLI flags > doppel.config.ts > defaultsCLI Options
| Flag | Description |
| ---------------------- | --------------------------------------------------- |
| [paths...] | Directories or glob patterns to scan (default: cwd) |
| --exclude <pattern> | Exclude files matching pattern (repeatable) |
| --threshold <number> | Minimum similarity score (0.0-1.0) |
| --detail | Show breakdown (terminal) or rich JSON (json mode) |
| --format <type> | Output format: terminal (default) or json |
| --include-local | Include non-exported local components |
| --no-suppress | Disable all suppress rules |
| --help | Show help |
| --version | Show version |
JSON Output
Default JSON output is lightweight (names, paths, scores):
{
"meta": {
"version": "1.0.0",
"totalComponents": 142,
"totalPairs": 3
},
"pairs": [
{
"score": 0.92,
"level": "high",
"a": { "name": "PrimaryButton", "path": "src/components/PrimaryButton.tsx" },
"b": { "name": "SubmitButton", "path": "src/components/SubmitButton.tsx" }
}
]
}Use --detail for rich output with Props, JSX tree, breakdown scores, and diff.
Suppressing Results
In config
export default defineConfig({
suppress: [
["BaseButton", "IconButton"], // exact names
["*Layout*", "*Container*"], // glob patterns
],
});In source
// doppel-ignore
export function SpecialButton(props: ButtonProps) {
// ...
}Architecture
doppel-ts uses a TypeScript + Rust hybrid architecture:
- TypeScript — AST parsing via TypeScript Compiler API, component detection, Props/JSX extraction, normalization
- Rust (napi-rs) — Feature vector generation, cosine similarity filtering, Tree Edit Distance computation
The parser layer is abstracted to support future backends (Strada API, SWC/oxc).
Requirements
- Node.js ≥ 20 or Bun ≥ 1.x
- React project with TypeScript (.tsx files)
Roadmap
- [ ] Vue / Svelte / Web Components support (plugin system)
- [ ] Component clustering (grouping similar components)
- [ ] HTML visual reports
- [ ] CI bot integration (PR comments)
- [ ] SWC / oxc fast parser backend
- [ ] Strada API support (TypeScript 7.1+)
License
Licensed under either of:
- MIT License (LICENSE-MIT)
- Apache License, Version 2.0 (LICENSE-APACHE)
at your option.
