react-code-smell-detector 1.5.0 → 1.5.2
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/README.md +241 -4
- package/dist/__tests__/aiRefactoring.test.d.ts +2 -0
- package/dist/__tests__/aiRefactoring.test.d.ts.map +1 -0
- package/dist/__tests__/aiRefactoring.test.js +86 -0
- package/dist/__tests__/analyzer-real.test.d.ts +2 -0
- package/dist/__tests__/analyzer-real.test.d.ts.map +1 -0
- package/dist/__tests__/analyzer-real.test.js +149 -0
- package/dist/__tests__/analyzer.test.d.ts +2 -0
- package/dist/__tests__/analyzer.test.d.ts.map +1 -0
- package/dist/__tests__/analyzer.test.js +173 -0
- package/dist/__tests__/baseline.test.d.ts +2 -0
- package/dist/__tests__/baseline.test.d.ts.map +1 -0
- package/dist/__tests__/baseline.test.js +136 -0
- package/dist/__tests__/bundleAnalyzer.test.d.ts +2 -0
- package/dist/__tests__/bundleAnalyzer.test.d.ts.map +1 -0
- package/dist/__tests__/bundleAnalyzer.test.js +182 -0
- package/dist/__tests__/customRules.test.d.ts +2 -0
- package/dist/__tests__/customRules.test.d.ts.map +1 -0
- package/dist/__tests__/customRules.test.js +283 -0
- package/dist/__tests__/detectors/index.test.d.ts +2 -0
- package/dist/__tests__/detectors/index.test.d.ts.map +1 -0
- package/dist/__tests__/detectors/index.test.js +1012 -0
- package/dist/__tests__/detectors/newDetectors.test.d.ts +2 -0
- package/dist/__tests__/detectors/newDetectors.test.d.ts.map +1 -0
- package/dist/__tests__/detectors/newDetectors.test.js +333 -0
- package/dist/__tests__/docGenerator.test.d.ts +2 -0
- package/dist/__tests__/docGenerator.test.d.ts.map +1 -0
- package/dist/__tests__/docGenerator.test.js +157 -0
- package/dist/__tests__/fixer.test.d.ts +2 -0
- package/dist/__tests__/fixer.test.d.ts.map +1 -0
- package/dist/__tests__/fixer.test.js +193 -0
- package/dist/__tests__/git.test.d.ts +2 -0
- package/dist/__tests__/git.test.d.ts.map +1 -0
- package/dist/__tests__/git.test.js +38 -0
- package/dist/__tests__/graphGenerator.test.d.ts +2 -0
- package/dist/__tests__/graphGenerator.test.d.ts.map +1 -0
- package/dist/__tests__/graphGenerator.test.js +190 -0
- package/dist/__tests__/htmlReporter.test.d.ts +2 -0
- package/dist/__tests__/htmlReporter.test.d.ts.map +1 -0
- package/dist/__tests__/htmlReporter.test.js +258 -0
- package/dist/__tests__/interactiveFixer.test.d.ts +2 -0
- package/dist/__tests__/interactiveFixer.test.d.ts.map +1 -0
- package/dist/__tests__/interactiveFixer.test.js +231 -0
- package/dist/__tests__/performanceBudget.test.js +195 -44
- package/dist/__tests__/reporter.test.d.ts +2 -0
- package/dist/__tests__/reporter.test.d.ts.map +1 -0
- package/dist/__tests__/reporter.test.js +136 -0
- package/dist/__tests__/watcher.test.d.ts +2 -0
- package/dist/__tests__/watcher.test.d.ts.map +1 -0
- package/dist/__tests__/watcher.test.js +161 -0
- package/dist/__tests__/webhooks.test.d.ts +2 -0
- package/dist/__tests__/webhooks.test.d.ts.map +1 -0
- package/dist/__tests__/webhooks.test.js +209 -0
- package/dist/aiRefactoring.d.ts +29 -0
- package/dist/aiRefactoring.d.ts.map +1 -0
- package/dist/aiRefactoring.js +290 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +23 -0
- package/dist/cli.js +17 -0
- package/dist/detectors/contextApi.d.ts +11 -0
- package/dist/detectors/contextApi.d.ts.map +1 -0
- package/dist/detectors/contextApi.js +151 -0
- package/dist/detectors/errorBoundary.d.ts +11 -0
- package/dist/detectors/errorBoundary.d.ts.map +1 -0
- package/dist/detectors/errorBoundary.js +167 -0
- package/dist/detectors/formValidation.d.ts +11 -0
- package/dist/detectors/formValidation.d.ts.map +1 -0
- package/dist/detectors/formValidation.js +193 -0
- package/dist/detectors/index.d.ts +5 -0
- package/dist/detectors/index.d.ts.map +1 -1
- package/dist/detectors/index.js +10 -0
- package/dist/detectors/stateManagement.d.ts +11 -0
- package/dist/detectors/stateManagement.d.ts.map +1 -0
- package/dist/detectors/stateManagement.js +193 -0
- package/dist/detectors/testingGaps.d.ts +15 -0
- package/dist/detectors/testingGaps.d.ts.map +1 -0
- package/dist/detectors/testingGaps.js +182 -0
- package/dist/guide.d.ts +9 -0
- package/dist/guide.d.ts.map +1 -0
- package/dist/guide.js +922 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/types/index.d.ts +11 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +16 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ A CLI tool that analyzes React projects and detects common code smells, providin
|
|
|
7
7
|
- 🔍 **Detect Code Smells**: Identifies common React anti-patterns (70+ smell types)
|
|
8
8
|
- 📊 **Technical Debt Score**: Grades your codebase from A to F
|
|
9
9
|
- 💡 **Refactoring Suggestions**: Actionable recommendations for each issue
|
|
10
|
-
- 📝 **Multiple Output Formats**: Console (colored), JSON, Markdown, and
|
|
10
|
+
- 📝 **Multiple Output Formats**: Console (colored), JSON, Markdown, HTML, and PDF
|
|
11
11
|
- 🔒 **Security Scanning**: Detects XSS vulnerabilities, eval usage, exposed secrets
|
|
12
12
|
- ♿ **Accessibility Checks**: Missing alt text, labels, ARIA attributes
|
|
13
13
|
- 🐛 **Debug Cleanup**: console.log, debugger, TODO/FIXME detection
|
|
@@ -19,7 +19,7 @@ A CLI tool that analyzes React projects and detects common code smells, providin
|
|
|
19
19
|
- 💧 **Memory Leak Detection**: Find missing cleanup in useEffect
|
|
20
20
|
- 🔄 **Import Analysis**: Detect circular dependencies and barrel file issues
|
|
21
21
|
- 🗑️ **Unused Code Detection**: Find unused exports and dead imports
|
|
22
|
-
- 📈 **
|
|
22
|
+
- 📈 **Advanced Trend Analytics**: ML-powered trend analysis with predictions and health scores
|
|
23
23
|
- 💬 **Chat Notifications**: Send analysis results to Slack, Discord, or custom webhooks
|
|
24
24
|
- 🔗 **Dependency Graph Visualization**: Visual SVG/HTML of component and import relationships
|
|
25
25
|
- 📦 **Bundle Size Impact**: Per-component bundle size estimates and optimization suggestions
|
|
@@ -27,6 +27,8 @@ A CLI tool that analyzes React projects and detects common code smells, providin
|
|
|
27
27
|
- 🔧 **Interactive Fix Mode**: Review and apply fixes one by one with diff preview
|
|
28
28
|
- 💬 **GitHub PR Comments**: Auto-comment analysis results on pull requests
|
|
29
29
|
- 📊 **Performance Budget**: Set thresholds and enforce limits in CI/CD
|
|
30
|
+
- 📄 **PDF Report Generation**: Professional, printable reports with visual analytics
|
|
31
|
+
- 🎯 **ESLint Plugin Integration**: Run detectors directly as ESLint rules
|
|
30
32
|
- 📚 **Component Documentation**: Auto-generate docs from component analysis
|
|
31
33
|
- ⚛️ **React 19 Server Components**: Detect Server/Client boundary issues
|
|
32
34
|
- 🔮 **Context API Analysis**: Detect context overuse, missing memoization, re-render issues
|
|
@@ -81,6 +83,26 @@ npm install -D react-code-smell-detector
|
|
|
81
83
|
react-smell /path/to/react/project
|
|
82
84
|
```
|
|
83
85
|
|
|
86
|
+
### Interactive Guide
|
|
87
|
+
|
|
88
|
+
Launch an interactive tutorial to learn all features:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
react-smell guide
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Demo Project
|
|
95
|
+
|
|
96
|
+
Create a demo project with examples of all detectable code smells:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
react-smell demo
|
|
100
|
+
# Creates ./react-smell-demo with sample components
|
|
101
|
+
|
|
102
|
+
# Or specify a directory
|
|
103
|
+
react-smell demo /path/to/directory
|
|
104
|
+
```
|
|
105
|
+
|
|
84
106
|
### With Code Snippets
|
|
85
107
|
|
|
86
108
|
```bash
|
|
@@ -101,8 +123,154 @@ react-smell ./src -f markdown -o report.md
|
|
|
101
123
|
|
|
102
124
|
# HTML (beautiful visual report)
|
|
103
125
|
react-smell ./src -f html -o report.html
|
|
126
|
+
|
|
127
|
+
# PDF (professional formatted report with charts)
|
|
128
|
+
react-smell ./src -f pdf -o report.pdf
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### ESLint Plugin Integration
|
|
132
|
+
|
|
133
|
+
Run code smell detection directly integrated with ESLint:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Install ESLint if not already installed
|
|
137
|
+
npm install -D eslint
|
|
138
|
+
|
|
139
|
+
# Add to .eslintrc.json
|
|
140
|
+
{
|
|
141
|
+
"plugins": ["react-code-smell-detector"],
|
|
142
|
+
"extends": ["plugin:react-code-smell-detector/recommended"]
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
# Or use strict mode for more comprehensive checks
|
|
146
|
+
{
|
|
147
|
+
"plugins": ["react-code-smell-detector"],
|
|
148
|
+
"extends": ["plugin:react-code-smell-detector/strict"]
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Benefits:**
|
|
153
|
+
- Real-time feedback in your editor
|
|
154
|
+
- Integrates with existing ESLint workflow
|
|
155
|
+
- Runs on file save or pre-commit
|
|
156
|
+
- Works with git hooks and CI/CD pipelines
|
|
157
|
+
|
|
158
|
+
**Available ESLint Rules:**
|
|
159
|
+
- `no-excessive-use-effect`: Detect overuse of useEffect
|
|
160
|
+
- `no-prop-drilling`: Detect prop drilling patterns
|
|
161
|
+
- `no-large-components`: Warn on component size
|
|
162
|
+
- `no-unmemoized-calculations`: Flag unmemoized expensive operations
|
|
163
|
+
- `no-security-issues`: Detect security vulnerabilities
|
|
164
|
+
- `accessibility-checked`: Accessibility violations
|
|
165
|
+
- `no-memory-leaks`: Potential memory leaks
|
|
166
|
+
- `no-circular-dependencies`: Circular import detection
|
|
167
|
+
- `state-management-smell`: State management anti-patterns
|
|
168
|
+
- And 9 more rules for comprehensive coverage
|
|
169
|
+
|
|
170
|
+
### Advanced Trend Analytics
|
|
171
|
+
|
|
172
|
+
Track code quality trends over time with intelligent predictions and insights:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# Enable trend analysis in baseline tracking
|
|
176
|
+
react-smell ./src --baseline --trend-analysis
|
|
177
|
+
|
|
178
|
+
# View detailed trend insights
|
|
179
|
+
react-smell ./src --baseline --trend-report
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Features:**
|
|
183
|
+
- **Trend Direction**: Automatic detection of improving, worsening, or stable trends
|
|
184
|
+
- **Growth Metrics**: Daily velocity, change rate, consistency scoring
|
|
185
|
+
- **Predictive Analytics**: Machine learning-based predictions for next 30 days
|
|
186
|
+
- **Health Score**: Overall codebase health indicator (0-100)
|
|
187
|
+
- **Zero Smell Timeline**: Estimated days to reach zero code smells
|
|
188
|
+
- **Seasonality Detection**: Identify patterns in code quality changes
|
|
189
|
+
- **Smart Recommendations**: AI-powered suggestions based on trend analysis
|
|
190
|
+
|
|
191
|
+
**Trend Analysis Output:**
|
|
192
|
+
```
|
|
193
|
+
📈 Trend Analysis
|
|
194
|
+
──────────────────────────────────────────────────
|
|
195
|
+
Current: 42 smells
|
|
196
|
+
Previous: 48 smells
|
|
197
|
+
Change: -12.5%
|
|
198
|
+
Trend: IMPROVING
|
|
199
|
+
|
|
200
|
+
✅ Health Score: 72/100 (good)
|
|
201
|
+
|
|
202
|
+
📊 Metrics
|
|
203
|
+
• Daily Velocity: 0.75 smells/day (improving)
|
|
204
|
+
• Consistency: 85%
|
|
205
|
+
• Volatility: 2.1
|
|
206
|
+
|
|
207
|
+
🔮 Predictions
|
|
208
|
+
• Next Month: ~35 smells
|
|
209
|
+
• Trajectory: DOWNWARD
|
|
210
|
+
• Confidence: 92%
|
|
211
|
+
• Days to Zero Smells: 56
|
|
212
|
+
|
|
213
|
+
✨ Improvements
|
|
214
|
+
• useEffect-overuse (3 fewer)
|
|
215
|
+
• prop-drilling (2 fewer)
|
|
216
|
+
|
|
217
|
+
💡 Recommendations
|
|
218
|
+
🟢 Path to Code Smell-Free Codebase
|
|
219
|
+
At the current improvement rate, your codebase could be
|
|
220
|
+
smell-free in approximately 56 days. Focus on maintaining
|
|
221
|
+
this momentum.
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Configuration:**
|
|
225
|
+
```json
|
|
226
|
+
{
|
|
227
|
+
"baselineEnabled": true,
|
|
228
|
+
"trendAnalysisEnabled": true,
|
|
229
|
+
"trendReportFrequency": "weekly",
|
|
230
|
+
"predictiveDaysLookahead": 30
|
|
231
|
+
}
|
|
104
232
|
```
|
|
105
233
|
|
|
234
|
+
### PDF Report Generation
|
|
235
|
+
|
|
236
|
+
Generate professional, printable PDF reports with visual analytics:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# Generate PDF report
|
|
240
|
+
react-smell ./src -f pdf -o smell-report.pdf
|
|
241
|
+
|
|
242
|
+
# Include code snippets in PDF
|
|
243
|
+
react-smell ./src -f pdf -o report.pdf --snippets
|
|
244
|
+
|
|
245
|
+
# Generate PDF with trend analysis
|
|
246
|
+
react-smell ./src -f pdf -o report.pdf --baseline --trend-analysis
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
**PDF Report Contents:**
|
|
250
|
+
- Executive Summary with key metrics
|
|
251
|
+
- Technical Debt Grade with visual indicators
|
|
252
|
+
- Smell breakdown by type and severity
|
|
253
|
+
- Top affected files with risk levels
|
|
254
|
+
- Detailed smell analysis with code snippets
|
|
255
|
+
- Actionable recommendations prioritized by impact
|
|
256
|
+
- Trend analysis and predictions
|
|
257
|
+
- Estimated refactoring timeline
|
|
258
|
+
|
|
259
|
+
**Visual Elements:**
|
|
260
|
+
- Color-coded severity levels (error/warning/info)
|
|
261
|
+
- Grade badges with color gradients
|
|
262
|
+
- Data visualizations and charts
|
|
263
|
+
- Summary metrics in card layout
|
|
264
|
+
- Code snippets with syntax highlighting
|
|
265
|
+
- Professional typography and layout
|
|
266
|
+
|
|
267
|
+
**Use Cases:**
|
|
268
|
+
- Share reports with stakeholders
|
|
269
|
+
- Archive analysis history
|
|
270
|
+
- Print for code review meetings
|
|
271
|
+
- Executive dashboards
|
|
272
|
+
- Compliance documentation
|
|
273
|
+
|
|
106
274
|
### Configuration
|
|
107
275
|
|
|
108
276
|
Create a `.smellrc.json` file:
|
|
@@ -122,11 +290,28 @@ Or create manually:
|
|
|
122
290
|
}
|
|
123
291
|
```
|
|
124
292
|
|
|
293
|
+
Create a `.smellrc.json` file:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
react-smell init
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Or create manually:
|
|
300
|
+
|
|
301
|
+
```json
|
|
302
|
+
{
|
|
303
|
+
"maxUseEffectsPerComponent": 3,
|
|
304
|
+
"maxPropDrillingDepth": 3,
|
|
305
|
+
"maxComponentLines": 300,
|
|
306
|
+
"maxPropsCount": 7
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
125
310
|
### CLI Options
|
|
126
311
|
|
|
127
312
|
| Option | Description | Default |
|
|
128
313
|
|--------|-------------|---------|
|
|
129
|
-
| `-f, --format <format>` | Output format: console, json, markdown, html | `console` |
|
|
314
|
+
| `-f, --format <format>` | Output format: console, json, markdown, html, pdf | `console` |
|
|
130
315
|
| `-s, --snippets` | Show code snippets in output | `false` |
|
|
131
316
|
| `-c, --config <file>` | Path to config file | `.smellrc.json` |
|
|
132
317
|
| `--ci` | CI mode: exit with code 1 if any issues | `false` |
|
|
@@ -141,6 +326,8 @@ Or create manually:
|
|
|
141
326
|
| `--exclude <patterns>` | Glob patterns to exclude | `node_modules,dist` |
|
|
142
327
|
| `-o, --output <file>` | Write output to file | - |
|
|
143
328
|
| `--baseline` | Enable baseline tracking and trend analysis | `false` |
|
|
329
|
+
| `--trend-analysis` | Show detailed trend analysis and predictions | `false` |
|
|
330
|
+
| `--trend-report` | Generate comprehensive trend report | `false` |
|
|
144
331
|
| `--slack <url>` | Slack webhook URL for notifications | - |
|
|
145
332
|
| `--discord <url>` | Discord webhook URL for notifications | - |
|
|
146
333
|
| `--webhook <url>` | Generic webhook URL for notifications | - |
|
|
@@ -598,6 +785,11 @@ react-smell ./src
|
|
|
598
785
|
import {
|
|
599
786
|
analyzeProject,
|
|
600
787
|
reportResults,
|
|
788
|
+
// PDF Reporting
|
|
789
|
+
generatePDFReport,
|
|
790
|
+
// Trend Analytics
|
|
791
|
+
analyzeTrends,
|
|
792
|
+
formatTrendAnalysis,
|
|
601
793
|
// Interactive fixing
|
|
602
794
|
runInteractiveFix,
|
|
603
795
|
previewFixes,
|
|
@@ -611,6 +803,9 @@ import {
|
|
|
611
803
|
// Documentation
|
|
612
804
|
generateComponentDocs,
|
|
613
805
|
writeComponentDocs,
|
|
806
|
+
// Baseline & Trends
|
|
807
|
+
recordBaseline,
|
|
808
|
+
initializeBaseline,
|
|
614
809
|
} from 'react-code-smell-detector';
|
|
615
810
|
|
|
616
811
|
const result = await analyzeProject({
|
|
@@ -626,6 +821,28 @@ const result = await analyzeProject({
|
|
|
626
821
|
console.log(`Grade: ${result.debtScore.grade}`);
|
|
627
822
|
console.log(`Total issues: ${result.summary.totalSmells}`);
|
|
628
823
|
|
|
824
|
+
// Generate PDF report
|
|
825
|
+
await generatePDFReport(result, {
|
|
826
|
+
outputPath: './report.pdf',
|
|
827
|
+
rootDir: './src',
|
|
828
|
+
includeCharts: true,
|
|
829
|
+
includeCodeSnippets: true,
|
|
830
|
+
title: 'Code Quality Report',
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
// Record baseline for trend tracking
|
|
834
|
+
initializeBaseline('./');
|
|
835
|
+
const baselineRecord = recordBaseline('./', result.smells);
|
|
836
|
+
|
|
837
|
+
// Analyze trends from baseline history
|
|
838
|
+
// (requires multiple baseline recordings over time)
|
|
839
|
+
import { BaselineData } from 'react-code-smell-detector';
|
|
840
|
+
const baselineData: BaselineData = JSON.parse(
|
|
841
|
+
fs.readFileSync('.smellrc-baseline.json', 'utf-8')
|
|
842
|
+
);
|
|
843
|
+
const trendAnalysis = analyzeTrends(baselineData);
|
|
844
|
+
console.log(formatTrendAnalysis(trendAnalysis));
|
|
845
|
+
|
|
629
846
|
// Check against performance budget
|
|
630
847
|
const budget = await loadBudget();
|
|
631
848
|
const budgetResult = checkBudget(result, budget);
|
|
@@ -642,7 +859,7 @@ const docsPath = await writeComponentDocs(result, './src', {
|
|
|
642
859
|
// Generate PR comment
|
|
643
860
|
const prComment = generatePRComment(result, './src');
|
|
644
861
|
|
|
645
|
-
// Or use the reporter
|
|
862
|
+
// Or use the reporter for any format
|
|
646
863
|
const report = reportResults(result, {
|
|
647
864
|
format: 'markdown',
|
|
648
865
|
showCodeSnippets: true,
|
|
@@ -650,6 +867,26 @@ const report = reportResults(result, {
|
|
|
650
867
|
});
|
|
651
868
|
```
|
|
652
869
|
|
|
870
|
+
**Trend Analytics API:**
|
|
871
|
+
```typescript
|
|
872
|
+
import { TrendAnalysis, TrendPrediction } from 'react-code-smell-detector';
|
|
873
|
+
|
|
874
|
+
// Access trend data
|
|
875
|
+
const currentHealth = trendAnalysis.healthScore; // HealthScore
|
|
876
|
+
const predictions = trendAnalysis.predictions; // TrendPrediction
|
|
877
|
+
const metrics = trendAnalysis.metrics; // TrendMetrics
|
|
878
|
+
const recommendations = trendAnalysis.recommendations; // TrendRecommendation[]
|
|
879
|
+
|
|
880
|
+
// Timeline to zero smells
|
|
881
|
+
if (predictions.daysToZeroSmells) {
|
|
882
|
+
console.log(`Projected zero smells in ${predictions.daysToZeroSmells} days`);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// Access velocity and consistency metrics
|
|
886
|
+
console.log(`Daily Velocity: ${metrics.velocityPerDay} smells/day`);
|
|
887
|
+
console.log(`Consistency: ${metrics.consistency}%`);
|
|
888
|
+
```
|
|
889
|
+
|
|
653
890
|
## CI/CD Integration
|
|
654
891
|
|
|
655
892
|
The tool provides flexible exit codes and notification capabilities for CI/CD pipelines:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aiRefactoring.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/aiRefactoring.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { getQuickRefactoringTemplates } from '../aiRefactoring.js';
|
|
3
|
+
describe('AI Refactoring', () => {
|
|
4
|
+
describe('getQuickRefactoringTemplates', () => {
|
|
5
|
+
it('should return templates for useEffect-overuse', () => {
|
|
6
|
+
const templates = getQuickRefactoringTemplates('useEffect-overuse');
|
|
7
|
+
expect(templates.length).toBeGreaterThan(0);
|
|
8
|
+
expect(templates.some(t => t.includes('effect'))).toBe(true);
|
|
9
|
+
});
|
|
10
|
+
it('should return templates for prop-drilling', () => {
|
|
11
|
+
const templates = getQuickRefactoringTemplates('prop-drilling');
|
|
12
|
+
expect(templates.length).toBeGreaterThan(0);
|
|
13
|
+
expect(templates.some(t => t.includes('Context') || t.includes('composition'))).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
it('should return templates for large-component', () => {
|
|
16
|
+
const templates = getQuickRefactoringTemplates('large-component');
|
|
17
|
+
expect(templates.length).toBeGreaterThan(0);
|
|
18
|
+
expect(templates.some(t => t.includes('Extract') || t.includes('component'))).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
it('should return templates for context-overuse', () => {
|
|
21
|
+
const templates = getQuickRefactoringTemplates('context-overuse');
|
|
22
|
+
expect(templates.length).toBeGreaterThan(0);
|
|
23
|
+
expect(templates.some(t => t.includes('context'))).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
it('should return templates for missing-error-boundary', () => {
|
|
26
|
+
const templates = getQuickRefactoringTemplates('missing-error-boundary');
|
|
27
|
+
expect(templates.length).toBeGreaterThan(0);
|
|
28
|
+
expect(templates.some(t => t.includes('ErrorBoundary') || t.includes('Suspense'))).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
it('should return templates for state-sync-anti-pattern', () => {
|
|
31
|
+
const templates = getQuickRefactoringTemplates('state-sync-anti-pattern');
|
|
32
|
+
expect(templates.length).toBeGreaterThan(0);
|
|
33
|
+
expect(templates.some(t => t.includes('Derive') || t.includes('useMemo'))).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
it('should return generic template for unknown smell types', () => {
|
|
36
|
+
const templates = getQuickRefactoringTemplates('unknown-smell-type');
|
|
37
|
+
expect(templates.length).toBeGreaterThan(0);
|
|
38
|
+
expect(templates[0]).toContain('Review');
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe('AIRefactoringConfig', () => {
|
|
42
|
+
it('should accept valid config object', () => {
|
|
43
|
+
const config = {
|
|
44
|
+
apiKey: 'test-key',
|
|
45
|
+
model: 'gpt-4',
|
|
46
|
+
maxTokens: 1000,
|
|
47
|
+
temperature: 0.3,
|
|
48
|
+
provider: 'openai',
|
|
49
|
+
};
|
|
50
|
+
expect(config.apiKey).toBe('test-key');
|
|
51
|
+
expect(config.model).toBe('gpt-4');
|
|
52
|
+
expect(config.provider).toBe('openai');
|
|
53
|
+
});
|
|
54
|
+
it('should accept anthropic provider', () => {
|
|
55
|
+
const config = {
|
|
56
|
+
apiKey: 'test-key',
|
|
57
|
+
model: 'claude-3-sonnet',
|
|
58
|
+
provider: 'anthropic',
|
|
59
|
+
};
|
|
60
|
+
expect(config.provider).toBe('anthropic');
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe('AIRefactoringSuggestion type', () => {
|
|
64
|
+
it('should have expected structure', () => {
|
|
65
|
+
const suggestion = {
|
|
66
|
+
smell: {
|
|
67
|
+
type: 'debug-statement',
|
|
68
|
+
severity: 'warning',
|
|
69
|
+
message: 'Test',
|
|
70
|
+
file: '/test.tsx',
|
|
71
|
+
line: 1,
|
|
72
|
+
column: 0,
|
|
73
|
+
suggestion: 'Remove it',
|
|
74
|
+
},
|
|
75
|
+
originalCode: 'console.log("test")',
|
|
76
|
+
suggestedCode: '// removed',
|
|
77
|
+
explanation: 'Debug statements should be removed',
|
|
78
|
+
confidence: 0.9,
|
|
79
|
+
estimatedEffort: 'low',
|
|
80
|
+
};
|
|
81
|
+
expect(suggestion.confidence).toBeGreaterThanOrEqual(0);
|
|
82
|
+
expect(suggestion.confidence).toBeLessThanOrEqual(1);
|
|
83
|
+
expect(['low', 'medium', 'high']).toContain(suggestion.estimatedEffort);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzer-real.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/analyzer-real.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { analyzeProject } from '../analyzer.js';
|
|
3
|
+
import * as fg from 'fast-glob';
|
|
4
|
+
import * as fs from 'fs/promises';
|
|
5
|
+
// Mock fast-glob
|
|
6
|
+
vi.mock('fast-glob', () => ({
|
|
7
|
+
default: vi.fn(),
|
|
8
|
+
}));
|
|
9
|
+
// Mock fs/promises for parseFile
|
|
10
|
+
vi.mock('fs/promises', async (importOriginal) => {
|
|
11
|
+
const actual = await importOriginal();
|
|
12
|
+
return {
|
|
13
|
+
...actual,
|
|
14
|
+
default: actual,
|
|
15
|
+
readFile: vi.fn(),
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
describe('Analyzer - Real Tests', () => {
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
vi.clearAllMocks();
|
|
21
|
+
});
|
|
22
|
+
describe('analyzeProject', () => {
|
|
23
|
+
it('should return empty results when no files found', async () => {
|
|
24
|
+
fg.default.mockResolvedValue([]);
|
|
25
|
+
const result = await analyzeProject({
|
|
26
|
+
rootDir: '/fake/path',
|
|
27
|
+
});
|
|
28
|
+
expect(result.files).toEqual([]);
|
|
29
|
+
expect(result.summary.totalFiles).toBe(0);
|
|
30
|
+
expect(result.summary.totalComponents).toBe(0);
|
|
31
|
+
expect(result.summary.totalSmells).toBe(0);
|
|
32
|
+
});
|
|
33
|
+
it('should call fast-glob with correct patterns', async () => {
|
|
34
|
+
fg.default.mockResolvedValue([]);
|
|
35
|
+
await analyzeProject({
|
|
36
|
+
rootDir: '/fake/path',
|
|
37
|
+
include: ['**/*.tsx'],
|
|
38
|
+
});
|
|
39
|
+
expect(fg.default).toHaveBeenCalled();
|
|
40
|
+
});
|
|
41
|
+
it('should handle parse errors gracefully', async () => {
|
|
42
|
+
fg.default.mockResolvedValue(['/fake/path/broken.tsx']);
|
|
43
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
44
|
+
const result = await analyzeProject({
|
|
45
|
+
rootDir: '/fake/path',
|
|
46
|
+
});
|
|
47
|
+
// Should return empty since file doesn't exist
|
|
48
|
+
expect(result.files.length).toBe(0);
|
|
49
|
+
warnSpy.mockRestore();
|
|
50
|
+
});
|
|
51
|
+
it('should use custom include patterns', async () => {
|
|
52
|
+
fg.default.mockResolvedValue([]);
|
|
53
|
+
await analyzeProject({
|
|
54
|
+
rootDir: '/fake/path',
|
|
55
|
+
include: ['**/*.ts'],
|
|
56
|
+
exclude: ['**/node_modules/**'],
|
|
57
|
+
});
|
|
58
|
+
expect(fg.default).toHaveBeenCalledWith(expect.arrayContaining([expect.stringContaining('.ts')]), expect.objectContaining({ ignore: ['**/node_modules/**'] }));
|
|
59
|
+
});
|
|
60
|
+
it('should merge user config with defaults', async () => {
|
|
61
|
+
fg.default.mockResolvedValue([]);
|
|
62
|
+
const result = await analyzeProject({
|
|
63
|
+
rootDir: '/fake/path',
|
|
64
|
+
config: { maxUseEffectsPerComponent: 5 },
|
|
65
|
+
});
|
|
66
|
+
expect(result.summary).toBeDefined();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe('Technical Debt Score Calculation', () => {
|
|
70
|
+
it('should calculate grade A for clean projects', async () => {
|
|
71
|
+
const cleanCode = `
|
|
72
|
+
function CleanComponent() {
|
|
73
|
+
return <div>Clean</div>;
|
|
74
|
+
}
|
|
75
|
+
`;
|
|
76
|
+
fg.default.mockResolvedValue(['/fake/path/Clean.tsx']);
|
|
77
|
+
fs.readFile.mockResolvedValue(cleanCode);
|
|
78
|
+
const result = await analyzeProject({
|
|
79
|
+
rootDir: '/fake/path',
|
|
80
|
+
config: { checkDebugStatements: false },
|
|
81
|
+
});
|
|
82
|
+
expect(result.debtScore.grade).toBe('A');
|
|
83
|
+
expect(result.debtScore.score).toBeGreaterThanOrEqual(90);
|
|
84
|
+
});
|
|
85
|
+
it('should estimate refactor time based on issues', async () => {
|
|
86
|
+
fg.default.mockResolvedValue([]);
|
|
87
|
+
const result = await analyzeProject({
|
|
88
|
+
rootDir: '/fake/path',
|
|
89
|
+
});
|
|
90
|
+
expect(result.debtScore.estimatedRefactorTime).toBeDefined();
|
|
91
|
+
});
|
|
92
|
+
it('should include breakdown scores', async () => {
|
|
93
|
+
fg.default.mockResolvedValue([]);
|
|
94
|
+
const result = await analyzeProject({
|
|
95
|
+
rootDir: '/fake/path',
|
|
96
|
+
});
|
|
97
|
+
expect(result.debtScore.breakdown).toBeDefined();
|
|
98
|
+
expect(result.debtScore.breakdown.useEffectScore).toBeDefined();
|
|
99
|
+
expect(result.debtScore.breakdown.propDrillingScore).toBeDefined();
|
|
100
|
+
expect(result.debtScore.breakdown.componentSizeScore).toBeDefined();
|
|
101
|
+
expect(result.debtScore.breakdown.memoizationScore).toBeDefined();
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
describe('Smell Ignore Comments', () => {
|
|
105
|
+
it('should filter smells with @smell-ignore comment', async () => {
|
|
106
|
+
// Test behavior: when no files match, result should be empty
|
|
107
|
+
fg.default.mockResolvedValue([]);
|
|
108
|
+
const result = await analyzeProject({
|
|
109
|
+
rootDir: '/fake/path',
|
|
110
|
+
config: { checkDebugStatements: true },
|
|
111
|
+
});
|
|
112
|
+
// Empty result with no smells
|
|
113
|
+
expect(result.files.length).toBe(0);
|
|
114
|
+
expect(result.summary.totalSmells).toBe(0);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
describe('Summary Calculation', () => {
|
|
118
|
+
it('should have smells by type structure', async () => {
|
|
119
|
+
fg.default.mockResolvedValue([]);
|
|
120
|
+
const result = await analyzeProject({
|
|
121
|
+
rootDir: '/fake/path',
|
|
122
|
+
config: { checkDebugStatements: true },
|
|
123
|
+
});
|
|
124
|
+
// Should have smellsByType object even if empty
|
|
125
|
+
expect(result.summary.smellsByType).toBeDefined();
|
|
126
|
+
});
|
|
127
|
+
it('should aggregate smells by severity', async () => {
|
|
128
|
+
fg.default.mockResolvedValue([]);
|
|
129
|
+
const result = await analyzeProject({
|
|
130
|
+
rootDir: '/fake/path',
|
|
131
|
+
});
|
|
132
|
+
expect(result.summary.smellsBySeverity).toBeDefined();
|
|
133
|
+
expect(result.summary.smellsBySeverity.error).toBeDefined();
|
|
134
|
+
expect(result.summary.smellsBySeverity.warning).toBeDefined();
|
|
135
|
+
expect(result.summary.smellsBySeverity.info).toBeDefined();
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
describe('Component Info Collection', () => {
|
|
139
|
+
it('should return components array in file results', async () => {
|
|
140
|
+
// Test that result structure includes components array
|
|
141
|
+
fg.default.mockResolvedValue([]);
|
|
142
|
+
const result = await analyzeProject({
|
|
143
|
+
rootDir: '/fake/path',
|
|
144
|
+
});
|
|
145
|
+
// For non-existent files, we get empty results
|
|
146
|
+
expect(Array.isArray(result.files)).toBe(true);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzer.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/analyzer.test.ts"],"names":[],"mappings":""}
|