react-code-smell-detector 1.1.1 → 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.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +115 -11
  3. package/dist/analyzer.d.ts.map +1 -1
  4. package/dist/analyzer.js +65 -2
  5. package/dist/cli.js +134 -33
  6. package/dist/detectors/accessibility.d.ts +12 -0
  7. package/dist/detectors/accessibility.d.ts.map +1 -0
  8. package/dist/detectors/accessibility.js +191 -0
  9. package/dist/detectors/complexity.d.ts +17 -0
  10. package/dist/detectors/complexity.d.ts.map +1 -0
  11. package/dist/detectors/complexity.js +69 -0
  12. package/dist/detectors/debug.d.ts +10 -0
  13. package/dist/detectors/debug.d.ts.map +1 -0
  14. package/dist/detectors/debug.js +87 -0
  15. package/dist/detectors/imports.d.ts +22 -0
  16. package/dist/detectors/imports.d.ts.map +1 -0
  17. package/dist/detectors/imports.js +210 -0
  18. package/dist/detectors/index.d.ts +6 -0
  19. package/dist/detectors/index.d.ts.map +1 -1
  20. package/dist/detectors/index.js +8 -0
  21. package/dist/detectors/memoryLeak.d.ts +7 -0
  22. package/dist/detectors/memoryLeak.d.ts.map +1 -0
  23. package/dist/detectors/memoryLeak.js +111 -0
  24. package/dist/detectors/security.d.ts +12 -0
  25. package/dist/detectors/security.d.ts.map +1 -0
  26. package/dist/detectors/security.js +161 -0
  27. package/dist/fixer.d.ts +23 -0
  28. package/dist/fixer.d.ts.map +1 -0
  29. package/dist/fixer.js +133 -0
  30. package/dist/git.d.ts +28 -0
  31. package/dist/git.d.ts.map +1 -0
  32. package/dist/git.js +117 -0
  33. package/dist/htmlReporter.d.ts +6 -0
  34. package/dist/htmlReporter.d.ts.map +1 -0
  35. package/dist/htmlReporter.js +453 -0
  36. package/dist/reporter.js +26 -0
  37. package/dist/types/index.d.ts +10 -1
  38. package/dist/types/index.d.ts.map +1 -1
  39. package/dist/types/index.js +13 -0
  40. package/dist/watcher.d.ts +16 -0
  41. package/dist/watcher.d.ts.map +1 -0
  42. package/dist/watcher.js +89 -0
  43. package/package.json +8 -2
  44. package/src/analyzer.ts +0 -269
  45. package/src/cli.ts +0 -125
  46. package/src/detectors/deadCode.ts +0 -163
  47. package/src/detectors/dependencyArray.ts +0 -176
  48. package/src/detectors/hooksRules.ts +0 -101
  49. package/src/detectors/index.ts +0 -16
  50. package/src/detectors/javascript.ts +0 -169
  51. package/src/detectors/largeComponent.ts +0 -63
  52. package/src/detectors/magicValues.ts +0 -114
  53. package/src/detectors/memoization.ts +0 -177
  54. package/src/detectors/missingKey.ts +0 -105
  55. package/src/detectors/nestedTernary.ts +0 -75
  56. package/src/detectors/nextjs.ts +0 -124
  57. package/src/detectors/nodejs.ts +0 -199
  58. package/src/detectors/propDrilling.ts +0 -103
  59. package/src/detectors/reactNative.ts +0 -154
  60. package/src/detectors/typescript.ts +0 -151
  61. package/src/detectors/useEffect.ts +0 -117
  62. package/src/index.ts +0 -4
  63. package/src/parser/index.ts +0 -195
  64. package/src/reporter.ts +0 -278
  65. package/src/types/index.ts +0 -144
  66. package/tsconfig.json +0 -19
@@ -0,0 +1,453 @@
1
+ import path from 'path';
2
+ /**
3
+ * Generate a beautiful HTML report with charts and styling
4
+ */
5
+ export function generateHTMLReport(result, rootDir) {
6
+ const { summary, debtScore, files } = result;
7
+ // Generate chart data
8
+ const smellTypeData = Object.entries(summary.smellsByType)
9
+ .filter(([_, count]) => count > 0)
10
+ .sort((a, b) => b[1] - a[1])
11
+ .slice(0, 10);
12
+ const severityColors = {
13
+ error: '#ef4444',
14
+ warning: '#f59e0b',
15
+ info: '#3b82f6',
16
+ };
17
+ const gradeColors = {
18
+ A: '#22c55e',
19
+ B: '#84cc16',
20
+ C: '#eab308',
21
+ D: '#f97316',
22
+ F: '#ef4444',
23
+ };
24
+ return `<!DOCTYPE html>
25
+ <html lang="en">
26
+ <head>
27
+ <meta charset="UTF-8">
28
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
29
+ <title>Code Smell Detector Report</title>
30
+ <style>
31
+ * {
32
+ margin: 0;
33
+ padding: 0;
34
+ box-sizing: border-box;
35
+ }
36
+
37
+ body {
38
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
39
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
40
+ min-height: 100vh;
41
+ color: #e2e8f0;
42
+ line-height: 1.6;
43
+ }
44
+
45
+ .container {
46
+ max-width: 1200px;
47
+ margin: 0 auto;
48
+ padding: 2rem;
49
+ }
50
+
51
+ header {
52
+ text-align: center;
53
+ padding: 3rem 0;
54
+ border-bottom: 1px solid rgba(255,255,255,0.1);
55
+ margin-bottom: 2rem;
56
+ }
57
+
58
+ h1 {
59
+ font-size: 2.5rem;
60
+ margin-bottom: 0.5rem;
61
+ background: linear-gradient(90deg, #60a5fa, #a78bfa);
62
+ -webkit-background-clip: text;
63
+ -webkit-text-fill-color: transparent;
64
+ }
65
+
66
+ .subtitle {
67
+ color: #94a3b8;
68
+ font-size: 1.1rem;
69
+ }
70
+
71
+ .dashboard {
72
+ display: grid;
73
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
74
+ gap: 1.5rem;
75
+ margin-bottom: 2rem;
76
+ }
77
+
78
+ .card {
79
+ background: rgba(255,255,255,0.05);
80
+ border-radius: 1rem;
81
+ padding: 1.5rem;
82
+ border: 1px solid rgba(255,255,255,0.1);
83
+ backdrop-filter: blur(10px);
84
+ }
85
+
86
+ .card h2 {
87
+ font-size: 1rem;
88
+ text-transform: uppercase;
89
+ letter-spacing: 0.1em;
90
+ color: #94a3b8;
91
+ margin-bottom: 1rem;
92
+ }
93
+
94
+ .grade-container {
95
+ display: flex;
96
+ align-items: center;
97
+ gap: 2rem;
98
+ }
99
+
100
+ .grade {
101
+ font-size: 5rem;
102
+ font-weight: 800;
103
+ color: ${gradeColors[debtScore.grade]};
104
+ text-shadow: 0 0 30px ${gradeColors[debtScore.grade]}40;
105
+ }
106
+
107
+ .score-details {
108
+ flex: 1;
109
+ }
110
+
111
+ .score-bar {
112
+ height: 8px;
113
+ background: rgba(255,255,255,0.1);
114
+ border-radius: 4px;
115
+ overflow: hidden;
116
+ margin: 0.5rem 0;
117
+ }
118
+
119
+ .score-bar-fill {
120
+ height: 100%;
121
+ border-radius: 4px;
122
+ transition: width 1s ease;
123
+ }
124
+
125
+ .score-label {
126
+ display: flex;
127
+ justify-content: space-between;
128
+ font-size: 0.9rem;
129
+ color: #94a3b8;
130
+ }
131
+
132
+ .stat-grid {
133
+ display: grid;
134
+ grid-template-columns: repeat(3, 1fr);
135
+ gap: 1rem;
136
+ text-align: center;
137
+ }
138
+
139
+ .stat-value {
140
+ font-size: 2rem;
141
+ font-weight: 700;
142
+ color: #60a5fa;
143
+ }
144
+
145
+ .stat-label {
146
+ font-size: 0.8rem;
147
+ color: #94a3b8;
148
+ text-transform: uppercase;
149
+ }
150
+
151
+ .severity-badges {
152
+ display: flex;
153
+ gap: 1rem;
154
+ margin-top: 1rem;
155
+ }
156
+
157
+ .severity-badge {
158
+ padding: 0.5rem 1rem;
159
+ border-radius: 2rem;
160
+ font-size: 0.9rem;
161
+ font-weight: 600;
162
+ }
163
+
164
+ .severity-error { background: rgba(239,68,68,0.2); color: #ef4444; }
165
+ .severity-warning { background: rgba(245,158,11,0.2); color: #f59e0b; }
166
+ .severity-info { background: rgba(59,130,246,0.2); color: #3b82f6; }
167
+
168
+ .issues-by-type {
169
+ margin-top: 1rem;
170
+ }
171
+
172
+ .type-bar {
173
+ display: flex;
174
+ align-items: center;
175
+ margin: 0.75rem 0;
176
+ }
177
+
178
+ .type-name {
179
+ flex: 1;
180
+ font-size: 0.9rem;
181
+ }
182
+
183
+ .type-bar-bg {
184
+ width: 150px;
185
+ height: 20px;
186
+ background: rgba(255,255,255,0.1);
187
+ border-radius: 4px;
188
+ overflow: hidden;
189
+ margin: 0 1rem;
190
+ }
191
+
192
+ .type-bar-fill {
193
+ height: 100%;
194
+ background: linear-gradient(90deg, #60a5fa, #a78bfa);
195
+ border-radius: 4px;
196
+ }
197
+
198
+ .type-count {
199
+ width: 40px;
200
+ text-align: right;
201
+ font-weight: 600;
202
+ }
203
+
204
+ .findings-section {
205
+ margin-top: 2rem;
206
+ }
207
+
208
+ .findings-section h2 {
209
+ font-size: 1.5rem;
210
+ margin-bottom: 1rem;
211
+ padding-bottom: 0.5rem;
212
+ border-bottom: 1px solid rgba(255,255,255,0.1);
213
+ }
214
+
215
+ .file-group {
216
+ margin-bottom: 1.5rem;
217
+ }
218
+
219
+ .file-header {
220
+ font-family: 'Monaco', 'Menlo', monospace;
221
+ font-size: 0.9rem;
222
+ color: #60a5fa;
223
+ padding: 0.75rem 1rem;
224
+ background: rgba(96,165,250,0.1);
225
+ border-radius: 0.5rem 0.5rem 0 0;
226
+ border: 1px solid rgba(96,165,250,0.2);
227
+ border-bottom: none;
228
+ }
229
+
230
+ .issue {
231
+ padding: 1rem;
232
+ background: rgba(255,255,255,0.03);
233
+ border: 1px solid rgba(255,255,255,0.1);
234
+ border-top: none;
235
+ }
236
+
237
+ .issue:last-child {
238
+ border-radius: 0 0 0.5rem 0.5rem;
239
+ }
240
+
241
+ .issue-header {
242
+ display: flex;
243
+ align-items: flex-start;
244
+ gap: 0.75rem;
245
+ }
246
+
247
+ .issue-icon {
248
+ font-size: 1rem;
249
+ padding: 0.25rem;
250
+ }
251
+
252
+ .issue-message {
253
+ flex: 1;
254
+ font-weight: 500;
255
+ }
256
+
257
+ .issue-line {
258
+ font-family: monospace;
259
+ font-size: 0.8rem;
260
+ color: #94a3b8;
261
+ }
262
+
263
+ .issue-suggestion {
264
+ margin-top: 0.5rem;
265
+ padding: 0.5rem 0.75rem;
266
+ background: rgba(167,139,250,0.1);
267
+ border-left: 3px solid #a78bfa;
268
+ font-size: 0.9rem;
269
+ color: #c4b5fd;
270
+ }
271
+
272
+ .code-snippet {
273
+ margin-top: 0.5rem;
274
+ padding: 0.75rem;
275
+ background: #0d1117;
276
+ border-radius: 0.5rem;
277
+ font-family: 'Monaco', 'Menlo', monospace;
278
+ font-size: 0.8rem;
279
+ overflow-x: auto;
280
+ white-space: pre;
281
+ color: #c9d1d9;
282
+ }
283
+
284
+ .refactor-time {
285
+ font-size: 1.1rem;
286
+ color: #f59e0b;
287
+ margin-top: 0.5rem;
288
+ }
289
+
290
+ footer {
291
+ text-align: center;
292
+ padding: 2rem;
293
+ color: #64748b;
294
+ font-size: 0.9rem;
295
+ }
296
+
297
+ @keyframes fadeIn {
298
+ from { opacity: 0; transform: translateY(10px); }
299
+ to { opacity: 1; transform: translateY(0); }
300
+ }
301
+
302
+ .card, .file-group {
303
+ animation: fadeIn 0.5s ease forwards;
304
+ }
305
+ </style>
306
+ </head>
307
+ <body>
308
+ <div class="container">
309
+ <header>
310
+ <h1>šŸ” Code Smell Detector</h1>
311
+ <p class="subtitle">Analysis Report - ${new Date().toLocaleDateString()}</p>
312
+ </header>
313
+
314
+ <div class="dashboard">
315
+ <div class="card">
316
+ <h2>Technical Debt Score</h2>
317
+ <div class="grade-container">
318
+ <div class="grade">${debtScore.grade}</div>
319
+ <div class="score-details">
320
+ <div class="score-label"><span>Overall Score</span><span>${debtScore.score}/100</span></div>
321
+ <div class="score-bar">
322
+ <div class="score-bar-fill" style="width: ${debtScore.score}%; background: ${gradeColors[debtScore.grade]}"></div>
323
+ </div>
324
+ ${generateBreakdownBars(debtScore.breakdown)}
325
+ <p class="refactor-time">ā±ļø Est. refactor time: ${debtScore.estimatedRefactorTime}</p>
326
+ </div>
327
+ </div>
328
+ </div>
329
+
330
+ <div class="card">
331
+ <h2>Summary</h2>
332
+ <div class="stat-grid">
333
+ <div>
334
+ <div class="stat-value">${summary.totalFiles}</div>
335
+ <div class="stat-label">Files</div>
336
+ </div>
337
+ <div>
338
+ <div class="stat-value">${summary.totalComponents}</div>
339
+ <div class="stat-label">Components</div>
340
+ </div>
341
+ <div>
342
+ <div class="stat-value">${summary.totalSmells}</div>
343
+ <div class="stat-label">Issues</div>
344
+ </div>
345
+ </div>
346
+ <div class="severity-badges">
347
+ <span class="severity-badge severity-error">${summary.smellsBySeverity.error} Errors</span>
348
+ <span class="severity-badge severity-warning">${summary.smellsBySeverity.warning} Warnings</span>
349
+ <span class="severity-badge severity-info">${summary.smellsBySeverity.info} Info</span>
350
+ </div>
351
+ </div>
352
+
353
+ <div class="card">
354
+ <h2>Issues by Type</h2>
355
+ <div class="issues-by-type">
356
+ ${smellTypeData.map(([type, count]) => {
357
+ const maxCount = Math.max(...smellTypeData.map(([_, c]) => c));
358
+ const percentage = (count / maxCount) * 100;
359
+ return `
360
+ <div class="type-bar">
361
+ <span class="type-name">${formatTypeLabel(type)}</span>
362
+ <div class="type-bar-bg">
363
+ <div class="type-bar-fill" style="width: ${percentage}%"></div>
364
+ </div>
365
+ <span class="type-count">${count}</span>
366
+ </div>
367
+ `;
368
+ }).join('')}
369
+ </div>
370
+ </div>
371
+ </div>
372
+
373
+ <section class="findings-section">
374
+ <h2>šŸ“‹ Detailed Findings</h2>
375
+ ${files.filter(f => f.smells.length > 0).map(file => `
376
+ <div class="file-group">
377
+ <div class="file-header">${path.relative(rootDir, file.file)}</div>
378
+ ${file.smells.map(smell => `
379
+ <div class="issue">
380
+ <div class="issue-header">
381
+ <span class="issue-icon">${getSeverityIcon(smell.severity)}</span>
382
+ <span class="issue-message" style="color: ${severityColors[smell.severity]}">${escapeHtml(smell.message)}</span>
383
+ <span class="issue-line">Line ${smell.line}</span>
384
+ </div>
385
+ <div class="issue-suggestion">šŸ’” ${escapeHtml(smell.suggestion)}</div>
386
+ ${smell.codeSnippet ? `<div class="code-snippet">${escapeHtml(smell.codeSnippet)}</div>` : ''}
387
+ </div>
388
+ `).join('')}
389
+ </div>
390
+ `).join('')}
391
+ </section>
392
+
393
+ <footer>
394
+ Generated by React Code Smell Detector • ${new Date().toISOString()}
395
+ </footer>
396
+ </div>
397
+ </body>
398
+ </html>`;
399
+ }
400
+ function generateBreakdownBars(breakdown) {
401
+ const items = [
402
+ { label: 'useEffect', score: breakdown.useEffectScore },
403
+ { label: 'Prop Drilling', score: breakdown.propDrillingScore },
404
+ { label: 'Component Size', score: breakdown.componentSizeScore },
405
+ { label: 'Memoization', score: breakdown.memoizationScore },
406
+ ];
407
+ return items.map(({ label, score }) => {
408
+ const color = score >= 80 ? '#22c55e' : score >= 60 ? '#eab308' : '#ef4444';
409
+ return `
410
+ <div class="score-label" style="margin-top: 0.5rem"><span>${label}</span><span>${score}</span></div>
411
+ <div class="score-bar">
412
+ <div class="score-bar-fill" style="width: ${score}%; background: ${color}"></div>
413
+ </div>
414
+ `;
415
+ }).join('');
416
+ }
417
+ function formatTypeLabel(type) {
418
+ const labels = {
419
+ 'useEffect-overuse': '⚔ useEffect',
420
+ 'prop-drilling': 'šŸ”— Prop Drilling',
421
+ 'large-component': 'šŸ“ Large Component',
422
+ 'unmemoized-calculation': 'šŸ’¾ Unmemoized',
423
+ 'inline-function-prop': 'šŸ“Ž Inline Func',
424
+ 'deep-nesting': 'šŸ“Š Deep Nesting',
425
+ 'missing-key': 'šŸ”‘ Missing Key',
426
+ 'magic-value': 'šŸ”¢ Magic Value',
427
+ 'debug-statement': 'šŸ› Debug',
428
+ 'todo-comment': 'šŸ“ TODO',
429
+ 'security-xss': 'šŸ”’ Security XSS',
430
+ 'security-eval': 'šŸ”’ Security Eval',
431
+ 'security-secrets': 'šŸ”‘ Secrets',
432
+ 'a11y-missing-alt': '♿ Missing Alt',
433
+ 'a11y-missing-label': '♿ Missing Label',
434
+ 'ts-any-usage': 'šŸ”· TS any',
435
+ 'ts-missing-return-type': 'šŸ”· TS Return Type',
436
+ };
437
+ return labels[type] || type.replace(/-/g, ' ');
438
+ }
439
+ function getSeverityIcon(severity) {
440
+ switch (severity) {
441
+ case 'error': return 'āŒ';
442
+ case 'warning': return 'āš ļø';
443
+ case 'info': return 'ā„¹ļø';
444
+ }
445
+ }
446
+ function escapeHtml(text) {
447
+ return text
448
+ .replace(/&/g, '&amp;')
449
+ .replace(/</g, '&lt;')
450
+ .replace(/>/g, '&gt;')
451
+ .replace(/"/g, '&quot;')
452
+ .replace(/'/g, '&#039;');
453
+ }
package/dist/reporter.js CHANGED
@@ -242,6 +242,32 @@ function formatSmellType(type) {
242
242
  'ts-missing-return-type': 'šŸ”· TS Missing Return Type',
243
243
  'ts-non-null-assertion': 'šŸ”· TS Non-null Assertion',
244
244
  'ts-type-assertion': 'šŸ”· TS Type Assertion',
245
+ // Debug
246
+ 'debug-statement': 'šŸ› Debug Statement',
247
+ 'todo-comment': 'šŸ“ TODO/FIXME Comment',
248
+ // Security
249
+ 'security-xss': 'šŸ”’ XSS Vulnerability',
250
+ 'security-eval': 'šŸ”’ Eval Usage',
251
+ 'security-secrets': 'šŸ”’ Exposed Secret',
252
+ // Accessibility
253
+ 'a11y-missing-alt': '♿ Missing Alt Text',
254
+ 'a11y-missing-label': '♿ Missing Label',
255
+ 'a11y-interactive-role': '♿ Interactive Role',
256
+ 'a11y-keyboard': '♿ Keyboard Handler',
257
+ 'a11y-semantic': '♿ Semantic HTML',
258
+ // Complexity
259
+ 'high-cyclomatic-complexity': '🧮 High Cyclomatic Complexity',
260
+ 'high-cognitive-complexity': '🧠 High Cognitive Complexity',
261
+ // Memory Leaks
262
+ 'memory-leak-event-listener': 'šŸ’§ Event Listener Leak',
263
+ 'memory-leak-subscription': 'šŸ’§ Subscription Leak',
264
+ 'memory-leak-timer': 'šŸ’§ Timer Leak',
265
+ 'memory-leak-async': 'šŸ’§ Async Leak Risk',
266
+ // Import Issues
267
+ 'circular-dependency': 'šŸ”„ Circular Dependency',
268
+ 'barrel-file-import': 'šŸ“¦ Barrel File Import',
269
+ 'namespace-import': 'šŸ“¦ Namespace Import',
270
+ 'excessive-imports': 'šŸ“¦ Excessive Imports',
245
271
  };
246
272
  return labels[type] || type;
247
273
  }
@@ -1,5 +1,5 @@
1
1
  export type SmellSeverity = 'error' | 'warning' | 'info';
2
- export type SmellType = 'useEffect-overuse' | 'prop-drilling' | 'large-component' | 'unmemoized-calculation' | 'missing-dependency' | 'state-in-loop' | 'inline-function-prop' | 'deep-nesting' | 'missing-key' | 'hooks-rules-violation' | 'dependency-array-issue' | 'nested-ternary' | 'dead-code' | 'magic-value' | 'nextjs-client-server-boundary' | 'nextjs-missing-metadata' | 'nextjs-image-unoptimized' | 'nextjs-router-misuse' | 'rn-inline-style' | 'rn-missing-accessibility' | 'rn-performance-issue' | 'nodejs-callback-hell' | 'nodejs-unhandled-promise' | 'nodejs-sync-io' | 'nodejs-missing-error-handling' | 'js-var-usage' | 'js-loose-equality' | 'js-implicit-coercion' | 'js-global-pollution' | 'ts-any-usage' | 'ts-missing-return-type' | 'ts-non-null-assertion' | 'ts-type-assertion';
2
+ export type SmellType = 'useEffect-overuse' | 'prop-drilling' | 'large-component' | 'unmemoized-calculation' | 'missing-dependency' | 'state-in-loop' | 'inline-function-prop' | 'deep-nesting' | 'missing-key' | 'hooks-rules-violation' | 'dependency-array-issue' | 'nested-ternary' | 'dead-code' | 'magic-value' | 'debug-statement' | 'todo-comment' | 'security-xss' | 'security-eval' | 'security-secrets' | 'a11y-missing-alt' | 'a11y-missing-label' | 'a11y-interactive-role' | 'a11y-keyboard' | 'a11y-semantic' | 'nextjs-client-server-boundary' | 'nextjs-missing-metadata' | 'nextjs-image-unoptimized' | 'nextjs-router-misuse' | 'rn-inline-style' | 'rn-missing-accessibility' | 'rn-performance-issue' | 'nodejs-callback-hell' | 'nodejs-unhandled-promise' | 'nodejs-sync-io' | 'nodejs-missing-error-handling' | 'js-var-usage' | 'js-loose-equality' | 'js-implicit-coercion' | 'js-global-pollution' | 'ts-any-usage' | 'ts-missing-return-type' | 'ts-non-null-assertion' | 'ts-type-assertion' | 'high-cyclomatic-complexity' | 'high-cognitive-complexity' | 'memory-leak-event-listener' | 'memory-leak-subscription' | 'memory-leak-timer' | 'memory-leak-async' | 'circular-dependency' | 'barrel-file-import' | 'namespace-import' | 'excessive-imports';
3
3
  export interface CodeSmell {
4
4
  type: SmellType;
5
5
  severity: SmellSeverity;
@@ -72,6 +72,15 @@ export interface DetectorConfig {
72
72
  checkJavascript: boolean;
73
73
  checkTypescript: boolean;
74
74
  maxCallbackDepth: number;
75
+ checkDebugStatements: boolean;
76
+ checkSecurity: boolean;
77
+ checkAccessibility: boolean;
78
+ checkComplexity: boolean;
79
+ maxCyclomaticComplexity: number;
80
+ maxCognitiveComplexity: number;
81
+ maxNestingDepth: number;
82
+ checkMemoryLeaks: boolean;
83
+ checkImports: boolean;
75
84
  }
76
85
  export declare const DEFAULT_CONFIG: DetectorConfig;
77
86
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -18,4 +18,17 @@ export const DEFAULT_CONFIG = {
18
18
  checkJavascript: true,
19
19
  checkTypescript: true,
20
20
  maxCallbackDepth: 3,
21
+ // New detectors
22
+ checkDebugStatements: true,
23
+ checkSecurity: true,
24
+ checkAccessibility: true,
25
+ // Complexity
26
+ checkComplexity: true,
27
+ maxCyclomaticComplexity: 10,
28
+ maxCognitiveComplexity: 15,
29
+ maxNestingDepth: 4,
30
+ // Memory leaks
31
+ checkMemoryLeaks: true,
32
+ // Import analysis
33
+ checkImports: true,
21
34
  };
@@ -0,0 +1,16 @@
1
+ import { DetectorConfig } from './types/index.js';
2
+ export interface WatchOptions {
3
+ rootDir: string;
4
+ include: string[];
5
+ exclude: string[];
6
+ config: DetectorConfig;
7
+ showSnippets: boolean;
8
+ onAnalysis?: (fileCount: number, issueCount: number) => void;
9
+ }
10
+ /**
11
+ * Start watching a project for changes and re-analyze on each change
12
+ */
13
+ export declare function startWatch(options: WatchOptions): {
14
+ close: () => void;
15
+ };
16
+ //# sourceMappingURL=watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAkB,MAAM,kBAAkB,CAAC;AAElE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,cAAc,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG;IAAE,KAAK,EAAE,MAAM,IAAI,CAAA;CAAE,CAgGvE"}
@@ -0,0 +1,89 @@
1
+ import chokidar from 'chokidar';
2
+ import path from 'path';
3
+ import chalk from 'chalk';
4
+ import { analyzeProject } from './analyzer.js';
5
+ import { reportResults } from './reporter.js';
6
+ /**
7
+ * Start watching a project for changes and re-analyze on each change
8
+ */
9
+ export function startWatch(options) {
10
+ const { rootDir, include = ['**/*.tsx', '**/*.jsx'], exclude = ['**/node_modules/**', '**/dist/**', '**/build/**'], config, showSnippets, onAnalysis, } = options;
11
+ console.log(chalk.cyan('\nšŸ‘€ Watch mode started'));
12
+ console.log(chalk.dim(` Watching: ${rootDir}`));
13
+ console.log(chalk.dim(` Patterns: ${include.join(', ')}`));
14
+ console.log(chalk.dim(' Press Ctrl+C to stop\n'));
15
+ // Debounce timer
16
+ let debounceTimer = null;
17
+ let isAnalyzing = false;
18
+ const runAnalysis = async () => {
19
+ if (isAnalyzing)
20
+ return;
21
+ isAnalyzing = true;
22
+ console.log(chalk.dim('\n─────────────────────────────────────────'));
23
+ console.log(chalk.cyan('šŸ”„ Re-analyzing...'));
24
+ try {
25
+ const result = await analyzeProject({
26
+ rootDir,
27
+ include,
28
+ exclude,
29
+ config,
30
+ });
31
+ // Clear console and show results
32
+ console.clear();
33
+ console.log(chalk.cyan('\nšŸ‘€ Watch mode - Last update: ' + new Date().toLocaleTimeString()));
34
+ console.log(chalk.dim(' Press Ctrl+C to stop\n'));
35
+ const output = reportResults(result, {
36
+ format: 'console',
37
+ showCodeSnippets: showSnippets,
38
+ rootDir,
39
+ });
40
+ console.log(output);
41
+ if (onAnalysis) {
42
+ onAnalysis(result.summary.totalFiles, result.summary.totalSmells);
43
+ }
44
+ }
45
+ catch (error) {
46
+ console.error(chalk.red('Analysis error:'), error.message);
47
+ }
48
+ finally {
49
+ isAnalyzing = false;
50
+ }
51
+ };
52
+ // Debounced analysis trigger
53
+ const triggerAnalysis = () => {
54
+ if (debounceTimer) {
55
+ clearTimeout(debounceTimer);
56
+ }
57
+ debounceTimer = setTimeout(runAnalysis, 300);
58
+ };
59
+ // Set up file watcher
60
+ const watchPatterns = include.map(p => path.join(rootDir, p));
61
+ const watcher = chokidar.watch(watchPatterns, {
62
+ ignored: exclude.map(p => {
63
+ if (p.startsWith('**/'))
64
+ return p;
65
+ return path.join(rootDir, p);
66
+ }),
67
+ persistent: true,
68
+ ignoreInitial: false,
69
+ awaitWriteFinish: {
70
+ stabilityThreshold: 100,
71
+ pollInterval: 50,
72
+ },
73
+ });
74
+ watcher
75
+ .on('add', () => triggerAnalysis())
76
+ .on('change', () => triggerAnalysis())
77
+ .on('unlink', () => triggerAnalysis())
78
+ .on('error', error => console.error(chalk.red('Watcher error:'), error));
79
+ // Initial analysis
80
+ triggerAnalysis();
81
+ return {
82
+ close: () => {
83
+ if (debounceTimer)
84
+ clearTimeout(debounceTimer);
85
+ watcher.close();
86
+ console.log(chalk.yellow('\nšŸ‘‹ Watch mode stopped'));
87
+ },
88
+ };
89
+ }
package/package.json CHANGED
@@ -1,12 +1,17 @@
1
1
  {
2
2
  "name": "react-code-smell-detector",
3
- "version": "1.1.1",
4
- "description": "Detect code smells in React projects - useEffect overuse, prop drilling, large components, and more",
3
+ "version": "1.3.1",
4
+ "description": "Detect code smells in React projects - useEffect overuse, prop drilling, large components, security issues, accessibility, memory leaks, and more",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "react-smell": "dist/cli.js"
8
8
  },
9
9
  "type": "module",
10
+ "files": [
11
+ "dist",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
10
15
  "scripts": {
11
16
  "build": "tsc",
12
17
  "dev": "tsc --watch",
@@ -29,6 +34,7 @@
29
34
  "@babel/traverse": "^7.23.0",
30
35
  "@babel/types": "^7.23.0",
31
36
  "chalk": "^5.3.0",
37
+ "chokidar": "^5.0.0",
32
38
  "commander": "^11.1.0",
33
39
  "fast-glob": "^3.3.2",
34
40
  "ora": "^8.0.1"