@sun-asterisk/sunlint 1.3.2 → 1.3.3
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/CHANGELOG.md +38 -0
- package/README.md +5 -3
- package/config/rules/enhanced-rules-registry.json +144 -33
- package/core/analysis-orchestrator.js +167 -42
- package/core/auto-performance-manager.js +243 -0
- package/core/cli-action-handler.js +9 -1
- package/core/cli-program.js +19 -5
- package/core/constants/defaults.js +56 -0
- package/core/performance-optimizer.js +271 -0
- package/docs/FILE_LIMITS_COMPLETION_REPORT.md +151 -0
- package/docs/FILE_LIMITS_EXPLANATION.md +190 -0
- package/docs/PERFORMANCE.md +311 -0
- package/docs/PERFORMANCE_MIGRATION_GUIDE.md +368 -0
- package/docs/PERFORMANCE_OPTIMIZATION_PLAN.md +255 -0
- package/docs/QUICK_FILE_LIMITS.md +64 -0
- package/docs/SIMPLIFIED_USAGE_GUIDE.md +208 -0
- package/engines/heuristic-engine.js +182 -5
- package/package.json +2 -1
- package/rules/common/C048_no_bypass_architectural_layers/analyzer.js +180 -0
- package/rules/common/C048_no_bypass_architectural_layers/config.json +50 -0
- package/rules/common/C048_no_bypass_architectural_layers/symbol-based-analyzer.js +235 -0
- package/rules/common/C052_parsing_or_data_transformation/analyzer.js +180 -0
- package/rules/common/C052_parsing_or_data_transformation/config.json +50 -0
- package/rules/common/C052_parsing_or_data_transformation/symbol-based-analyzer.js +132 -0
- package/rules/index.js +2 -0
- package/rules/security/S017_use_parameterized_queries/README.md +128 -0
- package/rules/security/S017_use_parameterized_queries/analyzer.js +286 -0
- package/rules/security/S017_use_parameterized_queries/config.json +109 -0
- package/rules/security/S017_use_parameterized_queries/regex-based-analyzer.js +541 -0
- package/rules/security/S017_use_parameterized_queries/symbol-based-analyzer.js +777 -0
- package/rules/security/S031_secure_session_cookies/README.md +127 -0
- package/rules/security/S031_secure_session_cookies/analyzer.js +245 -0
- package/rules/security/S031_secure_session_cookies/config.json +86 -0
- package/rules/security/S031_secure_session_cookies/regex-based-analyzer.js +196 -0
- package/rules/security/S031_secure_session_cookies/symbol-based-analyzer.js +1084 -0
- package/rules/security/S032_httponly_session_cookies/FRAMEWORK_SUPPORT.md +209 -0
- package/rules/security/S032_httponly_session_cookies/README.md +184 -0
- package/rules/security/S032_httponly_session_cookies/analyzer.js +282 -0
- package/rules/security/S032_httponly_session_cookies/config.json +96 -0
- package/rules/security/S032_httponly_session_cookies/regex-based-analyzer.js +715 -0
- package/rules/security/S032_httponly_session_cookies/symbol-based-analyzer.js +1348 -0
- package/rules/security/S033_samesite_session_cookies/README.md +227 -0
- package/rules/security/S033_samesite_session_cookies/analyzer.js +242 -0
- package/rules/security/S033_samesite_session_cookies/config.json +87 -0
- package/rules/security/S033_samesite_session_cookies/regex-based-analyzer.js +703 -0
- package/rules/security/S033_samesite_session_cookies/symbol-based-analyzer.js +732 -0
- package/rules/security/S034_host_prefix_session_cookies/README.md +204 -0
- package/rules/security/S034_host_prefix_session_cookies/analyzer.js +290 -0
- package/rules/security/S034_host_prefix_session_cookies/config.json +62 -0
- package/rules/security/S034_host_prefix_session_cookies/regex-based-analyzer.js +478 -0
- package/rules/security/S034_host_prefix_session_cookies/symbol-based-analyzer.js +277 -0
- package/rules/security/S035_path_session_cookies/README.md +257 -0
- package/rules/security/S035_path_session_cookies/analyzer.js +316 -0
- package/rules/security/S035_path_session_cookies/config.json +99 -0
- package/rules/security/S035_path_session_cookies/regex-based-analyzer.js +724 -0
- package/rules/security/S035_path_session_cookies/symbol-based-analyzer.js +373 -0
- package/scripts/batch-processing-demo.js +334 -0
- package/scripts/performance-test.js +541 -0
- package/scripts/quick-performance-test.js +108 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# 🚀 SunLint Performance - Simplified Usage Guide
|
|
2
|
+
|
|
3
|
+
## 🎯 **TÓM TẮT: 3 Commands Duy Nhất Bạn Cần Biết**
|
|
4
|
+
|
|
5
|
+
### **1. 🏃♂️ Quick Start (90% use cases)**
|
|
6
|
+
```bash
|
|
7
|
+
sunlint --all --input=src
|
|
8
|
+
```
|
|
9
|
+
✅ **Auto-detects** project size và chọn settings tối ưu
|
|
10
|
+
✅ **Zero configuration** - chỉ cần chỉ định input folder
|
|
11
|
+
✅ **Works everywhere** - small projects đến enterprise
|
|
12
|
+
|
|
13
|
+
### **2. ⚡ Performance Modes (khi cần tùy chỉnh)**
|
|
14
|
+
```bash
|
|
15
|
+
# Fast scan (for testing/development)
|
|
16
|
+
sunlint --all --input=src --performance=fast
|
|
17
|
+
|
|
18
|
+
# Thorough analysis (for CI/CD)
|
|
19
|
+
sunlint --all --input=src --performance=careful
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### **3. 🛠️ Custom Timeout (khi project rất lớn)**
|
|
23
|
+
```bash
|
|
24
|
+
sunlint --all --input=src --timeout=120000 # 2 minutes
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 🤖 **Auto Performance Detection**
|
|
30
|
+
|
|
31
|
+
SunLint **tự động phát hiện** project size và chọn settings tối ưu:
|
|
32
|
+
|
|
33
|
+
| **Project Size** | **Files** | **Auto Settings** | **Timeout** |
|
|
34
|
+
|------------------|-----------|-------------------|-------------|
|
|
35
|
+
| **Small** | < 100 | Fast analysis | 30s |
|
|
36
|
+
| **Medium** | 100-500 | Balanced | 60s |
|
|
37
|
+
| **Large** | 500-1000 | Careful + progressive | 120s |
|
|
38
|
+
| **Enterprise** | 1000+ | Conservative + streaming | 300s |
|
|
39
|
+
|
|
40
|
+
### **Auto-Detection Logic**
|
|
41
|
+
```bash
|
|
42
|
+
# ✅ SunLint tự động:
|
|
43
|
+
# - Đếm số files trong input folder
|
|
44
|
+
# - Phát hiện TypeScript, Node.js project
|
|
45
|
+
# - Chọn timeout và batch size phù hợp
|
|
46
|
+
# - Bật progressive results cho large projects
|
|
47
|
+
|
|
48
|
+
sunlint --all --input=src # Làm tất cả tự động!
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 📋 **Common Usage Patterns**
|
|
54
|
+
|
|
55
|
+
### **Development (hàng ngày)**
|
|
56
|
+
```bash
|
|
57
|
+
# Quick feedback loop
|
|
58
|
+
sunlint --rules=C019,C041,S027 --input=src
|
|
59
|
+
|
|
60
|
+
# Check specific files
|
|
61
|
+
sunlint --all --input=src/components --performance=fast
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### **Code Review/PR**
|
|
65
|
+
```bash
|
|
66
|
+
# Check changed files only
|
|
67
|
+
sunlint --all --changed-files
|
|
68
|
+
|
|
69
|
+
# Quick but comprehensive
|
|
70
|
+
sunlint --all --input=src --performance=fast --verbose
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### **CI/CD Pipeline**
|
|
74
|
+
```bash
|
|
75
|
+
# Thorough analysis with auto-optimization
|
|
76
|
+
sunlint --all --input=src --format=json --output=results.json
|
|
77
|
+
|
|
78
|
+
# For large projects in CI
|
|
79
|
+
sunlint --all --input=src --performance=careful --quiet
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### **Weekly Code Quality Review**
|
|
83
|
+
```bash
|
|
84
|
+
# Full analysis with detailed reporting
|
|
85
|
+
sunlint --all --input=src --verbose --format=table
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 🚨 **Troubleshooting Simplified**
|
|
91
|
+
|
|
92
|
+
### **❌ Getting Timeouts?**
|
|
93
|
+
```bash
|
|
94
|
+
# Try longer timeout
|
|
95
|
+
sunlint --all --input=src --timeout=120000
|
|
96
|
+
|
|
97
|
+
# Or limit files
|
|
98
|
+
sunlint --all --input=src --max-files=500
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### **❌ Taking Too Long?**
|
|
102
|
+
```bash
|
|
103
|
+
# Use fast mode
|
|
104
|
+
sunlint --all --input=src --performance=fast
|
|
105
|
+
|
|
106
|
+
# Or check specific rules
|
|
107
|
+
sunlint --rules=C002,C019,S027 --input=src
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### **❌ Memory Issues?**
|
|
111
|
+
```bash
|
|
112
|
+
# Automatic handling - just use auto mode
|
|
113
|
+
sunlint --all --input=src --performance=auto
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 🎛️ **Migration from Complex Commands**
|
|
119
|
+
|
|
120
|
+
### **BEFORE (v3.x - Complex)**
|
|
121
|
+
```bash
|
|
122
|
+
# ❌ Too many options to remember
|
|
123
|
+
sunlint --all --input=src \
|
|
124
|
+
--performance-profile=balanced \
|
|
125
|
+
--adaptive-timeout \
|
|
126
|
+
--max-memory=2GB \
|
|
127
|
+
--batch-size=10 \
|
|
128
|
+
--progressive-results \
|
|
129
|
+
--verbose
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### **AFTER (v4.x - Simplified)**
|
|
133
|
+
```bash
|
|
134
|
+
# ✅ Simple and effective
|
|
135
|
+
sunlint --all --input=src --verbose
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### **Advanced Users Can Still Customize**
|
|
139
|
+
```bash
|
|
140
|
+
# For power users who need control
|
|
141
|
+
sunlint --all --input=src --performance=careful --timeout=180000
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 📊 **Performance Comparison**
|
|
147
|
+
|
|
148
|
+
| **Command** | **Small Project** | **Large Project** | **Enterprise** |
|
|
149
|
+
|-------------|-------------------|-------------------|----------------|
|
|
150
|
+
| `--performance=auto` | ~10s | ~60s | ~120s |
|
|
151
|
+
| `--performance=fast` | ~5s | ~30s | ~60s |
|
|
152
|
+
| `--performance=careful` | ~15s | ~90s | ~180s |
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## ✅ **Best Practices**
|
|
157
|
+
|
|
158
|
+
### **🎯 DO (Recommended)**
|
|
159
|
+
```bash
|
|
160
|
+
✅ sunlint --all --input=src # Let auto-detection work
|
|
161
|
+
✅ sunlint --all --input=src --verbose # See what's happening
|
|
162
|
+
✅ sunlint --quality --input=src --performance=fast # Quick quality check
|
|
163
|
+
✅ sunlint --all --changed-files # Only check changes
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### **❌ DON'T (Avoid)**
|
|
167
|
+
```bash
|
|
168
|
+
❌ sunlint --all --input=src --performance-profile=conservative --batch-size=5 --streaming-analysis
|
|
169
|
+
# Too complex - just use --performance=careful
|
|
170
|
+
|
|
171
|
+
❌ sunlint --all --input=src --timeout=5000
|
|
172
|
+
# Too short - let auto-detection choose
|
|
173
|
+
|
|
174
|
+
❌ sunlint --all --input=huge-project
|
|
175
|
+
# Missing performance hint - add --performance=careful
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## 🏆 **Success Metrics**
|
|
181
|
+
|
|
182
|
+
### **✅ Simplified CLI Achieved**
|
|
183
|
+
- **3 main commands** cover 90% of use cases
|
|
184
|
+
- **Auto-detection** eliminates guesswork
|
|
185
|
+
- **Zero configuration** for most projects
|
|
186
|
+
- **Predictable performance** across project sizes
|
|
187
|
+
|
|
188
|
+
### **✅ Backward Compatibility**
|
|
189
|
+
- Old commands still work but show deprecation warnings
|
|
190
|
+
- Gradual migration path for existing users
|
|
191
|
+
- Advanced options available for power users
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## 🚀 **Quick Start Checklist**
|
|
196
|
+
|
|
197
|
+
- [ ] **Update to SunLint v4.x** with auto-performance
|
|
198
|
+
- [ ] **Use basic command**: `sunlint --all --input=src`
|
|
199
|
+
- [ ] **Add --verbose** if you want to see progress
|
|
200
|
+
- [ ] **Use --performance=fast** for quick checks
|
|
201
|
+
- [ ] **Use --performance=careful** for thorough analysis
|
|
202
|
+
- [ ] **Test with your project** to validate performance
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
**🎯 Bottom Line: Chỉ cần nhớ `sunlint --all --input=src` - mọi thứ khác được tự động optimize!**
|
|
207
|
+
|
|
208
|
+
*🚀 Simple • ⚡ Fast • 🎯 Effective*
|
|
@@ -12,12 +12,13 @@ const SunlintRuleAdapter = require('../core/adapters/sunlint-rule-adapter');
|
|
|
12
12
|
const SemanticEngine = require('../core/semantic-engine');
|
|
13
13
|
const SemanticRuleBase = require('../core/semantic-rule-base');
|
|
14
14
|
const { getInstance: getUnifiedRegistry } = require('../core/unified-rule-registry');
|
|
15
|
+
const AutoPerformanceManager = require('../core/auto-performance-manager');
|
|
15
16
|
const fs = require('fs');
|
|
16
17
|
const path = require('path');
|
|
17
18
|
|
|
18
19
|
class HeuristicEngine extends AnalysisEngineInterface {
|
|
19
20
|
constructor() {
|
|
20
|
-
super('heuristic', '
|
|
21
|
+
super('heuristic', '4.0', ['typescript', 'javascript', 'dart', 'swift', 'kotlin', 'java', 'python', 'go', 'rust', 'all']);
|
|
21
22
|
|
|
22
23
|
this.ruleAnalyzers = new Map();
|
|
23
24
|
this.supportedRulesList = [];
|
|
@@ -32,18 +33,38 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
32
33
|
|
|
33
34
|
// Unified rule registry
|
|
34
35
|
this.unifiedRegistry = getUnifiedRegistry();
|
|
36
|
+
|
|
37
|
+
// ✅ PERFORMANCE OPTIMIZATIONS (Integrated)
|
|
38
|
+
this.performanceManager = new AutoPerformanceManager();
|
|
39
|
+
this.performanceConfig = null;
|
|
40
|
+
this.metrics = {
|
|
41
|
+
startTime: null,
|
|
42
|
+
filesProcessed: 0,
|
|
43
|
+
rulesProcessed: 0,
|
|
44
|
+
violationsFound: 0,
|
|
45
|
+
memoryUsage: 0
|
|
46
|
+
};
|
|
35
47
|
}
|
|
36
48
|
|
|
37
49
|
/**
|
|
38
50
|
* Initialize Heuristic engine with ts-morph core and configuration
|
|
51
|
+
* ✅ ENHANCED: Now includes performance optimization
|
|
39
52
|
* Following Rule C006: Verb-noun naming
|
|
40
53
|
* @param {Object} config - Engine configuration
|
|
41
54
|
*/
|
|
42
55
|
async initialize(config) {
|
|
43
56
|
try {
|
|
57
|
+
// ✅ PERFORMANCE: Get optimal settings based on project
|
|
58
|
+
this.performanceConfig = this.performanceManager.getOptimalSettings(config, config?.targetFiles || []);
|
|
59
|
+
|
|
44
60
|
// Store verbosity setting
|
|
45
61
|
this.verbose = config?.verbose || false;
|
|
46
62
|
|
|
63
|
+
if (this.verbose && this.performanceConfig.autoDetected) {
|
|
64
|
+
console.log(`🤖 [HeuristicEngine] Auto-detected performance profile: ${this.performanceConfig.name}`);
|
|
65
|
+
console.log(` ⚡ Settings: ${this.performanceConfig.timeout/1000}s timeout, ${this.performanceConfig.batchSize || 'auto'} batch size`);
|
|
66
|
+
}
|
|
67
|
+
|
|
47
68
|
// Initialize unified rule registry
|
|
48
69
|
await this.unifiedRegistry.initialize({ verbose: this.verbose });
|
|
49
70
|
|
|
@@ -66,10 +87,11 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
66
87
|
|
|
67
88
|
this.initialized = true;
|
|
68
89
|
if (this.verbose) {
|
|
69
|
-
console.log(`🔍 Heuristic engine
|
|
90
|
+
console.log(`🔍 Heuristic engine v4.0 initialized:`);
|
|
70
91
|
console.log(` 📊 Total rules: ${this.supportedRulesList.length}`);
|
|
71
92
|
console.log(` 🧠 Symbol Table: ${this.symbolTableInitialized ? 'enabled' : 'disabled'}`);
|
|
72
93
|
console.log(` 🔧 Semantic rules: ${this.semanticRules.size}`);
|
|
94
|
+
console.log(` ⚡ Performance: ${this.performanceConfig.name || 'standard'}`);
|
|
73
95
|
}
|
|
74
96
|
|
|
75
97
|
} catch (error) {
|
|
@@ -607,6 +629,7 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
607
629
|
|
|
608
630
|
/**
|
|
609
631
|
* Analyze files using heuristic patterns
|
|
632
|
+
* ✅ ENHANCED: Now includes performance optimizations and batch processing
|
|
610
633
|
* Following Rule C006: Verb-noun naming
|
|
611
634
|
* @param {string[]} files - Files to analyze
|
|
612
635
|
* @param {Object[]} rules - Rules to apply
|
|
@@ -618,12 +641,166 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
618
641
|
throw new Error('Heuristic engine not initialized');
|
|
619
642
|
}
|
|
620
643
|
|
|
644
|
+
// ✅ PERFORMANCE: Apply file limits and timeout protection
|
|
645
|
+
const startTime = Date.now();
|
|
646
|
+
this.metrics.startTime = startTime;
|
|
647
|
+
|
|
648
|
+
// Apply analysis file limits (different from semantic file limits)
|
|
649
|
+
const maxFiles = this.getAnalysisFileLimit(options);
|
|
650
|
+
const limitedFiles = files.slice(0, maxFiles);
|
|
651
|
+
|
|
652
|
+
if (files.length > maxFiles && this.verbose) {
|
|
653
|
+
console.warn(`⚠️ [HeuristicEngine] Analysis file limit: ${limitedFiles.length}/${files.length} files`);
|
|
654
|
+
console.log(` 💡 Note: Symbol table uses separate limit (--max-semantic-files)`);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// Set up timeout if configured
|
|
658
|
+
const timeout = this.performanceConfig?.timeout || parseInt(options.timeout) || 0;
|
|
659
|
+
let timeoutId = null;
|
|
660
|
+
|
|
661
|
+
if (timeout > 0) {
|
|
662
|
+
timeoutId = setTimeout(() => {
|
|
663
|
+
throw new Error(`Analysis timeout after ${timeout}ms`);
|
|
664
|
+
}, timeout);
|
|
665
|
+
}
|
|
666
|
+
|
|
621
667
|
if (options.verbose) {
|
|
622
|
-
console.log(`🔍 [HeuristicEngine] Analyzing ${
|
|
623
|
-
|
|
624
|
-
|
|
668
|
+
console.log(`🔍 [HeuristicEngine] Analyzing ${limitedFiles.length} files with ${rules.length} rules`);
|
|
669
|
+
if (this.performanceConfig?.name) {
|
|
670
|
+
console.log(`⚡ [Performance] Using ${this.performanceConfig.name} profile`);
|
|
671
|
+
}
|
|
672
|
+
if (timeout > 0) {
|
|
673
|
+
console.log(`⏰ [Timeout] Analysis will timeout after ${timeout/1000}s`);
|
|
674
|
+
}
|
|
625
675
|
}
|
|
626
676
|
|
|
677
|
+
try {
|
|
678
|
+
// Check if we should use batch processing
|
|
679
|
+
if (this.shouldUseBatchProcessing(limitedFiles, rules)) {
|
|
680
|
+
return await this.analyzeBatched(limitedFiles, rules, options);
|
|
681
|
+
} else {
|
|
682
|
+
return await this.analyzeStandard(limitedFiles, rules, options);
|
|
683
|
+
}
|
|
684
|
+
} finally {
|
|
685
|
+
// Clear timeout
|
|
686
|
+
if (timeoutId) {
|
|
687
|
+
clearTimeout(timeoutId);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Log performance metrics
|
|
691
|
+
const duration = Date.now() - startTime;
|
|
692
|
+
this.metrics.filesProcessed = limitedFiles.length;
|
|
693
|
+
this.metrics.rulesProcessed = rules.length;
|
|
694
|
+
|
|
695
|
+
if (options.verbose) {
|
|
696
|
+
console.log(`✅ [HeuristicEngine] Analysis completed in ${duration}ms`);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* ✅ NEW: Get analysis file limit (separate from semantic file limit)
|
|
703
|
+
*/
|
|
704
|
+
getAnalysisFileLimit(options) {
|
|
705
|
+
// User-specified limit
|
|
706
|
+
if (options.maxFiles && parseInt(options.maxFiles) > 0) {
|
|
707
|
+
return parseInt(options.maxFiles);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// Performance config limit
|
|
711
|
+
if (this.performanceConfig?.maxFiles) {
|
|
712
|
+
return this.performanceConfig.maxFiles;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// Default based on performance mode
|
|
716
|
+
const mode = options.performance || 'auto';
|
|
717
|
+
const defaults = {
|
|
718
|
+
fast: 500,
|
|
719
|
+
auto: 1000,
|
|
720
|
+
careful: 1500
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
return defaults[mode] || 1000;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* ✅ NEW: Determine if batch processing should be used
|
|
728
|
+
*/
|
|
729
|
+
shouldUseBatchProcessing(files, rules) {
|
|
730
|
+
const batchThreshold = this.performanceConfig?.batchThreshold || 100;
|
|
731
|
+
const totalWorkload = files.length * rules.length;
|
|
732
|
+
|
|
733
|
+
return totalWorkload > batchThreshold ||
|
|
734
|
+
files.length > 200 ||
|
|
735
|
+
rules.length > 30;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* ✅ NEW: Batch processing for large workloads
|
|
740
|
+
*/
|
|
741
|
+
async analyzeBatched(files, rules, options) {
|
|
742
|
+
if (options.verbose) {
|
|
743
|
+
console.log(`� [HeuristicEngine] Using batch processing for large workload`);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
const results = {
|
|
747
|
+
results: [],
|
|
748
|
+
filesAnalyzed: files.length,
|
|
749
|
+
engine: 'heuristic',
|
|
750
|
+
metadata: {
|
|
751
|
+
rulesAnalyzed: rules.map(r => r.id),
|
|
752
|
+
analyzersUsed: [],
|
|
753
|
+
batchProcessing: true
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
|
|
757
|
+
// Create rule batches
|
|
758
|
+
const batchSize = this.performanceConfig?.batchSize || 10;
|
|
759
|
+
const ruleBatches = [];
|
|
760
|
+
|
|
761
|
+
for (let i = 0; i < rules.length; i += batchSize) {
|
|
762
|
+
ruleBatches.push(rules.slice(i, i + batchSize));
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
if (options.verbose) {
|
|
766
|
+
console.log(`📦 [Batch] Processing ${ruleBatches.length} rule batches (${batchSize} rules each)`);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// Process each batch
|
|
770
|
+
for (let batchIndex = 0; batchIndex < ruleBatches.length; batchIndex++) {
|
|
771
|
+
const ruleBatch = ruleBatches[batchIndex];
|
|
772
|
+
|
|
773
|
+
if (options.verbose) {
|
|
774
|
+
console.log(`⚡ [Batch ${batchIndex + 1}/${ruleBatches.length}] Processing ${ruleBatch.length} rules...`);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
const batchResults = await this.analyzeStandard(files, ruleBatch, options);
|
|
778
|
+
|
|
779
|
+
// Merge batch results
|
|
780
|
+
for (const fileResult of batchResults.results) {
|
|
781
|
+
let existingFile = results.results.find(r => r.file === fileResult.file);
|
|
782
|
+
if (!existingFile) {
|
|
783
|
+
existingFile = { file: fileResult.file, violations: [] };
|
|
784
|
+
results.results.push(existingFile);
|
|
785
|
+
}
|
|
786
|
+
existingFile.violations.push(...fileResult.violations);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
results.metadata.analyzersUsed.push(...batchResults.metadata.analyzersUsed);
|
|
790
|
+
|
|
791
|
+
// Memory management
|
|
792
|
+
if (batchIndex % 3 === 0 && global.gc) {
|
|
793
|
+
global.gc(); // Trigger garbage collection every 3 batches
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
return results;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* ✅ REFACTORED: Standard analysis method (extracted from original analyze)
|
|
802
|
+
*/
|
|
803
|
+
async analyzeStandard(files, rules, options) {
|
|
627
804
|
const results = {
|
|
628
805
|
results: [],
|
|
629
806
|
filesAnalyzed: files.length,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sun-asterisk/sunlint",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.3",
|
|
4
4
|
"description": "☀️ SunLint - Multi-language static analysis tool for code quality and security | Sun* Engineering Standards",
|
|
5
5
|
"main": "cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"test:integration": "echo 'Temporarily disabled - config extension issue'",
|
|
23
23
|
"test:realworld": "node examples/integration-tests/realworld-integration-test.js",
|
|
24
24
|
"test:cli": "node examples/integration-tests/direct-cli-test.js",
|
|
25
|
+
"test:performance": "node test/performance-test.js",
|
|
25
26
|
"test:c019": "node cli.js --rule=C019 --input=examples/test-fixtures --format=eslint",
|
|
26
27
|
"test:c006": "node cli.js --rule=C006 --input=examples/test-fixtures --format=eslint",
|
|
27
28
|
"test:c029": "node cli.js --rule=C029 --input=examples/test-fixtures --format=eslint",
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C048 Main Analyzer - Do not bypass architectural layers (controller/service/repository)
|
|
3
|
+
* Primary: Maintain a clear layered architecture, ensuring logic and data flow are well-structured and maintainable.
|
|
4
|
+
* Fallback: Regex-based for all other cases
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const C048SymbolBasedAnalyzer = require('./symbol-based-analyzer');
|
|
8
|
+
|
|
9
|
+
class C048Analyzer {
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
12
|
+
console.log(`🔧 [C048] Constructor called with options:`, !!options);
|
|
13
|
+
console.log(`🔧 [C048] Options type:`, typeof options, Object.keys(options || {}));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
this.ruleId = 'C048';
|
|
17
|
+
this.ruleName = 'Do not bypass architectural layers (controller/service/repository)';
|
|
18
|
+
this.description = 'Maintain a clear layered architecture, ensuring logic and data flow are well-structured and maintainable.';
|
|
19
|
+
this.semanticEngine = options.semanticEngine || null;
|
|
20
|
+
this.verbose = options.verbose || false;
|
|
21
|
+
|
|
22
|
+
// Configuration
|
|
23
|
+
this.config = {
|
|
24
|
+
useSymbolBased: true, // Primary approach
|
|
25
|
+
fallbackToRegex: false, // Only when symbol fails completely
|
|
26
|
+
symbolBasedOnly: false // Can be set to true for pure mode
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Initialize both analyzers
|
|
30
|
+
try {
|
|
31
|
+
this.symbolAnalyzer = new C048SymbolBasedAnalyzer(this.semanticEngine);
|
|
32
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
33
|
+
console.log(`🔧 [C048] Symbol analyzer created successfully`);
|
|
34
|
+
}
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error(`🔧 [C048] Error creating symbol analyzer:`, error);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Initialize with semantic engine
|
|
42
|
+
*/
|
|
43
|
+
async initialize(semanticEngine = null) {
|
|
44
|
+
if (semanticEngine) {
|
|
45
|
+
this.semanticEngine = semanticEngine;
|
|
46
|
+
}
|
|
47
|
+
this.verbose = semanticEngine?.verbose || false;
|
|
48
|
+
|
|
49
|
+
// Initialize both analyzers
|
|
50
|
+
await this.symbolAnalyzer.initialize(semanticEngine);
|
|
51
|
+
|
|
52
|
+
// Ensure verbose flag is propagated
|
|
53
|
+
this.symbolAnalyzer.verbose = this.verbose;
|
|
54
|
+
|
|
55
|
+
if (this.verbose) {
|
|
56
|
+
console.log(`🔧 [C048 Hybrid] Analyzer initialized - verbose: ${this.verbose}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async analyze(files, language, options = {}) {
|
|
61
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
62
|
+
console.log(`🔧 [C048] analyze() method called with ${files.length} files, language: ${language}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const violations = [];
|
|
66
|
+
|
|
67
|
+
for (const filePath of files) {
|
|
68
|
+
try {
|
|
69
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
70
|
+
console.log(`🔧 [C048] Processing file: ${filePath}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const fileViolations = await this.analyzeFile(filePath, options);
|
|
74
|
+
violations.push(...fileViolations);
|
|
75
|
+
|
|
76
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
77
|
+
console.log(`🔧 [C048] File ${filePath}: Found ${fileViolations.length} violations`);
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.warn(`❌ [C048] Analysis failed for ${filePath}:`, error.message);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
85
|
+
console.log(`🔧 [C048] Total violations found: ${violations.length}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return violations;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async analyzeFile(filePath, options = {}) {
|
|
92
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
93
|
+
console.log(`🔧 [C048] analyzeFile() called for: ${filePath}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 1. Try Symbol-based analysis first (primary)
|
|
97
|
+
if (this.config.useSymbolBased &&
|
|
98
|
+
this.semanticEngine?.project &&
|
|
99
|
+
this.semanticEngine?.initialized) {
|
|
100
|
+
try {
|
|
101
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
102
|
+
console.log(`🔧 [C048] Trying symbol-based analysis...`);
|
|
103
|
+
}
|
|
104
|
+
const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
|
|
105
|
+
if (sourceFile) {
|
|
106
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
107
|
+
console.log(`🔧 [C048] Source file found, analyzing with symbol-based...`);
|
|
108
|
+
}
|
|
109
|
+
const violations = await this.symbolAnalyzer.analyzeFileWithSymbols(filePath, { ...options, verbose: options.verbose });
|
|
110
|
+
|
|
111
|
+
// Mark violations with analysis strategy
|
|
112
|
+
violations.forEach(v => v.analysisStrategy = 'symbol-based');
|
|
113
|
+
|
|
114
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
115
|
+
console.log(`✅ [C048] Symbol-based analysis: ${violations.length} violations`);
|
|
116
|
+
}
|
|
117
|
+
return violations; // Return even if 0 violations - symbol analysis completed successfully
|
|
118
|
+
} else {
|
|
119
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
120
|
+
console.log(`⚠️ [C048] Source file not found in project`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.warn(`⚠️ [C048] Symbol analysis failed: ${error.message}`);
|
|
125
|
+
// Continue to fallback
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
129
|
+
console.log(`🔄 [C048] Symbol analysis conditions check:`);
|
|
130
|
+
console.log(` - useSymbolBased: ${this.config.useSymbolBased}`);
|
|
131
|
+
console.log(` - semanticEngine: ${!!this.semanticEngine}`);
|
|
132
|
+
console.log(` - semanticEngine.project: ${!!this.semanticEngine?.project}`);
|
|
133
|
+
console.log(` - semanticEngine.initialized: ${this.semanticEngine?.initialized}`);
|
|
134
|
+
console.log(`🔄 [C048] Symbol analysis unavailable, using regex fallback`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (options?.verbose) {
|
|
139
|
+
console.log(`🔧 [C048] No analysis methods succeeded, returning empty`);
|
|
140
|
+
}
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async analyzeFileBasic(filePath, options = {}) {
|
|
145
|
+
console.log(`🔧 [C048] analyzeFileBasic() called for: ${filePath}`);
|
|
146
|
+
console.log(`🔧 [C048] semanticEngine exists: ${!!this.semanticEngine}`);
|
|
147
|
+
console.log(`🔧 [C048] symbolAnalyzer exists: ${!!this.symbolAnalyzer}`);
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
// Try symbol-based analysis first
|
|
151
|
+
if (this.semanticEngine?.isSymbolEngineReady?.() &&
|
|
152
|
+
this.semanticEngine.project) {
|
|
153
|
+
|
|
154
|
+
if (this.verbose) {
|
|
155
|
+
console.log(`🔍 [C048] Using symbol-based analysis for ${filePath}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const violations = await this.symbolAnalyzer.analyzeFileBasic(filePath, options);
|
|
159
|
+
return violations;
|
|
160
|
+
}
|
|
161
|
+
} catch (error) {
|
|
162
|
+
if (this.verbose) {
|
|
163
|
+
console.warn(`⚠️ [C048] Symbol analysis failed: ${error.message}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Methods for compatibility with different engine invocation patterns
|
|
170
|
+
*/
|
|
171
|
+
async analyzeFileWithSymbols(filePath, options = {}) {
|
|
172
|
+
return this.analyzeFile(filePath, options);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async analyzeWithSemantics(filePath, options = {}) {
|
|
176
|
+
return this.analyzeFile(filePath, options);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
module.exports = C048Analyzer;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "C048",
|
|
3
|
+
"name": "C048_do_not_bypass_architectural_layers",
|
|
4
|
+
"category": "architecture",
|
|
5
|
+
"description": "C048 - Do not bypass architectural layers (controller/service/repository)",
|
|
6
|
+
"severity": "warning",
|
|
7
|
+
"enabled": true,
|
|
8
|
+
"semantic": {
|
|
9
|
+
"enabled": true,
|
|
10
|
+
"priority": "high",
|
|
11
|
+
"fallback": "heuristic"
|
|
12
|
+
},
|
|
13
|
+
"patterns": {
|
|
14
|
+
"include": [
|
|
15
|
+
"**/*.js",
|
|
16
|
+
"**/*.ts",
|
|
17
|
+
"**/*.jsx",
|
|
18
|
+
"**/*.tsx"
|
|
19
|
+
],
|
|
20
|
+
"exclude": [
|
|
21
|
+
"**/*.test.*",
|
|
22
|
+
"**/*.spec.*",
|
|
23
|
+
"**/*.mock.*",
|
|
24
|
+
"**/test/**",
|
|
25
|
+
"**/tests/**",
|
|
26
|
+
"**/spec/**"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"options": {
|
|
30
|
+
"strictMode": false,
|
|
31
|
+
"allowedDbMethods": [],
|
|
32
|
+
"repositoryPatterns": [
|
|
33
|
+
"*Repository*",
|
|
34
|
+
"*Repo*",
|
|
35
|
+
"*DAO*",
|
|
36
|
+
"*Store*"
|
|
37
|
+
],
|
|
38
|
+
"servicePatterns": [
|
|
39
|
+
"*Service*",
|
|
40
|
+
"*UseCase*",
|
|
41
|
+
"*Handler*",
|
|
42
|
+
"*Manager*"
|
|
43
|
+
],
|
|
44
|
+
"complexityThreshold": {
|
|
45
|
+
"methodLength": 200,
|
|
46
|
+
"cyclomaticComplexity": 5,
|
|
47
|
+
"nestedDepth": 3
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|