dslop 1.0.6 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,32 +1,19 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to this project will be documented in this file.
3
+ ## v1.1.0
4
4
 
5
- ## v1.0.6
6
-
7
- [compare changes](https://github.com/turf-sports/dslop/compare/v1.0.5...v1.0.6)
5
+ [compare changes](https://github.com/turf-sports/dslop/compare/v1.0.7...v1.1.0)
8
6
 
9
- ### 🏡 Chore
7
+ ### 🚀 Enhancements
10
8
 
11
- - Add changelogen for automated changelogs ([7cb82db](https://github.com/turf-sports/dslop/commit/7cb82db))
9
+ - Add --staged flag to check uncommitted changes ([48d66b1](https://github.com/turf-sports/dslop/commit/48d66b1))
12
10
 
13
11
  ### ❤️ Contributors
14
12
 
15
13
  - Siddharth Sharma <sharmasiddharthcs@gmail.com>
16
14
 
17
- ## v1.0.5
18
-
19
- ### 🚀 Features
15
+ ## v1.0.7
20
16
 
21
- - Initial public release
22
- - Detect duplicate and similar code blocks across your codebase
23
- - Smart normalization (strings, numbers, colors) for structural comparison
24
- - Monorepo mode with `--cross-package` flag
25
- - JSON output support
26
- - Configurable similarity threshold and minimum block size
27
-
28
- ### 📦 Publishing
29
-
30
- - Available via `npx dslop`
31
- - Auto-versioning with changelogs
17
+ ## v1.0.6
32
18
 
19
+ - Initial release
package/README.md CHANGED
@@ -1,138 +1,37 @@
1
1
  # dslop
2
2
 
3
- **D**etect **S**imilar/**L**ines **O**f **P**rogramming - A fast duplicate code detector.
4
-
5
- ## Quick Start
6
-
7
- Run instantly with npx (no install required):
3
+ Find duplicate code in your codebase.
8
4
 
9
5
  ```bash
10
6
  npx dslop .
11
7
  ```
12
8
 
13
- ## Installation
9
+ ## Install
14
10
 
15
11
  ```bash
16
- # Install globally
17
- npm install -g dslop
18
-
19
- # Or with other package managers
20
- pnpm add -g dslop
21
- bun add -g dslop
22
- yarn global add dslop
12
+ npm i -g dslop
23
13
  ```
24
14
 
25
15
  ## Usage
26
16
 
27
17
  ```bash
28
- # Scan current directory
29
- dslop .
30
-
31
- # Scan specific directory with options
32
- dslop ./src -m 6 -s 80
33
-
34
- # Only show duplicates across packages (great for monorepos)
35
- dslop . --cross-package
36
-
37
- # Output as JSON
38
- dslop . --json
39
- ```
40
-
41
- Or run without installing:
42
-
43
- ```bash
44
- npx dslop .
45
- bunx dslop .
46
- pnpm dlx dslop .
18
+ dslop . # scan current directory
19
+ dslop --staged # check uncommitted changes for dupes
20
+ dslop ./src -m 6 -s 80 # 6 line min, 80% similarity
21
+ dslop . --cross-package # only cross-package dupes (monorepos)
22
+ dslop . --json # json output
47
23
  ```
48
24
 
49
25
  ## Options
50
26
 
51
- | Option | Short | Default | Description |
52
- |--------|-------|---------|-------------|
53
- | `--min-lines` | `-m` | 4 | Minimum block size in lines |
54
- | `--similarity` | `-s` | 70 | Minimum similarity threshold (0-100) |
55
- | `--extensions` | `-e` | ts,tsx,js,jsx | File extensions to scan |
56
- | `--ignore` | `-i` | node_modules,dist,... | Patterns to ignore |
57
- | `--no-normalize` | | | Disable string/number normalization |
58
- | `--json` | | | Output as JSON |
59
- | `--help` | `-h` | | Show help |
60
- | `--version` | `-v` | | Show version |
61
-
62
- ## How It Works
63
-
64
- 1. **Scanning**: Recursively scans files matching the specified extensions
65
- 2. **Block Extraction**: Extracts code blocks using a sliding window approach at multiple granularities
66
- 3. **Normalization**: Replaces string literals, numbers, and colors with placeholders for structural comparison
67
- 4. **Hash Grouping**: Groups exact duplicates by hash for fast matching
68
- 5. **Similarity Matching**: Uses Jaccard similarity on line sets for near-duplicates
69
- 6. **Filtering**: Removes overlapping blocks and deduplicates groups
70
-
71
- ## Monorepo Mode
72
-
73
- Use `--cross-package` to find duplicates that span across different packages/apps - perfect for identifying code that should be moved to a shared library:
74
-
75
- ```bash
76
- dslop . --cross-package
77
- ```
78
-
79
- This filters results to only show duplicates where occurrences are in different `apps/`, `packages/`, or `libs/` directories.
80
-
81
- ## Example Output
82
-
83
- ```
84
- Scanning ./src...
85
- Extensions: ts, tsx, js, jsx
86
- Min block size: 4 lines
87
- Similarity threshold: 70%
88
- Normalization: enabled
89
-
90
- Scanned 81 files (15,672 lines) in 129ms
91
- Extracted 13,920 code blocks
92
-
93
- Found 500 duplicate groups in 51ms
94
-
95
- ────────────────────────────────────────────────────────────────────────────────
96
- DUPLICATE CODE DETECTED
97
- ────────────────────────────────────────────────────────────────────────────────
98
-
99
- Group 1 │ EXACT │ 28 lines × 2 occurrences = 56 lines of duplication
100
-
101
- ├─ QuarterlyWinnerMessagePreview.tsx:197-224
102
- ├─ CoachEnteredMessagePreview.tsx:113-140
103
-
104
- Code preview:
105
- │ <View style={{
106
- │ backgroundColor: "white",
107
- │ ...
108
-
109
- SUMMARY
110
- ────────────────────────────────────────────────────────────────────────────────
111
- Total duplicate groups: 500
112
- Exact matches: 450
113
- Similar matches: 50
114
- Files affected: 65
115
- Total duplicate lines: 12,340
116
- Average similarity: 95%
117
- ```
118
-
119
- ## Development
120
-
121
- ```bash
122
- # Clone and install
123
- git clone https://github.com/turf-sports/dslop.git
124
- cd dslop
125
- bun install
126
-
127
- # Run in dev mode
128
- bun run dev
129
-
130
- # Build for npm
131
- bun run build
132
-
133
- # Create standalone binary
134
- bun run build:binary
135
- ```
27
+ | Flag | Default | Description |
28
+ |------|---------|-------------|
29
+ | `-m, --min-lines` | 4 | min lines per block |
30
+ | `-s, --similarity` | 70 | similarity threshold (0-100) |
31
+ | `-e, --extensions` | ts,tsx,js,jsx | file extensions |
32
+ | `--staged` | | only show dupes involving uncommitted changes |
33
+ | `--cross-package` | | only show dupes across packages |
34
+ | `--json` | | json output |
136
35
 
137
36
  ## License
138
37
 
package/dist/index.js CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  // index.ts
4
4
  import { parseArgs } from "util";
5
+ import { execSync } from "child_process";
6
+ import path4 from "path";
5
7
 
6
8
  // src/constants.ts
7
9
  var DEFAULT_MIN_LINES = 4;
@@ -6559,7 +6561,27 @@ async function scanDirectory(targetPath, options) {
6559
6561
  }
6560
6562
 
6561
6563
  // index.ts
6562
- var VERSION = process.env.npm_package_version || "1.0.6";
6564
+ var VERSION = process.env.npm_package_version || "1.1.0";
6565
+ function getChangedFiles(targetPath) {
6566
+ const absolutePath = path4.resolve(targetPath);
6567
+ try {
6568
+ const staged = execSync("git diff --cached --name-only", { cwd: absolutePath, encoding: "utf-8" });
6569
+ const unstaged = execSync("git diff --name-only", { cwd: absolutePath, encoding: "utf-8" });
6570
+ const untracked = execSync("git ls-files --others --exclude-standard", { cwd: absolutePath, encoding: "utf-8" });
6571
+ const files = new Set;
6572
+ for (const file of [...staged.split(`
6573
+ `), ...unstaged.split(`
6574
+ `), ...untracked.split(`
6575
+ `)]) {
6576
+ if (file.trim()) {
6577
+ files.add(path4.resolve(absolutePath, file.trim()));
6578
+ }
6579
+ }
6580
+ return files;
6581
+ } catch {
6582
+ return new Set;
6583
+ }
6584
+ }
6563
6585
  function showHelp() {
6564
6586
  console.log(`
6565
6587
  dslop - Detect Similar/Duplicate Lines Of Programming
@@ -6575,6 +6597,7 @@ Options:
6575
6597
  -s, --similarity <n> Minimum similarity threshold 0-100 (default: ${Math.round(DEFAULT_SIMILARITY * 100)})
6576
6598
  -e, --extensions <s> File extensions to scan (default: ${DEFAULT_EXTENSIONS.join(",")})
6577
6599
  -i, --ignore <s> Patterns to ignore (default: ${DEFAULT_IGNORE_PATTERNS.slice(0, 4).join(",")},...)
6600
+ --staged Only show duplicates involving uncommitted changes
6578
6601
  --no-normalize Disable string/number normalization
6579
6602
  --cross-package Only show duplicates across different packages/apps (monorepo mode)
6580
6603
  --json Output as JSON
@@ -6583,8 +6606,8 @@ Options:
6583
6606
 
6584
6607
  Examples:
6585
6608
  dslop . Scan current directory
6609
+ dslop --staged Check uncommitted changes for duplicates
6586
6610
  dslop ./src -m 6 -s 80 Scan src with 6 line min, 80% similarity
6587
- dslop . -e ts,tsx --json Scan TypeScript files, output JSON
6588
6611
  dslop . --cross-package Only duplicates across packages (great for monorepos)
6589
6612
  `);
6590
6613
  }
@@ -6601,6 +6624,7 @@ async function main() {
6601
6624
  ignore: { type: "string", short: "i", default: DEFAULT_IGNORE_PATTERNS.join(",") },
6602
6625
  normalize: { type: "boolean", default: true },
6603
6626
  "no-normalize": { type: "boolean", default: false },
6627
+ staged: { type: "boolean", default: false },
6604
6628
  "cross-package": { type: "boolean", default: false },
6605
6629
  json: { type: "boolean", default: false },
6606
6630
  help: { type: "boolean", short: "h", default: false },
@@ -6622,6 +6646,7 @@ async function main() {
6622
6646
  const extensions = values.extensions.split(",").map((e) => e.trim());
6623
6647
  const ignorePatterns = values.ignore.split(",").map((p) => p.trim());
6624
6648
  const normalize2 = !values["no-normalize"];
6649
+ const staged = values.staged;
6625
6650
  const crossPackage = values["cross-package"];
6626
6651
  const jsonOutput = values.json;
6627
6652
  if (minLines < 2) {
@@ -6638,14 +6663,23 @@ async function main() {
6638
6663
  minLines,
6639
6664
  normalize: normalize2
6640
6665
  };
6666
+ const changedFiles = staged ? getChangedFiles(targetPath) : null;
6667
+ if (staged && changedFiles?.size === 0) {
6668
+ console.log(`
6669
+ No uncommitted changes found.`);
6670
+ process.exit(0);
6671
+ }
6641
6672
  console.log(`
6642
6673
  Scanning ${targetPath}...`);
6643
6674
  console.log(` Extensions: ${extensions.join(", ")}`);
6644
6675
  console.log(` Min block size: ${minLines} lines`);
6645
6676
  console.log(` Similarity threshold: ${Math.round(similarity * 100)}%`);
6646
6677
  console.log(` Normalization: ${normalize2 ? "enabled" : "disabled"}`);
6678
+ if (staged) {
6679
+ console.log(` Staged mode: checking ${changedFiles.size} changed files against codebase`);
6680
+ }
6647
6681
  if (crossPackage) {
6648
- console.log(` Cross-package mode: enabled (only showing duplicates across different packages/apps)`);
6682
+ console.log(` Cross-package mode: enabled`);
6649
6683
  }
6650
6684
  console.log();
6651
6685
  try {
@@ -6662,6 +6696,9 @@ Scanning ${targetPath}...`);
6662
6696
  const detectStart = performance.now();
6663
6697
  let duplicates = findDuplicates(blocks, similarity, targetPath);
6664
6698
  const detectTime = performance.now() - detectStart;
6699
+ if (staged && changedFiles) {
6700
+ duplicates = duplicates.filter((group) => group.matches.some((m) => changedFiles.has(m.filePath)));
6701
+ }
6665
6702
  if (crossPackage) {
6666
6703
  duplicates = duplicates.filter((group) => {
6667
6704
  const packages = new Set(group.matches.map((m) => {
@@ -6674,7 +6711,13 @@ Scanning ${targetPath}...`);
6674
6711
  console.log(`Found ${duplicates.length} duplicate groups in ${Math.round(detectTime)}ms
6675
6712
  `);
6676
6713
  if (duplicates.length === 0) {
6677
- console.log(crossPackage ? "No cross-package duplicates found!" : "No duplicates found!");
6714
+ if (staged) {
6715
+ console.log("No duplicates in your changes. You're good!");
6716
+ } else if (crossPackage) {
6717
+ console.log("No cross-package duplicates found!");
6718
+ } else {
6719
+ console.log("No duplicates found!");
6720
+ }
6678
6721
  process.exit(0);
6679
6722
  }
6680
6723
  if (jsonOutput) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dslop",
3
- "version": "1.0.6",
3
+ "version": "1.1.0",
4
4
  "description": "Detect Similar/Duplicate Lines Of Programming - Find code duplication in your codebase",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",