@sun-asterisk/sunlint 1.3.17 → 1.3.18
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/core/analysis-orchestrator.js +11 -3
- package/core/output-service.js +27 -9
- package/core/summary-report-service.js +21 -3
- package/package.json +1 -1
- package/rules/common/C047_no_duplicate_retry_logic/analyzer.js +96 -40
- package/rules/common/C047_no_duplicate_retry_logic/symbol-analyzer-enhanced.js +17 -2
- package/docs/CONSTANTS-ARCHITECTURE.md +0 -288
- package/docs/DEPLOYMENT-STRATEGIES.md +0 -270
- package/docs/ESLINT_INTEGRATION.md +0 -238
- package/docs/PERFORMANCE_MIGRATION_GUIDE.md +0 -368
- package/docs/PERFORMANCE_OPTIMIZATION_PLAN.md +0 -255
|
@@ -547,9 +547,17 @@ class AnalysisOrchestrator {
|
|
|
547
547
|
for (const engineResult of engineResults) {
|
|
548
548
|
uniqueEngines.add(engineResult.engine);
|
|
549
549
|
|
|
550
|
-
// Add engine-specific results
|
|
551
|
-
if (engineResult.results) {
|
|
552
|
-
|
|
550
|
+
// Add engine-specific results with validation
|
|
551
|
+
if (engineResult.results && Array.isArray(engineResult.results)) {
|
|
552
|
+
// Filter out invalid entries (non-objects or config objects)
|
|
553
|
+
const validResults = engineResult.results.filter(result => {
|
|
554
|
+
if (!result || typeof result !== 'object') return false;
|
|
555
|
+
// Skip objects that look like metadata/config
|
|
556
|
+
if (result.semanticEngine || result.project || result._context) return false;
|
|
557
|
+
// Must have either file/filePath or be a valid result object
|
|
558
|
+
return result.file || result.filePath || result.violations || result.messages;
|
|
559
|
+
});
|
|
560
|
+
mergedResults.results.push(...validResults);
|
|
553
561
|
}
|
|
554
562
|
|
|
555
563
|
// Track engine statistics
|
package/core/output-service.js
CHANGED
|
@@ -28,9 +28,9 @@ class OutputService {
|
|
|
28
28
|
try {
|
|
29
29
|
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
30
30
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
31
|
-
return packageJson.version || '1.3.
|
|
31
|
+
return packageJson.version || '1.3.18';
|
|
32
32
|
} catch (error) {
|
|
33
|
-
return '1.3.
|
|
33
|
+
return '1.3.18'; // Fallback version
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -140,6 +140,17 @@ class OutputService {
|
|
|
140
140
|
const allViolations = [];
|
|
141
141
|
let totalFiles = results.filesAnalyzed || results.summary?.totalFiles || results.totalFiles || results.fileCount || 0;
|
|
142
142
|
|
|
143
|
+
// Helper function to validate violation object
|
|
144
|
+
const isValidViolation = (violation) => {
|
|
145
|
+
if (!violation || typeof violation !== 'object') return false;
|
|
146
|
+
// Skip config/metadata objects (have nested objects like semanticEngine, project, etc.)
|
|
147
|
+
if (violation.semanticEngine || violation.project || violation._context) return false;
|
|
148
|
+
// Must have ruleId as string
|
|
149
|
+
const ruleId = violation.ruleId || violation.rule;
|
|
150
|
+
if (!ruleId || typeof ruleId !== 'string') return false;
|
|
151
|
+
return true;
|
|
152
|
+
};
|
|
153
|
+
|
|
143
154
|
// Collect all violations - handle both file-based and rule-based results
|
|
144
155
|
if (results.results) {
|
|
145
156
|
results.results.forEach(result => {
|
|
@@ -147,16 +158,20 @@ class OutputService {
|
|
|
147
158
|
// Handle rule-based format (MultiRuleRunner)
|
|
148
159
|
if (result.ruleId) {
|
|
149
160
|
result.violations.forEach(violation => {
|
|
150
|
-
|
|
161
|
+
if (isValidViolation(violation)) {
|
|
162
|
+
allViolations.push(violation); // violation already has file path
|
|
163
|
+
}
|
|
151
164
|
});
|
|
152
165
|
}
|
|
153
166
|
// Handle file-based format (legacy)
|
|
154
167
|
else {
|
|
155
168
|
result.violations.forEach(violation => {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
169
|
+
if (isValidViolation(violation)) {
|
|
170
|
+
allViolations.push({
|
|
171
|
+
...violation,
|
|
172
|
+
file: result.filePath || result.file // Use filePath first, then file
|
|
173
|
+
});
|
|
174
|
+
}
|
|
160
175
|
});
|
|
161
176
|
}
|
|
162
177
|
}
|
|
@@ -164,7 +179,7 @@ class OutputService {
|
|
|
164
179
|
// Handle ESLint format (messages array)
|
|
165
180
|
if (result.messages) {
|
|
166
181
|
result.messages.forEach(message => {
|
|
167
|
-
|
|
182
|
+
const violation = {
|
|
168
183
|
file: result.filePath || message.file,
|
|
169
184
|
ruleId: message.ruleId,
|
|
170
185
|
severity: message.severity === 2 ? 'error' : 'warning',
|
|
@@ -172,7 +187,10 @@ class OutputService {
|
|
|
172
187
|
line: message.line,
|
|
173
188
|
column: message.column,
|
|
174
189
|
source: message.source || 'eslint'
|
|
175
|
-
}
|
|
190
|
+
};
|
|
191
|
+
if (isValidViolation(violation)) {
|
|
192
|
+
allViolations.push(violation);
|
|
193
|
+
}
|
|
176
194
|
});
|
|
177
195
|
}
|
|
178
196
|
});
|
|
@@ -23,9 +23,9 @@ class SummaryReportService {
|
|
|
23
23
|
try {
|
|
24
24
|
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
25
25
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
26
|
-
return packageJson.version || '1.3.
|
|
26
|
+
return packageJson.version || '1.3.18';
|
|
27
27
|
} catch (error) {
|
|
28
|
-
return '1.3.
|
|
28
|
+
return '1.3.18'; // Fallback version
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -198,7 +198,25 @@ class SummaryReportService {
|
|
|
198
198
|
// Count violations by rule
|
|
199
199
|
const violationsByRule = {};
|
|
200
200
|
violations.forEach(violation => {
|
|
201
|
-
|
|
201
|
+
// Validate that this is actually a violation object (not metadata/config)
|
|
202
|
+
// A valid violation should have ruleId/rule as string and message
|
|
203
|
+
if (!violation || typeof violation !== 'object') {
|
|
204
|
+
return; // Skip non-objects
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Skip objects that look like metadata/config (have nested objects like semanticEngine, project, etc.)
|
|
208
|
+
if (violation.semanticEngine || violation.project || violation.options) {
|
|
209
|
+
return; // Skip config objects
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Get ruleId from various possible fields
|
|
213
|
+
const ruleId = violation.ruleId || violation.rule || 'unknown';
|
|
214
|
+
|
|
215
|
+
// Ensure ruleId is a string (not an object)
|
|
216
|
+
if (typeof ruleId !== 'string') {
|
|
217
|
+
return; // Skip invalid ruleId
|
|
218
|
+
}
|
|
219
|
+
|
|
202
220
|
if (!violationsByRule[ruleId]) {
|
|
203
221
|
violationsByRule[ruleId] = {
|
|
204
222
|
rule_code: ruleId,
|
package/package.json
CHANGED
|
@@ -47,7 +47,9 @@ class C047Analyzer {
|
|
|
47
47
|
|
|
48
48
|
async analyze(files, language, options = {}) {
|
|
49
49
|
const violations = [];
|
|
50
|
+
this.retryPatterns = []; // ✅ Reset once for entire analysis, not per file
|
|
50
51
|
|
|
52
|
+
// Phase 1: Collect all retry patterns from all files
|
|
51
53
|
for (const filePath of files) {
|
|
52
54
|
if (options.verbose) {
|
|
53
55
|
console.log(`🔍 Running C047 analysis on ${require('path').basename(filePath)}`);
|
|
@@ -55,41 +57,65 @@ class C047Analyzer {
|
|
|
55
57
|
|
|
56
58
|
try {
|
|
57
59
|
const content = require('fs').readFileSync(filePath, 'utf8');
|
|
58
|
-
this.
|
|
59
|
-
const fileViolations = this.analyzeFile(content, filePath);
|
|
60
|
-
violations.push(...fileViolations);
|
|
60
|
+
this.collectRetryPatterns(content, filePath);
|
|
61
61
|
} catch (error) {
|
|
62
62
|
console.warn(`⚠️ Failed to analyze ${filePath}: ${error.message}`);
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
// ✅ Safety check: If no valid retry patterns found, return empty (no violations)
|
|
67
|
+
if (this.retryPatterns.length === 0) {
|
|
68
|
+
if (options.verbose) {
|
|
69
|
+
console.log('🔍 [C047] No retry patterns found - skipping');
|
|
70
|
+
}
|
|
71
|
+
return violations;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (options.verbose) {
|
|
75
|
+
console.log(`🔍 [C047] Found ${this.retryPatterns.length} retry patterns:`);
|
|
76
|
+
this.retryPatterns.forEach((p, i) => {
|
|
77
|
+
console.log(` [${i}] ${p.filePath}:${p.line} - type: ${p.type}`);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Phase 2: Detect duplicates across all collected patterns
|
|
82
|
+
const duplicateGroups = this.enhancedDuplicateDetection();
|
|
83
|
+
|
|
84
|
+
// ✅ Safety check: If no duplicate groups found, return empty (no violations)
|
|
85
|
+
if (duplicateGroups.length === 0) {
|
|
86
|
+
return violations;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Phase 3: Generate violations for each file
|
|
90
|
+
for (const filePath of files) {
|
|
91
|
+
try {
|
|
92
|
+
const fileViolations = this.generateViolationsForFile(duplicateGroups, filePath);
|
|
93
|
+
violations.push(...fileViolations);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.warn(`⚠️ Failed to generate violations for ${filePath}: ${error.message}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
66
99
|
return violations;
|
|
67
100
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const violations = [];
|
|
101
|
+
|
|
102
|
+
collectRetryPatterns(content, filePath) {
|
|
71
103
|
const lines = content.split('\n');
|
|
72
|
-
|
|
73
|
-
// Find all retry patterns in the file
|
|
74
104
|
this.findRetryPatterns(lines, filePath);
|
|
75
105
|
|
|
76
106
|
// Add architectural context to patterns
|
|
77
107
|
this.retryPatterns.forEach(pattern => {
|
|
78
|
-
if (!pattern.context) {
|
|
79
|
-
const
|
|
108
|
+
if (!pattern.context && pattern.filePath === filePath) {
|
|
109
|
+
const lineIndex = pattern.line - 1;
|
|
110
|
+
const contextLines = lines.slice(Math.max(0, lineIndex - 10), lineIndex + 10);
|
|
80
111
|
const contextContent = contextLines.join('\n');
|
|
81
112
|
pattern.context = this.analyzeArchitecturalContext(filePath, contextContent);
|
|
82
113
|
}
|
|
83
114
|
});
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
// Generate violations with architectural context
|
|
89
|
-
const enhancedViolations = this.generateEnhancedViolations(duplicateGroups, filePath);
|
|
90
|
-
violations.push(...enhancedViolations);
|
|
91
|
-
|
|
92
|
-
return violations;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
generateViolationsForFile(duplicateGroups, filePath) {
|
|
118
|
+
return this.generateEnhancedViolations(duplicateGroups, filePath);
|
|
93
119
|
}
|
|
94
120
|
|
|
95
121
|
findRetryPatterns(lines, filePath) {
|
|
@@ -114,7 +140,8 @@ class C047Analyzer {
|
|
|
114
140
|
this.retryPatterns.push({
|
|
115
141
|
...pattern,
|
|
116
142
|
line: i + 1,
|
|
117
|
-
column: line.indexOf(line.trim()) + 1
|
|
143
|
+
column: line.indexOf(line.trim()) + 1,
|
|
144
|
+
filePath: filePath // ✅ Store file path with pattern
|
|
118
145
|
});
|
|
119
146
|
}
|
|
120
147
|
}
|
|
@@ -132,12 +159,26 @@ class C047Analyzer {
|
|
|
132
159
|
continue;
|
|
133
160
|
}
|
|
134
161
|
|
|
162
|
+
// Skip if it's just a data field assignment (not retry logic)
|
|
163
|
+
// e.g., "retryAttempt: payment.retryCount || 0" in logging context
|
|
164
|
+
if (line.includes(':') && !line.includes('const') && !line.includes('let') && !line.includes('var')) {
|
|
165
|
+
// This is likely an object property, not a variable declaration
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Skip if it's part of error logging object (common false positive)
|
|
170
|
+
if (contextText.includes('logger.error') || contextText.includes('console.error') ||
|
|
171
|
+
contextText.includes('log.error') || line.includes('error:') || line.includes('message:')) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
135
175
|
const pattern = this.extractRetryPattern(lines, i, 'variable');
|
|
136
176
|
if (pattern) {
|
|
137
177
|
this.retryPatterns.push({
|
|
138
178
|
...pattern,
|
|
139
179
|
line: i + 1,
|
|
140
|
-
column: line.indexOf(line.trim()) + 1
|
|
180
|
+
column: line.indexOf(line.trim()) + 1,
|
|
181
|
+
filePath: filePath // ✅ Store file path with pattern
|
|
141
182
|
});
|
|
142
183
|
}
|
|
143
184
|
}
|
|
@@ -159,7 +200,8 @@ class C047Analyzer {
|
|
|
159
200
|
this.retryPatterns.push({
|
|
160
201
|
...pattern,
|
|
161
202
|
line: i + 1,
|
|
162
|
-
column: line.indexOf(line.trim()) + 1
|
|
203
|
+
column: line.indexOf(line.trim()) + 1,
|
|
204
|
+
filePath: filePath // ✅ Store file path with pattern
|
|
163
205
|
});
|
|
164
206
|
}
|
|
165
207
|
}
|
|
@@ -205,10 +247,20 @@ class C047Analyzer {
|
|
|
205
247
|
return false;
|
|
206
248
|
}
|
|
207
249
|
|
|
250
|
+
// ✅ Skip object properties (data fields) - not variable declarations
|
|
251
|
+
// Pattern: "propertyName: value," or "propertyName: value }"
|
|
252
|
+
const trimmed = line.trim();
|
|
253
|
+
if (/^\w+\s*:\s*.+[,}]?\s*$/.test(trimmed) &&
|
|
254
|
+
!trimmed.startsWith('const') &&
|
|
255
|
+
!trimmed.startsWith('let') &&
|
|
256
|
+
!trimmed.startsWith('var')) {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
|
|
208
260
|
// Check for variable declarations with retry-related names that involve logic
|
|
209
261
|
const declarationPatterns = [
|
|
210
|
-
/(?:const|let|var)\s+.*(?:retry|attempt|tries|maxretries|maxattempts)
|
|
211
|
-
|
|
262
|
+
/(?:const|let|var)\s+.*(?:retry|attempt|tries|maxretries|maxattempts)/
|
|
263
|
+
// ✅ Removed the object property pattern that causes false positives
|
|
212
264
|
];
|
|
213
265
|
|
|
214
266
|
// Only consider it a retry pattern if it has logical complexity
|
|
@@ -560,25 +612,29 @@ class C047Analyzer {
|
|
|
560
612
|
};
|
|
561
613
|
}
|
|
562
614
|
|
|
563
|
-
generateEnhancedViolations(duplicateGroups,
|
|
615
|
+
generateEnhancedViolations(duplicateGroups, currentFilePath) {
|
|
564
616
|
const violations = [];
|
|
565
617
|
|
|
566
618
|
duplicateGroups.forEach(group => {
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
619
|
+
// Create violations for each pattern in the group
|
|
620
|
+
group.patterns.forEach(pattern => {
|
|
621
|
+
// Only create violation for patterns in the current file being analyzed
|
|
622
|
+
if (pattern.filePath === currentFilePath) {
|
|
623
|
+
violations.push({
|
|
624
|
+
file: pattern.filePath || currentFilePath,
|
|
625
|
+
line: pattern.line,
|
|
626
|
+
column: pattern.column || 1,
|
|
627
|
+
message: `${group.legitimacy.reason} (${group.patterns.length} similar patterns found). Consider using a centralized retry utility.`,
|
|
628
|
+
severity: group.legitimacy.severity || 'warning',
|
|
629
|
+
ruleId: this.ruleId,
|
|
630
|
+
type: 'duplicate_retry_logic',
|
|
631
|
+
duplicateCount: group.patterns.length,
|
|
632
|
+
architecturalContext: {
|
|
633
|
+
layers: [...new Set(group.patterns.map(p => p.context?.layer))],
|
|
634
|
+
purposes: [...new Set(group.patterns.map(p => p.context?.purpose))],
|
|
635
|
+
confidence: group.legitimacy.confidence
|
|
636
|
+
}
|
|
637
|
+
});
|
|
582
638
|
}
|
|
583
639
|
});
|
|
584
640
|
});
|
|
@@ -600,6 +600,21 @@ class C047SymbolAnalyzerEnhanced {
|
|
|
600
600
|
if (!catchClause) continue;
|
|
601
601
|
|
|
602
602
|
const catchBlock = catchClause.getBlock();
|
|
603
|
+
const catchText = catchBlock.getText().toLowerCase();
|
|
604
|
+
|
|
605
|
+
// ✅ STRICT VALIDATION: Must have retry indicators (maxRetries, attempts, delay, backoff)
|
|
606
|
+
const hasRetryIndicators = /(?:maxretries|maxattempts|attempt|retry|backoff|delay|timeout)/i.test(catchText);
|
|
607
|
+
if (!hasRetryIndicators) {
|
|
608
|
+
// Skip try-catch without retry logic (e.g., just logging errors)
|
|
609
|
+
continue;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// ✅ Skip logging-only catch blocks (common false positive)
|
|
613
|
+
const isLoggingOnly = /(?:logger\.|console\.|log\.)(?:error|warn|info)/i.test(catchText) &&
|
|
614
|
+
!hasRetryIndicators;
|
|
615
|
+
if (isLoggingOnly) {
|
|
616
|
+
continue;
|
|
617
|
+
}
|
|
603
618
|
|
|
604
619
|
// Look for retry calls in catch block
|
|
605
620
|
const callExpressions = catchBlock.getDescendantsOfKind(require('ts-morph').SyntaxKind.CallExpression);
|
|
@@ -615,8 +630,8 @@ class C047SymbolAnalyzerEnhanced {
|
|
|
615
630
|
);
|
|
616
631
|
}
|
|
617
632
|
|
|
618
|
-
// Pattern 2: Direct API retry
|
|
619
|
-
if (this.isApiCall(callText)) {
|
|
633
|
+
// Pattern 2: Direct API retry (ONLY if has retry indicators)
|
|
634
|
+
if (this.isApiCall(callText) && hasRetryIndicators) {
|
|
620
635
|
return this.createRetryPattern(
|
|
621
636
|
functionName, filePath, 'exception_api_retry',
|
|
622
637
|
func.getStartLineNumber(), 'try-catch with API re-call'
|
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
# SunLint Constants Architecture
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
SunLint now uses a centralized constants sub-package located at `core/constants/` to manage all constants and configuration values. This improves code organization, maintainability, and extensibility.
|
|
6
|
-
|
|
7
|
-
## Structure
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
core/
|
|
11
|
-
constants/
|
|
12
|
-
categories.js # Category-principle mappings and functions
|
|
13
|
-
defaults.js # Default configurations and values
|
|
14
|
-
engines.js # Engine capabilities and configurations
|
|
15
|
-
rules.js # Rule-related constants and utilities
|
|
16
|
-
index.js # Barrel export for all constants
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Migration Guide
|
|
20
|
-
|
|
21
|
-
### Before (Old approach)
|
|
22
|
-
```javascript
|
|
23
|
-
// Scattered constants across multiple files
|
|
24
|
-
const { getValidCategories } = require('./core/category-constants');
|
|
25
|
-
const defaultRules = ['C006', 'C019']; // Hardcoded in various files
|
|
26
|
-
const SUPPORTED_ENGINES = { /* scattered */ };
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
### After (New centralized approach)
|
|
30
|
-
```javascript
|
|
31
|
-
// Option 1: Import specific module
|
|
32
|
-
const { getValidCategories } = require('./core/constants/categories');
|
|
33
|
-
const { getDefaultRuleSet } = require('./core/constants/defaults');
|
|
34
|
-
const { SUPPORTED_ENGINES } = require('./core/constants/engines');
|
|
35
|
-
|
|
36
|
-
// Option 2: Import from barrel export
|
|
37
|
-
const {
|
|
38
|
-
getValidCategories,
|
|
39
|
-
getDefaultRuleSet,
|
|
40
|
-
SUPPORTED_ENGINES
|
|
41
|
-
} = require('./core/constants');
|
|
42
|
-
|
|
43
|
-
// Option 3: Import entire module
|
|
44
|
-
const constants = require('./core/constants');
|
|
45
|
-
const categories = constants.getValidCategories();
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## Modules
|
|
49
|
-
|
|
50
|
-
### 1. Categories (`core/constants/categories.js`)
|
|
51
|
-
|
|
52
|
-
**Purpose**: Category-principle mappings, validation, and normalization.
|
|
53
|
-
|
|
54
|
-
**Key Exports**:
|
|
55
|
-
```javascript
|
|
56
|
-
// Constants
|
|
57
|
-
SUNLINT_PRINCIPLES // Object with all principle constants
|
|
58
|
-
CATEGORY_PRINCIPLE_MAP // Category to principle mapping
|
|
59
|
-
CATEGORY_DESCRIPTIONS // Human-readable descriptions
|
|
60
|
-
|
|
61
|
-
// Functions
|
|
62
|
-
getValidCategories() // Get all valid categories
|
|
63
|
-
getCategoryPrinciples(category) // Get principles for category
|
|
64
|
-
isValidCategory(category) // Validate category
|
|
65
|
-
normalizeCategory(category) // Normalize and validate
|
|
66
|
-
getCategoryStats() // Get statistics
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
**Example Usage**:
|
|
70
|
-
```javascript
|
|
71
|
-
const { getValidCategories, normalizeCategory } = require('./core/constants/categories');
|
|
72
|
-
|
|
73
|
-
const validCategories = getValidCategories();
|
|
74
|
-
// ['security', 'quality', 'performance', ...]
|
|
75
|
-
|
|
76
|
-
const normalized = normalizeCategory('QUALITY');
|
|
77
|
-
// 'quality'
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### 2. Defaults (`core/constants/defaults.js`)
|
|
81
|
-
|
|
82
|
-
**Purpose**: Default configurations, rule sets, and standard values.
|
|
83
|
-
|
|
84
|
-
**Key Exports**:
|
|
85
|
-
```javascript
|
|
86
|
-
// Constants
|
|
87
|
-
DEFAULT_RULE_SETS // Predefined rule sets (MINIMAL, ESSENTIAL, etc.)
|
|
88
|
-
DEFAULT_CONFIG // Default configuration object
|
|
89
|
-
DEFAULT_SEVERITIES // Severity levels
|
|
90
|
-
DEFAULT_TIMEOUTS // Timeout configurations
|
|
91
|
-
DEFAULT_LIMITS // File size and processing limits
|
|
92
|
-
|
|
93
|
-
// Functions
|
|
94
|
-
getDefaultRuleSet(name) // Get predefined rule set
|
|
95
|
-
getDefaultConfig(overrides) // Get configuration with overrides
|
|
96
|
-
getLanguageExtensions(lang) // Get file extensions for language
|
|
97
|
-
isFileSizeValid(size) // Check file size limits
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
**Example Usage**:
|
|
101
|
-
```javascript
|
|
102
|
-
const { getDefaultRuleSet, getDefaultConfig } = require('./core/constants/defaults');
|
|
103
|
-
|
|
104
|
-
const essentialRules = getDefaultRuleSet('ESSENTIAL');
|
|
105
|
-
// ['C001', 'C002', 'C003', ...]
|
|
106
|
-
|
|
107
|
-
const config = getDefaultConfig({ verbose: true });
|
|
108
|
-
// { verbose: true, useRegistry: true, ... }
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### 3. Engines (`core/constants/engines.js`)
|
|
112
|
-
|
|
113
|
-
**Purpose**: Engine capabilities, configurations, and language support.
|
|
114
|
-
|
|
115
|
-
**Key Exports**:
|
|
116
|
-
```javascript
|
|
117
|
-
// Constants
|
|
118
|
-
SUPPORTED_ENGINES // Object with all supported engines
|
|
119
|
-
ENGINE_CAPABILITIES // Engine features and language support
|
|
120
|
-
ENGINE_MODES // Execution modes (sequential, parallel, etc.)
|
|
121
|
-
ENGINE_PERFORMANCE // Performance characteristics
|
|
122
|
-
|
|
123
|
-
// Functions
|
|
124
|
-
getEngineLanguages(engine) // Get supported languages
|
|
125
|
-
getEnginesForLanguage(language) // Get engines for language
|
|
126
|
-
getRecommendedEngine(language) // Get best engine for language
|
|
127
|
-
isLanguageSupported(engine, lang) // Check support
|
|
128
|
-
getEnginePerformance(engine) // Get performance info
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
**Example Usage**:
|
|
132
|
-
```javascript
|
|
133
|
-
const { getEnginesForLanguage, getRecommendedEngine } = require('./core/constants/engines');
|
|
134
|
-
|
|
135
|
-
const jsEngines = getEnginesForLanguage('javascript');
|
|
136
|
-
// [{ name: 'heuristic', priority: 1, features: [...] }, ...]
|
|
137
|
-
|
|
138
|
-
const recommended = getRecommendedEngine('typescript');
|
|
139
|
-
// 'heuristic'
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### 4. Rules (`core/constants/rules.js`)
|
|
143
|
-
|
|
144
|
-
**Purpose**: Rule-related constants, metadata, and utilities.
|
|
145
|
-
|
|
146
|
-
**Key Exports**:
|
|
147
|
-
```javascript
|
|
148
|
-
// Constants
|
|
149
|
-
RULE_SEVERITIES // Severity levels (ERROR, WARNING, etc.)
|
|
150
|
-
RULE_STATUS // Execution status values
|
|
151
|
-
RULE_TYPES // Analysis types (HEURISTIC, AST, etc.)
|
|
152
|
-
RULE_SCOPES // Operation scopes (FILE, PROJECT, etc.)
|
|
153
|
-
RULE_LANGUAGE_PATTERNS // Regex patterns for rule IDs
|
|
154
|
-
RULE_TIMEOUTS // Timeout values by rule type
|
|
155
|
-
|
|
156
|
-
// Functions
|
|
157
|
-
getLanguageFromRuleId(ruleId) // Extract language from rule ID
|
|
158
|
-
isValidRuleId(ruleId) // Validate rule ID format
|
|
159
|
-
getRuleTimeout(type) // Get timeout for rule type
|
|
160
|
-
getDefaultRuleMetadata(overrides) // Get rule metadata template
|
|
161
|
-
isValidSeverity(severity) // Validate severity level
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
**Example Usage**:
|
|
165
|
-
```javascript
|
|
166
|
-
const { getLanguageFromRuleId, isValidRuleId } = require('./core/constants/rules');
|
|
167
|
-
|
|
168
|
-
const language = getLanguageFromRuleId('C001');
|
|
169
|
-
// 'common'
|
|
170
|
-
|
|
171
|
-
const isValid = isValidRuleId('CUSTOM_RULE');
|
|
172
|
-
// true
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
## Backward Compatibility
|
|
176
|
-
|
|
177
|
-
The following files are maintained for backward compatibility but are deprecated:
|
|
178
|
-
|
|
179
|
-
- `core/category-constants.js` - Proxies to `core/constants/categories.js`
|
|
180
|
-
- `core/categories.js` - Proxies to `core/constants/categories.js`
|
|
181
|
-
|
|
182
|
-
**Migration Path**:
|
|
183
|
-
1. Update imports to use `core/constants/*` directly
|
|
184
|
-
2. Replace deprecated file imports gradually
|
|
185
|
-
3. Legacy files will be removed in future versions
|
|
186
|
-
|
|
187
|
-
## Benefits
|
|
188
|
-
|
|
189
|
-
### 1. **Better Organization**
|
|
190
|
-
- Related constants grouped together
|
|
191
|
-
- Clear separation of concerns
|
|
192
|
-
- Easier to locate and modify constants
|
|
193
|
-
|
|
194
|
-
### 2. **Improved Maintainability**
|
|
195
|
-
- Single source of truth for each type of constant
|
|
196
|
-
- Centralized documentation and examples
|
|
197
|
-
- Easier to add new constants or modify existing ones
|
|
198
|
-
|
|
199
|
-
### 3. **Enhanced Extensibility**
|
|
200
|
-
- Modular structure supports new constant types
|
|
201
|
-
- Barrel export provides flexible import options
|
|
202
|
-
- Framework for adding new engines, rules, categories
|
|
203
|
-
|
|
204
|
-
### 4. **Developer Experience**
|
|
205
|
-
- Clearer imports with specific module names
|
|
206
|
-
- Better IDE support and autocomplete
|
|
207
|
-
- Self-documenting code structure
|
|
208
|
-
|
|
209
|
-
## Best Practices
|
|
210
|
-
|
|
211
|
-
### 1. **Import Strategy**
|
|
212
|
-
```javascript
|
|
213
|
-
// ✅ Good: Import specific functions
|
|
214
|
-
const { getValidCategories, normalizeCategory } = require('./core/constants/categories');
|
|
215
|
-
|
|
216
|
-
// ✅ Good: Import from barrel for multiple modules
|
|
217
|
-
const { getValidCategories, getDefaultRuleSet } = require('./core/constants');
|
|
218
|
-
|
|
219
|
-
// ❌ Avoid: Importing entire modules unnecessarily
|
|
220
|
-
const allConstants = require('./core/constants');
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
### 2. **Adding New Constants**
|
|
224
|
-
```javascript
|
|
225
|
-
// Add to appropriate module (e.g., categories.js)
|
|
226
|
-
const NEW_CATEGORY_FEATURE = 'new-feature';
|
|
227
|
-
|
|
228
|
-
// Export in module
|
|
229
|
-
module.exports = {
|
|
230
|
-
NEW_CATEGORY_FEATURE,
|
|
231
|
-
// ... other exports
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
// Update barrel export (index.js) if needed
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### 3. **Extending Functionality**
|
|
238
|
-
```javascript
|
|
239
|
-
// Add utility functions to appropriate modules
|
|
240
|
-
function getAdvancedCategoryInfo(category) {
|
|
241
|
-
// Implementation
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Export with other functions
|
|
245
|
-
module.exports = {
|
|
246
|
-
// ... existing exports
|
|
247
|
-
getAdvancedCategoryInfo
|
|
248
|
-
};
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
## Testing
|
|
252
|
-
|
|
253
|
-
Each constants module includes comprehensive tests:
|
|
254
|
-
|
|
255
|
-
```bash
|
|
256
|
-
# Test entire constants structure
|
|
257
|
-
node test-constants-structure.js
|
|
258
|
-
|
|
259
|
-
# Test backward compatibility
|
|
260
|
-
node test-centralized-categories.js
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
## Future Enhancements
|
|
264
|
-
|
|
265
|
-
### Planned Features
|
|
266
|
-
1. **Dynamic Configuration Loading** - Load constants from external files
|
|
267
|
-
2. **Environment-specific Constants** - Different values for dev/prod
|
|
268
|
-
3. **Validation Schemas** - JSON Schema validation for all constants
|
|
269
|
-
4. **Hot Reloading** - Update constants without restarting
|
|
270
|
-
|
|
271
|
-
### Extension Points
|
|
272
|
-
- Add new constant modules (e.g., `integrations.js`, `plugins.js`)
|
|
273
|
-
- Extend barrel export for new modules
|
|
274
|
-
- Add validation functions for new constant types
|
|
275
|
-
|
|
276
|
-
---
|
|
277
|
-
|
|
278
|
-
## Quick Reference
|
|
279
|
-
|
|
280
|
-
| Module | Purpose | Key Functions |
|
|
281
|
-
|--------|---------|---------------|
|
|
282
|
-
| `categories.js` | Category management | `getValidCategories()`, `normalizeCategory()` |
|
|
283
|
-
| `defaults.js` | Default values | `getDefaultConfig()`, `getDefaultRuleSet()` |
|
|
284
|
-
| `engines.js` | Engine configuration | `getEnginesForLanguage()`, `getRecommendedEngine()` |
|
|
285
|
-
| `rules.js` | Rule utilities | `getLanguageFromRuleId()`, `isValidRuleId()` |
|
|
286
|
-
| `index.js` | Barrel export | All functions from all modules |
|
|
287
|
-
|
|
288
|
-
For detailed API documentation, see the JSDoc comments in each module file.
|