dslop 1.6.1 → 1.7.1
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 +24 -0
- package/README.md +132 -32
- package/dist/index.cjs +774 -1736
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.7.1
|
|
4
|
+
|
|
5
|
+
[compare changes](https://github.com/turf-sports/dslop/compare/v1.7.0...v1.7.1)
|
|
6
|
+
|
|
7
|
+
### 🔥 Performance
|
|
8
|
+
|
|
9
|
+
- 28x faster with caching, remove text-based detection ([b93742c](https://github.com/turf-sports/dslop/commit/b93742c))
|
|
10
|
+
|
|
11
|
+
### ❤️ Contributors
|
|
12
|
+
|
|
13
|
+
- Siddharth Sharma <sharmasiddharthcs@gmail.com>
|
|
14
|
+
|
|
15
|
+
## v1.7.0
|
|
16
|
+
|
|
17
|
+
[compare changes](https://github.com/turf-sports/dslop/compare/v1.6.1...v1.7.0)
|
|
18
|
+
|
|
19
|
+
### 🚀 Enhancements
|
|
20
|
+
|
|
21
|
+
- AST-only output, filter same-file dupes, sort by occurrences ([53acc46](https://github.com/turf-sports/dslop/commit/53acc46))
|
|
22
|
+
|
|
23
|
+
### ❤️ Contributors
|
|
24
|
+
|
|
25
|
+
- Siddharth Sharma <sharmasiddharthcs@gmail.com>
|
|
26
|
+
|
|
3
27
|
## v1.6.1
|
|
4
28
|
|
|
5
29
|
[compare changes](https://github.com/turf-sports/dslop/compare/v1.6.0...v1.6.1)
|
package/README.md
CHANGED
|
@@ -1,12 +1,92 @@
|
|
|
1
1
|
# dslop
|
|
2
2
|
|
|
3
|
-
Find duplicate code in your codebase.
|
|
3
|
+
Find duplicate functions, types, and code in your codebase using AST analysis.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npx dslop
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## What it finds
|
|
10
|
+
|
|
11
|
+
**Real duplicates, not noise.** dslop uses AST parsing to find semantically identical code - functions with the same logic but different variable names, types defined in multiple places, copy-pasted utilities.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
$ dslop --all --json
|
|
15
|
+
|
|
16
|
+
{
|
|
17
|
+
"summary": { "duplicateGroups": 101 },
|
|
18
|
+
"duplicates": [
|
|
19
|
+
{
|
|
20
|
+
"type": "function",
|
|
21
|
+
"name": "loadEnv",
|
|
22
|
+
"occurrences": 6,
|
|
23
|
+
"locations": [
|
|
24
|
+
{ "name": "loadEnv", "file": "scripts/migrate.ts", "line": 8 },
|
|
25
|
+
{ "name": "loadEnv", "file": "scripts/seed.ts", "line": 12 },
|
|
26
|
+
{ "name": "loadEnv", "file": "lib/db/migrate.ts", "line": 7 },
|
|
27
|
+
{ "name": "loadEnv", "file": "lib/db/check-state.ts", "line": 9 }
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"type": "interface",
|
|
32
|
+
"name": "Params",
|
|
33
|
+
"occurrences": 6,
|
|
34
|
+
"locations": [
|
|
35
|
+
{ "name": "Params", "file": "app/api/games/[id]/route.ts", "line": 5 },
|
|
36
|
+
{ "name": "Params", "file": "app/api/games/[id]/state/route.ts", "line": 8 },
|
|
37
|
+
{ "name": "Params", "file": "app/api/games/[id]/details/route.ts", "line": 12 }
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"type": "type",
|
|
42
|
+
"name": "LeaderboardPlayer",
|
|
43
|
+
"occurrences": 3,
|
|
44
|
+
"locations": [
|
|
45
|
+
{ "name": "LeaderboardPlayer", "file": "packages/types/leaderboard.ts", "line": 45, "exported": true },
|
|
46
|
+
{ "name": "LeaderboardPlayer", "file": "apps/mobile/types.ts", "line": 52, "exported": false },
|
|
47
|
+
{ "name": "LeaderboardPlayer", "file": "apps/web/lib/types.ts", "line": 30, "exported": false }
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Monorepo cross-package duplicates
|
|
55
|
+
|
|
56
|
+
Find types and functions duplicated across packages:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
$ dslop --all --cross-package
|
|
60
|
+
|
|
61
|
+
Found 48 duplicate functions/types
|
|
62
|
+
|
|
63
|
+
# Types duplicated between packages/types and apps/
|
|
64
|
+
PrizeDistribution packages/types/game.ts ↔ packages/db/schema/game.ts
|
|
65
|
+
DevicePlatform apps/web/lib/notifications.ts ↔ apps/listener/lib/notifications.ts
|
|
66
|
+
TeamColors apps/mobile/store/types.ts ↔ apps/web/lib/types/colors.ts
|
|
67
|
+
|
|
68
|
+
# Functions copy-pasted between apps/
|
|
69
|
+
subscribeToChannel apps/web/lib/subscriptions.ts ↔ apps/listener/lib/subscriptions.ts
|
|
70
|
+
getTeamLogoUrl packages/shared/logos.ts → also in apps/web (3 copies)
|
|
71
|
+
normalizeError apps/web/sentry.config.ts ↔ apps/listener/lib/sentry.ts
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### PR review mode
|
|
75
|
+
|
|
76
|
+
By default, dslop checks your branch changes against the existing codebase:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
$ dslop
|
|
80
|
+
|
|
81
|
+
Scanning...
|
|
82
|
+
Mode: checking changed lines in 3 files
|
|
83
|
+
|
|
84
|
+
Found 2 duplicate functions/types
|
|
85
|
+
|
|
86
|
+
# You're adding code that already exists elsewhere:
|
|
87
|
+
getUserDisplayName your change: app/profile/page.tsx:19
|
|
88
|
+
exists in: components/sidebar.tsx:50
|
|
89
|
+
```
|
|
10
90
|
|
|
11
91
|
## Install
|
|
12
92
|
|
|
@@ -18,9 +98,10 @@ npm i -g dslop
|
|
|
18
98
|
|
|
19
99
|
```bash
|
|
20
100
|
dslop # check PR changes (or full scan if none)
|
|
21
|
-
dslop
|
|
22
|
-
dslop
|
|
23
|
-
dslop --cross-package # cross-package dupes (monorepos)
|
|
101
|
+
dslop --all # full codebase scan
|
|
102
|
+
dslop --all --json # JSON output for tooling
|
|
103
|
+
dslop --cross-package # only cross-package dupes (monorepos)
|
|
104
|
+
dslop ./apps/web # scan specific directory
|
|
24
105
|
```
|
|
25
106
|
|
|
26
107
|
## Options
|
|
@@ -32,47 +113,66 @@ dslop --cross-package # cross-package dupes (monorepos)
|
|
|
32
113
|
| `-m, --min-lines` | min lines per block (default: 4) |
|
|
33
114
|
| `-s, --similarity` | similarity threshold 0-100 (default: 70) |
|
|
34
115
|
| `-e, --extensions` | file extensions (default: ts,tsx,js,jsx) |
|
|
35
|
-
| `--cross-package` | only show dupes across packages |
|
|
36
|
-
| `--json` |
|
|
116
|
+
| `--cross-package` | only show dupes across packages/apps |
|
|
117
|
+
| `--json` | JSON output |
|
|
37
118
|
|
|
38
119
|
## How it works
|
|
39
120
|
|
|
40
|
-
dslop
|
|
121
|
+
dslop parses TypeScript/JavaScript with Babel and extracts functions, classes, types, and interfaces. It normalizes the AST by replacing all identifiers with generic placeholders (`$0`, `$1`, etc.), preserving only the code structure.
|
|
41
122
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
Parses TypeScript/JavaScript with Babel to extract functions and classes. Normalizes the AST by replacing all identifiers with generic placeholders (`$0`, `$1`, etc.), preserving only the code structure.
|
|
45
|
-
|
|
46
|
-
**This catches:**
|
|
123
|
+
This catches:
|
|
47
124
|
- Functions with identical logic but different variable names
|
|
48
|
-
-
|
|
49
|
-
-
|
|
125
|
+
- Types/interfaces defined in multiple places
|
|
126
|
+
- Copy-pasted utilities across packages
|
|
127
|
+
|
|
128
|
+
Example: these two functions are detected as duplicates:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
// apps/web/utils.ts
|
|
132
|
+
function getUserInitials(user: User): string {
|
|
133
|
+
const first = user.firstName?.[0] ?? '';
|
|
134
|
+
const last = user.lastName?.[0] ?? '';
|
|
135
|
+
return (first + last).toUpperCase();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// apps/admin/helpers.ts
|
|
139
|
+
function getInitials(person: Person): string {
|
|
140
|
+
const f = person.firstName?.[0] ?? '';
|
|
141
|
+
const l = person.lastName?.[0] ?? '';
|
|
142
|
+
return (f + l).toUpperCase();
|
|
143
|
+
}
|
|
144
|
+
```
|
|
50
145
|
|
|
51
|
-
|
|
146
|
+
### What it ignores
|
|
52
147
|
|
|
53
|
-
|
|
148
|
+
- Same-file duplicates (patterns within a single file)
|
|
149
|
+
- Tiny functions (configurable via `--min-lines`)
|
|
150
|
+
- Common patterns from UI libraries (shadcn components, etc.)
|
|
54
151
|
|
|
55
|
-
|
|
56
|
-
- String literals → `"<STRING>"`
|
|
57
|
-
- Numbers → `<NUMBER>`
|
|
58
|
-
- Whitespace collapsed
|
|
59
|
-
- Comments preserved (intentional - comments often indicate copy-paste)
|
|
152
|
+
## Performance
|
|
60
153
|
|
|
61
|
-
|
|
154
|
+
dslop uses aggressive caching to make subsequent runs fast:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# First run (builds cache)
|
|
158
|
+
$ dslop --all
|
|
159
|
+
Scanned 1410 files in 2862ms
|
|
160
|
+
Cache: 0 hits, 1410 misses (0% hit rate)
|
|
161
|
+
|
|
162
|
+
# Second run (uses cache)
|
|
163
|
+
$ dslop --all
|
|
164
|
+
Scanned 1410 files in 305ms
|
|
165
|
+
Cache: 1410 hits, 0 misses (100% hit rate)
|
|
166
|
+
```
|
|
62
167
|
|
|
63
|
-
|
|
168
|
+
Cache is stored in `.dslop-cache` in your project root. Add it to `.gitignore`.
|
|
64
169
|
|
|
65
|
-
|
|
66
|
-
2. If no changes found → automatically scans the entire target path
|
|
67
|
-
3. Use `-c` to force changes-only mode (useful in CI)
|
|
170
|
+
Use `--no-cache` to bypass the cache.
|
|
68
171
|
|
|
69
172
|
## Limitations
|
|
70
173
|
|
|
71
|
-
- **TypeScript/JavaScript only
|
|
72
|
-
- **No cross-language:** Won't detect
|
|
73
|
-
- **Comments affect text matching:** Intentional tradeoff. Copy-pasted code often includes comments.
|
|
74
|
-
- **Minimum 4 lines:** Shorter duplicates ignored to reduce noise. Use `-m 2` for stricter.
|
|
75
|
-
- **Memory:** Loads all blocks in memory. Very large codebases (>1M lines) may be slow.
|
|
174
|
+
- **TypeScript/JavaScript only:** AST parsing uses Babel with TS/JSX plugins
|
|
175
|
+
- **No cross-language:** Won't detect duplication across languages
|
|
76
176
|
|
|
77
177
|
## License
|
|
78
178
|
|