@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
package/CONTRIBUTING.md
CHANGED
|
@@ -1,698 +1,342 @@
|
|
|
1
|
-
# Contributing to
|
|
1
|
+
# Contributing to SunLint - Rule Development Guide
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## ๐ฏ Quick Start for Rule Development
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This guide focuses on developing new rules for SunLint. Based on practical experience from refactoring rules like C013, C035, we recommend a **symbol-based only** approach for maximum accuracy.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- Node.js 16+
|
|
9
|
-
- npm 8+
|
|
10
|
-
- Git
|
|
7
|
+
## ๐ Rule Development Steps
|
|
11
8
|
|
|
12
|
-
###
|
|
9
|
+
### Step 1: Register Rule in Registry
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
# Clone the repository
|
|
16
|
-
git clone https://github.com/sun-engineering/sunlint.git
|
|
17
|
-
cd sunlint
|
|
18
|
-
|
|
19
|
-
# Install dependencies
|
|
20
|
-
npm install
|
|
21
|
-
|
|
22
|
-
# Run tests
|
|
23
|
-
npm test
|
|
24
|
-
|
|
25
|
-
# Try the CLI locally
|
|
26
|
-
node cli.js --help
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## ๐ **Coding Standards**
|
|
30
|
-
|
|
31
|
-
When contributing to Sun Lint, please follow these coding rules:
|
|
32
|
-
|
|
33
|
-
### **Code Quality Rules**
|
|
34
|
-
- **Rule C005** โ Each function should do one thing only
|
|
35
|
-
- **Rule C006** โ Function names must be verb/verb-noun
|
|
36
|
-
- **Rule C007** โ Avoid comments that just describe the code
|
|
37
|
-
- **Rule C012** โ Separate Command and Query operations (CQS principle)
|
|
38
|
-
- **Rule C014** โ Use Dependency Injection instead of direct instantiation
|
|
39
|
-
- **Rule C015** โ Use domain language in class/function names
|
|
40
|
-
- **Rule C019** โ Don't use `error` log level for non-critical errors
|
|
41
|
-
- **Rule C031** โ Keep validation logic separate
|
|
42
|
-
- **Rule C032** โ Don't call external APIs in constructors or static blocks
|
|
43
|
-
- **Rule C033** โ Separate processing logic and data queries in service layer
|
|
44
|
-
- **Rule C034** โ Limit direct access to global state in domain logic
|
|
45
|
-
- **Rule C035** โ When handling errors, log complete relevant information
|
|
46
|
-
- **Rule C037** โ API handlers should return standard response objects (not raw strings)
|
|
47
|
-
- **Rule C038** โ Avoid logic depending on file/module loading order
|
|
48
|
-
- **Rule C040** โ Don't scatter validation logic across multiple classes
|
|
49
|
-
|
|
50
|
-
## ๐ง **Development Workflow**
|
|
11
|
+
Add your rule to `config/rules/enhanced-rules-registry.json`:
|
|
51
12
|
|
|
52
|
-
### **SunLint Architecture Overview**
|
|
53
|
-
|
|
54
|
-
SunLint uses a **multi-engine architecture** with rule mapping system:
|
|
55
|
-
|
|
56
|
-
- **Heuristic Engine**: Pattern-based analysis using AST (Abstract Syntax Tree)
|
|
57
|
-
- **ESLint Engine**: JavaScript/TypeScript linting using ESLint rules
|
|
58
|
-
- **OpenAI Engine**: AI-powered code analysis
|
|
59
|
-
|
|
60
|
-
**Rule Configuration Files:**
|
|
61
|
-
- `rules/` - Unified rule registry (auto-generated from origin-rules)
|
|
62
|
-
- `config/eslint-rule-mapping.json` - ESLint engine rule mappings
|
|
63
|
-
- `origin-rules/` - Original rule definitions (markdown format)
|
|
64
|
-
|
|
65
|
-
### **Adding a New Rule (Current Process)**
|
|
66
|
-
|
|
67
|
-
#### **Step 1: Choose Rule Implementation Approach**
|
|
68
|
-
|
|
69
|
-
**Option A: ESLint Engine Rule (Recommended for JS/TS)**
|
|
70
|
-
For JavaScript/TypeScript rules that can use existing ESLint rules:
|
|
71
|
-
|
|
72
|
-
1. **Add to ESLint mapping**: Edit `config/eslint-rule-mapping.json`
|
|
73
13
|
```json
|
|
74
14
|
{
|
|
75
|
-
"
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
1.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
meta: {
|
|
93
|
-
type: 'problem',
|
|
94
|
-
docs: { description: 'Detect hardcoded configuration values' },
|
|
95
|
-
schema: []
|
|
96
|
-
},
|
|
97
|
-
create(context) {
|
|
98
|
-
return {
|
|
99
|
-
VariableDeclaration(node) {
|
|
100
|
-
// Rule C005: Single responsibility - detect hardcoded values
|
|
101
|
-
if (this.isHardcodedConfig(node)) {
|
|
102
|
-
context.report({
|
|
103
|
-
node,
|
|
104
|
-
message: 'Avoid hardcoded configuration values'
|
|
105
|
-
});
|
|
15
|
+
"rules": {
|
|
16
|
+
"C010": {
|
|
17
|
+
"name": "Limit Block Nesting",
|
|
18
|
+
"description": "Limit nested blocks (if/for/while/switch) to maximum 3 levels for readability",
|
|
19
|
+
"category": "complexity",
|
|
20
|
+
"severity": "warning",
|
|
21
|
+
"languages": ["typescript", "javascript", "dart", "kotlin"],
|
|
22
|
+
"analyzer": "./rules/common/C010_limit_block_nesting/analyzer.js",
|
|
23
|
+
"config": "./rules/common/C010_limit_block_nesting/config.json",
|
|
24
|
+
"version": "1.0.0",
|
|
25
|
+
"status": "stable",
|
|
26
|
+
"tags": ["complexity", "readability", "nesting", "maintainability"],
|
|
27
|
+
"strategy": {
|
|
28
|
+
"preferred": "ast",
|
|
29
|
+
"fallbacks": ["ast"],
|
|
30
|
+
"accuracy": {
|
|
31
|
+
"ast": 95
|
|
106
32
|
}
|
|
33
|
+
},
|
|
34
|
+
"engineMappings": {
|
|
35
|
+
"eslint": ["complexity", "max-depth"]
|
|
107
36
|
}
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
**For Heuristic Engine Rules:**
|
|
114
|
-
```javascript
|
|
115
|
-
// custom-rules/c042-no-hardcoded-config.js
|
|
116
|
-
const SemanticRuleBase = require('../core/semantic-rule-base');
|
|
117
|
-
|
|
118
|
-
class NoHardcodedConfigRule extends SemanticRuleBase {
|
|
119
|
-
constructor() {
|
|
120
|
-
super('C042', 'No Hardcoded Configuration Values');
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
analyze(node, context) {
|
|
124
|
-
// Rule C005: Single responsibility - focus only on hardcoded config detection
|
|
125
|
-
if (this.isHardcodedConfigValue(node)) {
|
|
126
|
-
return this.createViolation(node, 'Hardcoded configuration value detected');
|
|
127
37
|
}
|
|
128
|
-
return null;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
isHardcodedConfigValue(node) {
|
|
132
|
-
// Rule C031: Keep validation logic separate
|
|
133
|
-
const configPatterns = /^(API_URL|DATABASE_URL|MAX_|MIN_|TIMEOUT|PORT)/;
|
|
134
|
-
return node.type === 'VariableDeclaration' &&
|
|
135
|
-
configPatterns.test(node.declarations[0]?.id?.name);
|
|
136
38
|
}
|
|
137
39
|
}
|
|
138
|
-
|
|
139
|
-
module.exports = NoHardcodedConfigRule;
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
#### **Step 3: Update Rule Registry**
|
|
143
|
-
|
|
144
|
-
Add rule definition to origin rules:
|
|
145
|
-
```markdown
|
|
146
|
-
<!-- origin-rules/common-en.md -->
|
|
147
|
-
## C042: No Hardcoded Configuration Values
|
|
148
|
-
|
|
149
|
-
**Category**: Maintainability
|
|
150
|
-
**Severity**: Warning
|
|
151
|
-
|
|
152
|
-
**Description**: Avoid hardcoding configuration values in business logic
|
|
153
|
-
|
|
154
|
-
**Examples**:
|
|
155
|
-
โ Bad:
|
|
156
|
-
```javascript
|
|
157
|
-
const API_URL = "https://api.example.com";
|
|
158
|
-
const MAX_RETRIES = 5;
|
|
159
40
|
```
|
|
160
41
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
42
|
+
**Key Registry Fields:**
|
|
43
|
+
- `analyzer`: Path to main analyzer file
|
|
44
|
+
- `strategy.preferred`: Use "ast" for symbol-based analysis
|
|
45
|
+
- `strategy.fallbacks`: Recommend ["ast"] only for accuracy
|
|
46
|
+
- `strategy.accuracy`: Expected accuracy percentage
|
|
166
47
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
```javascript
|
|
170
|
-
// test/rules/c042.test.js
|
|
171
|
-
const { testRule } = require('../test-utils');
|
|
172
|
-
|
|
173
|
-
describe('Rule C042: No Hardcoded Configuration Values', () => {
|
|
174
|
-
testRule('C042', {
|
|
175
|
-
valid: [
|
|
176
|
-
'const apiUrl = process.env.API_URL;',
|
|
177
|
-
'const config = loadConfig();'
|
|
178
|
-
],
|
|
179
|
-
invalid: [
|
|
180
|
-
'const API_URL = "https://api.example.com";',
|
|
181
|
-
'const MAX_RETRIES = 5;'
|
|
182
|
-
]
|
|
183
|
-
});
|
|
184
|
-
});
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
#### **Step 5: Test Your Rule**
|
|
48
|
+
### Step 2: Create Rule Directory Structure
|
|
188
49
|
|
|
189
50
|
```bash
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
node cli.js --rule=C042 --engine=heuristic --input=test/fixtures
|
|
51
|
+
mkdir -p rules/common/C010_limit_block_nesting
|
|
52
|
+
cd rules/common/C010_limit_block_nesting
|
|
193
53
|
|
|
194
|
-
#
|
|
195
|
-
|
|
54
|
+
# Required files
|
|
55
|
+
touch analyzer.js # Main orchestrator
|
|
56
|
+
touch symbol-based-analyzer.js # Core analysis logic
|
|
57
|
+
touch config.json # Rule configuration
|
|
196
58
|
```
|
|
197
59
|
|
|
198
|
-
###
|
|
199
|
-
|
|
200
|
-
#### **Heuristic Engine Rules**
|
|
60
|
+
### Step 3: Implement Main Analyzer (analyzer.js)
|
|
201
61
|
|
|
202
|
-
For custom pattern-based analysis:
|
|
203
|
-
|
|
204
|
-
1. **Create Rule Class**:
|
|
205
62
|
```javascript
|
|
206
|
-
// rules/
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
class
|
|
210
|
-
constructor() {
|
|
211
|
-
|
|
63
|
+
// rules/common/C010_limit_block_nesting/analyzer.js
|
|
64
|
+
const C010SymbolBasedAnalyzer = require('./symbol-based-analyzer.js');
|
|
65
|
+
|
|
66
|
+
class C010Analyzer {
|
|
67
|
+
constructor(semanticEngine = null) {
|
|
68
|
+
this.ruleId = 'C010';
|
|
69
|
+
this.ruleName = 'Limit Block Nesting';
|
|
70
|
+
this.description = 'Limit nested blocks to maximum 3 levels';
|
|
71
|
+
this.semanticEngine = semanticEngine;
|
|
72
|
+
this.verbose = false;
|
|
73
|
+
|
|
74
|
+
// Use symbol-based only for accuracy
|
|
75
|
+
this.symbolAnalyzer = new C010SymbolBasedAnalyzer(semanticEngine);
|
|
212
76
|
}
|
|
213
77
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
2. **Add to Registry**:
|
|
222
|
-
```json
|
|
223
|
-
{
|
|
224
|
-
"CXXX": {
|
|
225
|
-
"engineMappings": {
|
|
226
|
-
"heuristic": {
|
|
227
|
-
"implementation": "custom/your-rule",
|
|
228
|
-
"supportedLanguages": ["typescript", "javascript"]
|
|
229
|
-
}
|
|
78
|
+
async initialize(semanticEngine = null) {
|
|
79
|
+
if (semanticEngine) {
|
|
80
|
+
this.semanticEngine = semanticEngine;
|
|
230
81
|
}
|
|
82
|
+
this.verbose = semanticEngine?.verbose || false;
|
|
83
|
+
await this.symbolAnalyzer.initialize(semanticEngine);
|
|
231
84
|
}
|
|
232
|
-
}
|
|
233
|
-
```
|
|
234
85
|
|
|
235
|
-
|
|
86
|
+
async analyzeFileBasic(filePath, options = {}) {
|
|
87
|
+
try {
|
|
88
|
+
// Check if symbol engine is ready
|
|
89
|
+
if (!this.semanticEngine?.isSymbolEngineReady?.() || !this.semanticEngine.project) {
|
|
90
|
+
throw new Error('Symbol engine not available');
|
|
91
|
+
}
|
|
236
92
|
|
|
237
|
-
|
|
93
|
+
if (this.verbose) {
|
|
94
|
+
console.log(`[DEBUG] ๐ฏ C010: Using symbol-based analysis for ${filePath.split('/').pop()}`);
|
|
95
|
+
}
|
|
238
96
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
{
|
|
242
|
-
|
|
243
|
-
"engineMappings": {
|
|
244
|
-
"eslint": {
|
|
245
|
-
"rules": ["no-console", "prefer-const"],
|
|
246
|
-
"config": {
|
|
247
|
-
"no-console": ["error"],
|
|
248
|
-
"prefer-const": ["warn"]
|
|
249
|
-
}
|
|
97
|
+
const violations = await this.symbolAnalyzer.analyzeFileBasic(filePath, options);
|
|
98
|
+
|
|
99
|
+
if (this.verbose) {
|
|
100
|
+
console.log(`[DEBUG] ๐ฏ C010: Symbol-based analysis found ${violations.length} violations`);
|
|
250
101
|
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
```
|
|
255
102
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
meta: {
|
|
261
|
-
type: 'problem',
|
|
262
|
-
docs: { description: 'Your rule description' }
|
|
263
|
-
},
|
|
264
|
-
create(context) {
|
|
265
|
-
return {
|
|
266
|
-
FunctionDeclaration(node) {
|
|
267
|
-
// Your ESLint rule logic
|
|
103
|
+
return violations;
|
|
104
|
+
} catch (error) {
|
|
105
|
+
if (this.verbose) {
|
|
106
|
+
console.error(`[DEBUG] โ C010: Analysis failed: ${error.message}`);
|
|
268
107
|
}
|
|
269
|
-
|
|
108
|
+
throw new Error(`C010 analysis failed: ${error.message}`);
|
|
109
|
+
}
|
|
270
110
|
}
|
|
271
|
-
};
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
#### **OpenAI Engine Rules**
|
|
275
111
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
{
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
"examples": [
|
|
285
|
-
"// Bad example",
|
|
286
|
-
"// Good example"
|
|
287
|
-
],
|
|
288
|
-
"temperature": 0.1,
|
|
289
|
-
"maxTokens": 500
|
|
112
|
+
async analyzeFiles(files, options = {}) {
|
|
113
|
+
const allViolations = [];
|
|
114
|
+
for (const filePath of files) {
|
|
115
|
+
try {
|
|
116
|
+
const violations = await this.analyzeFileBasic(filePath, options);
|
|
117
|
+
allViolations.push(...violations);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.warn(`C010: Skipping ${filePath}: ${error.message}`);
|
|
290
120
|
}
|
|
291
121
|
}
|
|
122
|
+
return allViolations;
|
|
292
123
|
}
|
|
293
124
|
}
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
### **Adding a New Security Rule**
|
|
297
|
-
|
|
298
|
-
Same process as above, just use `"category": "security"` in the rule definition.
|
|
299
|
-
|
|
300
|
-
### **Testing Your New Rule**
|
|
301
|
-
|
|
302
|
-
```bash
|
|
303
|
-
# Test all engines with your new rule
|
|
304
|
-
node cli.js --rule=C042 --input=test/fixtures --format=json
|
|
305
|
-
|
|
306
|
-
# Test specific engine
|
|
307
|
-
node cli.js --rule=C042 --engine=heuristic --input=test/fixtures
|
|
308
|
-
node cli.js --rule=C042 --engine=eslint --input=test/fixtures
|
|
309
|
-
node cli.js --rule=C042 --engine=openai --input=test/fixtures
|
|
310
125
|
|
|
311
|
-
|
|
312
|
-
node validate-system.js
|
|
126
|
+
module.exports = C010Analyzer;
|
|
313
127
|
```
|
|
314
128
|
|
|
315
|
-
###
|
|
129
|
+
### Step 4: Implement Symbol-Based Analyzer
|
|
316
130
|
|
|
317
|
-
|
|
131
|
+
```javascript
|
|
132
|
+
// rules/common/C010_limit_block_nesting/symbol-based-analyzer.js
|
|
133
|
+
const { SyntaxKind } = require('ts-morph');
|
|
134
|
+
|
|
135
|
+
class C010SymbolBasedAnalyzer {
|
|
136
|
+
constructor(semanticEngine = null) {
|
|
137
|
+
this.semanticEngine = semanticEngine;
|
|
138
|
+
this.maxNestingLevel = 3;
|
|
139
|
+
this.verbose = false;
|
|
140
|
+
}
|
|
318
141
|
|
|
319
|
-
|
|
320
|
-
{
|
|
321
|
-
|
|
322
|
-
"id": "CXXX", // Required: Rule ID
|
|
323
|
-
"title": "Rule Title", // Required: Human-readable title
|
|
324
|
-
"description": "Detailed description", // Required: Rule description
|
|
325
|
-
"severity": "error|warning|info", // Required: Severity level
|
|
326
|
-
"category": "quality|security|performance|maintainability", // Required
|
|
327
|
-
"tags": ["tag1", "tag2"], // Optional: Tags for filtering
|
|
328
|
-
"languages": ["typescript", "javascript"], // Optional: Supported languages
|
|
329
|
-
"engineMappings": { // Required: Engine configurations
|
|
330
|
-
"heuristic": {
|
|
331
|
-
"implementation": "custom/rule-name",
|
|
332
|
-
"supportedLanguages": ["typescript"],
|
|
333
|
-
"astTargets": ["FunctionDeclaration"]
|
|
334
|
-
},
|
|
335
|
-
"eslint": {
|
|
336
|
-
"rules": ["eslint-rule-name"],
|
|
337
|
-
"config": { "rule-option": true }
|
|
338
|
-
},
|
|
339
|
-
"openai": {
|
|
340
|
-
"prompt": "AI analysis prompt",
|
|
341
|
-
"examples": ["code example"],
|
|
342
|
-
"temperature": 0.1
|
|
343
|
-
}
|
|
344
|
-
},
|
|
345
|
-
"analysisStrategy": { // Optional: Analysis metadata
|
|
346
|
-
"type": "ast|regex|semantic",
|
|
347
|
-
"patterns": ["pattern1"],
|
|
348
|
-
"astTargets": ["NodeType"],
|
|
349
|
-
"heuristics": ["detection-method"]
|
|
350
|
-
},
|
|
351
|
-
"metadata": { // Optional: Additional metadata
|
|
352
|
-
"author": "Developer Name",
|
|
353
|
-
"created": "2025-08-07",
|
|
354
|
-
"updated": "2025-08-07",
|
|
355
|
-
"version": "1.0.0"
|
|
142
|
+
async initialize(semanticEngine = null) {
|
|
143
|
+
if (semanticEngine) {
|
|
144
|
+
this.semanticEngine = semanticEngine;
|
|
356
145
|
}
|
|
146
|
+
this.verbose = semanticEngine?.verbose || false;
|
|
357
147
|
}
|
|
358
|
-
}
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
### **Architecture Deep Dive**
|
|
362
|
-
|
|
363
|
-
#### **Unified Rule Registry**
|
|
364
|
-
- **Location**: `rules/` (auto-generated from origin-rules)
|
|
365
|
-
- **Source**: `origin-rules/` (markdown files)
|
|
366
|
-
- **ESLint Mappings**: `config/eslint-rule-mapping.json`
|
|
367
|
-
- **Loader**: `core/unified-rule-registry.js`
|
|
368
148
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
โ โโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโโโโ โ
|
|
378
|
-
โ โ Heuristic โ ESLint โ OpenAI โ โ
|
|
379
|
-
โ โ Engine โ Engine โ Engine โ โ
|
|
380
|
-
โ โ (244 rules) โ (57 rules) โ(256 all)โ โ
|
|
381
|
-
โ โโโโโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโโ โ
|
|
382
|
-
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
|
|
383
|
-
โ Rule Configuration โ
|
|
384
|
-
โ โโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
|
|
385
|
-
โ โ ESLint โ Unified Registry โ โ
|
|
386
|
-
โ โ Mappings โ (Generated Rules) โ โ
|
|
387
|
-
โ โ (.json) โ (origin-rules) โ โ
|
|
388
|
-
โ โโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
|
|
389
|
-
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
390
|
-
```
|
|
149
|
+
async analyzeFileBasic(filePath, options = {}) {
|
|
150
|
+
const violations = [];
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
|
|
154
|
+
if (!sourceFile) {
|
|
155
|
+
throw new Error(`Source file not found: ${filePath}`);
|
|
156
|
+
}
|
|
391
157
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
3. **ESLint Mapping**: ESLint engine loads rule mappings from `config/eslint-rule-mapping.json`
|
|
396
|
-
4. **Engine Filtering**: Each engine filters rules based on their capabilities
|
|
397
|
-
5. **Analysis**: Engines analyze code using their specific rule implementations
|
|
158
|
+
if (this.verbose) {
|
|
159
|
+
console.log(`[DEBUG] ๐ C010: Analyzing nesting in ${filePath.split('/').pop()}`);
|
|
160
|
+
}
|
|
398
161
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
162
|
+
// Find nested blocks
|
|
163
|
+
const nestedBlocks = this.findNestedBlocks(sourceFile);
|
|
164
|
+
|
|
165
|
+
for (const block of nestedBlocks) {
|
|
166
|
+
if (block.nestingLevel > this.maxNestingLevel) {
|
|
167
|
+
violations.push({
|
|
168
|
+
ruleId: 'C010',
|
|
169
|
+
message: `Nested block exceeds maximum depth of ${this.maxNestingLevel} (current: ${block.nestingLevel}). Consider extracting to separate functions.`,
|
|
170
|
+
filePath: filePath,
|
|
171
|
+
line: block.line,
|
|
172
|
+
column: block.column,
|
|
173
|
+
severity: 'warning',
|
|
174
|
+
category: 'complexity'
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
403
178
|
|
|
404
|
-
|
|
179
|
+
if (this.verbose) {
|
|
180
|
+
console.log(`[DEBUG] ๐ C010: Found ${violations.length} nesting violations`);
|
|
181
|
+
}
|
|
405
182
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
183
|
+
return violations;
|
|
184
|
+
} catch (error) {
|
|
185
|
+
if (this.verbose) {
|
|
186
|
+
console.error(`[DEBUG] โ C010: Symbol analysis error: ${error.message}`);
|
|
187
|
+
}
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
410
191
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
node
|
|
192
|
+
findNestedBlocks(sourceFile) {
|
|
193
|
+
const blocks = [];
|
|
194
|
+
|
|
195
|
+
function traverse(node, currentDepth = 0) {
|
|
196
|
+
// Check for block statements that increase nesting
|
|
197
|
+
if (this.isNestingNode(node)) {
|
|
198
|
+
currentDepth++;
|
|
199
|
+
|
|
200
|
+
const position = sourceFile.getLineAndColumnAtPos(node.getStart());
|
|
201
|
+
blocks.push({
|
|
202
|
+
node: node,
|
|
203
|
+
nestingLevel: currentDepth,
|
|
204
|
+
line: position.line,
|
|
205
|
+
column: position.column
|
|
206
|
+
});
|
|
207
|
+
}
|
|
415
208
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
209
|
+
// Traverse children
|
|
210
|
+
node.forEachChild(child => traverse.call(this, child, currentDepth));
|
|
211
|
+
}
|
|
419
212
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
node cli.js --rule=C042 --input=test/fixtures
|
|
213
|
+
traverse.call(this, sourceFile, 0);
|
|
214
|
+
return blocks;
|
|
215
|
+
}
|
|
424
216
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
217
|
+
isNestingNode(node) {
|
|
218
|
+
return [
|
|
219
|
+
SyntaxKind.IfStatement,
|
|
220
|
+
SyntaxKind.ForStatement,
|
|
221
|
+
SyntaxKind.ForInStatement,
|
|
222
|
+
SyntaxKind.ForOfStatement,
|
|
223
|
+
SyntaxKind.WhileStatement,
|
|
224
|
+
SyntaxKind.DoStatement,
|
|
225
|
+
SyntaxKind.SwitchStatement,
|
|
226
|
+
SyntaxKind.TryStatement,
|
|
227
|
+
SyntaxKind.CatchClause
|
|
228
|
+
].includes(node.getKind());
|
|
229
|
+
}
|
|
230
|
+
}
|
|
429
231
|
|
|
430
|
-
|
|
431
|
-
node cli.js --rule=C006,C019,C042 --input=test/fixtures
|
|
232
|
+
module.exports = C010SymbolBasedAnalyzer;
|
|
432
233
|
```
|
|
433
234
|
|
|
434
|
-
###
|
|
435
|
-
```bash
|
|
436
|
-
# Create test fixtures
|
|
437
|
-
mkdir -p test/fixtures/c042
|
|
438
|
-
echo 'const API_URL = "https://api.example.com";' > test/fixtures/c042/invalid.ts
|
|
439
|
-
echo 'const apiUrl = process.env.API_URL;' > test/fixtures/c042/valid.ts
|
|
235
|
+
### Step 5: Create Rule Configuration
|
|
440
236
|
|
|
441
|
-
|
|
442
|
-
|
|
237
|
+
```json
|
|
238
|
+
// rules/common/C010_limit_block_nesting/config.json
|
|
239
|
+
{
|
|
240
|
+
"maxNestingLevel": 3,
|
|
241
|
+
"excludePatterns": [
|
|
242
|
+
"**/*.test.js",
|
|
243
|
+
"**/*.spec.js"
|
|
244
|
+
],
|
|
245
|
+
"includePatterns": [
|
|
246
|
+
"**/*.js",
|
|
247
|
+
"**/*.ts",
|
|
248
|
+
"**/*.jsx",
|
|
249
|
+
"**/*.tsx"
|
|
250
|
+
]
|
|
251
|
+
}
|
|
443
252
|
```
|
|
444
253
|
|
|
445
|
-
|
|
446
|
-
```bash
|
|
447
|
-
# Test all engines work together
|
|
448
|
-
npm run test:integration
|
|
449
|
-
|
|
450
|
-
# Test rule registry loading
|
|
451
|
-
npm run test:registry
|
|
452
|
-
|
|
453
|
-
# Performance testing
|
|
454
|
-
npm run test:performance
|
|
455
|
-
```
|
|
254
|
+
## ๐จ Common Pitfalls & Solutions
|
|
456
255
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
- [ ] Rule C035: Error handling includes complete logging
|
|
462
|
-
- [ ] Rule C037: API responses use standard format
|
|
463
|
-
- [ ] Rule C040: Validation logic is centralized
|
|
464
|
-
- [ ] Tests pass and cover edge cases
|
|
465
|
-
- [ ] Documentation updated
|
|
466
|
-
|
|
467
|
-
2. **Submit Pull Request**
|
|
468
|
-
- Clear title and description
|
|
469
|
-
- Reference related issues
|
|
470
|
-
- Include test results
|
|
471
|
-
- Follow template
|
|
472
|
-
- **NEW**: Validate rule registry with `node validate-system.js`
|
|
473
|
-
|
|
474
|
-
3. **Review Criteria**
|
|
475
|
-
- Code quality (follows our own rules!)
|
|
476
|
-
- Rule properly defined in `enhanced-rules-registry.json`
|
|
477
|
-
- All engines can load the rule correctly
|
|
478
|
-
- Test coverage for all supported engines
|
|
479
|
-
- Documentation completeness
|
|
480
|
-
- Performance impact
|
|
481
|
-
- Backward compatibility
|
|
482
|
-
|
|
483
|
-
## ๐ **Documentation**
|
|
484
|
-
|
|
485
|
-
### **Update Documentation**
|
|
486
|
-
When adding features:
|
|
487
|
-
- Update `README.md`
|
|
488
|
-
- Add rule to `enhanced-rules-registry.json` (this is your main documentation!)
|
|
489
|
-
- Update configuration examples
|
|
490
|
-
- Add usage examples
|
|
491
|
-
- Update `RULE_MIGRATION_SUMMARY.md` if changing rule system
|
|
492
|
-
|
|
493
|
-
### **Rule Documentation Template**
|
|
494
|
-
|
|
495
|
-
All rule documentation is now centralized in `enhanced-rules-registry.json`:
|
|
256
|
+
### 1. Symbol Engine Not Ready
|
|
257
|
+
```javascript
|
|
258
|
+
// โ Wrong - Will fail if symbol engine not ready
|
|
259
|
+
const violations = await this.symbolAnalyzer.analyzeFileBasic(filePath);
|
|
496
260
|
|
|
497
|
-
|
|
498
|
-
{
|
|
499
|
-
|
|
500
|
-
"title": "Clear, descriptive rule title",
|
|
501
|
-
"description": "Detailed explanation of what the rule checks, why it matters, and how to fix violations. Follow Rule C015 (domain language) - use clear business terms.",
|
|
502
|
-
"examples": {
|
|
503
|
-
"invalid": [
|
|
504
|
-
"// Bad example that violates the rule",
|
|
505
|
-
"const API_URL = 'https://hardcoded-url.com';"
|
|
506
|
-
],
|
|
507
|
-
"valid": [
|
|
508
|
-
"// Good example that follows the rule",
|
|
509
|
-
"const apiUrl = process.env.API_URL;"
|
|
510
|
-
]
|
|
511
|
-
},
|
|
512
|
-
"fixSuggestions": [
|
|
513
|
-
"Move configuration to environment variables",
|
|
514
|
-
"Use a configuration management system",
|
|
515
|
-
"Extract constants to a separate config file"
|
|
516
|
-
],
|
|
517
|
-
"relatedRules": ["C031", "C034"]
|
|
518
|
-
}
|
|
261
|
+
// โ
Correct - Check engine availability
|
|
262
|
+
if (!this.semanticEngine?.isSymbolEngineReady?.() || !this.semanticEngine.project) {
|
|
263
|
+
throw new Error('Symbol engine not available');
|
|
519
264
|
}
|
|
520
265
|
```
|
|
521
266
|
|
|
522
|
-
###
|
|
267
|
+
### 2. Missing Source File Check
|
|
268
|
+
```javascript
|
|
269
|
+
// โ Wrong - Will crash if file not found
|
|
270
|
+
const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
|
|
523
271
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
{
|
|
528
|
-
"analysisStrategy": {
|
|
529
|
-
"type": "ast",
|
|
530
|
-
"description": "Analyzes AST nodes for hardcoded configuration patterns",
|
|
531
|
-
"astTargets": ["VariableDeclaration", "PropertyAssignment"],
|
|
532
|
-
"patterns": ["literal-values-in-config-context"],
|
|
533
|
-
"complexity": "O(n) where n is number of variable declarations"
|
|
534
|
-
}
|
|
272
|
+
// โ
Correct - Always check source file existence
|
|
273
|
+
const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
|
|
274
|
+
if (!sourceFile) {
|
|
275
|
+
throw new Error(`Source file not found: ${filePath}`);
|
|
535
276
|
}
|
|
536
277
|
```
|
|
537
278
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
{
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
"rationale": "ESLint's no-magic-numbers rule catches hardcoded values",
|
|
546
|
-
"limitations": "May have false positives for legitimate constants",
|
|
547
|
-
"customConfig": {
|
|
548
|
-
"no-magic-numbers": ["error", { "ignore": [0, 1, -1] }]
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
}
|
|
279
|
+
### 3. Improper Error Handling
|
|
280
|
+
```javascript
|
|
281
|
+
// โ Wrong - Silent failures
|
|
282
|
+
try {
|
|
283
|
+
const violations = await this.analyzeFile(filePath);
|
|
284
|
+
} catch (error) {
|
|
285
|
+
return [];
|
|
552
286
|
}
|
|
553
|
-
```
|
|
554
287
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
"context": "Look for URLs, timeouts, limits, and other configuration that might change between environments",
|
|
563
|
-
"examples": [
|
|
564
|
-
"โ const API_URL = 'https://api.example.com';",
|
|
565
|
-
"โ
const API_URL = process.env.API_URL || 'https://default-api.com';"
|
|
566
|
-
]
|
|
567
|
-
}
|
|
288
|
+
// โ
Correct - Proper error propagation
|
|
289
|
+
try {
|
|
290
|
+
const violations = await this.analyzeFile(filePath);
|
|
291
|
+
return violations;
|
|
292
|
+
} catch (error) {
|
|
293
|
+
if (this.verbose) {
|
|
294
|
+
console.error(`Analysis failed: ${error.message}`);
|
|
568
295
|
}
|
|
296
|
+
throw error;
|
|
569
297
|
}
|
|
570
298
|
```
|
|
571
299
|
|
|
572
|
-
##
|
|
573
|
-
|
|
574
|
-
When reporting bugs:
|
|
575
|
-
1. Use clear, descriptive title
|
|
576
|
-
2. Include reproduction steps
|
|
577
|
-
3. Provide sample code
|
|
578
|
-
4. Include environment details
|
|
579
|
-
5. Include sunlint output
|
|
580
|
-
6. **NEW**: Run `node validate-system.js` and include output
|
|
300
|
+
## ๐งช Testing Your Rule
|
|
581
301
|
|
|
582
|
-
## ๐ง **Troubleshooting**
|
|
583
|
-
|
|
584
|
-
### **Common Issues**
|
|
585
|
-
|
|
586
|
-
#### **Rule Not Loading**
|
|
587
302
|
```bash
|
|
588
|
-
#
|
|
589
|
-
node
|
|
303
|
+
# Test single file
|
|
304
|
+
node cli.js --input=test-file.js --rule=C010 --engine=heuristic --verbose
|
|
590
305
|
|
|
591
|
-
#
|
|
592
|
-
node
|
|
593
|
-
```
|
|
306
|
+
# Test project
|
|
307
|
+
node cli.js --input=src/ --rule=C010 --engine=heuristic --max-semantic-files=-1
|
|
594
308
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
# Check engine-specific mapping
|
|
598
|
-
node -e "const registry = require('./core/unified-rule-registry'); registry.getInstance().initialize().then(r => { const rule = r.rules.get('C042'); console.log('ESLint mapping:', rule?.engineMappings?.eslint); })"
|
|
309
|
+
# Performance test
|
|
310
|
+
time node cli.js --input=large-project/ --rule=C010 --engine=heuristic
|
|
599
311
|
```
|
|
600
312
|
|
|
601
|
-
|
|
602
|
-
```bash
|
|
603
|
-
# Common issues:
|
|
604
|
-
# 1. JSON syntax errors in enhanced-rules-registry.json
|
|
605
|
-
# 2. Missing required fields (id, title, description, severity, category)
|
|
606
|
-
# 3. Invalid engine mapping structure
|
|
313
|
+
## ๐ Best Practices
|
|
607
314
|
|
|
608
|
-
|
|
609
|
-
|
|
315
|
+
1. **Use Symbol-Based Only**: More accurate than regex patterns
|
|
316
|
+
2. **Always Check Engine State**: Verify semantic engine is ready
|
|
317
|
+
3. **Handle Errors Gracefully**: Don't silently ignore failures
|
|
318
|
+
4. **Add Debug Logging**: Use `this.verbose` for troubleshooting
|
|
319
|
+
5. **Test on Real Projects**: Validate with large codebases
|
|
320
|
+
6. **Document Accuracy**: Update strategy.accuracy in registry
|
|
610
321
|
|
|
611
|
-
|
|
612
|
-
node validate-system.js
|
|
613
|
-
```
|
|
322
|
+
## ๐ง Development Environment
|
|
614
323
|
|
|
615
|
-
### **Performance Issues**
|
|
616
324
|
```bash
|
|
617
|
-
#
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
# Check memory usage
|
|
621
|
-
node --inspect cli.js --rule=C042 --input=test/fixtures/
|
|
622
|
-
```
|
|
623
|
-
|
|
624
|
-
## ๐ก **Feature Requests**
|
|
625
|
-
|
|
626
|
-
For new features:
|
|
627
|
-
1. Check existing issues first
|
|
628
|
-
2. Describe the use case
|
|
629
|
-
3. Provide examples
|
|
630
|
-
4. Consider implementation complexity
|
|
631
|
-
5. Think about backward compatibility
|
|
632
|
-
|
|
633
|
-
## ๐ **Quick Reference**
|
|
634
|
-
|
|
635
|
-
### **Essential Commands**
|
|
636
|
-
```bash
|
|
637
|
-
# Add ESLint engine rule mapping
|
|
638
|
-
vim config/eslint-rule-mapping.json
|
|
639
|
-
|
|
640
|
-
# Create custom heuristic rule
|
|
641
|
-
vim custom-rules/c042-rule-name.js
|
|
642
|
-
|
|
643
|
-
# Add rule documentation
|
|
644
|
-
vim origin-rules/common-en.md
|
|
645
|
-
|
|
646
|
-
# Test new rule with specific engine
|
|
647
|
-
node cli.js --rule=CXXX --engine=eslint --input=test/fixtures
|
|
648
|
-
node cli.js --rule=CXXX --engine=heuristic --input=test/fixtures
|
|
649
|
-
|
|
650
|
-
# Test strict engine mode (no fallback)
|
|
651
|
-
node cli.js --rule=CXXX --engine=eslint --input=test/fixtures
|
|
325
|
+
# Setup
|
|
326
|
+
npm install
|
|
327
|
+
npm test
|
|
652
328
|
|
|
653
|
-
#
|
|
654
|
-
node cli.js --rule=
|
|
329
|
+
# Development workflow
|
|
330
|
+
node cli.js --input=examples/ --rule=YOUR_RULE --engine=heuristic --verbose
|
|
655
331
|
```
|
|
656
332
|
|
|
657
|
-
|
|
658
|
-
- `config/eslint-rule-mapping.json` - **ESLint engine rule mappings**
|
|
659
|
-
- `origin-rules/` - **Original rule definitions** (markdown format)
|
|
660
|
-
- `rules/` - **Generated rule registry** (auto-generated)
|
|
661
|
-
- `core/unified-rule-registry.js` - Rule registry loader
|
|
662
|
-
- `engines/heuristic-engine.js` - Custom pattern analysis
|
|
663
|
-
- `engines/eslint-engine.js` - JavaScript/TypeScript linting
|
|
664
|
-
- `engines/openai-engine.js` - AI-powered analysis
|
|
665
|
-
- `integrations/eslint/plugin/` - Custom ESLint rules
|
|
666
|
-
- `custom-rules/` - Heuristic engine custom rules
|
|
667
|
-
|
|
668
|
-
### **Rule Development Checklist**
|
|
669
|
-
- [ ] Choose appropriate engine (ESLint for JS/TS, Heuristic for universal)
|
|
670
|
-
- [ ] Add ESLint mapping to `config/eslint-rule-mapping.json` (if using ESLint engine)
|
|
671
|
-
- [ ] Create custom rule implementation (if needed)
|
|
672
|
-
- [ ] Add rule definition to `origin-rules/` (markdown format)
|
|
673
|
-
- [ ] Add test cases and examples
|
|
674
|
-
- [ ] Test with `node cli.js --rule=CXXX --input=test/fixtures`
|
|
675
|
-
- [ ] Test engine-specific behavior (`--engine=eslint`, `--engine=heuristic`)
|
|
676
|
-
- [ ] Update documentation if needed
|
|
677
|
-
|
|
678
|
-
---
|
|
679
|
-
|
|
680
|
-
**๐ Ready to contribute? Start by choosing your engine and editing the appropriate mapping file!**
|
|
681
|
-
|
|
682
|
-
**For ESLint rules**: Edit `config/eslint-rule-mapping.json`
|
|
683
|
-
**For Heuristic rules**: Create files in `custom-rules/`
|
|
684
|
-
**For documentation**: Add to `origin-rules/` markdown files
|
|
685
|
-
|
|
686
|
-
## ๐ค **Community**
|
|
687
|
-
|
|
688
|
-
- **Discord**: [Sun Engineering Discord](https://discord.gg/sun-engineering)
|
|
689
|
-
- **Issues**: [GitHub Issues](https://github.com/sun-engineering/sunlint/issues)
|
|
690
|
-
- **Discussions**: [GitHub Discussions](https://github.com/sun-engineering/sunlint/discussions)
|
|
691
|
-
|
|
692
|
-
## ๐ **License**
|
|
333
|
+
## ๐ Advanced Topics
|
|
693
334
|
|
|
694
|
-
|
|
335
|
+
- **AST Navigation**: Use ts-morph documentation for node traversal
|
|
336
|
+
- **Performance**: Symbol-based analysis is ~15s for 2000+ files
|
|
337
|
+
- **Multi-language**: Extend analyzers for Dart, Kotlin support
|
|
338
|
+
- **Custom Patterns**: Leverage SyntaxKind for specific constructs
|
|
695
339
|
|
|
696
340
|
---
|
|
697
341
|
|
|
698
|
-
|
|
342
|
+
This guide is based on real experience refactoring C013 and other rules. Focus on accuracy over speed - symbol-based analysis provides much better results than regex patterns.
|