vibecodingmachine-core 2026.2.20-438 → 2026.2.26-1739
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 +240 -0
- package/package.json +10 -2
- package/src/agents/Agent.js +300 -0
- package/src/agents/AgentAdditionService.js +311 -0
- package/src/agents/AgentCheckService.js +690 -0
- package/src/agents/AgentInstallationService.js +140 -0
- package/src/agents/AgentSetupService.js +467 -0
- package/src/agents/AgentStatus.js +183 -0
- package/src/agents/AgentVerificationService.js +634 -0
- package/src/agents/ConfigurationSchemaValidator.js +543 -0
- package/src/agents/EnvironmentConfigurationManager.js +602 -0
- package/src/agents/InstallationErrorHandler.js +372 -0
- package/src/agents/InstallationLog.js +363 -0
- package/src/agents/InstallationMethod.js +510 -0
- package/src/agents/InstallationOrchestrator.js +352 -0
- package/src/agents/InstallationProgressReporter.js +372 -0
- package/src/agents/InstallationRetryManager.js +322 -0
- package/src/agents/InstallationType.js +254 -0
- package/src/agents/OperationTypes.js +310 -0
- package/src/agents/PerformanceMetricsCollector.js +493 -0
- package/src/agents/SecurityValidationService.js +534 -0
- package/src/agents/VerificationTest.js +354 -0
- package/src/agents/VerificationType.js +226 -0
- package/src/agents/WindowsPermissionHandler.js +518 -0
- package/src/agents/config/AgentConfigManager.js +393 -0
- package/src/agents/config/AgentDefaultsRegistry.js +373 -0
- package/src/agents/config/ConfigValidator.js +281 -0
- package/src/agents/discovery/AgentDiscoveryService.js +707 -0
- package/src/agents/logging/AgentLogger.js +511 -0
- package/src/agents/status/AgentStatusManager.js +481 -0
- package/src/agents/storage/FileManager.js +454 -0
- package/src/agents/verification/AgentCommunicationTester.js +474 -0
- package/src/agents/verification/BaseVerifier.js +430 -0
- package/src/agents/verification/CommandVerifier.js +480 -0
- package/src/agents/verification/FileOperationVerifier.js +453 -0
- package/src/agents/verification/ResultAnalyzer.js +707 -0
- package/src/agents/verification/TestRequirementManager.js +495 -0
- package/src/agents/verification/VerificationRunner.js +433 -0
- package/src/agents/windows/BaseWindowsInstaller.js +441 -0
- package/src/agents/windows/ChocolateyInstaller.js +509 -0
- package/src/agents/windows/DirectInstaller.js +443 -0
- package/src/agents/windows/InstallerFactory.js +391 -0
- package/src/agents/windows/NpmInstaller.js +505 -0
- package/src/agents/windows/PowerShellInstaller.js +458 -0
- package/src/agents/windows/WinGetInstaller.js +390 -0
- package/src/analysis/analysis-reporter.js +132 -0
- package/src/analysis/boundary-detector.js +712 -0
- package/src/analysis/categorizer.js +340 -0
- package/src/analysis/codebase-scanner.js +384 -0
- package/src/analysis/line-counter.js +513 -0
- package/src/analysis/priority-calculator.js +679 -0
- package/src/analysis/report/analysis-report.js +250 -0
- package/src/analysis/report/package-analyzer.js +278 -0
- package/src/analysis/report/recommendation-generator.js +382 -0
- package/src/analysis/report/statistics-generator.js +515 -0
- package/src/analysis/reports/analysis-report-model.js +101 -0
- package/src/analysis/reports/recommendation-generator.js +283 -0
- package/src/analysis/reports/report-generators.js +191 -0
- package/src/analysis/reports/statistics-calculator.js +231 -0
- package/src/analysis/reports/trend-analyzer.js +219 -0
- package/src/analysis/strategy-generator.js +814 -0
- package/src/auto-mode/AutoModeBusinessLogic.js +836 -0
- package/src/config/refactoring-config.js +307 -0
- package/src/health-tracking/json-storage.js +38 -2
- package/src/ide-integration/applescript-manager-core.js +233 -0
- package/src/ide-integration/applescript-manager.cjs +357 -28
- package/src/ide-integration/applescript-manager.js +89 -3599
- package/src/ide-integration/cdp-manager.js +306 -0
- package/src/ide-integration/claude-code-cli-manager.cjs +1 -1
- package/src/ide-integration/continuation-handler.js +337 -0
- package/src/ide-integration/ide-status-checker.js +292 -0
- package/src/ide-integration/macos-ide-manager.js +627 -0
- package/src/ide-integration/macos-text-sender.js +528 -0
- package/src/ide-integration/response-reader.js +548 -0
- package/src/ide-integration/windows-automation-manager.js +121 -0
- package/src/ide-integration/windows-ide-manager.js +373 -0
- package/src/index.cjs +25 -3
- package/src/index.js +15 -1
- package/src/llm/direct-llm-manager.cjs +90 -2
- package/src/models/compliance-report.js +538 -0
- package/src/models/file-analysis.js +681 -0
- package/src/models/refactoring-plan.js +770 -0
- package/src/monitoring/alert-system.js +834 -0
- package/src/monitoring/compliance-progress-tracker.js +437 -0
- package/src/monitoring/continuous-scan-notifications.js +661 -0
- package/src/monitoring/continuous-scanner.js +279 -0
- package/src/monitoring/file-monitor/file-analyzer.js +262 -0
- package/src/monitoring/file-monitor/file-monitor.js +237 -0
- package/src/monitoring/file-monitor/watcher.js +194 -0
- package/src/monitoring/file-monitor.js +17 -0
- package/src/monitoring/notification-manager.js +437 -0
- package/src/monitoring/scanner-core.js +368 -0
- package/src/monitoring/scanner-events.js +214 -0
- package/src/monitoring/violation-notification-system.js +515 -0
- package/src/refactoring/boundaries/cohesion-analyzer.js +316 -0
- package/src/refactoring/boundaries/extraction-result.js +285 -0
- package/src/refactoring/boundaries/extraction-strategies.js +392 -0
- package/src/refactoring/boundaries/module-boundary.js +209 -0
- package/src/refactoring/boundary/boundary-detector.js +741 -0
- package/src/refactoring/boundary/boundary-types.js +405 -0
- package/src/refactoring/boundary/extraction-strategies.js +554 -0
- package/src/refactoring/boundary-extraction-result.js +77 -0
- package/src/refactoring/boundary-extraction-strategies.js +330 -0
- package/src/refactoring/boundary-extractor.js +384 -0
- package/src/refactoring/boundary-types.js +46 -0
- package/src/refactoring/circular/circular-dependency.js +88 -0
- package/src/refactoring/circular/cycle-detection.js +147 -0
- package/src/refactoring/circular/dependency-node.js +82 -0
- package/src/refactoring/circular/dependency-result.js +107 -0
- package/src/refactoring/circular/dependency-types.js +58 -0
- package/src/refactoring/circular/graph-builder.js +213 -0
- package/src/refactoring/circular/resolution-strategy.js +72 -0
- package/src/refactoring/circular/strategy-generator.js +229 -0
- package/src/refactoring/circular-dependency-resolver-original.js +809 -0
- package/src/refactoring/circular-dependency-resolver.js +200 -0
- package/src/refactoring/code-mover.js +761 -0
- package/src/refactoring/file-splitter.js +696 -0
- package/src/refactoring/functionality-validator.js +816 -0
- package/src/refactoring/import-manager.js +774 -0
- package/src/refactoring/module-boundary.js +107 -0
- package/src/refactoring/refactoring-executor.js +672 -0
- package/src/refactoring/refactoring-rollback.js +614 -0
- package/src/refactoring/test-validator.js +631 -0
- package/src/requirement-management/default-requirement-manager.js +321 -0
- package/src/requirement-management/requirement-file-parser.js +159 -0
- package/src/requirement-management/requirement-sequencer.js +221 -0
- package/src/rui/commands/AgentCommandParser.js +600 -0
- package/src/rui/commands/AgentCommands.js +487 -0
- package/src/rui/commands/AgentResponseFormatter.js +832 -0
- package/src/scripts/verify-full-compliance.js +269 -0
- package/src/sync/sync-engine-core.js +1 -0
- package/src/sync/sync-engine-remote-handlers.js +135 -0
- package/src/task-generation/automated-task-generator.js +351 -0
- package/src/task-generation/prioritizer.js +287 -0
- package/src/task-generation/task-list-updater.js +215 -0
- package/src/task-generation/task-management-integration.js +480 -0
- package/src/task-generation/task-manager-integration.js +270 -0
- package/src/task-generation/violation-task-generator.js +474 -0
- package/src/task-management/continuous-scan-integration.js +342 -0
- package/src/timeout-management/index.js +12 -3
- package/src/timeout-management/response-time-tracker.js +167 -0
- package/src/timeout-management/timeout-calculator.js +159 -0
- package/src/timeout-management/timeout-config-manager.js +172 -0
- package/src/utils/ast-analyzer.js +417 -0
- package/src/utils/current-requirement-manager.js +276 -0
- package/src/utils/current-requirement-operations.js +472 -0
- package/src/utils/dependency-mapper.js +456 -0
- package/src/utils/download-with-progress.js +4 -2
- package/src/utils/electron-update-checker.js +4 -1
- package/src/utils/file-size-analyzer.js +272 -0
- package/src/utils/import-updater.js +280 -0
- package/src/utils/refactoring-tools.js +512 -0
- package/src/utils/report-generator.js +569 -0
- package/src/utils/reports/report-analysis.js +218 -0
- package/src/utils/reports/report-types.js +55 -0
- package/src/utils/reports/summary-generators.js +102 -0
- package/src/utils/requirement-file-management.js +157 -0
- package/src/utils/requirement-helpers/requirement-file-ops.js +392 -0
- package/src/utils/requirement-helpers/requirement-mover.js +414 -0
- package/src/utils/requirement-helpers/requirement-parser.js +326 -0
- package/src/utils/requirement-helpers/requirement-status.js +320 -0
- package/src/utils/requirement-helpers-new.js +55 -0
- package/src/utils/requirement-helpers-refactored.js +367 -0
- package/src/utils/requirement-helpers.js +291 -1191
- package/src/utils/requirement-movement-operations.js +450 -0
- package/src/utils/requirement-movement.js +312 -0
- package/src/utils/requirement-parsing-helpers.js +56 -0
- package/src/utils/requirement-statistics.js +200 -0
- package/src/utils/requirement-text-utils.js +58 -0
- package/src/utils/rollback/rollback-handlers.js +125 -0
- package/src/utils/rollback/rollback-operation.js +63 -0
- package/src/utils/rollback/rollback-recorder.js +166 -0
- package/src/utils/rollback/rollback-state-manager.js +175 -0
- package/src/utils/rollback/rollback-types.js +33 -0
- package/src/utils/rollback/rollback-utils.js +110 -0
- package/src/utils/rollback-manager-original.js +569 -0
- package/src/utils/rollback-manager.js +202 -0
- package/src/utils/smoke-test-cli.js +362 -0
- package/src/utils/smoke-test-gui.js +351 -0
- package/src/utils/smoke-test-orchestrator.js +321 -0
- package/src/utils/smoke-test-runner.js +60 -0
- package/src/utils/smoke-test-web.js +347 -0
- package/src/utils/specification-helpers.js +39 -13
- package/src/utils/specification-migration.js +97 -0
- package/src/utils/test-runner.js +579 -0
- package/src/utils/validation-framework.js +518 -0
- package/src/validation/compliance-analyzer.js +197 -0
- package/src/validation/compliance-report-generator.js +343 -0
- package/src/validation/compliance-reporter.js +711 -0
- package/src/validation/compliance-rules.js +127 -0
- package/src/validation/constitution-validator-new.js +196 -0
- package/src/validation/constitution-validator.js +17 -0
- package/src/validation/file-validators.js +170 -0
- package/src/validation/line-limit/file-analyzer.js +201 -0
- package/src/validation/line-limit/line-limit-validator.js +208 -0
- package/src/validation/line-limit/validation-result.js +144 -0
- package/src/validation/line-limit-core.js +225 -0
- package/src/validation/line-limit-reporter.js +134 -0
- package/src/validation/line-limit-result.js +125 -0
- package/src/validation/line-limit-validator.js +41 -0
- package/src/validation/metrics-calculator.js +660 -0
- package/src/sync/sync-engine-backup.js +0 -559
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recommendation Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates refactoring recommendations based on analysis results.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Recommendation Generator class
|
|
9
|
+
*/
|
|
10
|
+
class RecommendationGenerator {
|
|
11
|
+
constructor(report) {
|
|
12
|
+
this.report = report;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
generateAllRecommendations() {
|
|
16
|
+
const recommendations = [];
|
|
17
|
+
|
|
18
|
+
// File-level recommendations
|
|
19
|
+
recommendations.push(...this.generateFileRecommendations());
|
|
20
|
+
|
|
21
|
+
// Package-level recommendations
|
|
22
|
+
recommendations.push(...this.generatePackageRecommendations());
|
|
23
|
+
|
|
24
|
+
// Strategic recommendations
|
|
25
|
+
recommendations.push(...this.generateStrategicRecommendations());
|
|
26
|
+
|
|
27
|
+
// Priority-based recommendations
|
|
28
|
+
recommendations.push(...this.generatePriorityRecommendations());
|
|
29
|
+
|
|
30
|
+
return recommendations;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
generateFileRecommendations() {
|
|
34
|
+
const recommendations = [];
|
|
35
|
+
const violatingFiles = this.report.fileAnalysis.filter(f => f.needsRefactoring);
|
|
36
|
+
|
|
37
|
+
// Group files by size category
|
|
38
|
+
const sizeGroups = this.groupFilesBySize(violatingFiles);
|
|
39
|
+
|
|
40
|
+
// Recommendations for extra-large files
|
|
41
|
+
if (sizeGroups.xlarge.length > 0) {
|
|
42
|
+
recommendations.push({
|
|
43
|
+
type: 'urgent',
|
|
44
|
+
category: 'file_size',
|
|
45
|
+
title: `Refactor ${sizeGroups.xlarge.length} extra-large files immediately`,
|
|
46
|
+
description: 'Files over 1000 lines should be split into smaller, focused modules',
|
|
47
|
+
affectedFiles: sizeGroups.xlarge.map(f => f.filePath),
|
|
48
|
+
estimatedEffort: this.calculateEffortForFiles(sizeGroups.xlarge),
|
|
49
|
+
priority: 'urgent'
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Recommendations for large files
|
|
54
|
+
if (sizeGroups.large.length > 0) {
|
|
55
|
+
recommendations.push({
|
|
56
|
+
type: 'high',
|
|
57
|
+
category: 'file_size',
|
|
58
|
+
title: `Refactor ${sizeGroups.large.length} large files`,
|
|
59
|
+
description: 'Files between 556-1000 lines should be refactored to improve maintainability',
|
|
60
|
+
affectedFiles: sizeGroups.large.map(f => f.filePath),
|
|
61
|
+
estimatedEffort: this.calculateEffortForFiles(sizeGroups.large),
|
|
62
|
+
priority: 'high'
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Complexity-based recommendations
|
|
67
|
+
const highComplexityFiles = violatingFiles.filter(f =>
|
|
68
|
+
f.getComplexityScore && f.getComplexityScore() > 50
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
if (highComplexityFiles.length > 0) {
|
|
72
|
+
recommendations.push({
|
|
73
|
+
type: 'high',
|
|
74
|
+
category: 'complexity',
|
|
75
|
+
title: `Reduce complexity in ${highComplexityFiles.length} files`,
|
|
76
|
+
description: 'High complexity files are difficult to maintain and should be simplified',
|
|
77
|
+
affectedFiles: highComplexityFiles.map(f => f.filePath),
|
|
78
|
+
estimatedEffort: this.calculateEffortForFiles(highComplexityFiles),
|
|
79
|
+
priority: 'high'
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return recommendations;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
generatePackageRecommendations() {
|
|
87
|
+
const recommendations = [];
|
|
88
|
+
const packages = this.report.packageAnalysis;
|
|
89
|
+
|
|
90
|
+
// Find packages with many violations
|
|
91
|
+
const problematicPackages = Object.entries(packages)
|
|
92
|
+
.filter(([name, data]) => data.filesNeedingRefactoring > 0)
|
|
93
|
+
.sort(([,a], [,b]) => b.filesNeedingRefactoring - a.filesNeedingRefactoring);
|
|
94
|
+
|
|
95
|
+
for (const [packageName, packageData] of problematicPackages) {
|
|
96
|
+
const violationRate = (packageData.filesNeedingRefactoring / packageData.files.length * 100).toFixed(1);
|
|
97
|
+
|
|
98
|
+
if (violationRate > 50) {
|
|
99
|
+
recommendations.push({
|
|
100
|
+
type: 'high',
|
|
101
|
+
category: 'package_health',
|
|
102
|
+
title: `Comprehensive refactoring needed for ${packageName} package`,
|
|
103
|
+
description: `${violationRate}% of files in this package violate size limits`,
|
|
104
|
+
affectedPackage: packageName,
|
|
105
|
+
violationRate: parseFloat(violationRate),
|
|
106
|
+
estimatedEffort: packageData.filesNeedingRefactoring * 3, // 3 hours per file average
|
|
107
|
+
priority: 'high'
|
|
108
|
+
});
|
|
109
|
+
} else if (violationRate > 25) {
|
|
110
|
+
recommendations.push({
|
|
111
|
+
type: 'medium',
|
|
112
|
+
category: 'package_health',
|
|
113
|
+
title: `Partial refactoring recommended for ${packageName} package`,
|
|
114
|
+
description: `${violationRate}% of files in this package violate size limits`,
|
|
115
|
+
affectedPackage: packageName,
|
|
116
|
+
violationRate: parseFloat(violationRate),
|
|
117
|
+
estimatedEffort: packageData.filesNeedingRefactoring * 2, // 2 hours per file average
|
|
118
|
+
priority: 'medium'
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return recommendations;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
generateStrategicRecommendations() {
|
|
127
|
+
const recommendations = [];
|
|
128
|
+
const complianceRate = parseFloat(this.report.summary.complianceRate);
|
|
129
|
+
|
|
130
|
+
// Overall compliance strategy
|
|
131
|
+
if (complianceRate < 70) {
|
|
132
|
+
recommendations.push({
|
|
133
|
+
type: 'strategic',
|
|
134
|
+
category: 'compliance_strategy',
|
|
135
|
+
title: 'Implement comprehensive refactoring initiative',
|
|
136
|
+
description: 'With less than 70% compliance, a systematic approach is needed',
|
|
137
|
+
strategy: 'comprehensive',
|
|
138
|
+
phases: [
|
|
139
|
+
'Phase 1: Focus on urgent and high-priority files',
|
|
140
|
+
'Phase 2: Address medium-priority violations',
|
|
141
|
+
'Phase 3: Handle remaining low-priority files'
|
|
142
|
+
],
|
|
143
|
+
estimatedTimeline: '8-12 weeks',
|
|
144
|
+
priority: 'urgent'
|
|
145
|
+
});
|
|
146
|
+
} else if (complianceRate < 90) {
|
|
147
|
+
recommendations.push({
|
|
148
|
+
type: 'strategic',
|
|
149
|
+
category: 'compliance_strategy',
|
|
150
|
+
title: 'Targeted refactoring initiative',
|
|
151
|
+
description: 'With good compliance baseline, focus on remaining violations',
|
|
152
|
+
strategy: 'targeted',
|
|
153
|
+
phases: [
|
|
154
|
+
'Phase 1: Address remaining high-priority files',
|
|
155
|
+
'Phase 2: Clean up medium and low priority files'
|
|
156
|
+
],
|
|
157
|
+
estimatedTimeline: '4-6 weeks',
|
|
158
|
+
priority: 'high'
|
|
159
|
+
});
|
|
160
|
+
} else {
|
|
161
|
+
recommendations.push({
|
|
162
|
+
type: 'strategic',
|
|
163
|
+
category: 'compliance_strategy',
|
|
164
|
+
title: 'Final compliance push',
|
|
165
|
+
description: 'Near-complete compliance, focus on remaining edge cases',
|
|
166
|
+
strategy: 'cleanup',
|
|
167
|
+
phases: [
|
|
168
|
+
'Phase 1: Address final violations',
|
|
169
|
+
'Phase 2: Implement compliance monitoring'
|
|
170
|
+
],
|
|
171
|
+
estimatedTimeline: '1-2 weeks',
|
|
172
|
+
priority: 'medium'
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Process recommendations
|
|
177
|
+
recommendations.push({
|
|
178
|
+
type: 'process',
|
|
179
|
+
category: 'prevention',
|
|
180
|
+
title: 'Implement size validation in development workflow',
|
|
181
|
+
description: 'Prevent future violations by adding size checks to CI/CD and pre-commit hooks',
|
|
182
|
+
implementation: [
|
|
183
|
+
'Add pre-commit hook for file size validation',
|
|
184
|
+
'Integrate size checks into CI pipeline',
|
|
185
|
+
'Set up automated monitoring for new files',
|
|
186
|
+
'Establish size limits in code review guidelines'
|
|
187
|
+
],
|
|
188
|
+
priority: 'medium'
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
return recommendations;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
generatePriorityRecommendations() {
|
|
195
|
+
const recommendations = [];
|
|
196
|
+
const violatingFiles = this.report.fileAnalysis.filter(f => f.needsRefactoring);
|
|
197
|
+
|
|
198
|
+
// Urgent priority files
|
|
199
|
+
const urgentFiles = violatingFiles.filter(f => f.priority === 'urgent');
|
|
200
|
+
if (urgentFiles.length > 0) {
|
|
201
|
+
recommendations.push({
|
|
202
|
+
type: 'priority_action',
|
|
203
|
+
category: 'immediate',
|
|
204
|
+
title: `Immediate action required for ${urgentFiles.length} urgent files`,
|
|
205
|
+
description: 'These files pose the highest risk and should be addressed first',
|
|
206
|
+
files: urgentFiles.map(f => ({
|
|
207
|
+
path: f.filePath,
|
|
208
|
+
lines: f.lineCount,
|
|
209
|
+
complexity: f.getComplexityScore ? f.getComplexityScore() : 0,
|
|
210
|
+
reason: this.getUrgentReason(f)
|
|
211
|
+
})),
|
|
212
|
+
suggestedOrder: 'largest_first',
|
|
213
|
+
priority: 'urgent'
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// High priority files
|
|
218
|
+
const highPriorityFiles = violatingFiles.filter(f => f.priority === 'high');
|
|
219
|
+
if (highPriorityFiles.length > 0) {
|
|
220
|
+
recommendations.push({
|
|
221
|
+
type: 'priority_action',
|
|
222
|
+
category: 'short_term',
|
|
223
|
+
title: `Address ${highPriorityFiles.length} high-priority files next`,
|
|
224
|
+
description: 'These files should be addressed after urgent issues are resolved',
|
|
225
|
+
files: highPriorityFiles.map(f => ({
|
|
226
|
+
path: f.filePath,
|
|
227
|
+
lines: f.lineCount,
|
|
228
|
+
complexity: f.getComplexityScore ? f.getComplexityScore() : 0,
|
|
229
|
+
reason: this.getHighPriorityReason(f)
|
|
230
|
+
})),
|
|
231
|
+
suggestedOrder: 'complexity_first',
|
|
232
|
+
priority: 'high'
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return recommendations;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Helper methods
|
|
240
|
+
groupFilesBySize(files) {
|
|
241
|
+
return {
|
|
242
|
+
small: files.filter(f => f.lineCount < 100),
|
|
243
|
+
medium: files.filter(f => f.lineCount >= 100 && f.lineCount <= 300),
|
|
244
|
+
large: files.filter(f => f.lineCount >= 301 && f.lineCount <= 1000),
|
|
245
|
+
xlarge: files.filter(f => f.lineCount > 1000)
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
calculateEffortForFiles(files) {
|
|
250
|
+
let totalEffort = 0;
|
|
251
|
+
for (const file of files) {
|
|
252
|
+
// Base effort: 1 hour per 100 lines
|
|
253
|
+
const baseEffort = Math.ceil(file.lineCount / 100);
|
|
254
|
+
|
|
255
|
+
// Complexity multiplier
|
|
256
|
+
const complexity = file.getComplexityScore ? file.getComplexityScore() : 0;
|
|
257
|
+
const complexityMultiplier = complexity > 50 ? 2 : (complexity > 20 ? 1.5 : 1);
|
|
258
|
+
|
|
259
|
+
totalEffort += baseEffort * complexityMultiplier;
|
|
260
|
+
}
|
|
261
|
+
return Math.ceil(totalEffort);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
getUrgentReason(file) {
|
|
265
|
+
if (file.lineCount > 2000) return 'Extremely large file - immediate splitting required';
|
|
266
|
+
if (file.lineCount > 1000) return 'Very large file - high maintenance burden';
|
|
267
|
+
const complexity = file.getComplexityScore ? file.getComplexityScore() : 0;
|
|
268
|
+
if (complexity > 100) return 'Critical complexity - unmanageable code';
|
|
269
|
+
if (complexity > 50) return 'High complexity - difficult to maintain';
|
|
270
|
+
return 'Size and complexity thresholds exceeded';
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
getHighPriorityReason(file) {
|
|
274
|
+
if (file.lineCount > 800) return 'Large file - should be split soon';
|
|
275
|
+
if (file.lineCount > 600) return 'Approaching size limit - plan refactoring';
|
|
276
|
+
const complexity = file.getComplexityScore ? file.getComplexityScore() : 0;
|
|
277
|
+
if (complexity > 30) return 'Moderate-high complexity - simplification needed';
|
|
278
|
+
if (complexity > 20) return 'Moderate complexity - consider refactoring';
|
|
279
|
+
return 'Size limit exceeded - standard refactoring needed';
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
module.exports = RecommendationGenerator;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Report Generators
|
|
3
|
+
*
|
|
4
|
+
* Handles generation of different report formats (JSON, HTML, CSV).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { ReportGenerator } = require('../../utils/report-generator');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* JSON Report Generator
|
|
13
|
+
*/
|
|
14
|
+
class JSONReportGenerator {
|
|
15
|
+
constructor(report) {
|
|
16
|
+
this.report = report;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
generate(outputPath) {
|
|
20
|
+
const jsonContent = JSON.stringify(this.report.toJSON(), null, 2);
|
|
21
|
+
|
|
22
|
+
if (outputPath) {
|
|
23
|
+
fs.writeFileSync(outputPath, jsonContent, 'utf8');
|
|
24
|
+
return { success: true, path: outputPath, format: 'json' };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return { success: true, content: jsonContent, format: 'json' };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* HTML Report Generator
|
|
33
|
+
*/
|
|
34
|
+
class HTMLReportGenerator {
|
|
35
|
+
constructor(report) {
|
|
36
|
+
this.report = report;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
generate(outputPath) {
|
|
40
|
+
const html = this.generateHTMLContent();
|
|
41
|
+
|
|
42
|
+
if (outputPath) {
|
|
43
|
+
fs.writeFileSync(outputPath, html, 'utf8');
|
|
44
|
+
return { success: true, path: outputPath, format: 'html' };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return { success: true, content: html, format: 'html' };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
generateHTMLContent() {
|
|
51
|
+
const report = this.report;
|
|
52
|
+
|
|
53
|
+
return `
|
|
54
|
+
<!DOCTYPE html>
|
|
55
|
+
<html>
|
|
56
|
+
<head>
|
|
57
|
+
<title>File Size Analysis Report</title>
|
|
58
|
+
<style>
|
|
59
|
+
body { font-family: Arial, sans-serif; margin: 20px; }
|
|
60
|
+
.summary { background: #f5f5f5; padding: 15px; border-radius: 5px; margin-bottom: 20px; }
|
|
61
|
+
.metric { display: inline-block; margin: 10px; padding: 10px; background: white; border-radius: 3px; }
|
|
62
|
+
.file-item { margin: 5px 0; padding: 8px; border-left: 3px solid #ccc; }
|
|
63
|
+
.urgent { border-left-color: #d32f2f; }
|
|
64
|
+
.high { border-left-color: #f57c00; }
|
|
65
|
+
.medium { border-left-color: #fbc02d; }
|
|
66
|
+
.low { border-left-color: #388e3c; }
|
|
67
|
+
table { width: 100%; border-collapse: collapse; margin: 10px 0; }
|
|
68
|
+
th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }
|
|
69
|
+
th { background-color: #f2f2f2; }
|
|
70
|
+
</style>
|
|
71
|
+
</head>
|
|
72
|
+
<body>
|
|
73
|
+
<h1>File Size Analysis Report</h1>
|
|
74
|
+
|
|
75
|
+
<div class="summary">
|
|
76
|
+
<h2>Summary</h2>
|
|
77
|
+
<div class="metric">Total Files: ${report.summary.totalFiles}</div>
|
|
78
|
+
<div class="metric">Total Lines: ${report.summary.totalLines.toLocaleString()}</div>
|
|
79
|
+
<div class="metric">Files Needing Refactoring: ${report.summary.filesNeedingRefactoring}</div>
|
|
80
|
+
<div class="metric">Compliance Rate: ${report.summary.complianceRate}%</div>
|
|
81
|
+
<div class="metric">Average Complexity: ${report.summary.averageComplexity}</div>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<h2>Files Requiring Refactoring</h2>
|
|
85
|
+
<table>
|
|
86
|
+
<thead>
|
|
87
|
+
<tr>
|
|
88
|
+
<th>File</th>
|
|
89
|
+
<th>Lines</th>
|
|
90
|
+
<th>Priority</th>
|
|
91
|
+
<th>Complexity</th>
|
|
92
|
+
<th>Recommendation</th>
|
|
93
|
+
</tr>
|
|
94
|
+
</thead>
|
|
95
|
+
<tbody>
|
|
96
|
+
${report.fileAnalysis
|
|
97
|
+
.filter(f => f.needsRefactoring)
|
|
98
|
+
.map(f => `
|
|
99
|
+
<tr class="file-item ${f.priority}">
|
|
100
|
+
<td>${f.filePath}</td>
|
|
101
|
+
<td>${f.lineCount}</td>
|
|
102
|
+
<td>${f.priority.toUpperCase()}</td>
|
|
103
|
+
<td>${f.getComplexityScore ? f.getComplexityScore() : 'N/A'}</td>
|
|
104
|
+
<td>${f.recommendation || 'See detailed analysis'}</td>
|
|
105
|
+
</tr>
|
|
106
|
+
`).join('')}
|
|
107
|
+
</tbody>
|
|
108
|
+
</table>
|
|
109
|
+
|
|
110
|
+
<h2>Package Analysis</h2>
|
|
111
|
+
<table>
|
|
112
|
+
<thead>
|
|
113
|
+
<tr>
|
|
114
|
+
<th>Package</th>
|
|
115
|
+
<th>Files</th>
|
|
116
|
+
<th>Total Lines</th>
|
|
117
|
+
<th>Files Needing Refactoring</th>
|
|
118
|
+
<th>Priority</th>
|
|
119
|
+
</tr>
|
|
120
|
+
</thead>
|
|
121
|
+
<tbody>
|
|
122
|
+
${Object.entries(report.packageAnalysis)
|
|
123
|
+
.map(([name, data]) => `
|
|
124
|
+
<tr>
|
|
125
|
+
<td>${name}</td>
|
|
126
|
+
<td>${data.files.length}</td>
|
|
127
|
+
<td>${data.totalLines.toLocaleString()}</td>
|
|
128
|
+
<td>${data.filesNeedingRefactoring}</td>
|
|
129
|
+
<td>${data.priority.toUpperCase()}</td>
|
|
130
|
+
</tr>
|
|
131
|
+
`).join('')}
|
|
132
|
+
</tbody>
|
|
133
|
+
</table>
|
|
134
|
+
|
|
135
|
+
<h2>Recommendations</h2>
|
|
136
|
+
<ul>
|
|
137
|
+
${report.recommendations.map(r => `<li>${r}</li>`).join('')}
|
|
138
|
+
</ul>
|
|
139
|
+
|
|
140
|
+
<div style="margin-top: 30px; font-size: 12px; color: #666;">
|
|
141
|
+
Report generated on ${report.metadata.generatedAt}<br>
|
|
142
|
+
Analysis version: ${report.metadata.version}
|
|
143
|
+
</div>
|
|
144
|
+
</body>
|
|
145
|
+
</html>`;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* CSV Report Generator
|
|
151
|
+
*/
|
|
152
|
+
class CSVReportGenerator {
|
|
153
|
+
constructor(report) {
|
|
154
|
+
this.report = report;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
generate(outputPath) {
|
|
158
|
+
const csv = this.generateCSVContent();
|
|
159
|
+
|
|
160
|
+
if (outputPath) {
|
|
161
|
+
fs.writeFileSync(outputPath, csv, 'utf8');
|
|
162
|
+
return { success: true, path: outputPath, format: 'csv' };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return { success: true, content: csv, format: 'csv' };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
generateCSVContent() {
|
|
169
|
+
const headers = ['File Path', 'Lines', 'Priority', 'Complexity', 'Needs Refactoring', 'Recommendation'];
|
|
170
|
+
const rows = this.report.fileAnalysis.map(f => [
|
|
171
|
+
f.filePath,
|
|
172
|
+
f.lineCount,
|
|
173
|
+
f.priority,
|
|
174
|
+
f.getComplexityScore ? f.getComplexityScore() : '',
|
|
175
|
+
f.needsRefactoring ? 'Yes' : 'No',
|
|
176
|
+
f.recommendation || ''
|
|
177
|
+
]);
|
|
178
|
+
|
|
179
|
+
const csvContent = [headers, ...rows]
|
|
180
|
+
.map(row => row.map(cell => `"${cell}"`).join(','))
|
|
181
|
+
.join('\n');
|
|
182
|
+
|
|
183
|
+
return csvContent;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
module.exports = {
|
|
188
|
+
JSONReportGenerator,
|
|
189
|
+
HTMLReportGenerator,
|
|
190
|
+
CSVReportGenerator
|
|
191
|
+
};
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Statistics Calculator
|
|
3
|
+
*
|
|
4
|
+
* Calculates various statistics and metrics for analysis reports.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Statistics Calculator class
|
|
9
|
+
*/
|
|
10
|
+
class StatisticsCalculator {
|
|
11
|
+
constructor(report) {
|
|
12
|
+
this.report = report;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
calculateAllStatistics() {
|
|
16
|
+
return {
|
|
17
|
+
fileStatistics: this.calculateFileStatistics(),
|
|
18
|
+
packageStatistics: this.calculatePackageStatistics(),
|
|
19
|
+
complexityStatistics: this.calculateComplexityStatistics(),
|
|
20
|
+
sizeStatistics: this.calculateSizeStatistics(),
|
|
21
|
+
complianceStatistics: this.calculateComplianceStatistics()
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
calculateFileStatistics() {
|
|
26
|
+
const files = this.report.fileAnalysis;
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
totalFiles: files.length,
|
|
30
|
+
filesNeedingRefactoring: files.filter(f => f.needsRefactoring).length,
|
|
31
|
+
averageFileSize: this.calculateAverage(files.map(f => f.lineCount)),
|
|
32
|
+
medianFileSize: this.calculateMedian(files.map(f => f.lineCount)),
|
|
33
|
+
largestFile: this.findLargestFile(),
|
|
34
|
+
smallestFile: this.findSmallestFile(),
|
|
35
|
+
sizeDistribution: this.calculateDistribution(files.map(f => f.lineCount))
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
calculatePackageStatistics() {
|
|
40
|
+
const packages = this.report.packageAnalysis;
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
totalPackages: Object.keys(packages).length,
|
|
44
|
+
packagePriorityDistribution: this.calculatePackagePriorityDistribution(),
|
|
45
|
+
largestPackage: this.findLargestPackage(),
|
|
46
|
+
mostComplexPackage: this.findMostComplexPackage(),
|
|
47
|
+
packagesNeedingRefactoring: Object.values(packages).filter(p => p.filesNeedingRefactoring > 0).length
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
calculateComplexityStatistics() {
|
|
52
|
+
const complexities = this.report.fileAnalysis
|
|
53
|
+
.map(f => f.getComplexityScore ? f.getComplexityScore() : 0)
|
|
54
|
+
.filter(c => c > 0);
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
averageComplexity: this.calculateAverage(complexities),
|
|
58
|
+
medianComplexity: this.calculateMedian(complexities),
|
|
59
|
+
highestComplexity: Math.max(...complexities, 0),
|
|
60
|
+
lowestComplexity: Math.min(...complexities, 0),
|
|
61
|
+
complexityDistribution: this.calculateDistribution(complexities)
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
calculateSizeStatistics() {
|
|
66
|
+
const totalLines = this.report.summary.totalLines;
|
|
67
|
+
const filesNeedingRefactoring = this.report.summary.filesNeedingRefactoring;
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
totalLinesOfCode: totalLines,
|
|
71
|
+
linesInViolatingFiles: this.calculateLinesInViolatingFiles(),
|
|
72
|
+
refactoringEffort: this.calculateTotalEffort(),
|
|
73
|
+
potentialLineReduction: this.calculatePotentialLineReduction()
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
calculateComplianceStatistics() {
|
|
78
|
+
const totalFiles = this.report.summary.totalFiles;
|
|
79
|
+
const compliantFiles = totalFiles - this.report.summary.filesNeedingRefactoring;
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
complianceRate: this.report.summary.complianceRate,
|
|
83
|
+
compliantFiles,
|
|
84
|
+
nonCompliantFiles: this.report.summary.filesNeedingRefactoring,
|
|
85
|
+
urgentFiles: this.report.summary.urgentRefactoring,
|
|
86
|
+
highPriorityFiles: this.report.fileAnalysis.filter(f => f.priority === 'high').length,
|
|
87
|
+
mediumPriorityFiles: this.report.fileAnalysis.filter(f => f.priority === 'medium').length,
|
|
88
|
+
lowPriorityFiles: this.report.fileAnalysis.filter(f => f.priority === 'low').length
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Helper methods
|
|
93
|
+
calculateAverage(values) {
|
|
94
|
+
if (values.length === 0) return 0;
|
|
95
|
+
return values.reduce((sum, val) => sum + val, 0) / values.length;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
calculateMedian(values) {
|
|
99
|
+
if (values.length === 0) return 0;
|
|
100
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
101
|
+
const mid = Math.floor(sorted.length / 2);
|
|
102
|
+
return sorted.length % 2 === 0 ?
|
|
103
|
+
(sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
calculateDistribution(values) {
|
|
107
|
+
const distribution = { '0-5': 0, '6-10': 0, '11-20': 0, '21+': 0 };
|
|
108
|
+
for (const value of values) {
|
|
109
|
+
if (value <= 5) distribution['0-5']++;
|
|
110
|
+
else if (value <= 10) distribution['6-10']++;
|
|
111
|
+
else if (value <= 20) distribution['11-20']++;
|
|
112
|
+
else distribution['21+']++;
|
|
113
|
+
}
|
|
114
|
+
return distribution;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
findLargestFile() {
|
|
118
|
+
let largest = null;
|
|
119
|
+
let maxLines = 0;
|
|
120
|
+
|
|
121
|
+
for (const fileAnalysis of this.report.fileAnalysis) {
|
|
122
|
+
if (fileAnalysis.lineCount > maxLines) {
|
|
123
|
+
maxLines = fileAnalysis.lineCount;
|
|
124
|
+
largest = {
|
|
125
|
+
path: fileAnalysis.filePath,
|
|
126
|
+
lines: fileAnalysis.lineCount,
|
|
127
|
+
priority: fileAnalysis.priority
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return largest;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
findSmallestFile() {
|
|
136
|
+
let smallest = null;
|
|
137
|
+
let minLines = Infinity;
|
|
138
|
+
|
|
139
|
+
for (const fileAnalysis of this.report.fileAnalysis) {
|
|
140
|
+
if (fileAnalysis.lineCount < minLines) {
|
|
141
|
+
minLines = fileAnalysis.lineCount;
|
|
142
|
+
smallest = {
|
|
143
|
+
path: fileAnalysis.filePath,
|
|
144
|
+
lines: fileAnalysis.lineCount,
|
|
145
|
+
priority: fileAnalysis.priority
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return smallest;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
calculatePackagePriorityDistribution() {
|
|
154
|
+
const distribution = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
155
|
+
for (const packageData of Object.values(this.report.packageAnalysis)) {
|
|
156
|
+
distribution[packageData.priority]++;
|
|
157
|
+
}
|
|
158
|
+
return distribution;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
findLargestPackage() {
|
|
162
|
+
let largest = null;
|
|
163
|
+
let maxLines = 0;
|
|
164
|
+
|
|
165
|
+
for (const [packageName, packageData] of Object.entries(this.report.packageAnalysis)) {
|
|
166
|
+
if (packageData.totalLines > maxLines) {
|
|
167
|
+
maxLines = packageData.totalLines;
|
|
168
|
+
largest = {
|
|
169
|
+
name: packageName,
|
|
170
|
+
lines: packageData.totalLines,
|
|
171
|
+
files: packageData.files.length
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return largest;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
findMostComplexPackage() {
|
|
180
|
+
let mostComplex = null;
|
|
181
|
+
let maxComplexity = 0;
|
|
182
|
+
|
|
183
|
+
for (const [packageName, packageData] of Object.entries(this.report.packageAnalysis)) {
|
|
184
|
+
if (packageData.averageComplexity > maxComplexity) {
|
|
185
|
+
maxComplexity = packageData.averageComplexity;
|
|
186
|
+
mostComplex = {
|
|
187
|
+
name: packageName,
|
|
188
|
+
complexity: packageData.averageComplexity
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return mostComplex;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
calculateLinesInViolatingFiles() {
|
|
197
|
+
return this.report.fileAnalysis
|
|
198
|
+
.filter(f => f.needsRefactoring)
|
|
199
|
+
.reduce((sum, f) => sum + f.lineCount, 0);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
calculateTotalEffort() {
|
|
203
|
+
let totalEffort = 0;
|
|
204
|
+
for (const fileAnalysis of this.report.fileAnalysis) {
|
|
205
|
+
if (fileAnalysis.needsRefactoring) {
|
|
206
|
+
// Simple effort calculation based on line count and complexity
|
|
207
|
+
const lineEffort = Math.ceil(fileAnalysis.lineCount / 100);
|
|
208
|
+
const complexity = fileAnalysis.getComplexityScore ? fileAnalysis.getComplexityScore() : 0;
|
|
209
|
+
const complexityEffort = Math.ceil(complexity / 20);
|
|
210
|
+
totalEffort += lineEffort + complexityEffort;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return totalEffort;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
calculatePotentialLineReduction() {
|
|
217
|
+
// Estimate potential line reduction from refactoring
|
|
218
|
+
const violatingFiles = this.report.fileAnalysis.filter(f => f.needsRefactoring);
|
|
219
|
+
let potentialReduction = 0;
|
|
220
|
+
|
|
221
|
+
for (const file of violatingFiles) {
|
|
222
|
+
// Assume 10-30% reduction based on file size and complexity
|
|
223
|
+
const reductionRate = file.lineCount > 1000 ? 0.3 : (file.lineCount > 500 ? 0.2 : 0.1);
|
|
224
|
+
potentialReduction += Math.floor(file.lineCount * reductionRate);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return potentialReduction;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
module.exports = StatisticsCalculator;
|