react-code-smell-detector 1.4.2 → 1.5.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 (110) hide show
  1. package/README.md +227 -22
  2. package/dist/__tests__/aiRefactoring.test.d.ts +2 -0
  3. package/dist/__tests__/aiRefactoring.test.d.ts.map +1 -0
  4. package/dist/__tests__/aiRefactoring.test.js +86 -0
  5. package/dist/__tests__/analyzer-real.test.d.ts +2 -0
  6. package/dist/__tests__/analyzer-real.test.d.ts.map +1 -0
  7. package/dist/__tests__/analyzer-real.test.js +149 -0
  8. package/dist/__tests__/analyzer.test.d.ts +2 -0
  9. package/dist/__tests__/analyzer.test.d.ts.map +1 -0
  10. package/dist/__tests__/analyzer.test.js +173 -0
  11. package/dist/__tests__/baseline.test.d.ts +2 -0
  12. package/dist/__tests__/baseline.test.d.ts.map +1 -0
  13. package/dist/__tests__/baseline.test.js +136 -0
  14. package/dist/__tests__/bundleAnalyzer.test.d.ts +2 -0
  15. package/dist/__tests__/bundleAnalyzer.test.d.ts.map +1 -0
  16. package/dist/__tests__/bundleAnalyzer.test.js +182 -0
  17. package/dist/__tests__/customRules.test.d.ts +2 -0
  18. package/dist/__tests__/customRules.test.d.ts.map +1 -0
  19. package/dist/__tests__/customRules.test.js +283 -0
  20. package/dist/__tests__/detectors/index.test.d.ts +2 -0
  21. package/dist/__tests__/detectors/index.test.d.ts.map +1 -0
  22. package/dist/__tests__/detectors/index.test.js +1012 -0
  23. package/dist/__tests__/detectors/newDetectors.test.d.ts +2 -0
  24. package/dist/__tests__/detectors/newDetectors.test.d.ts.map +1 -0
  25. package/dist/__tests__/detectors/newDetectors.test.js +333 -0
  26. package/dist/__tests__/docGenerator.test.d.ts +2 -0
  27. package/dist/__tests__/docGenerator.test.d.ts.map +1 -0
  28. package/dist/__tests__/docGenerator.test.js +157 -0
  29. package/dist/__tests__/fixer.test.d.ts +2 -0
  30. package/dist/__tests__/fixer.test.d.ts.map +1 -0
  31. package/dist/__tests__/fixer.test.js +193 -0
  32. package/dist/__tests__/git.test.d.ts +2 -0
  33. package/dist/__tests__/git.test.d.ts.map +1 -0
  34. package/dist/__tests__/git.test.js +38 -0
  35. package/dist/__tests__/graphGenerator.test.d.ts +2 -0
  36. package/dist/__tests__/graphGenerator.test.d.ts.map +1 -0
  37. package/dist/__tests__/graphGenerator.test.js +190 -0
  38. package/dist/__tests__/htmlReporter.test.d.ts +2 -0
  39. package/dist/__tests__/htmlReporter.test.d.ts.map +1 -0
  40. package/dist/__tests__/htmlReporter.test.js +258 -0
  41. package/dist/__tests__/interactiveFixer.test.d.ts +2 -0
  42. package/dist/__tests__/interactiveFixer.test.d.ts.map +1 -0
  43. package/dist/__tests__/interactiveFixer.test.js +231 -0
  44. package/dist/__tests__/parser.test.d.ts +2 -0
  45. package/dist/__tests__/parser.test.d.ts.map +1 -0
  46. package/dist/__tests__/parser.test.js +56 -0
  47. package/dist/__tests__/performanceBudget.test.d.ts +2 -0
  48. package/dist/__tests__/performanceBudget.test.d.ts.map +1 -0
  49. package/dist/__tests__/performanceBudget.test.js +242 -0
  50. package/dist/__tests__/prComments.test.d.ts +2 -0
  51. package/dist/__tests__/prComments.test.d.ts.map +1 -0
  52. package/dist/__tests__/prComments.test.js +118 -0
  53. package/dist/__tests__/reporter.test.d.ts +2 -0
  54. package/dist/__tests__/reporter.test.d.ts.map +1 -0
  55. package/dist/__tests__/reporter.test.js +136 -0
  56. package/dist/__tests__/watcher.test.d.ts +2 -0
  57. package/dist/__tests__/watcher.test.d.ts.map +1 -0
  58. package/dist/__tests__/watcher.test.js +161 -0
  59. package/dist/__tests__/webhooks.test.d.ts +2 -0
  60. package/dist/__tests__/webhooks.test.d.ts.map +1 -0
  61. package/dist/__tests__/webhooks.test.js +209 -0
  62. package/dist/aiRefactoring.d.ts +29 -0
  63. package/dist/aiRefactoring.d.ts.map +1 -0
  64. package/dist/aiRefactoring.js +290 -0
  65. package/dist/analyzer.d.ts.map +1 -1
  66. package/dist/analyzer.js +33 -1
  67. package/dist/cli.js +123 -1
  68. package/dist/detectors/contextApi.d.ts +11 -0
  69. package/dist/detectors/contextApi.d.ts.map +1 -0
  70. package/dist/detectors/contextApi.js +151 -0
  71. package/dist/detectors/errorBoundary.d.ts +11 -0
  72. package/dist/detectors/errorBoundary.d.ts.map +1 -0
  73. package/dist/detectors/errorBoundary.js +167 -0
  74. package/dist/detectors/formValidation.d.ts +11 -0
  75. package/dist/detectors/formValidation.d.ts.map +1 -0
  76. package/dist/detectors/formValidation.js +193 -0
  77. package/dist/detectors/index.d.ts +6 -0
  78. package/dist/detectors/index.d.ts.map +1 -1
  79. package/dist/detectors/index.js +12 -0
  80. package/dist/detectors/serverComponents.d.ts +11 -0
  81. package/dist/detectors/serverComponents.d.ts.map +1 -0
  82. package/dist/detectors/serverComponents.js +222 -0
  83. package/dist/detectors/stateManagement.d.ts +11 -0
  84. package/dist/detectors/stateManagement.d.ts.map +1 -0
  85. package/dist/detectors/stateManagement.js +193 -0
  86. package/dist/detectors/testingGaps.d.ts +15 -0
  87. package/dist/detectors/testingGaps.d.ts.map +1 -0
  88. package/dist/detectors/testingGaps.js +182 -0
  89. package/dist/docGenerator.d.ts +37 -0
  90. package/dist/docGenerator.d.ts.map +1 -0
  91. package/dist/docGenerator.js +306 -0
  92. package/dist/guide.d.ts +9 -0
  93. package/dist/guide.d.ts.map +1 -0
  94. package/dist/guide.js +922 -0
  95. package/dist/index.d.ts +5 -0
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +5 -0
  98. package/dist/interactiveFixer.d.ts +20 -0
  99. package/dist/interactiveFixer.d.ts.map +1 -0
  100. package/dist/interactiveFixer.js +178 -0
  101. package/dist/performanceBudget.d.ts +54 -0
  102. package/dist/performanceBudget.d.ts.map +1 -0
  103. package/dist/performanceBudget.js +218 -0
  104. package/dist/prComments.d.ts +47 -0
  105. package/dist/prComments.d.ts.map +1 -0
  106. package/dist/prComments.js +233 -0
  107. package/dist/types/index.d.ts +12 -1
  108. package/dist/types/index.d.ts.map +1 -1
  109. package/dist/types/index.js +18 -0
  110. package/package.json +10 -4
@@ -0,0 +1,306 @@
1
+ import path from 'path';
2
+ import fs from 'fs/promises';
3
+ /**
4
+ * Generate documentation from component analysis
5
+ */
6
+ export async function generateComponentDocs(result, rootDir, options = { format: 'markdown' }) {
7
+ const docs = extractComponentDocs(result, rootDir);
8
+ switch (options.format) {
9
+ case 'html':
10
+ return generateHTMLDocs(docs, result, options);
11
+ case 'json':
12
+ return JSON.stringify(docs, null, 2);
13
+ case 'markdown':
14
+ default:
15
+ return generateMarkdownDocs(docs, result, options);
16
+ }
17
+ }
18
+ /**
19
+ * Extract documentation data from analysis
20
+ */
21
+ function extractComponentDocs(result, rootDir) {
22
+ const docs = [];
23
+ for (const file of result.files) {
24
+ for (const component of file.components) {
25
+ const relativePath = file.file.replace(rootDir, '').replace(/^\//, '');
26
+ const componentSmells = file.smells.filter(s => s.line >= component.startLine && s.line <= component.endLine);
27
+ // Determine complexity rating
28
+ const complexity = getComplexityRating(component);
29
+ const maintainability = getMaintainabilityRating(componentSmells.length, component.lineCount);
30
+ docs.push({
31
+ name: component.name,
32
+ file: file.file,
33
+ relativePath,
34
+ lineCount: component.lineCount,
35
+ props: [], // Would need parser enhancement to get prop names
36
+ hooks: {
37
+ useState: component.useStateCount,
38
+ useEffect: component.useEffectCount,
39
+ useMemo: component.useMemoCount,
40
+ useCallback: component.useCallbackCount,
41
+ useRef: 0, // Would need parser enhancement
42
+ custom: [],
43
+ },
44
+ smells: componentSmells,
45
+ metrics: {
46
+ complexity,
47
+ maintainability,
48
+ },
49
+ });
50
+ }
51
+ }
52
+ return docs.sort((a, b) => a.name.localeCompare(b.name));
53
+ }
54
+ /**
55
+ * Generate Markdown documentation
56
+ */
57
+ function generateMarkdownDocs(docs, result, options) {
58
+ let md = '# Component Documentation\n\n';
59
+ md += `*Generated by react-code-smell-detector*\n\n`;
60
+ md += `---\n\n`;
61
+ // Summary
62
+ md += '## Summary\n\n';
63
+ md += `| Metric | Value |\n`;
64
+ md += `|--------|-------|\n`;
65
+ md += `| Total Components | ${docs.length} |\n`;
66
+ md += `| Total Files | ${result.summary.totalFiles} |\n`;
67
+ md += `| Technical Debt Grade | ${result.debtScore.grade} |\n`;
68
+ md += `| Estimated Refactor Time | ${result.debtScore.estimatedRefactorTime} |\n\n`;
69
+ // Table of contents
70
+ md += '## Components\n\n';
71
+ if (options.groupByFolder) {
72
+ const grouped = groupByFolder(docs);
73
+ for (const [folder, folderDocs] of Object.entries(grouped)) {
74
+ md += `### 📁 ${folder || 'Root'}\n\n`;
75
+ for (const doc of folderDocs) {
76
+ md += formatComponentMarkdown(doc, options);
77
+ }
78
+ }
79
+ }
80
+ else {
81
+ for (const doc of docs) {
82
+ md += formatComponentMarkdown(doc, options);
83
+ }
84
+ }
85
+ // Index
86
+ md += '\n---\n\n';
87
+ md += '## Index\n\n';
88
+ md += '| Component | File | Lines | Hooks | Issues |\n';
89
+ md += '|-----------|------|-------|-------|--------|\n';
90
+ for (const doc of docs) {
91
+ const totalHooks = doc.hooks.useState + doc.hooks.useEffect + doc.hooks.useMemo + doc.hooks.useCallback;
92
+ md += `| [${doc.name}](#${doc.name.toLowerCase()}) | \`${doc.relativePath}\` | ${doc.lineCount} | ${totalHooks} | ${doc.smells.length} |\n`;
93
+ }
94
+ return md;
95
+ }
96
+ /**
97
+ * Format a single component as Markdown
98
+ */
99
+ function formatComponentMarkdown(doc, options) {
100
+ let md = `#### ${doc.name}\n\n`;
101
+ md += `📄 \`${doc.relativePath}\`\n\n`;
102
+ // Metrics table
103
+ if (options.includeMetrics !== false) {
104
+ md += `| Metric | Value |\n`;
105
+ md += `|--------|-------|\n`;
106
+ md += `| Lines | ${doc.lineCount} |\n`;
107
+ md += `| Complexity | ${doc.metrics.complexity} |\n`;
108
+ md += `| Maintainability | ${doc.metrics.maintainability} |\n`;
109
+ }
110
+ // Hooks
111
+ const hooks = [];
112
+ if (doc.hooks.useState > 0)
113
+ hooks.push(`useState (${doc.hooks.useState})`);
114
+ if (doc.hooks.useEffect > 0)
115
+ hooks.push(`useEffect (${doc.hooks.useEffect})`);
116
+ if (doc.hooks.useMemo > 0)
117
+ hooks.push(`useMemo (${doc.hooks.useMemo})`);
118
+ if (doc.hooks.useCallback > 0)
119
+ hooks.push(`useCallback (${doc.hooks.useCallback})`);
120
+ if (hooks.length > 0) {
121
+ md += `\n**Hooks:** ${hooks.join(', ')}\n`;
122
+ }
123
+ // Issues
124
+ if (options.includeSmells !== false && doc.smells.length > 0) {
125
+ md += `\n**Issues (${doc.smells.length}):**\n`;
126
+ for (const smell of doc.smells.slice(0, 5)) {
127
+ const emoji = smell.severity === 'error' ? '🔴' : smell.severity === 'warning' ? '🟡' : '🔵';
128
+ md += `- ${emoji} ${smell.type}: ${smell.message}\n`;
129
+ }
130
+ if (doc.smells.length > 5) {
131
+ md += `- *... and ${doc.smells.length - 5} more*\n`;
132
+ }
133
+ }
134
+ md += '\n---\n\n';
135
+ return md;
136
+ }
137
+ /**
138
+ * Generate HTML documentation
139
+ */
140
+ function generateHTMLDocs(docs, result, options) {
141
+ return `<!DOCTYPE html>
142
+ <html lang="en">
143
+ <head>
144
+ <meta charset="UTF-8">
145
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
146
+ <title>Component Documentation</title>
147
+ <style>
148
+ * { box-sizing: border-box; margin: 0; padding: 0; }
149
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; background: #f5f5f5; }
150
+ .container { max-width: 1200px; margin: 0 auto; padding: 20px; }
151
+ header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 40px 20px; margin-bottom: 30px; border-radius: 10px; }
152
+ h1 { font-size: 2.5rem; margin-bottom: 10px; }
153
+ .subtitle { opacity: 0.9; }
154
+ .summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 30px; }
155
+ .summary-card { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); text-align: center; }
156
+ .summary-value { font-size: 2rem; font-weight: bold; color: #667eea; }
157
+ .summary-label { color: #666; font-size: 0.9rem; }
158
+ .components { display: grid; gap: 20px; }
159
+ .component-card { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
160
+ .component-name { font-size: 1.3rem; color: #333; margin-bottom: 5px; }
161
+ .component-path { color: #666; font-size: 0.85rem; font-family: monospace; background: #f0f0f0; padding: 2px 8px; border-radius: 4px; }
162
+ .metrics { display: flex; gap: 15px; margin: 15px 0; flex-wrap: wrap; }
163
+ .metric { background: #f0f0f0; padding: 5px 12px; border-radius: 20px; font-size: 0.85rem; }
164
+ .hooks { display: flex; gap: 10px; flex-wrap: wrap; margin: 10px 0; }
165
+ .hook { background: #e3f2fd; color: #1976d2; padding: 4px 10px; border-radius: 4px; font-size: 0.8rem; }
166
+ .issues { margin-top: 15px; }
167
+ .issue { padding: 8px; margin: 5px 0; border-radius: 4px; font-size: 0.9rem; }
168
+ .issue.error { background: #ffebee; color: #c62828; }
169
+ .issue.warning { background: #fff3e0; color: #ef6c00; }
170
+ .issue.info { background: #e3f2fd; color: #1565c0; }
171
+ .search { margin-bottom: 20px; }
172
+ .search input { width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 8px; font-size: 1rem; }
173
+ .grade { font-size: 3rem; }
174
+ footer { text-align: center; padding: 20px; color: #666; font-size: 0.85rem; }
175
+ </style>
176
+ </head>
177
+ <body>
178
+ <div class="container">
179
+ <header>
180
+ <h1>📚 Component Documentation</h1>
181
+ <p class="subtitle">Generated by react-code-smell-detector</p>
182
+ </header>
183
+
184
+ <div class="summary">
185
+ <div class="summary-card">
186
+ <div class="summary-value">${docs.length}</div>
187
+ <div class="summary-label">Components</div>
188
+ </div>
189
+ <div class="summary-card">
190
+ <div class="summary-value">${result.summary.totalFiles}</div>
191
+ <div class="summary-label">Files</div>
192
+ </div>
193
+ <div class="summary-card">
194
+ <div class="summary-value grade">${result.debtScore.grade}</div>
195
+ <div class="summary-label">Debt Grade</div>
196
+ </div>
197
+ <div class="summary-card">
198
+ <div class="summary-value">${result.debtScore.score}</div>
199
+ <div class="summary-label">Score</div>
200
+ </div>
201
+ </div>
202
+
203
+ <div class="search">
204
+ <input type="text" placeholder="Search components..." id="search" onkeyup="filterComponents()">
205
+ </div>
206
+
207
+ <div class="components" id="components">
208
+ ${docs.map(doc => `
209
+ <div class="component-card" data-name="${doc.name.toLowerCase()}">
210
+ <div class="component-name">${doc.name}</div>
211
+ <span class="component-path">${doc.relativePath}</span>
212
+
213
+ <div class="metrics">
214
+ <span class="metric">📏 ${doc.lineCount} lines</span>
215
+ <span class="metric">⚡ ${doc.metrics.complexity}</span>
216
+ <span class="metric">🔧 ${doc.metrics.maintainability}</span>
217
+ </div>
218
+
219
+ <div class="hooks">
220
+ ${doc.hooks.useState > 0 ? `<span class="hook">useState ×${doc.hooks.useState}</span>` : ''}
221
+ ${doc.hooks.useEffect > 0 ? `<span class="hook">useEffect ×${doc.hooks.useEffect}</span>` : ''}
222
+ ${doc.hooks.useMemo > 0 ? `<span class="hook">useMemo ×${doc.hooks.useMemo}</span>` : ''}
223
+ ${doc.hooks.useCallback > 0 ? `<span class="hook">useCallback ×${doc.hooks.useCallback}</span>` : ''}
224
+ </div>
225
+
226
+ ${doc.smells.length > 0 ? `
227
+ <div class="issues">
228
+ ${doc.smells.slice(0, 3).map(s => `
229
+ <div class="issue ${s.severity}">${s.type}: ${s.message}</div>
230
+ `).join('')}
231
+ ${doc.smells.length > 3 ? `<div class="issue info">... and ${doc.smells.length - 3} more</div>` : ''}
232
+ </div>
233
+ ` : ''}
234
+ </div>
235
+ `).join('')}
236
+ </div>
237
+
238
+ <footer>
239
+ Generated by <a href="https://github.com/vsthakur101/react-code-smell-detector">react-code-smell-detector</a>
240
+ </footer>
241
+ </div>
242
+
243
+ <script>
244
+ function filterComponents() {
245
+ const query = document.getElementById('search').value.toLowerCase();
246
+ const cards = document.querySelectorAll('.component-card');
247
+ cards.forEach(card => {
248
+ const name = card.dataset.name;
249
+ card.style.display = name.includes(query) ? 'block' : 'none';
250
+ });
251
+ }
252
+ </script>
253
+ </body>
254
+ </html>`;
255
+ }
256
+ /**
257
+ * Group components by folder
258
+ */
259
+ function groupByFolder(docs) {
260
+ const grouped = {};
261
+ for (const doc of docs) {
262
+ const parts = doc.relativePath.split('/');
263
+ const folder = parts.length > 1 ? parts.slice(0, -1).join('/') : '';
264
+ if (!grouped[folder]) {
265
+ grouped[folder] = [];
266
+ }
267
+ grouped[folder].push(doc);
268
+ }
269
+ return grouped;
270
+ }
271
+ /**
272
+ * Get complexity rating based on component metrics
273
+ */
274
+ function getComplexityRating(component) {
275
+ const hookCount = component.useEffectCount + component.useStateCount +
276
+ component.useMemoCount + component.useCallbackCount;
277
+ if (component.lineCount > 300 || hookCount > 10)
278
+ return '🔴 High';
279
+ if (component.lineCount > 150 || hookCount > 5)
280
+ return '🟡 Medium';
281
+ return '🟢 Low';
282
+ }
283
+ /**
284
+ * Get maintainability rating based on smells and size
285
+ */
286
+ function getMaintainabilityRating(smellCount, lineCount) {
287
+ const ratio = smellCount / Math.max(lineCount / 50, 1);
288
+ if (ratio > 2 || smellCount > 5)
289
+ return '🔴 Poor';
290
+ if (ratio > 1 || smellCount > 2)
291
+ return '🟡 Fair';
292
+ return '🟢 Good';
293
+ }
294
+ /**
295
+ * Write documentation to file
296
+ */
297
+ export async function writeComponentDocs(result, rootDir, options) {
298
+ const content = await generateComponentDocs(result, rootDir, options);
299
+ const ext = options.format === 'html' ? 'html' : options.format === 'json' ? 'json' : 'md';
300
+ const filename = `COMPONENTS.${ext}`;
301
+ const outputPath = options.outputDir
302
+ ? path.join(options.outputDir, filename)
303
+ : path.join(rootDir, filename);
304
+ await fs.writeFile(outputPath, content, 'utf-8');
305
+ return outputPath;
306
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Interactive guide for React Code Smell Detector
3
+ */
4
+ export declare function runGuide(): Promise<void>;
5
+ /**
6
+ * Create a demo project with examples of all detectable smells
7
+ */
8
+ export declare function createDemoProject(targetDir: string): Promise<void>;
9
+ //# sourceMappingURL=guide.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guide.d.ts","sourceRoot":"","sources":["../src/guide.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAmF9C;AAmWD;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiFxE"}