@sun-asterisk/sunlint 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +115 -1
- package/CONTRIBUTING.md +249 -605
- package/README.md +3 -4
- package/config/ci-cd.json +54 -0
- package/config/development.json +56 -0
- package/config/large-project.json +143 -0
- package/config/presets/all.json +0 -1
- package/config/release.json +70 -0
- package/config/rule-analysis-strategies.js +38 -3
- package/config/rules/enhanced-rules-registry.json +474 -1179
- package/config/rules/rules-registry-generated.json +3 -3
- package/core/cli-action-handler.js +24 -30
- package/core/cli-program.js +11 -3
- package/core/config-merger.js +29 -2
- package/core/enhanced-rules-registry.js +3 -2
- package/core/semantic-engine.js +129 -19
- package/core/semantic-rule-base.js +4 -2
- package/core/unified-rule-registry.js +1 -1
- package/docs/COMMAND-EXAMPLES.md +134 -0
- package/docs/LARGE-PROJECT-GUIDE.md +324 -0
- package/engines/heuristic-engine.js +135 -16
- package/integrations/eslint/plugin/index.js +0 -2
- package/integrations/eslint/plugin/rules/common/c003-no-vague-abbreviations.js +59 -1
- package/integrations/eslint/plugin/rules/common/c006-function-name-verb-noun.js +26 -1
- package/integrations/eslint/plugin/rules/common/c030-use-custom-error-classes.js +54 -19
- package/origin-rules/common-en.md +19 -15
- package/package.json +1 -1
- package/rules/common/C002_no_duplicate_code/analyzer.js +334 -36
- package/rules/common/C003_no_vague_abbreviations/analyzer.js +220 -35
- package/rules/common/C006_function_naming/analyzer.js +29 -3
- package/rules/common/C010_limit_block_nesting/analyzer.js +181 -337
- package/rules/common/C010_limit_block_nesting/config.json +64 -0
- package/rules/common/C010_limit_block_nesting/regex-based-analyzer.js +379 -0
- package/rules/common/C010_limit_block_nesting/symbol-based-analyzer.js +231 -0
- package/rules/common/C013_no_dead_code/analyzer.js +75 -177
- package/rules/common/C013_no_dead_code/config.json +61 -0
- package/rules/common/C013_no_dead_code/regex-based-analyzer.js +345 -0
- package/rules/common/C013_no_dead_code/symbol-based-analyzer.js +640 -0
- package/rules/common/C014_dependency_injection/analyzer.js +48 -313
- package/rules/common/C014_dependency_injection/config.json +26 -0
- package/rules/common/C014_dependency_injection/symbol-based-analyzer.js +751 -0
- package/rules/common/C017_constructor_logic/analyzer.js +254 -17
- package/rules/common/C017_constructor_logic/semantic-analyzer.js +340 -0
- package/rules/common/C018_no_throw_generic_error/analyzer.js +232 -0
- package/rules/common/C018_no_throw_generic_error/config.json +50 -0
- package/rules/common/C018_no_throw_generic_error/regex-based-analyzer.js +387 -0
- package/rules/common/C018_no_throw_generic_error/symbol-based-analyzer.js +314 -0
- package/rules/common/C019_log_level_usage/analyzer.js +110 -317
- package/rules/common/C019_log_level_usage/pattern-analyzer.js +88 -0
- package/rules/common/C019_log_level_usage/system-log-analyzer.js +1267 -0
- package/rules/common/C023_no_duplicate_variable/analyzer.js +180 -0
- package/rules/common/C023_no_duplicate_variable/config.json +50 -0
- package/rules/common/C023_no_duplicate_variable/symbol-based-analyzer.js +158 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/analyzer.js +180 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/config.json +50 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +181 -0
- package/rules/common/C030_use_custom_error_classes/analyzer.js +200 -0
- package/rules/common/C033_separate_service_repository/README.md +78 -0
- package/rules/common/C033_separate_service_repository/analyzer.js +160 -0
- package/rules/common/C033_separate_service_repository/config.json +50 -0
- package/rules/common/C033_separate_service_repository/regex-based-analyzer.js +585 -0
- package/rules/common/C033_separate_service_repository/symbol-based-analyzer.js +368 -0
- package/rules/common/C035_error_logging_context/STRATEGY.md +99 -0
- package/rules/common/C035_error_logging_context/analyzer.js +232 -0
- package/rules/common/C035_error_logging_context/config.json +54 -0
- package/rules/common/C035_error_logging_context/regex-based-analyzer.js +299 -0
- package/rules/common/C035_error_logging_context/symbol-based-analyzer.js +454 -0
- package/rules/common/C040_centralized_validation/analyzer.js +165 -0
- package/rules/common/C040_centralized_validation/config.json +46 -0
- package/rules/common/C040_centralized_validation/regex-based-analyzer.js +243 -0
- package/rules/common/C040_centralized_validation/symbol-based-analyzer.js +416 -0
- package/rules/common/{C076_single_test_behavior → C072_single_test_behavior}/analyzer.js +6 -6
- package/rules/common/C076_explicit_function_types/README.md +30 -0
- package/rules/common/C076_explicit_function_types/analyzer.js +172 -0
- package/rules/common/C076_explicit_function_types/config.json +15 -0
- package/rules/common/C076_explicit_function_types/semantic-analyzer.js +341 -0
- package/rules/index.js +6 -1
- package/rules/parser/rule-parser.js +13 -2
- package/rules/security/S005_no_origin_auth/README.md +226 -0
- package/rules/security/S005_no_origin_auth/analyzer.js +184 -0
- package/rules/security/S005_no_origin_auth/ast-analyzer.js +406 -0
- package/rules/security/S005_no_origin_auth/config.json +85 -0
- package/rules/security/S006_no_plaintext_recovery_codes/README.md +139 -0
- package/rules/security/S006_no_plaintext_recovery_codes/analyzer.js +306 -0
- package/rules/security/S006_no_plaintext_recovery_codes/config.json +48 -0
- package/rules/security/S007_no_plaintext_otp/README.md +198 -0
- package/rules/security/S007_no_plaintext_otp/analyzer.js +406 -0
- package/rules/security/S007_no_plaintext_otp/config.json +79 -0
- package/rules/security/S007_no_plaintext_otp/semantic-analyzer.js +609 -0
- package/rules/security/S007_no_plaintext_otp/semantic-config.json +195 -0
- package/rules/security/S007_no_plaintext_otp/semantic-wrapper.js +280 -0
- package/rules/security/S009_no_insecure_encryption/README.md +158 -0
- package/rules/security/S009_no_insecure_encryption/analyzer.js +319 -0
- package/rules/security/S009_no_insecure_encryption/config.json +55 -0
- package/rules/security/S010_no_insecure_encryption/README.md +224 -0
- package/rules/security/S010_no_insecure_encryption/analyzer.js +493 -0
- package/rules/security/S010_no_insecure_encryption/config.json +48 -0
- package/rules/security/S016_no_sensitive_querystring/STRATEGY.md +149 -0
- package/rules/security/S016_no_sensitive_querystring/analyzer.js +276 -0
- package/rules/security/S016_no_sensitive_querystring/config.json +127 -0
- package/rules/security/S016_no_sensitive_querystring/regex-based-analyzer.js +258 -0
- package/rules/security/S016_no_sensitive_querystring/symbol-based-analyzer.js +495 -0
- package/rules/security/S027_no_hardcoded_secrets/analyzer.js +180 -366
- package/rules/security/S027_no_hardcoded_secrets/categories.json +153 -0
- package/rules/security/S027_no_hardcoded_secrets/categorized-analyzer.js +250 -0
- package/rules/security/S048_no_current_password_in_reset/README.md +222 -0
- package/rules/security/S048_no_current_password_in_reset/analyzer.js +366 -0
- package/rules/security/S048_no_current_password_in_reset/config.json +48 -0
- package/rules/security/S055_content_type_validation/README.md +176 -0
- package/rules/security/S055_content_type_validation/analyzer.js +312 -0
- package/rules/security/S055_content_type_validation/config.json +48 -0
- package/rules/utils/rule-helpers.js +140 -1
- package/scripts/consolidate-config.js +116 -0
- package/scripts/prepare-release.sh +1 -1
- package/config/rules/rules-registry.json +0 -765
- package/docs/ESLINT-INTEGRATION-STRATEGY.md +0 -392
- package/docs/FUTURE_PACKAGES.md +0 -83
- package/docs/HEURISTIC_VS_AI.md +0 -113
- package/docs/PRODUCTION_DEPLOYMENT_ANALYSIS.md +0 -112
- package/docs/PRODUCTION_SIZE_IMPACT.md +0 -183
- package/docs/RELEASE_GUIDE.md +0 -230
- package/docs/STANDARDIZED-CATEGORY-FILTERING.md +0 -156
- package/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +0 -254
- package/rules/common/C006_function_naming/smart-analyzer.js +0 -503
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
# 🏗️ Large Project Analysis Guide
|
|
2
|
+
|
|
3
|
+
> **For projects with 1000+ files**: Complete strategies to optimize SunLint performance while maintaining comprehensive analysis coverage.
|
|
4
|
+
|
|
5
|
+
## 📊 Overview
|
|
6
|
+
|
|
7
|
+
SunLint uses **semantic analysis** for advanced rules like `C047` (retry pattern detection). For large projects, you can control the scope of semantic analysis to balance accuracy vs performance.
|
|
8
|
+
|
|
9
|
+
### 🎯 Key Benefits
|
|
10
|
+
|
|
11
|
+
- **Configurable file limits**: Control memory usage and analysis time
|
|
12
|
+
- **Smart defaults**: Automatic optimization for different project sizes
|
|
13
|
+
- **Multiple strategies**: Choose the best approach for your workflow
|
|
14
|
+
- **Full coverage options**: Ensure no violations are missed
|
|
15
|
+
|
|
16
|
+
## ⚙️ Configuration Options
|
|
17
|
+
|
|
18
|
+
### CLI Option: `--max-semantic-files`
|
|
19
|
+
|
|
20
|
+
Controls how many files are loaded for semantic analysis:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Default behavior (auto-detect)
|
|
24
|
+
node cli.js --all --input=.
|
|
25
|
+
|
|
26
|
+
# Conservative analysis (500 files)
|
|
27
|
+
node cli.js --all --input=. --max-semantic-files=500
|
|
28
|
+
|
|
29
|
+
# Balanced analysis (1000 files - default)
|
|
30
|
+
node cli.js --all --input=. --max-semantic-files=1000
|
|
31
|
+
|
|
32
|
+
# Comprehensive analysis (2000 files)
|
|
33
|
+
node cli.js --all --input=. --max-semantic-files=2000
|
|
34
|
+
|
|
35
|
+
# Unlimited analysis (all files)
|
|
36
|
+
node cli.js --all --input=. --max-semantic-files=-1
|
|
37
|
+
|
|
38
|
+
# Disable semantic analysis (heuristic only)
|
|
39
|
+
node cli.js --all --input=. --max-semantic-files=0
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 📋 Recommended Limits by Project Size
|
|
43
|
+
|
|
44
|
+
| Project Size | Files Count | Recommended Limit | Memory Usage | Analysis Time |
|
|
45
|
+
|-------------|-------------|-------------------|--------------|---------------|
|
|
46
|
+
| **Small** | < 100 files | `0` (all files) | Low | Fast |
|
|
47
|
+
| **Medium** | 100-500 files | `500` | Medium | Medium |
|
|
48
|
+
| **Large** | 500-2000 files | `1000` ⭐ | Medium-High | Medium |
|
|
49
|
+
| **Enterprise** | 2000-5000 files | `1500` | High | Slow |
|
|
50
|
+
| **Massive** | 5000+ files | `500-1000` | Controlled | Reasonable |
|
|
51
|
+
|
|
52
|
+
⭐ **Default recommended setting**
|
|
53
|
+
|
|
54
|
+
## 🚀 Analysis Strategies
|
|
55
|
+
|
|
56
|
+
### Strategy 1: Incremental Development
|
|
57
|
+
|
|
58
|
+
Perfect for daily development work:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Focus on changed files only (fastest)
|
|
62
|
+
node cli.js --all --changed-files --max-semantic-files=300 --format=summary
|
|
63
|
+
|
|
64
|
+
# Target specific modules
|
|
65
|
+
node cli.js --all --input=src/auth --max-semantic-files=1000 --format=summary
|
|
66
|
+
node cli.js --all --input=src/api --max-semantic-files=1000 --format=summary
|
|
67
|
+
|
|
68
|
+
# Use file patterns to focus on critical code
|
|
69
|
+
node cli.js --all --include="src/**/*.ts" --exclude="**/*.test.*" --max-semantic-files=1500
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Strategy 2: CI/CD Pipeline Optimization
|
|
73
|
+
|
|
74
|
+
Optimize for different CI stages:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# PR checks: Fast semantic analysis
|
|
78
|
+
node cli.js --all --changed-files --max-semantic-files=300 --format=github --no-ai
|
|
79
|
+
|
|
80
|
+
# Nightly builds: Medium coverage
|
|
81
|
+
node cli.js --all --input=. --max-semantic-files=1000 --format=json --output=nightly.json
|
|
82
|
+
|
|
83
|
+
# Weekly reports: Comprehensive analysis
|
|
84
|
+
node cli.js --all --input=. --max-semantic-files=-1 --format=detailed --output=weekly.json
|
|
85
|
+
|
|
86
|
+
# Release validation: Full coverage with baseline
|
|
87
|
+
node cli.js --all --input=. --max-semantic-files=2000 --baseline=last-release.json
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Strategy 3: Rule-Based Prioritization
|
|
91
|
+
|
|
92
|
+
Different limits for different rule types:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Phase 1: Critical security (fast heuristic rules)
|
|
96
|
+
node cli.js --security --input=. --max-semantic-files=0 --format=summary
|
|
97
|
+
|
|
98
|
+
# Phase 2: Code quality basics
|
|
99
|
+
node cli.js --rules=C006,C019,C029 --input=. --max-semantic-files=500 --format=summary
|
|
100
|
+
|
|
101
|
+
# Phase 3: Advanced semantic rules (targeted)
|
|
102
|
+
node cli.js --rules=C047 --input=src --max-semantic-files=1000 --format=summary
|
|
103
|
+
|
|
104
|
+
# Phase 4: Full comprehensive scan
|
|
105
|
+
node cli.js --all --input=. --max-semantic-files=-1 --format=detailed
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Strategy 4: Monorepo Management
|
|
109
|
+
|
|
110
|
+
For large monorepos with multiple packages:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Analyze each package separately
|
|
114
|
+
for package in packages/*/; do
|
|
115
|
+
node cli.js --all --input="$package" --max-semantic-files=1000 \
|
|
116
|
+
--format=json --output="${package//\//-}-report.json"
|
|
117
|
+
done
|
|
118
|
+
|
|
119
|
+
# Focus on core packages first
|
|
120
|
+
node cli.js --all --input=packages/core --max-semantic-files=2000 --format=summary
|
|
121
|
+
node cli.js --all --input=packages/api --max-semantic-files=1500 --format=summary
|
|
122
|
+
node cli.js --all --input=packages/ui --max-semantic-files=1000 --format=summary
|
|
123
|
+
|
|
124
|
+
# Changed files across the entire monorepo
|
|
125
|
+
node cli.js --all --changed-files --max-semantic-files=500 --format=summary
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## 📈 Performance Monitoring
|
|
129
|
+
|
|
130
|
+
### Memory & Time Tracking
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# Monitor performance with different limits
|
|
134
|
+
time node cli.js --all --input=. --max-semantic-files=500 --format=summary
|
|
135
|
+
time node cli.js --all --input=. --max-semantic-files=1000 --format=summary
|
|
136
|
+
time node cli.js --all --input=. --max-semantic-files=2000 --format=summary
|
|
137
|
+
|
|
138
|
+
# Memory-conscious analysis for CI
|
|
139
|
+
node cli.js --all --input=. --max-semantic-files=300 --max-concurrent=2 --format=summary
|
|
140
|
+
|
|
141
|
+
# Debug file loading behavior
|
|
142
|
+
node cli.js --all --input=. --max-semantic-files=1000 --verbose --debug
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Coverage Analysis
|
|
146
|
+
|
|
147
|
+
Check what percentage of your project is being analyzed:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# Show file loading statistics
|
|
151
|
+
node cli.js --all --input=. --max-semantic-files=1000 --verbose --format=summary
|
|
152
|
+
|
|
153
|
+
# Compare different limits
|
|
154
|
+
node cli.js --all --input=. --max-semantic-files=500 --verbose --dry-run
|
|
155
|
+
node cli.js --all --input=. --max-semantic-files=1000 --verbose --dry-run
|
|
156
|
+
node cli.js --all --input=. --max-semantic-files=-1 --verbose --dry-run
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## 🎛️ Configuration Files
|
|
160
|
+
|
|
161
|
+
### sunlint.config.json
|
|
162
|
+
|
|
163
|
+
Create a configuration file for consistent settings:
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
{
|
|
167
|
+
"performance": {
|
|
168
|
+
"maxSemanticFiles": 1000,
|
|
169
|
+
"maxConcurrentRules": 5,
|
|
170
|
+
"timeoutMs": 30000
|
|
171
|
+
},
|
|
172
|
+
"input": ["src", "lib"],
|
|
173
|
+
"exclude": [
|
|
174
|
+
"**/*.test.*",
|
|
175
|
+
"**/*.d.ts",
|
|
176
|
+
"**/generated/**"
|
|
177
|
+
],
|
|
178
|
+
"output": {
|
|
179
|
+
"format": "summary"
|
|
180
|
+
},
|
|
181
|
+
"engines": {
|
|
182
|
+
"semantic": {
|
|
183
|
+
"enabled": true,
|
|
184
|
+
"fileLimit": 1000
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Environment-Specific Configs
|
|
191
|
+
|
|
192
|
+
Different configs for different environments:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
# Development (fast feedback)
|
|
196
|
+
cp config/sunlint.dev.json sunlint.config.json
|
|
197
|
+
node cli.js --all --input=.
|
|
198
|
+
|
|
199
|
+
# CI (balanced coverage)
|
|
200
|
+
cp config/sunlint.ci.json sunlint.config.json
|
|
201
|
+
node cli.js --all --changed-files
|
|
202
|
+
|
|
203
|
+
# Release (comprehensive)
|
|
204
|
+
cp config/sunlint.release.json sunlint.config.json
|
|
205
|
+
node cli.js --all --input=.
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## 💡 Best Practices
|
|
209
|
+
|
|
210
|
+
### 1. Start Conservative, Scale Up
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
# Begin with conservative limits
|
|
214
|
+
node cli.js --all --input=. --max-semantic-files=500 --format=summary
|
|
215
|
+
|
|
216
|
+
# Gradually increase if performance allows
|
|
217
|
+
node cli.js --all --input=. --max-semantic-files=1000 --format=summary
|
|
218
|
+
node cli.js --all --input=. --max-semantic-files=1500 --format=summary
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### 2. Use Different Limits for Different Contexts
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
# Daily development: Focus on changed files
|
|
225
|
+
alias sunlint-dev="node cli.js --all --changed-files --max-semantic-files=300"
|
|
226
|
+
|
|
227
|
+
# Code review: Medium coverage
|
|
228
|
+
alias sunlint-review="node cli.js --all --changed-files --max-semantic-files=500"
|
|
229
|
+
|
|
230
|
+
# Release preparation: Full coverage
|
|
231
|
+
alias sunlint-release="node cli.js --all --input=. --max-semantic-files=-1"
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### 3. Monitor and Adjust
|
|
235
|
+
|
|
236
|
+
Track your analysis performance over time:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# Create performance baseline
|
|
240
|
+
echo "Project size: $(find . -name '*.ts' -o -name '*.js' | wc -l) files"
|
|
241
|
+
time node cli.js --all --input=. --max-semantic-files=1000 --format=summary
|
|
242
|
+
|
|
243
|
+
# Adjust based on CI constraints
|
|
244
|
+
if [[ $CI_MEMORY_LIMIT -lt 4096 ]]; then
|
|
245
|
+
SEMANTIC_LIMIT=500
|
|
246
|
+
else
|
|
247
|
+
SEMANTIC_LIMIT=1000
|
|
248
|
+
fi
|
|
249
|
+
|
|
250
|
+
node cli.js --all --input=. --max-semantic-files=$SEMANTIC_LIMIT --format=summary
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### 4. Combine with File Targeting
|
|
254
|
+
|
|
255
|
+
Use semantic limits together with file patterns:
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
# Focus semantic analysis on source files only
|
|
259
|
+
node cli.js --all --include="src/**/*.ts" --exclude="**/*.test.*" --max-semantic-files=1500
|
|
260
|
+
|
|
261
|
+
# Analyze tests separately with lower limits
|
|
262
|
+
node cli.js --all --include="**/*.test.*" --max-semantic-files=500
|
|
263
|
+
|
|
264
|
+
# Target critical modules with higher limits
|
|
265
|
+
node cli.js --all --input=src/security --max-semantic-files=2000
|
|
266
|
+
node cli.js --all --input=src/api --max-semantic-files=1500
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## 🔍 Troubleshooting
|
|
270
|
+
|
|
271
|
+
### Memory Issues
|
|
272
|
+
|
|
273
|
+
If you encounter out-of-memory errors:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
# Reduce semantic file limit
|
|
277
|
+
node cli.js --all --input=. --max-semantic-files=500
|
|
278
|
+
|
|
279
|
+
# Disable semantic analysis completely
|
|
280
|
+
node cli.js --all --input=. --max-semantic-files=0
|
|
281
|
+
|
|
282
|
+
# Reduce concurrent rules
|
|
283
|
+
node cli.js --all --input=. --max-semantic-files=1000 --max-concurrent=2
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Slow Analysis
|
|
287
|
+
|
|
288
|
+
If analysis takes too long:
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
# Use incremental analysis
|
|
292
|
+
node cli.js --all --changed-files --max-semantic-files=300
|
|
293
|
+
|
|
294
|
+
# Focus on specific directories
|
|
295
|
+
node cli.js --all --input=src/critical --max-semantic-files=1000
|
|
296
|
+
|
|
297
|
+
# Use timeout limits
|
|
298
|
+
node cli.js --all --input=. --max-semantic-files=1000 --timeout=60000
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Missed Violations
|
|
302
|
+
|
|
303
|
+
If you suspect violations are being missed:
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
# Run comprehensive analysis periodically
|
|
307
|
+
node cli.js --all --input=. --max-semantic-files=-1 --format=detailed
|
|
308
|
+
|
|
309
|
+
# Compare different limits
|
|
310
|
+
node cli.js --all --input=. --max-semantic-files=1000 --output=report-1k.json
|
|
311
|
+
node cli.js --all --input=. --max-semantic-files=-1 --output=report-full.json
|
|
312
|
+
diff report-1k.json report-full.json
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## 📚 Related Documentation
|
|
316
|
+
|
|
317
|
+
- [Command Examples](./COMMAND-EXAMPLES.md) - Complete CLI usage examples
|
|
318
|
+
- [Configuration Guide](./CONFIGURATION.md) - Detailed configuration options
|
|
319
|
+
- [CI/CD Guide](./CI-CD-GUIDE.md) - Integration with CI/CD pipelines
|
|
320
|
+
- [Architecture](./ARCHITECTURE.md) - Technical implementation details
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
**💡 Pro Tip**: For projects with 2000+ files, consider breaking analysis into modules and running them in parallel, rather than analyzing everything at once.
|
|
@@ -25,7 +25,8 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
25
25
|
this.astRegistry = ASTModuleRegistry;
|
|
26
26
|
|
|
27
27
|
// ts-morph as core technology for heuristic engine
|
|
28
|
-
|
|
28
|
+
// Note: semantic engine will be initialized in initialize() with proper config
|
|
29
|
+
this.semanticEngine = null;
|
|
29
30
|
this.semanticRules = new Map();
|
|
30
31
|
this.symbolTableEnabled = false;
|
|
31
32
|
|
|
@@ -55,8 +56,13 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
55
56
|
// Initialize rule adapter
|
|
56
57
|
await this.ruleAdapter.initialize();
|
|
57
58
|
|
|
58
|
-
// Load available rules from unified registry
|
|
59
|
-
|
|
59
|
+
// Load available rules from unified registry (OPTIMIZED: skip for performance)
|
|
60
|
+
// Rules will be loaded on-demand in analyze() method
|
|
61
|
+
if (config.loadAllRules) {
|
|
62
|
+
await this.loadRulesFromRegistry(config);
|
|
63
|
+
} else if (this.verbose) {
|
|
64
|
+
console.log(`⚡ [HeuristicEngine] Skipping bulk rule loading for performance - will load on-demand`);
|
|
65
|
+
}
|
|
60
66
|
|
|
61
67
|
this.initialized = true;
|
|
62
68
|
if (this.verbose) {
|
|
@@ -74,13 +80,25 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
74
80
|
|
|
75
81
|
/**
|
|
76
82
|
* Initialize ts-morph Symbol Table as core requirement
|
|
83
|
+
* OPTIMIZED: Use targeted files instead of entire project for better performance
|
|
77
84
|
*/
|
|
78
85
|
async initializeSymbolTable(config) {
|
|
79
86
|
const projectPath = config?.projectPath || process.cwd();
|
|
80
87
|
|
|
81
88
|
try {
|
|
82
|
-
//
|
|
83
|
-
const
|
|
89
|
+
// Initialize semantic engine with config options including maxSemanticFiles
|
|
90
|
+
const semanticOptions = {
|
|
91
|
+
maxSemanticFiles: config?.maxSemanticFiles,
|
|
92
|
+
verbose: this.verbose,
|
|
93
|
+
...config?.semanticOptions
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
this.semanticEngine = new SemanticEngine(semanticOptions);
|
|
97
|
+
// Pass verbose option to semantic engine
|
|
98
|
+
this.semanticEngine.verbose = this.verbose;
|
|
99
|
+
|
|
100
|
+
// ts-morph is now a core dependency - but optimized for targeted files
|
|
101
|
+
const success = await this.semanticEngine.initialize(projectPath, config?.targetFiles);
|
|
84
102
|
|
|
85
103
|
if (success) {
|
|
86
104
|
this.semanticEnabled = true;
|
|
@@ -276,6 +294,43 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
276
294
|
}
|
|
277
295
|
}
|
|
278
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Lazy load a single rule on-demand
|
|
299
|
+
* @param {string} ruleId - Rule ID to load
|
|
300
|
+
* @param {Object} options - Loading options
|
|
301
|
+
*/
|
|
302
|
+
async lazyLoadRule(ruleId, options = {}) {
|
|
303
|
+
try {
|
|
304
|
+
const ruleDefinition = this.unifiedRegistry.getRuleDefinition(ruleId);
|
|
305
|
+
|
|
306
|
+
if (!ruleDefinition) {
|
|
307
|
+
if (options.verbose) {
|
|
308
|
+
console.warn(`⚠️ [HeuristicEngine] Rule definition not found for ${ruleId}`);
|
|
309
|
+
}
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Check if rule supports heuristic engine
|
|
314
|
+
if (!this.unifiedRegistry.isRuleSupported(ruleId, 'heuristic')) {
|
|
315
|
+
if (options.verbose) {
|
|
316
|
+
console.warn(`⚠️ [HeuristicEngine] Rule ${ruleId} not supported by heuristic engine`);
|
|
317
|
+
}
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (options.verbose) {
|
|
322
|
+
console.log(`🔄 [HeuristicEngine] Lazy loading rule ${ruleId}...`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
await this.loadRuleFromDefinition(ruleDefinition);
|
|
326
|
+
|
|
327
|
+
} catch (error) {
|
|
328
|
+
if (options.verbose) {
|
|
329
|
+
console.warn(`⚠️ [HeuristicEngine] Failed to lazy load rule ${ruleId}:`, error.message);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
279
334
|
/**
|
|
280
335
|
* Manually load C047 semantic rule (special case)
|
|
281
336
|
*/
|
|
@@ -407,23 +462,23 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
407
462
|
}
|
|
408
463
|
|
|
409
464
|
/**
|
|
410
|
-
* Register semantic rule
|
|
465
|
+
* Register semantic rule (lazy initialization)
|
|
411
466
|
*/
|
|
412
467
|
async registerSemanticRule(ruleId, analyzerClass, metadata) {
|
|
413
468
|
try {
|
|
414
|
-
|
|
415
|
-
instance.initialize(this.semanticEngine);
|
|
416
|
-
|
|
469
|
+
// Store rule class and metadata for lazy initialization
|
|
417
470
|
this.semanticRules.set(ruleId, {
|
|
418
|
-
|
|
471
|
+
analyzerClass,
|
|
419
472
|
metadata,
|
|
420
|
-
type: 'semantic'
|
|
473
|
+
type: 'semantic',
|
|
474
|
+
initialized: false,
|
|
475
|
+
instance: null
|
|
421
476
|
});
|
|
422
477
|
|
|
423
478
|
this.supportedRulesList.push(ruleId);
|
|
424
479
|
|
|
425
480
|
if (this.verbose) {
|
|
426
|
-
console.log(`🧠 Registered semantic rule: ${ruleId}`);
|
|
481
|
+
console.log(`🧠 Registered semantic rule: ${ruleId} (lazy initialization)`);
|
|
427
482
|
}
|
|
428
483
|
|
|
429
484
|
} catch (error) {
|
|
@@ -431,6 +486,34 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
431
486
|
}
|
|
432
487
|
}
|
|
433
488
|
|
|
489
|
+
/**
|
|
490
|
+
* Initialize semantic rule on-demand
|
|
491
|
+
*/
|
|
492
|
+
async initializeSemanticRule(ruleId) {
|
|
493
|
+
const ruleEntry = this.semanticRules.get(ruleId);
|
|
494
|
+
if (!ruleEntry || ruleEntry.initialized) {
|
|
495
|
+
return ruleEntry?.instance;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
try {
|
|
499
|
+
const instance = new ruleEntry.analyzerClass(ruleId);
|
|
500
|
+
instance.initialize(this.semanticEngine, { verbose: this.verbose });
|
|
501
|
+
|
|
502
|
+
// Update entry with initialized instance
|
|
503
|
+
ruleEntry.instance = instance;
|
|
504
|
+
ruleEntry.initialized = true;
|
|
505
|
+
|
|
506
|
+
if (this.verbose) {
|
|
507
|
+
console.log(`🔧 Rule ${ruleId} initialized with semantic analysis`);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return instance;
|
|
511
|
+
} catch (error) {
|
|
512
|
+
console.warn(`⚠️ Failed to initialize semantic rule ${ruleId}:`, error.message);
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
434
517
|
/**
|
|
435
518
|
* Register traditional (heuristic/AST/regex) rule
|
|
436
519
|
*/
|
|
@@ -563,6 +646,14 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
563
646
|
await this.manuallyLoadC047();
|
|
564
647
|
}
|
|
565
648
|
|
|
649
|
+
// Lazy load rule if not already loaded
|
|
650
|
+
if (!this.isRuleSupported(rule.id)) {
|
|
651
|
+
if (options.verbose) {
|
|
652
|
+
console.log(`🔄 [HeuristicEngine] Lazy loading rule ${rule.id}...`);
|
|
653
|
+
}
|
|
654
|
+
await this.lazyLoadRule(rule.id, options);
|
|
655
|
+
}
|
|
656
|
+
|
|
566
657
|
if (!this.isRuleSupported(rule.id)) {
|
|
567
658
|
if (options.verbose) {
|
|
568
659
|
console.warn(`⚠️ Rule ${rule.id} not supported by Heuristic engine, skipping...`);
|
|
@@ -629,7 +720,13 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
629
720
|
}
|
|
630
721
|
|
|
631
722
|
try {
|
|
632
|
-
|
|
723
|
+
// Initialize rule on-demand (lazy initialization)
|
|
724
|
+
const ruleInstance = await this.initializeSemanticRule(rule.id);
|
|
725
|
+
if (!ruleInstance) {
|
|
726
|
+
console.warn(`⚠️ Failed to initialize semantic rule ${rule.id}`);
|
|
727
|
+
return [];
|
|
728
|
+
}
|
|
729
|
+
|
|
633
730
|
const allViolations = [];
|
|
634
731
|
|
|
635
732
|
// Run semantic analysis for each file
|
|
@@ -677,6 +774,15 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
677
774
|
async analyzeRule(rule, filesByLanguage, options) {
|
|
678
775
|
// Get full rule ID (C029 -> C029_catch_block_logging)
|
|
679
776
|
const fullRuleId = this.getFullRuleId(rule.id);
|
|
777
|
+
|
|
778
|
+
// Lazy load rule if not already loaded
|
|
779
|
+
if (!this.ruleAnalyzers.has(fullRuleId)) {
|
|
780
|
+
if (options.verbose) {
|
|
781
|
+
console.log(`🔄 [HeuristicEngine] Lazy loading rule ${rule.id} for analysis...`);
|
|
782
|
+
}
|
|
783
|
+
await this.lazyLoadRule(rule.id, options);
|
|
784
|
+
}
|
|
785
|
+
|
|
680
786
|
const analyzerInfo = this.ruleAnalyzers.get(fullRuleId);
|
|
681
787
|
|
|
682
788
|
if (!analyzerInfo) {
|
|
@@ -691,13 +797,26 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
691
797
|
// Create analyzer instance from class
|
|
692
798
|
const AnalyzerClass = analyzerInfo.class;
|
|
693
799
|
try {
|
|
694
|
-
analyzer = new AnalyzerClass({
|
|
800
|
+
analyzer = new AnalyzerClass({
|
|
801
|
+
verbose: options.verbose,
|
|
802
|
+
semanticEngine: this.semanticEngine
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
// Initialize with semantic engine if method exists
|
|
806
|
+
if (analyzer.initialize && typeof analyzer.initialize === 'function') {
|
|
807
|
+
await analyzer.initialize(this.semanticEngine);
|
|
808
|
+
}
|
|
695
809
|
} catch (constructorError) {
|
|
696
810
|
throw new Error(`Failed to instantiate analyzer class: ${constructorError.message}`);
|
|
697
811
|
}
|
|
698
812
|
} else if (analyzerInfo.type === 'instance') {
|
|
699
813
|
// Use existing analyzer instance
|
|
700
814
|
analyzer = analyzerInfo.instance;
|
|
815
|
+
|
|
816
|
+
// Initialize existing instance with semantic engine if method exists
|
|
817
|
+
if (analyzer.initialize && typeof analyzer.initialize === 'function') {
|
|
818
|
+
await analyzer.initialize(this.semanticEngine);
|
|
819
|
+
}
|
|
701
820
|
} else {
|
|
702
821
|
throw new Error(`Unknown analyzer type: ${analyzerInfo.type}`);
|
|
703
822
|
}
|
|
@@ -730,7 +849,7 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
730
849
|
rule.id,
|
|
731
850
|
languageFiles,
|
|
732
851
|
language,
|
|
733
|
-
{ ...ruleConfig, ...options }
|
|
852
|
+
{ ...ruleConfig, ...options, semanticEngine: this.semanticEngine }
|
|
734
853
|
);
|
|
735
854
|
|
|
736
855
|
allViolations.push(...languageViolations);
|
|
@@ -1303,4 +1422,4 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
1303
1422
|
}
|
|
1304
1423
|
}
|
|
1305
1424
|
|
|
1306
|
-
module.exports = HeuristicEngine;
|
|
1425
|
+
module.exports = HeuristicEngine;
|
|
@@ -22,7 +22,6 @@ const c043 = require("./rules/common/c043-no-console-or-print.js");
|
|
|
22
22
|
const c047 = require("./rules/common/c047-no-duplicate-retry-logic.js");
|
|
23
23
|
const c072 = require("./rules/common/c072-one-assert-per-test.js");
|
|
24
24
|
const c075 = require("./rules/common/c075-explicit-function-return-types.js");
|
|
25
|
-
const c076 = require("./rules/common/c076-single-behavior-per-test.js");
|
|
26
25
|
|
|
27
26
|
// 📘 TypeScript Rules (T-series)
|
|
28
27
|
const t002 = require("./rules/typescript/t002-interface-prefix-i.js");
|
|
@@ -103,7 +102,6 @@ module.exports = {
|
|
|
103
102
|
"c047-no-duplicate-retry-logic": c047,
|
|
104
103
|
"c072-one-assert-per-test": c072,
|
|
105
104
|
"c075-explicit-function-return-types": c075,
|
|
106
|
-
"c076-single-behavior-per-test": c076,
|
|
107
105
|
"t002-interface-prefix-i": t002,
|
|
108
106
|
"t003-ts-ignore-reason": t003,
|
|
109
107
|
"t004-no-empty-type": t004,
|
|
@@ -92,6 +92,59 @@ const c003Rule = {
|
|
|
92
92
|
return false;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
function isMathContext(node, name) {
|
|
96
|
+
// Check for math variable patterns
|
|
97
|
+
const mathPatterns = [
|
|
98
|
+
// Coordinate pairs: x1, y1, x2, y2
|
|
99
|
+
/^[xyz][12]$/i,
|
|
100
|
+
// Delta notation: dx, dy, dt, dr
|
|
101
|
+
/^d[xyztr]$/i,
|
|
102
|
+
// Math constants: a, b, c in equations
|
|
103
|
+
/^[abc]$/i,
|
|
104
|
+
// Vector components: vx, vy, vz
|
|
105
|
+
/^v[xyz]$/i,
|
|
106
|
+
// Position/point notation: p1, p2
|
|
107
|
+
/^p\d+$/i
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
if (mathPatterns.some(pattern => pattern.test(name))) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Check if we're in a math function context
|
|
115
|
+
let parent = node.parent;
|
|
116
|
+
while (parent) {
|
|
117
|
+
if (parent.type === 'FunctionDeclaration' || parent.type === 'FunctionExpression' || parent.type === 'ArrowFunctionExpression') {
|
|
118
|
+
const functionName = parent.id && parent.id.name;
|
|
119
|
+
if (functionName && /^(distance|calculate|compute|solve|formula|algorithm|equation|math)/i.test(functionName)) {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
break; // Don't check beyond the immediate function
|
|
123
|
+
}
|
|
124
|
+
parent = parent.parent;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Check if we're in a context with Math operations
|
|
128
|
+
let currentNode = node.parent;
|
|
129
|
+
while (currentNode) {
|
|
130
|
+
if (currentNode.type === 'CallExpression') {
|
|
131
|
+
const callee = currentNode.callee;
|
|
132
|
+
if (callee && callee.object && callee.object.name === 'Math') {
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
if (callee && callee.name && /^(sqrt|pow|abs|sin|cos|tan|distance|calculate)$/i.test(callee.name)) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (currentNode.type === 'BinaryExpression' && ['+', '-', '*', '/'].includes(currentNode.operator)) {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
currentNode = currentNode.parent;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
|
|
95
148
|
function checkVariableName(node, name) {
|
|
96
149
|
// Safety checks
|
|
97
150
|
if (!node || !name || typeof name !== 'string') {
|
|
@@ -112,7 +165,7 @@ const c003Rule = {
|
|
|
112
165
|
|
|
113
166
|
// Single character check
|
|
114
167
|
if (name.length === 1) {
|
|
115
|
-
if (!allowedSingleChar.has(name.toLowerCase()) && !isCounterContext(node)) {
|
|
168
|
+
if (!allowedSingleChar.has(name.toLowerCase()) && !isCounterContext(node) && !isMathContext(node, name)) {
|
|
116
169
|
context.report({
|
|
117
170
|
node,
|
|
118
171
|
messageId: "singleChar",
|
|
@@ -137,6 +190,11 @@ const c003Rule = {
|
|
|
137
190
|
return;
|
|
138
191
|
}
|
|
139
192
|
|
|
193
|
+
// Check for math context before flagging as unclear
|
|
194
|
+
if (isMathContext(node, name)) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
140
198
|
// Check for unclear/generic names
|
|
141
199
|
if (unclearNames.has(name.toLowerCase())) {
|
|
142
200
|
context.report({
|