find-duplicate-js 1.2.0 ā 1.3.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 +33 -0
- package/README.md +393 -381
- package/find-duplicates-ui.js +1 -1
- package/find-duplicates.js +170 -11
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.3.1] - 2025-12-31
|
|
6
|
+
|
|
7
|
+
### š Changes
|
|
8
|
+
|
|
9
|
+
#### Simplified Command Names
|
|
10
|
+
- **Changed Command**: `find-duplicates` ā `find-duplicate` (removed 's')
|
|
11
|
+
- **Changed UI Command**: `find-duplicates-ui` ā `find-duplicate-ui` (removed 's')
|
|
12
|
+
- **Reason**: Avoid confusion - file names have 's' (find-duplicates.js) but commands don't
|
|
13
|
+
- **Backwards Compatibility**: Old commands may still work in cached installations
|
|
14
|
+
|
|
15
|
+
**New Usage:**
|
|
16
|
+
```bash
|
|
17
|
+
find-duplicate ./src 80
|
|
18
|
+
find-duplicate --ui ./src
|
|
19
|
+
find-duplicate-ui ./src
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## [1.3.0] - 2025-12-31
|
|
23
|
+
|
|
24
|
+
### š New Features
|
|
25
|
+
|
|
26
|
+
#### Unified CLI with --ui Flag
|
|
27
|
+
- **Single Command**: No need for separate `find-duplicates-ui` command
|
|
28
|
+
- **--ui Flag**: Add `--ui` flag to launch web interface from main command
|
|
29
|
+
- Example: `find-duplicates --ui ./src 80`
|
|
30
|
+
- **Backwards Compatible**: `find-duplicates-ui` command still works
|
|
31
|
+
- **Updated npm Scripts**: `npm run ui` now uses the `--ui` flag
|
|
32
|
+
|
|
33
|
+
### ⨠Improvements
|
|
34
|
+
- **Simplified Usage**: One command for both CLI and UI modes
|
|
35
|
+
- **Better UX**: More intuitive flag-based interface
|
|
36
|
+
- **Cleaner Architecture**: UI server code integrated into main file
|
|
37
|
+
|
|
5
38
|
## [1.2.0] - 2025-12-31
|
|
6
39
|
|
|
7
40
|
### š New Features
|
package/README.md
CHANGED
|
@@ -1,381 +1,393 @@
|
|
|
1
|
-
# š Find Duplicate JS
|
|
2
|
-
|
|
3
|
-
[](https://www.npmjs.com/package/find-duplicate-js)
|
|
4
|
-
[](https://www.npmjs.com/package/find-duplicate-js)
|
|
5
|
-
[](https://github.com/benshabbat/find-duplicate-js/blob/main/LICENSE)
|
|
6
|
-
|
|
7
|
-
A powerful and intelligent tool to detect duplicate and similar code in JavaScript projects. Find Duplicate JS helps you maintain cleaner codebases by automatically identifying redundant functions and code patterns across your project.
|
|
8
|
-
|
|
9
|
-
## š Table of Contents
|
|
10
|
-
|
|
11
|
-
- [Why Use Find Duplicate JS?](#why-use-find-duplicate-js)
|
|
12
|
-
- [Features](#features)
|
|
13
|
-
- [Installation](#installation)
|
|
14
|
-
- [Usage](#usage)
|
|
15
|
-
- [CLI Mode](#cli-mode)
|
|
16
|
-
- [Web UI Mode](#web-ui-mode)
|
|
17
|
-
- [How It Works](#how-it-works)
|
|
18
|
-
- [Configuration Options](#configuration-options)
|
|
19
|
-
- [Examples](#examples)
|
|
20
|
-
- [API](#api)
|
|
21
|
-
- [Contributing](#contributing)
|
|
22
|
-
- [Links](#links)
|
|
23
|
-
- [License](#license)
|
|
24
|
-
|
|
25
|
-
## šÆ Why Use Find Duplicate JS?
|
|
26
|
-
|
|
27
|
-
Duplicate code is a common problem in software development that leads to:
|
|
28
|
-
- **Maintenance Headaches**: Fixing bugs requires updating code in multiple places
|
|
29
|
-
- **Increased File Size**: Unnecessary code bloat
|
|
30
|
-
- **Inconsistencies**: Changes in one place might not be reflected elsewhere
|
|
31
|
-
- **Technical Debt**: Harder to refactor and improve code quality
|
|
32
|
-
|
|
33
|
-
Find Duplicate JS helps you identify these issues automatically, saving time and improving code quality.
|
|
34
|
-
|
|
35
|
-
## ⨠Features
|
|
36
|
-
|
|
37
|
-
- **šÆ Smart Function Detection**: Recognizes multiple function types
|
|
38
|
-
- Arrow functions (`const func = () => {}`)
|
|
39
|
-
- Function declarations (`function func() {}`)
|
|
40
|
-
- Class methods and object methods
|
|
41
|
-
- Async functions
|
|
42
|
-
|
|
43
|
-
- **š§ Intelligent Code Analysis**:
|
|
44
|
-
- Normalizes code to ignore irrelevant differences (whitespace, comments, variable names)
|
|
45
|
-
- Uses Levenshtein distance algorithm for accurate similarity scoring
|
|
46
|
-
- Configurable similarity threshold (default 70%)
|
|
47
|
-
|
|
48
|
-
- **šØ Two Usage Modes**:
|
|
49
|
-
- **CLI Mode**: Quick terminal-based analysis with detailed text output
|
|
50
|
-
- **Web UI Mode**: Beautiful, interactive web interface with visual comparisons
|
|
51
|
-
|
|
52
|
-
- **ā” Performance**:
|
|
53
|
-
- Recursively scans entire project directories
|
|
54
|
-
- Automatically skips `node_modules`, `.git`, `dist`, and `build` folders
|
|
55
|
-
- Handles both `.js` and `.jsx` files
|
|
56
|
-
|
|
57
|
-
- **š§ Zero Configuration**: Works out of the box with sensible defaults
|
|
58
|
-
|
|
59
|
-
## š¦ Installation
|
|
60
|
-
|
|
61
|
-
### Global Installation (Recommended)
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
npm install -g find-duplicate-js
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### Local Installation
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
npm install --save-dev find-duplicate-js
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Then add to your `package.json` scripts:
|
|
74
|
-
|
|
75
|
-
```json
|
|
76
|
-
{
|
|
77
|
-
"scripts": {
|
|
78
|
-
"find-
|
|
79
|
-
"find-
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
-
|
|
172
|
-
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
-
|
|
184
|
-
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
find-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
{
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
1
|
+
# š Find Duplicate JS
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/find-duplicate-js)
|
|
4
|
+
[](https://www.npmjs.com/package/find-duplicate-js)
|
|
5
|
+
[](https://github.com/benshabbat/find-duplicate-js/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
A powerful and intelligent tool to detect duplicate and similar code in JavaScript projects. Find Duplicate JS helps you maintain cleaner codebases by automatically identifying redundant functions and code patterns across your project.
|
|
8
|
+
|
|
9
|
+
## š Table of Contents
|
|
10
|
+
|
|
11
|
+
- [Why Use Find Duplicate JS?](#why-use-find-duplicate-js)
|
|
12
|
+
- [Features](#features)
|
|
13
|
+
- [Installation](#installation)
|
|
14
|
+
- [Usage](#usage)
|
|
15
|
+
- [CLI Mode](#cli-mode)
|
|
16
|
+
- [Web UI Mode](#web-ui-mode)
|
|
17
|
+
- [How It Works](#how-it-works)
|
|
18
|
+
- [Configuration Options](#configuration-options)
|
|
19
|
+
- [Examples](#examples)
|
|
20
|
+
- [API](#api)
|
|
21
|
+
- [Contributing](#contributing)
|
|
22
|
+
- [Links](#links)
|
|
23
|
+
- [License](#license)
|
|
24
|
+
|
|
25
|
+
## šÆ Why Use Find Duplicate JS?
|
|
26
|
+
|
|
27
|
+
Duplicate code is a common problem in software development that leads to:
|
|
28
|
+
- **Maintenance Headaches**: Fixing bugs requires updating code in multiple places
|
|
29
|
+
- **Increased File Size**: Unnecessary code bloat
|
|
30
|
+
- **Inconsistencies**: Changes in one place might not be reflected elsewhere
|
|
31
|
+
- **Technical Debt**: Harder to refactor and improve code quality
|
|
32
|
+
|
|
33
|
+
Find Duplicate JS helps you identify these issues automatically, saving time and improving code quality.
|
|
34
|
+
|
|
35
|
+
## ⨠Features
|
|
36
|
+
|
|
37
|
+
- **šÆ Smart Function Detection**: Recognizes multiple function types
|
|
38
|
+
- Arrow functions (`const func = () => {}`)
|
|
39
|
+
- Function declarations (`function func() {}`)
|
|
40
|
+
- Class methods and object methods
|
|
41
|
+
- Async functions
|
|
42
|
+
|
|
43
|
+
- **š§ Intelligent Code Analysis**:
|
|
44
|
+
- Normalizes code to ignore irrelevant differences (whitespace, comments, variable names)
|
|
45
|
+
- Uses Levenshtein distance algorithm for accurate similarity scoring
|
|
46
|
+
- Configurable similarity threshold (default 70%)
|
|
47
|
+
|
|
48
|
+
- **šØ Two Usage Modes**:
|
|
49
|
+
- **CLI Mode**: Quick terminal-based analysis with detailed text output
|
|
50
|
+
- **Web UI Mode**: Beautiful, interactive web interface with visual comparisons
|
|
51
|
+
|
|
52
|
+
- **ā” Performance**:
|
|
53
|
+
- Recursively scans entire project directories
|
|
54
|
+
- Automatically skips `node_modules`, `.git`, `dist`, and `build` folders
|
|
55
|
+
- Handles both `.js` and `.jsx` files
|
|
56
|
+
|
|
57
|
+
- **š§ Zero Configuration**: Works out of the box with sensible defaults
|
|
58
|
+
|
|
59
|
+
## š¦ Installation
|
|
60
|
+
|
|
61
|
+
### Global Installation (Recommended)
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm install -g find-duplicate-js
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Local Installation
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm install --save-dev find-duplicate-js
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Then add to your `package.json` scripts:
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"scripts": {
|
|
78
|
+
"find-duplicate": "find-duplicate",
|
|
79
|
+
"find-duplicate:ui": "find-duplicate --ui"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Or use the built-in npm scripts:
|
|
85
|
+
```bash
|
|
86
|
+
npm start # Run CLI mode
|
|
87
|
+
npm run ui # Run UI mode
|
|
88
|
+
npm test # Run tests
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## š Usage
|
|
92
|
+
|
|
93
|
+
### CLI Mode
|
|
94
|
+
|
|
95
|
+
Run a quick analysis from the command line and get results in your terminal:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Analyze current directory with default threshold (70%)
|
|
99
|
+
find-duplicate
|
|
100
|
+
|
|
101
|
+
# Analyze specific directory
|
|
102
|
+
find-duplicate ./src
|
|
103
|
+
|
|
104
|
+
# Custom similarity threshold (80%)
|
|
105
|
+
find-duplicate ./src 80
|
|
106
|
+
|
|
107
|
+
# Analyze entire project
|
|
108
|
+
find-duplicate . 75
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**CLI Output Example:**
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
š Searching for duplicate code in: ./src
|
|
115
|
+
š Similarity threshold: 70%
|
|
116
|
+
|
|
117
|
+
š Scanning 15 JavaScript files...
|
|
118
|
+
|
|
119
|
+
š Found 42 functions total
|
|
120
|
+
|
|
121
|
+
ā ļø Found 3 pairs of similar functions:
|
|
122
|
+
|
|
123
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
124
|
+
|
|
125
|
+
š Match #1 - Similarity: 95.5%
|
|
126
|
+
|
|
127
|
+
File 1: src/utils/math.js
|
|
128
|
+
Function: calculateTotal()
|
|
129
|
+
Code: const sum = items.reduce((acc, item) => acc + item.price, 0)...
|
|
130
|
+
|
|
131
|
+
File 2: src/components/cart.js
|
|
132
|
+
Function: getTotalPrice()
|
|
133
|
+
Code: const total = products.reduce((acc, prod) => acc + prod.pri...
|
|
134
|
+
|
|
135
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
136
|
+
|
|
137
|
+
š” Summary: Found 3 duplicate function pairs
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Web UI Mode
|
|
141
|
+
|
|
142
|
+
Launch an interactive web interface for a better visual experience using the `--ui` flag:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# Start web UI server (opens browser automatically)
|
|
146
|
+
find-duplicate --ui
|
|
147
|
+
|
|
148
|
+
# Analyze specific directory
|
|
149
|
+
find-duplicate --ui ./src
|
|
150
|
+
|
|
151
|
+
# Custom threshold
|
|
152
|
+
find-duplicate --ui ./src 80
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Alternative:** You can also use the dedicated UI command (backwards compatibility):
|
|
156
|
+
```bash
|
|
157
|
+
find-duplicate-ui ./src 80
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The web interface will:
|
|
161
|
+
1. Start a local server on `http://localhost:2712`
|
|
162
|
+
2. Automatically open your default browser
|
|
163
|
+
3. Display an interactive dashboard with:
|
|
164
|
+
- Project statistics
|
|
165
|
+
- Color-coded duplicate pairs
|
|
166
|
+
- Side-by-side code comparison
|
|
167
|
+
- Similarity percentages
|
|
168
|
+
- File paths and function names
|
|
169
|
+
|
|
170
|
+
**Features in Web UI:**
|
|
171
|
+
- š **Statistics Dashboard**: Overview of scanned files, functions found, and duplicates
|
|
172
|
+
- šØ **Beautiful Design**: Modern, responsive interface
|
|
173
|
+
- š **Live Refresh**: Re-analyze your code with a single click
|
|
174
|
+
- š± **Mobile Friendly**: Works on all devices
|
|
175
|
+
- šÆ **Easy Navigation**: Jump directly to problematic code
|
|
176
|
+
|
|
177
|
+
## š§ How It Works
|
|
178
|
+
|
|
179
|
+
### 1. **File Discovery**
|
|
180
|
+
Recursively scans your project directory and finds all `.js` and `.jsx` files, while intelligently skipping:
|
|
181
|
+
- `node_modules`
|
|
182
|
+
- `.git`
|
|
183
|
+
- `dist`
|
|
184
|
+
- `build`
|
|
185
|
+
|
|
186
|
+
### 2. **Function Extraction**
|
|
187
|
+
Uses sophisticated regex patterns to identify and extract:
|
|
188
|
+
- Arrow functions with `const`, `let`, or `var`
|
|
189
|
+
- Traditional function declarations
|
|
190
|
+
- Class and object methods
|
|
191
|
+
- Async functions
|
|
192
|
+
|
|
193
|
+
### 3. **Code Normalization**
|
|
194
|
+
Before comparison, the code is normalized to focus on logic rather than style:
|
|
195
|
+
- Removes all whitespace and line breaks
|
|
196
|
+
- Strips comments (single-line and multi-line)
|
|
197
|
+
- Replaces variable names with generic placeholders
|
|
198
|
+
- Replaces string literals with generic strings
|
|
199
|
+
- Removes template literals
|
|
200
|
+
|
|
201
|
+
**Example:**
|
|
202
|
+
```javascript
|
|
203
|
+
// Original Code
|
|
204
|
+
function calculateSum(num1, num2) {
|
|
205
|
+
// Calculate sum of two numbers
|
|
206
|
+
const result = num1 + num2;
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Normalized Code
|
|
211
|
+
V(){V=V+V;V;}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### 4. **Similarity Calculation**
|
|
215
|
+
Uses the **Levenshtein Distance** algorithm to calculate how similar two functions are:
|
|
216
|
+
- Measures the minimum number of edits needed to transform one string into another
|
|
217
|
+
- Converts to a percentage (0-100%)
|
|
218
|
+
- Compares against your configured threshold
|
|
219
|
+
|
|
220
|
+
### 5. **Results Presentation**
|
|
221
|
+
Presents findings in an easy-to-understand format (CLI or Web UI) showing:
|
|
222
|
+
- Which functions are similar
|
|
223
|
+
- Their similarity percentage
|
|
224
|
+
- File locations
|
|
225
|
+
- Code previews
|
|
226
|
+
|
|
227
|
+
## āļø Configuration Options
|
|
228
|
+
|
|
229
|
+
### Command Line Arguments
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
find-duplicate [directory] [threshold]
|
|
233
|
+
find-duplicate-ui [directory] [threshold]
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Parameters:**
|
|
237
|
+
- `directory` (optional): Path to analyze. Default: current directory (`.`)
|
|
238
|
+
- `threshold` (optional): Similarity percentage (0-100). Default: `70`
|
|
239
|
+
|
|
240
|
+
### Examples:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
# Very strict (only near-identical code)
|
|
244
|
+
find-duplicate ./src 95
|
|
245
|
+
|
|
246
|
+
# Moderate (recommended)
|
|
247
|
+
find-duplicate ./src 70
|
|
248
|
+
|
|
249
|
+
# Lenient (catches more potential duplicates)
|
|
250
|
+
find-duplicate ./src 50
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Threshold Guidelines:
|
|
254
|
+
|
|
255
|
+
- **90-100%**: Nearly identical functions (different variable names only)
|
|
256
|
+
- **70-89%**: Very similar logic with minor variations
|
|
257
|
+
- **50-69%**: Similar patterns but with notable differences
|
|
258
|
+
- **Below 50%**: May produce many false positives
|
|
259
|
+
|
|
260
|
+
## š Examples
|
|
261
|
+
|
|
262
|
+
### Example 1: Finding Exact Duplicates
|
|
263
|
+
|
|
264
|
+
**File 1: `auth.js`**
|
|
265
|
+
```javascript
|
|
266
|
+
function validateUser(username, password) {
|
|
267
|
+
if (!username || !password) {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**File 2: `login.js`**
|
|
275
|
+
```javascript
|
|
276
|
+
function checkCredentials(user, pass) {
|
|
277
|
+
if (!user || !pass) {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Result**: 100% similarity - Same logic, different names
|
|
285
|
+
|
|
286
|
+
### Example 2: Similar Functions
|
|
287
|
+
|
|
288
|
+
**File 1: `cart.js`**
|
|
289
|
+
```javascript
|
|
290
|
+
const calculateTotal = (items) => {
|
|
291
|
+
return items.reduce((sum, item) => sum + item.price, 0);
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
**File 2: `checkout.js`**
|
|
296
|
+
```javascript
|
|
297
|
+
const getTotalPrice = (products) => {
|
|
298
|
+
let total = 0;
|
|
299
|
+
products.forEach(product => {
|
|
300
|
+
total += product.price;
|
|
301
|
+
});
|
|
302
|
+
return total;
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
**Result**: ~75% similarity - Same logic, different implementation
|
|
307
|
+
|
|
308
|
+
## š API
|
|
309
|
+
|
|
310
|
+
You can also use Find Duplicate JS programmatically in your Node.js projects:
|
|
311
|
+
|
|
312
|
+
```javascript
|
|
313
|
+
import { findDuplicates, findJsFiles } from 'find-duplicate-js';
|
|
314
|
+
|
|
315
|
+
// Find all JavaScript files
|
|
316
|
+
const files = findJsFiles('./src');
|
|
317
|
+
console.log(`Found ${files.length} files`);
|
|
318
|
+
|
|
319
|
+
// Find duplicates with 70% threshold
|
|
320
|
+
const result = findDuplicates('./src', 70);
|
|
321
|
+
|
|
322
|
+
console.log(`Total functions: ${result.totalFunctions}`);
|
|
323
|
+
console.log(`Duplicate pairs: ${result.duplicates.length}`);
|
|
324
|
+
|
|
325
|
+
// Iterate through duplicates
|
|
326
|
+
result.duplicates.forEach((dup, index) => {
|
|
327
|
+
console.log(`\nMatch #${index + 1}:`);
|
|
328
|
+
console.log(`Similarity: ${dup.similarity}%`);
|
|
329
|
+
console.log(`Function 1: ${dup.func1.name} in ${dup.func1.filePath}`);
|
|
330
|
+
console.log(`Function 2: ${dup.func2.name} in ${dup.func2.filePath}`);
|
|
331
|
+
});
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Available Functions
|
|
335
|
+
|
|
336
|
+
#### `findJsFiles(directory)`
|
|
337
|
+
Returns an array of all `.js` and `.jsx` file paths in the directory.
|
|
338
|
+
|
|
339
|
+
#### `findDuplicates(directory, threshold = 70)`
|
|
340
|
+
Analyzes the directory and returns:
|
|
341
|
+
```javascript
|
|
342
|
+
{
|
|
343
|
+
duplicates: [
|
|
344
|
+
{
|
|
345
|
+
func1: { name, body, originalBody, filePath, startIndex },
|
|
346
|
+
func2: { name, body, originalBody, filePath, startIndex },
|
|
347
|
+
similarity: "95.50"
|
|
348
|
+
}
|
|
349
|
+
],
|
|
350
|
+
totalFunctions: 42
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
#### `calculateSimilarity(code1, code2)`
|
|
355
|
+
Calculates similarity percentage between two code strings.
|
|
356
|
+
|
|
357
|
+
#### `normalizeCode(code)`
|
|
358
|
+
Normalizes JavaScript code for comparison.
|
|
359
|
+
|
|
360
|
+
## š¤ Contributing
|
|
361
|
+
|
|
362
|
+
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
|
|
363
|
+
|
|
364
|
+
### Development Setup
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
# Clone the repository
|
|
368
|
+
git clone https://github.com/benshabbat/find-duplicate-js.git
|
|
369
|
+
|
|
370
|
+
# Install dependencies
|
|
371
|
+
cd find-duplicate-js
|
|
372
|
+
npm install
|
|
373
|
+
|
|
374
|
+
# Run locally
|
|
375
|
+
node find-duplicates.js ./src
|
|
376
|
+
node find-duplicates-ui.js ./src
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## š Links
|
|
380
|
+
|
|
381
|
+
- [npm Package](https://www.npmjs.com/package/find-duplicate-js)
|
|
382
|
+
- [GitHub Repository](https://github.com/benshabbat/find-duplicate-js)
|
|
383
|
+
- [Issues & Bug Reports](https://github.com/benshabbat/find-duplicate-js/issues)
|
|
384
|
+
|
|
385
|
+
## š License
|
|
386
|
+
|
|
387
|
+
MIT Ā© [benshabbat](https://github.com/benshabbat)
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
**Made with ā¤ļø to help developers write better code**
|
|
392
|
+
|
|
393
|
+
If you find this tool helpful, please consider giving it a ā on [GitHub](https://github.com/benshabbat/find-duplicate-js)!
|
package/find-duplicates-ui.js
CHANGED
package/find-duplicates.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
5
6
|
import { findDuplicates, findJsFiles } from './find-duplicates-core.js';
|
|
6
7
|
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
|
|
7
11
|
/**
|
|
8
12
|
* Displays the duplicate detection results to the console
|
|
9
13
|
* @param {{duplicates: Array, totalFunctions: number}} result - The analysis result object
|
|
@@ -34,26 +38,181 @@ function displayResults(result) {
|
|
|
34
38
|
console.log(`\nš” Summary: Found ${duplicates.length} duplicate function pair${duplicates.length > 1 ? 's' : ''}\n`);
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Starts the web UI server
|
|
43
|
+
* @param {string} directory - Directory to analyze
|
|
44
|
+
* @param {number} threshold - Similarity threshold
|
|
45
|
+
*/
|
|
46
|
+
async function startUIServer(directory, threshold) {
|
|
47
|
+
const { exec: open } = await import('child_process');
|
|
48
|
+
const http = await import('http');
|
|
49
|
+
|
|
50
|
+
const PORT = 2712;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Generates the HTML page for the web UI
|
|
54
|
+
*/
|
|
55
|
+
function generateHTML(duplicates, stats) {
|
|
56
|
+
const templatePath = path.join(__dirname, "ui-template.html");
|
|
57
|
+
const cssPath = path.join(__dirname, "ui-styles.css");
|
|
58
|
+
|
|
59
|
+
let template = fs.readFileSync(templatePath, "utf-8");
|
|
60
|
+
const css = fs.readFileSync(cssPath, "utf-8");
|
|
61
|
+
|
|
62
|
+
// Inject CSS inline
|
|
63
|
+
template = template.replace('<link rel="stylesheet" href="ui-styles.css">', `<style>${css}</style>`);
|
|
64
|
+
|
|
65
|
+
// Replace stats placeholders
|
|
66
|
+
template = template
|
|
67
|
+
.replace("{{filesScanned}}", stats.filesScanned)
|
|
68
|
+
.replace("{{functionsFound}}", stats.functionsFound)
|
|
69
|
+
.replace("{{duplicatesFound}}", stats.duplicatesFound)
|
|
70
|
+
.replace("{{threshold}}", stats.threshold);
|
|
71
|
+
|
|
72
|
+
// Generate results HTML
|
|
73
|
+
const resultsHTML = duplicates.length === 0
|
|
74
|
+
? `
|
|
75
|
+
<div class="no-duplicates">
|
|
76
|
+
<div class="icon">ā
</div>
|
|
77
|
+
<h2>Great! No Duplicates Found</h2>
|
|
78
|
+
<p>Your code is clean and well-organized.</p>
|
|
79
|
+
</div>
|
|
80
|
+
`
|
|
81
|
+
: duplicates
|
|
82
|
+
.map(
|
|
83
|
+
(dup, index) => `
|
|
84
|
+
<div class="duplicate-card">
|
|
85
|
+
<div class="duplicate-header">
|
|
86
|
+
<h3>š Match #${index + 1}</h3>
|
|
87
|
+
<div class="similarity-badge">${dup.similarity}% Similar</div>
|
|
88
|
+
</div>
|
|
89
|
+
<div class="duplicate-body">
|
|
90
|
+
<div class="function-comparison">
|
|
91
|
+
<div class="function-info">
|
|
92
|
+
<h4>Function 1</h4>
|
|
93
|
+
<div class="file-path">š ${dup.func1.filePath}</div>
|
|
94
|
+
<div class="function-name">${dup.func1.name}()</div>
|
|
95
|
+
<div class="code-preview">${escapeHtml(
|
|
96
|
+
dup.func1.originalBody.substring(0, 200)
|
|
97
|
+
)}${dup.func1.originalBody.length > 200 ? "..." : ""}</div>
|
|
98
|
+
</div>
|
|
99
|
+
<div class="function-info">
|
|
100
|
+
<h4>Function 2</h4>
|
|
101
|
+
<div class="file-path">š ${dup.func2.filePath}</div>
|
|
102
|
+
<div class="function-name">${dup.func2.name}()</div>
|
|
103
|
+
<div class="code-preview">${escapeHtml(
|
|
104
|
+
dup.func2.originalBody.substring(0, 200)
|
|
105
|
+
)}${dup.func2.originalBody.length > 200 ? "..." : ""}</div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
`
|
|
111
|
+
)
|
|
112
|
+
.join("");
|
|
113
|
+
|
|
114
|
+
// Inject results
|
|
115
|
+
template = template.replace('<div id="results">', `<div id="results">${resultsHTML}`);
|
|
116
|
+
|
|
117
|
+
return template;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Escapes HTML special characters to prevent XSS attacks
|
|
122
|
+
*/
|
|
123
|
+
function escapeHtml(text) {
|
|
124
|
+
return text
|
|
125
|
+
.replace(/&/g, "&")
|
|
126
|
+
.replace(/</g, "<")
|
|
127
|
+
.replace(/>/g, ">")
|
|
128
|
+
.replace(/"/g, """)
|
|
129
|
+
.replace(/'/g, "'");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
console.log("\nš Starting Duplicate Finder Server...\n");
|
|
133
|
+
console.log(`š Directory: ${directory}`);
|
|
134
|
+
console.log(`š Threshold: ${threshold}%`);
|
|
135
|
+
|
|
136
|
+
const server = http.createServer((req, res) => {
|
|
137
|
+
if (req.url === "/") {
|
|
138
|
+
try {
|
|
139
|
+
console.log("\nš Analyzing JavaScript files...");
|
|
140
|
+
|
|
141
|
+
const jsFiles = findJsFiles(directory);
|
|
142
|
+
const duplicates = findDuplicates(directory, threshold);
|
|
143
|
+
|
|
144
|
+
const stats = {
|
|
145
|
+
filesScanned: jsFiles.length,
|
|
146
|
+
functionsFound: duplicates.totalFunctions || 0,
|
|
147
|
+
duplicatesFound: duplicates.duplicates.length,
|
|
148
|
+
threshold: threshold,
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const html = generateHTML(duplicates.duplicates, stats);
|
|
152
|
+
|
|
153
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
154
|
+
res.end(html);
|
|
155
|
+
|
|
156
|
+
console.log("ā
Analysis complete!");
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error("ā Error:", error);
|
|
159
|
+
res.writeHead(500, { "Content-Type": "text/html" });
|
|
160
|
+
res.end(`<h1>Error</h1><pre>${error.message}</pre>`);
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
res.writeHead(404);
|
|
164
|
+
res.end("Not Found");
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
server.listen(PORT, () => {
|
|
169
|
+
console.log(`\n⨠Server running at http://localhost:${PORT}`);
|
|
170
|
+
console.log(`\nš” Open your browser and visit: http://localhost:${PORT}`);
|
|
171
|
+
console.log(`\nā¹ļø Press Ctrl+C to stop the server\n`);
|
|
172
|
+
|
|
173
|
+
// Try to open browser automatically
|
|
174
|
+
const url = `http://localhost:${PORT}`;
|
|
175
|
+
|
|
176
|
+
switch (process.platform) {
|
|
177
|
+
case "win32":
|
|
178
|
+
open(`start ${url}`);
|
|
179
|
+
break;
|
|
180
|
+
case "darwin":
|
|
181
|
+
open(`open ${url}`);
|
|
182
|
+
break;
|
|
183
|
+
default:
|
|
184
|
+
open(`xdg-open ${url}`);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Parse command line arguments
|
|
38
190
|
const args = process.argv.slice(2);
|
|
39
|
-
const
|
|
40
|
-
const
|
|
191
|
+
const hasUIFlag = args.includes('--ui');
|
|
192
|
+
const filteredArgs = args.filter(arg => arg !== '--ui');
|
|
193
|
+
const directory = filteredArgs[0] || process.cwd();
|
|
194
|
+
const threshold = parseInt(filteredArgs[1]) || 70;
|
|
41
195
|
|
|
42
196
|
if (!fs.existsSync(directory)) {
|
|
43
197
|
console.error(`ā Error: Directory "${directory}" does not exist`);
|
|
44
198
|
process.exit(1);
|
|
45
199
|
}
|
|
46
200
|
|
|
47
|
-
|
|
48
|
-
|
|
201
|
+
// Run UI server or CLI based on flag
|
|
202
|
+
if (hasUIFlag) {
|
|
203
|
+
startUIServer(directory, threshold);
|
|
204
|
+
} else {
|
|
205
|
+
console.log(`\nš Searching for duplicate code in: ${directory}`);
|
|
206
|
+
console.log(`š Similarity threshold: ${threshold}%`);
|
|
49
207
|
|
|
50
|
-
const jsFiles = findJsFiles(directory);
|
|
51
|
-
console.log(`\nš Scanning ${jsFiles.length} JavaScript files...\n`);
|
|
208
|
+
const jsFiles = findJsFiles(directory);
|
|
209
|
+
console.log(`\nš Scanning ${jsFiles.length} JavaScript files...\n`);
|
|
52
210
|
|
|
53
|
-
const result = findDuplicates(directory, threshold);
|
|
54
|
-
console.log(`š Found ${result.totalFunctions} functions total\n`);
|
|
211
|
+
const result = findDuplicates(directory, threshold);
|
|
212
|
+
console.log(`š Found ${result.totalFunctions} functions total\n`);
|
|
55
213
|
|
|
56
|
-
displayResults(result);
|
|
214
|
+
displayResults(result);
|
|
215
|
+
}
|
|
57
216
|
|
|
58
217
|
// Export functions for programmatic use
|
|
59
218
|
export { findDuplicates, findJsFiles, extractFunctions, normalizeCode, calculateSimilarity } from './find-duplicates-core.js';
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "find-duplicate-js",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "A tool to find duplicate code in JavaScript functions",
|
|
5
5
|
"main": "find-duplicates.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
|
-
"find-
|
|
9
|
-
"find-
|
|
8
|
+
"find-duplicate": "find-duplicates.js",
|
|
9
|
+
"find-duplicate-ui": "find-duplicates-ui.js"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"start": "node find-duplicates.js",
|
|
13
|
-
"ui": "node find-duplicates
|
|
13
|
+
"ui": "node find-duplicates.js --ui",
|
|
14
14
|
"test": "node --test tests/*.test.js"
|
|
15
15
|
},
|
|
16
16
|
"keywords": [
|