@sun-asterisk/sunlint 1.3.1 → 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.
Files changed (66) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/CONTRIBUTING.md +210 -1691
  3. package/config/rule-analysis-strategies.js +17 -1
  4. package/config/rules/enhanced-rules-registry.json +369 -1135
  5. package/config/rules/rules-registry-generated.json +1 -1
  6. package/core/enhanced-rules-registry.js +2 -1
  7. package/core/semantic-engine.js +15 -3
  8. package/core/semantic-rule-base.js +4 -2
  9. package/engines/heuristic-engine.js +65 -4
  10. package/integrations/eslint/plugin/rules/common/c003-no-vague-abbreviations.js +59 -1
  11. package/integrations/eslint/plugin/rules/common/c006-function-name-verb-noun.js +26 -1
  12. package/integrations/eslint/plugin/rules/common/c030-use-custom-error-classes.js +54 -19
  13. package/origin-rules/common-en.md +11 -7
  14. package/package.json +1 -1
  15. package/rules/common/C002_no_duplicate_code/analyzer.js +334 -36
  16. package/rules/common/C003_no_vague_abbreviations/analyzer.js +220 -35
  17. package/rules/common/C006_function_naming/analyzer.js +29 -3
  18. package/rules/common/C010_limit_block_nesting/analyzer.js +181 -337
  19. package/rules/common/C010_limit_block_nesting/config.json +64 -0
  20. package/rules/common/C010_limit_block_nesting/regex-based-analyzer.js +379 -0
  21. package/rules/common/C010_limit_block_nesting/symbol-based-analyzer.js +231 -0
  22. package/rules/common/C013_no_dead_code/analyzer.js +75 -177
  23. package/rules/common/C013_no_dead_code/config.json +61 -0
  24. package/rules/common/C013_no_dead_code/regex-based-analyzer.js +345 -0
  25. package/rules/common/C013_no_dead_code/symbol-based-analyzer.js +640 -0
  26. package/rules/common/C014_dependency_injection/analyzer.js +48 -313
  27. package/rules/common/C014_dependency_injection/config.json +26 -0
  28. package/rules/common/C014_dependency_injection/symbol-based-analyzer.js +751 -0
  29. package/rules/common/C018_no_throw_generic_error/analyzer.js +232 -0
  30. package/rules/common/C018_no_throw_generic_error/config.json +50 -0
  31. package/rules/common/C018_no_throw_generic_error/regex-based-analyzer.js +387 -0
  32. package/rules/common/C018_no_throw_generic_error/symbol-based-analyzer.js +314 -0
  33. package/rules/common/C019_log_level_usage/analyzer.js +110 -317
  34. package/rules/common/C019_log_level_usage/pattern-analyzer.js +88 -0
  35. package/rules/common/C019_log_level_usage/system-log-analyzer.js +1267 -0
  36. package/rules/common/C023_no_duplicate_variable/analyzer.js +180 -0
  37. package/rules/common/C023_no_duplicate_variable/config.json +50 -0
  38. package/rules/common/C023_no_duplicate_variable/symbol-based-analyzer.js +158 -0
  39. package/rules/common/C024_no_scatter_hardcoded_constants/analyzer.js +180 -0
  40. package/rules/common/C024_no_scatter_hardcoded_constants/config.json +50 -0
  41. package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +181 -0
  42. package/rules/common/C030_use_custom_error_classes/analyzer.js +200 -0
  43. package/rules/common/C035_error_logging_context/analyzer.js +3 -1
  44. package/rules/index.js +5 -1
  45. package/rules/security/S009_no_insecure_encryption/README.md +158 -0
  46. package/rules/security/S009_no_insecure_encryption/analyzer.js +319 -0
  47. package/rules/security/S009_no_insecure_encryption/config.json +55 -0
  48. package/rules/security/S010_no_insecure_encryption/README.md +224 -0
  49. package/rules/security/S010_no_insecure_encryption/analyzer.js +493 -0
  50. package/rules/security/S010_no_insecure_encryption/config.json +48 -0
  51. package/rules/security/S016_no_sensitive_querystring/STRATEGY.md +149 -0
  52. package/rules/security/S016_no_sensitive_querystring/analyzer.js +276 -0
  53. package/rules/security/S016_no_sensitive_querystring/config.json +127 -0
  54. package/rules/security/S016_no_sensitive_querystring/regex-based-analyzer.js +258 -0
  55. package/rules/security/S016_no_sensitive_querystring/symbol-based-analyzer.js +495 -0
  56. package/rules/security/S048_no_current_password_in_reset/README.md +222 -0
  57. package/rules/security/S048_no_current_password_in_reset/analyzer.js +366 -0
  58. package/rules/security/S048_no_current_password_in_reset/config.json +48 -0
  59. package/rules/security/S055_content_type_validation/README.md +176 -0
  60. package/rules/security/S055_content_type_validation/analyzer.js +312 -0
  61. package/rules/security/S055_content_type_validation/config.json +48 -0
  62. package/rules/utils/rule-helpers.js +140 -1
  63. package/scripts/consolidate-config.js +116 -0
  64. package/config/rules/S027-categories.json +0 -122
  65. package/config/rules/rules-registry.json +0 -777
  66. package/rules/common/C006_function_naming/smart-analyzer.js +0 -503
package/CONTRIBUTING.md CHANGED
@@ -1,113 +1,78 @@
1
- # Contributing to Sun Lint
1
+ # Contributing to SunLint - Rule Development Guide
2
2
 
3
- Thank you for your interest in contributing to Sun Lint! 🌟
3
+ ## 🎯 Quick Start for Rule Development
4
4
 
5
- ## 🚀 **Getting Started**
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
- ### **Prerequisites**
8
- - Node.js 16+
9
- - npm 8+
10
- - Git
7
+ ## 📋 Rule Development Steps
11
8
 
12
- ### **Setup Development Environment**
9
+ ### Step 1: Register Rule in Registry
13
10
 
14
- ```bash
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
11
+ Add your rule to `config/rules/enhanced-rules-registry.json`:
24
12
 
25
- # Try the CLI locally
26
- node cli.js --help
13
+ ```json
14
+ {
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
32
+ }
33
+ },
34
+ "engineMappings": {
35
+ "eslint": ["complexity", "max-depth"]
36
+ }
37
+ }
38
+ }
39
+ }
27
40
  ```
28
41
 
29
- ## 📋 **Coding Standards**
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
30
47
 
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**
51
-
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 (Modern Approach - 2025)**
66
-
67
- #### **Step 1: Choose Rule Implementation Strategy**
68
-
69
- **Modern Approach: Hybrid Symbol + Regex Analysis (Recommended)**
70
- Following the successful C035 pattern, implement rules with both symbol-based and regex-based analyzers for maximum accuracy and fallback coverage.
71
-
72
- #### **Step 2: Create Rule Directory Structure**
48
+ ### Step 2: Create Rule Directory Structure
73
49
 
74
50
  ```bash
75
- # Create rule directory
76
- mkdir -p rules/common/C042_no_hardcoded_config
51
+ mkdir -p rules/common/C010_limit_block_nesting
52
+ cd rules/common/C010_limit_block_nesting
77
53
 
78
- # Create required files
79
- touch rules/common/C042_no_hardcoded_config/analyzer.js
80
- touch rules/common/C042_no_hardcoded_config/symbol-based-analyzer.js
81
- touch rules/common/C042_no_hardcoded_config/regex-based-analyzer.js
82
- touch rules/common/C042_no_hardcoded_config/config.json
83
- touch rules/common/C042_no_hardcoded_config/STRATEGY.md
54
+ # Required files
55
+ touch analyzer.js # Main orchestrator
56
+ touch symbol-based-analyzer.js # Core analysis logic
57
+ touch config.json # Rule configuration
84
58
  ```
85
59
 
86
- #### **Step 3: Implement Main Analyzer (Hybrid Orchestrator)**
60
+ ### Step 3: Implement Main Analyzer (analyzer.js)
87
61
 
88
62
  ```javascript
89
- // rules/common/C042_no_hardcoded_config/analyzer.js
90
- const C042SymbolBasedAnalyzer = require('./symbol-based-analyzer.js');
91
- const C042RegexBasedAnalyzer = require('./regex-based-analyzer.js');
63
+ // rules/common/C010_limit_block_nesting/analyzer.js
64
+ const C010SymbolBasedAnalyzer = require('./symbol-based-analyzer.js');
92
65
 
93
- class C042Analyzer {
66
+ class C010Analyzer {
94
67
  constructor(semanticEngine = null) {
95
- this.ruleId = 'C042';
96
- this.ruleName = 'No Hardcoded Configuration Values';
97
- this.description = 'Avoid hardcoding configuration values in business logic';
68
+ this.ruleId = 'C010';
69
+ this.ruleName = 'Limit Block Nesting';
70
+ this.description = 'Limit nested blocks to maximum 3 levels';
98
71
  this.semanticEngine = semanticEngine;
99
72
  this.verbose = false;
100
73
 
101
- // Configuration
102
- this.config = {
103
- useSymbolBased: true, // Primary approach
104
- fallbackToRegex: true, // Only when symbol fails completely
105
- symbolBasedOnly: false // Can be set to true for pure mode
106
- };
107
-
108
- // Initialize both analyzers
109
- this.symbolAnalyzer = new C042SymbolBasedAnalyzer(semanticEngine);
110
- this.regexAnalyzer = new C042RegexBasedAnalyzer(semanticEngine);
74
+ // Use symbol-based only for accuracy
75
+ this.symbolAnalyzer = new C010SymbolBasedAnalyzer(semanticEngine);
111
76
  }
112
77
 
113
78
  async initialize(semanticEngine = null) {
@@ -115,82 +80,63 @@ class C042Analyzer {
115
80
  this.semanticEngine = semanticEngine;
116
81
  }
117
82
  this.verbose = semanticEngine?.verbose || false;
118
-
119
- // Initialize both analyzers
120
83
  await this.symbolAnalyzer.initialize(semanticEngine);
121
- await this.regexAnalyzer.initialize(semanticEngine);
122
-
123
- if (this.verbose) {
124
- console.log(`🔧 [C042 Hybrid] Analyzer initialized`);
125
- }
126
84
  }
127
85
 
128
86
  async analyzeFileBasic(filePath, options = {}) {
129
87
  try {
130
- // Try symbol-based analysis first
131
- if (this.semanticEngine?.isSymbolEngineReady?.() &&
132
- this.semanticEngine.project) {
133
-
134
- if (this.verbose) {
135
- console.log(`🔍 [C042] Using symbol-based analysis for ${filePath}`);
136
- }
137
-
138
- const violations = await this.symbolAnalyzer.analyzeFileBasic(filePath, options);
139
- return violations;
88
+ // Check if symbol engine is ready
89
+ if (!this.semanticEngine?.isSymbolEngineReady?.() || !this.semanticEngine.project) {
90
+ throw new Error('Symbol engine not available');
91
+ }
92
+
93
+ if (this.verbose) {
94
+ console.log(`[DEBUG] 🎯 C010: Using symbol-based analysis for ${filePath.split('/').pop()}`);
95
+ }
96
+
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`);
140
101
  }
102
+
103
+ return violations;
141
104
  } catch (error) {
142
105
  if (this.verbose) {
143
- console.warn(`⚠️ [C042] Symbol analysis failed: ${error.message}`);
106
+ console.error(`[DEBUG] C010: Analysis failed: ${error.message}`);
144
107
  }
108
+ throw new Error(`C010 analysis failed: ${error.message}`);
145
109
  }
146
-
147
- // Fallback to regex-based analysis
148
- if (this.verbose) {
149
- console.log(`🔄 [C042] Using regex-based analysis (fallback) for ${filePath}`);
150
- }
151
-
152
- return await this.regexAnalyzer.analyzeFileBasic(filePath, options);
153
110
  }
154
111
 
155
- async analyze(files, language, options = {}) {
156
- const violations = [];
157
-
112
+ async analyzeFiles(files, options = {}) {
113
+ const allViolations = [];
158
114
  for (const filePath of files) {
159
115
  try {
160
- const fileViolations = await this.analyzeFileBasic(filePath, options);
161
- violations.push(...fileViolations);
116
+ const violations = await this.analyzeFileBasic(filePath, options);
117
+ allViolations.push(...violations);
162
118
  } catch (error) {
163
- if (this.verbose) {
164
- console.warn(`❌ [C042] Analysis failed for ${filePath}:`, error.message);
165
- }
119
+ console.warn(`C010: Skipping ${filePath}: ${error.message}`);
166
120
  }
167
121
  }
168
-
169
- return violations;
122
+ return allViolations;
170
123
  }
171
124
  }
172
125
 
173
- module.exports = C042Analyzer;
126
+ module.exports = C010Analyzer;
174
127
  ```
175
128
 
176
- #### **Step 4: Implement Symbol-Based Analyzer (Primary)**
129
+ ### Step 4: Implement Symbol-Based Analyzer
177
130
 
178
131
  ```javascript
179
- // rules/common/C042_no_hardcoded_config/symbol-based-analyzer.js
180
- class C042SymbolBasedAnalyzer {
132
+ // rules/common/C010_limit_block_nesting/symbol-based-analyzer.js
133
+ const { SyntaxKind } = require('ts-morph');
134
+
135
+ class C010SymbolBasedAnalyzer {
181
136
  constructor(semanticEngine = null) {
182
- this.ruleId = 'C042';
183
- this.ruleName = 'No Hardcoded Configuration Values (Symbol-Based)';
184
137
  this.semanticEngine = semanticEngine;
138
+ this.maxNestingLevel = 3;
185
139
  this.verbose = false;
186
-
187
- // Configuration patterns to detect
188
- this.configPatterns = [
189
- /^API_/i, /^DATABASE_/i, /^DB_/i,
190
- /^MAX_/i, /^MIN_/i, /^LIMIT_/i,
191
- /^TIMEOUT/i, /^PORT$/i, /^HOST$/i,
192
- /^SECRET/i, /^KEY$/i, /^TOKEN/i
193
- ];
194
140
  }
195
141
 
196
142
  async initialize(semanticEngine = null) {
@@ -198,1626 +144,199 @@ class C042SymbolBasedAnalyzer {
198
144
  this.semanticEngine = semanticEngine;
199
145
  }
200
146
  this.verbose = semanticEngine?.verbose || false;
201
-
202
- if (this.verbose) {
203
- console.log(`🔧 [C042 Symbol-Based] Analyzer initialized`);
204
- }
205
147
  }
206
148
 
207
149
  async analyzeFileBasic(filePath, options = {}) {
208
150
  const violations = [];
209
151
 
210
- if (!this.semanticEngine?.project) {
211
- return violations;
212
- }
213
-
214
152
  try {
215
- const sourceFile = this.semanticEngine.project.getSourceFileByFilePath(filePath);
153
+ const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
216
154
  if (!sourceFile) {
217
- return violations;
155
+ throw new Error(`Source file not found: ${filePath}`);
156
+ }
157
+
158
+ if (this.verbose) {
159
+ console.log(`[DEBUG] 🔍 C010: Analyzing nesting in ${filePath.split('/').pop()}`);
218
160
  }
219
161
 
220
- // Analyze variable declarations
221
- const variableDeclarations = sourceFile.getVariableDeclarations();
222
- for (const declaration of variableDeclarations) {
223
- const violation = this.analyzeVariableDeclaration(declaration, filePath);
224
- if (violation) {
225
- violations.push(violation);
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
+ });
226
176
  }
227
177
  }
228
178
 
179
+ if (this.verbose) {
180
+ console.log(`[DEBUG] 🔍 C010: Found ${violations.length} nesting violations`);
181
+ }
182
+
229
183
  return violations;
230
184
  } catch (error) {
231
185
  if (this.verbose) {
232
- console.warn(`⚠️ [C042 Symbol-Based] Analysis failed for ${filePath}:`, error.message);
186
+ console.error(`[DEBUG] ❌ C010: Symbol analysis error: ${error.message}`);
233
187
  }
234
- return violations;
235
- }
236
- }
237
-
238
- analyzeVariableDeclaration(declaration, filePath) {
239
- const name = declaration.getName();
240
- const initializer = declaration.getInitializer();
241
-
242
- // Check if variable name suggests configuration
243
- if (!this.isConfigVariable(name)) {
244
- return null;
245
- }
246
-
247
- // Check if value is hardcoded
248
- if (this.isHardcodedValue(initializer)) {
249
- const startLineNumber = declaration.getStartLineNumber();
250
-
251
- return {
252
- ruleId: this.ruleId,
253
- severity: 'warning',
254
- message: `Configuration variable '${name}' should not be hardcoded`,
255
- source: this.ruleId,
256
- file: filePath,
257
- line: startLineNumber,
258
- column: declaration.getStart() - declaration.getStartLinePos() + 1,
259
- description: `Symbol-based analysis detected hardcoded configuration value. Use environment variables or config files.`,
260
- suggestion: `Use process.env.${name} or load from configuration file`,
261
- category: 'maintainability'
262
- };
263
- }
264
-
265
- return null;
266
- }
267
-
268
- isConfigVariable(name) {
269
- return this.configPatterns.some(pattern => pattern.test(name));
270
- }
271
-
272
- isHardcodedValue(initializer) {
273
- if (!initializer) return false;
274
-
275
- // Check for literal values (strings, numbers)
276
- if (initializer.getKind() === ts.SyntaxKind.StringLiteral ||
277
- initializer.getKind() === ts.SyntaxKind.NumericLiteral) {
278
- return true;
279
- }
280
-
281
- // Check for property access to process.env
282
- if (initializer.getText().includes('process.env')) {
283
- return false;
284
- }
285
-
286
- return false;
287
- }
288
- }
289
-
290
- module.exports = C042SymbolBasedAnalyzer;
291
- ```
292
-
293
- #### **Step 5: Implement Regex-Based Analyzer (Fallback)**
294
-
295
- ```javascript
296
- // rules/common/C042_no_hardcoded_config/regex-based-analyzer.js
297
- class C042RegexBasedAnalyzer {
298
- constructor(semanticEngine = null) {
299
- this.ruleId = 'C042';
300
- this.ruleName = 'No Hardcoded Configuration Values (Regex-Based)';
301
- this.semanticEngine = semanticEngine;
302
- this.verbose = false;
303
-
304
- // Patterns for hardcoded config detection
305
- this.hardcodedPatterns = [
306
- /const\s+(API_\w+|DATABASE_\w+|DB_\w+)\s*=\s*["'][^"']+["']/gi,
307
- /const\s+(MAX_\w+|MIN_\w+|LIMIT_\w+)\s*=\s*\d+/gi,
308
- /const\s+(TIMEOUT|PORT|HOST)\s*=\s*["']\w+["']|\d+/gi
309
- ];
310
- }
311
-
312
- async initialize(semanticEngine = null) {
313
- if (semanticEngine) {
314
- this.semanticEngine = semanticEngine;
315
- }
316
- this.verbose = semanticEngine?.verbose || false;
317
-
318
- if (this.verbose) {
319
- console.log(`🔧 [C042 Regex-Based] Analyzer initialized`);
188
+ throw error;
320
189
  }
321
190
  }
322
191
 
323
- async analyzeFileBasic(filePath, options = {}) {
324
- const fs = require('fs');
325
- const violations = [];
326
-
327
- if (!fs.existsSync(filePath)) {
328
- return violations;
329
- }
330
-
331
- const content = fs.readFileSync(filePath, 'utf8');
332
- const lines = content.split('\n');
192
+ findNestedBlocks(sourceFile) {
193
+ const blocks = [];
333
194
 
334
- for (let i = 0; i < lines.length; i++) {
335
- const line = lines[i];
336
-
337
- for (const pattern of this.hardcodedPatterns) {
338
- pattern.lastIndex = 0; // Reset regex
339
- let match;
195
+ function traverse(node, currentDepth = 0) {
196
+ // Check for block statements that increase nesting
197
+ if (this.isNestingNode(node)) {
198
+ currentDepth++;
340
199
 
341
- while ((match = pattern.exec(line)) !== null) {
342
- // Skip if using process.env
343
- if (line.includes('process.env')) {
344
- continue;
345
- }
346
-
347
- violations.push({
348
- ruleId: this.ruleId,
349
- severity: 'warning',
350
- message: `Configuration variable '${match[1]}' should not be hardcoded`,
351
- source: this.ruleId,
352
- file: filePath,
353
- line: i + 1,
354
- column: match.index + 1,
355
- description: `[REGEX-FALLBACK] Hardcoded configuration detected. Use environment variables or config files.`,
356
- suggestion: `Use process.env.${match[1]} or load from configuration file`,
357
- category: 'maintainability'
358
- });
359
- }
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
+ });
360
207
  }
208
+
209
+ // Traverse children
210
+ node.forEachChild(child => traverse.call(this, child, currentDepth));
361
211
  }
362
-
363
- return violations;
212
+
213
+ traverse.call(this, sourceFile, 0);
214
+ return blocks;
364
215
  }
365
216
 
366
- async analyze(files, language, options = {}) {
367
- const violations = [];
368
-
369
- for (const filePath of files) {
370
- try {
371
- const fileViolations = await this.analyzeFileBasic(filePath, options);
372
- violations.push(...fileViolations);
373
- } catch (error) {
374
- if (this.verbose) {
375
- console.warn(`❌ [C042 Regex] Analysis failed for ${filePath}:`, error.message);
376
- }
377
- }
378
- }
379
-
380
- return violations;
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());
381
229
  }
382
230
  }
383
231
 
384
- module.exports = C042RegexBasedAnalyzer;
232
+ module.exports = C010SymbolBasedAnalyzer;
385
233
  ```
386
234
 
387
- #### **Step 6: Create Rule Configuration**
235
+ ### Step 5: Create Rule Configuration
388
236
 
389
237
  ```json
390
- // rules/common/C042_no_hardcoded_config/config.json
238
+ // rules/common/C010_limit_block_nesting/config.json
391
239
  {
392
- "ruleId": "C042",
393
- "name": "No Hardcoded Configuration Values",
394
- "description": "Avoid hardcoding configuration values in business logic",
395
- "category": "maintainability",
396
- "severity": "warning",
397
- "languages": ["typescript", "javascript"],
398
- "version": "1.0.0",
399
- "tags": ["configuration", "maintainability", "environment"],
400
- "patterns": {
401
- "configVariables": [
402
- "API_", "DATABASE_", "DB_", "MAX_", "MIN_", "LIMIT_",
403
- "TIMEOUT", "PORT", "HOST", "SECRET", "KEY", "TOKEN"
404
- ],
405
- "excludePatterns": [
406
- "process.env", "config.", "getConfig(", "loadConfig("
407
- ]
408
- },
409
- "examples": {
410
- "bad": [
411
- "const API_URL = 'https://api.example.com';",
412
- "const MAX_RETRIES = 5;",
413
- "const DATABASE_PASSWORD = 'secret123';"
414
- ],
415
- "good": [
416
- "const API_URL = process.env.API_URL;",
417
- "const MAX_RETRIES = parseInt(process.env.MAX_RETRIES || '3');",
418
- "const config = loadConfigFromFile();"
419
- ]
420
- }
240
+ "maxNestingLevel": 3,
241
+ "excludePatterns": [
242
+ "**/*.test.js",
243
+ "**/*.spec.js"
244
+ ],
245
+ "includePatterns": [
246
+ "**/*.js",
247
+ "**/*.ts",
248
+ "**/*.jsx",
249
+ "**/*.tsx"
250
+ ]
421
251
  }
422
252
  ```
423
253
 
424
- #### **Step 7: Document Analysis Strategy**
425
-
426
- ```markdown
427
- <!-- rules/common/C042_no_hardcoded_config/STRATEGY.md -->
428
- # C042 Analysis Strategy: No Hardcoded Configuration Values
429
-
430
- ## Overview
431
- Hybrid approach using symbol-based analysis as primary with regex fallback.
432
-
433
- ## Symbol-Based Analysis (Primary)
434
- - Uses ts-morph AST to analyze variable declarations
435
- - Detects configuration variable patterns by name
436
- - Checks initializer values for hardcoded literals
437
- - Excludes process.env and config function calls
438
-
439
- ## Regex-Based Analysis (Fallback)
440
- - Pattern matching for common hardcoded config scenarios
441
- - Simpler but broader detection
442
- - Used when symbol analysis unavailable
443
-
444
- ## Strategy Selection
445
- 1. **Symbol-based**: When ts-morph project available (95% accuracy)
446
- 2. **Regex-fallback**: When symbol analysis fails (80% accuracy)
447
-
448
- ## Testing Approach
449
- Test both analyzers independently and verify hybrid orchestration.
450
- ```
451
-
452
- ---
453
-
454
- ## 🆕 **Modern Rule Implementation Guide (2025)**
254
+ ## 🚨 Common Pitfalls & Solutions
455
255
 
456
- Based on the successful C035 implementation, here's the **current recommended approach** for adding new rules:
457
-
458
- ### **Critical Success Patterns**
459
-
460
- #### **✅ 1. Constructor Pattern (REQUIRED)**
256
+ ### 1. Symbol Engine Not Ready
461
257
  ```javascript
462
- // CORRECT - Works with semantic engine injection
463
- class C042Analyzer {
464
- constructor(options = {}) {
465
- this.semanticEngine = options.semanticEngine || null;
466
- this.verbose = options.verbose || false;
467
- // ...
468
- }
469
- }
258
+ // Wrong - Will fail if symbol engine not ready
259
+ const violations = await this.symbolAnalyzer.analyzeFileBasic(filePath);
470
260
 
471
- // WRONG - Causes semantic engine injection failure
472
- class C042Analyzer {
473
- constructor(semanticEngine = null) {
474
- this.semanticEngine = semanticEngine;
475
- // ...
476
- }
261
+ // Correct - Check engine availability
262
+ if (!this.semanticEngine?.isSymbolEngineReady?.() || !this.semanticEngine.project) {
263
+ throw new Error('Symbol engine not available');
477
264
  }
478
265
  ```
479
266
 
480
- #### **✅ 2. Registry Configuration (REQUIRED)**
267
+ ### 2. Missing Source File Check
268
+ ```javascript
269
+ // ❌ Wrong - Will crash if file not found
270
+ const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
481
271
 
482
- **Enhanced Rules Registry** (`config/rules/enhanced-rules-registry.json`):
483
- ```json
484
- {
485
- "C042": {
486
- "name": "No Hardcoded Configuration Values",
487
- "description": "Avoid hardcoding configuration values in business logic",
488
- "category": "maintainability",
489
- "severity": "warning",
490
- "languages": ["typescript", "javascript", "dart", "kotlin"],
491
- "analyzer": "./rules/common/C042_no_hardcoded_config/analyzer.js",
492
- "config": "./rules/common/C042_no_hardcoded_config/config.json",
493
- "version": "1.0.0",
494
- "status": "stable",
495
- "tags": ["configuration", "hardcoding", "maintainability"],
496
- "engineMappings": {
497
- "eslint": ["no-magic-numbers", "@typescript-eslint/no-magic-numbers"]
498
- }
499
- }
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}`);
500
276
  }
501
277
  ```
502
278
 
503
- **Rule Analysis Strategies** (`config/rule-analysis-strategies.js`):
279
+ ### 3. Improper Error Handling
504
280
  ```javascript
505
- module.exports = {
506
- hybridOptimal: {
507
- 'C042': {
508
- reason: 'Configuration detection requires symbol resolution + pattern matching',
509
- methods: ['semantic', 'regex'],
510
- strategy: 'semantic-primary-regex-fallback',
511
- accuracy: { semantic: 90, regex: 75, combined: 95 }
512
- }
513
- }
281
+ // Wrong - Silent failures
282
+ try {
283
+ const violations = await this.analyzeFile(filePath);
284
+ } catch (error) {
285
+ return [];
514
286
  }
515
- ```
516
-
517
- #### **✅ 3. Hybrid Architecture Template**
518
-
519
- **Main Analyzer** (`analyzer.js`):
520
- ```javascript
521
- const C042SymbolBasedAnalyzer = require('./symbol-based-analyzer.js');
522
- const C042RegexBasedAnalyzer = require('./regex-based-analyzer.js');
523
-
524
- class C042Analyzer {
525
- constructor(options = {}) {
526
- if (process.env.SUNLINT_DEBUG) {
527
- console.log(`🔧 [C042] Constructor called with options:`, !!options);
528
- }
529
-
530
- this.ruleId = 'C042';
531
- this.semanticEngine = options.semanticEngine || null;
532
- this.verbose = options.verbose || false;
533
-
534
- // Initialize analyzers with THIS.semanticEngine
535
- this.symbolAnalyzer = new C042SymbolBasedAnalyzer(this.semanticEngine);
536
- this.regexAnalyzer = new C042RegexBasedAnalyzer(this.semanticEngine);
537
-
538
- this.config = {
539
- useSymbolBased: true,
540
- fallbackToRegex: true,
541
- symbolBasedOnly: false
542
- };
543
- }
544
-
545
- async analyze(files, language = 'javascript', options = {}) {
546
- const allViolations = [];
547
-
548
- for (const filePath of files) {
549
- const violations = await this.analyzeFile(filePath, options);
550
- allViolations.push(...violations);
551
- }
552
-
553
- return allViolations;
554
- }
555
287
 
556
- async analyzeFile(filePath, options = {}) {
557
- // 1. Try Symbol-based analysis first (primary)
558
- if (this.config.useSymbolBased &&
559
- this.semanticEngine?.project &&
560
- this.semanticEngine?.initialized) {
561
- try {
562
- const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
563
- if (sourceFile) {
564
- const violations = await this.symbolAnalyzer.analyzeFileWithSymbols(filePath, options);
565
- violations.forEach(v => v.analysisStrategy = 'symbol-based');
566
- return violations;
567
- }
568
- } catch (error) {
569
- console.warn(`⚠️ [C042] Symbol analysis failed: ${error.message}`);
570
- }
571
- }
572
-
573
- // 2. Fallback to regex-based analysis
574
- if (this.config.fallbackToRegex) {
575
- const violations = await this.regexAnalyzer.analyzeFileBasic(filePath, options);
576
- violations.forEach(v => v.analysisStrategy = 'regex-based');
577
- return violations;
578
- }
579
-
580
- return [];
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}`);
581
295
  }
296
+ throw error;
582
297
  }
583
-
584
- module.exports = C042Analyzer;
585
298
  ```
586
299
 
587
- #### **✅ 4. Testing & Validation**
300
+ ## 🧪 Testing Your Rule
588
301
 
589
- **Debug Output Verification**:
590
302
  ```bash
591
- SUNLINT_DEBUG=true node cli.js --input test-files/c042-test.js --rules C042 --engine=heuristic --debug
592
- ```
593
-
594
- **Expected Output**:
595
- ```
596
- 🔧 [HeuristicEngine] Rule C042: semantic (approach: hybrid-combined)
597
- 🔧 [C042] Constructor called with options: true
598
- 🔧 [C042] Options type: object [ 'verbose', 'semanticEngine' ]
599
- 🔧 [C042] Trying symbol-based analysis...
600
- ✅ [C042] Symbol-based analysis: X violations
601
- ```
602
-
603
- #### **✅ 5. Common Pitfalls & Solutions**
604
-
605
- | ❌ Problem | ✅ Solution |
606
- |------------|-------------|
607
- | `constructor(semanticEngine)` | `constructor(options = {})` |
608
- | Missing from `rule-analysis-strategies.js` | Add to `hybridOptimal` section |
609
- | `ast (approach: ast-primary)` output | Ensure rule in strategies config |
610
- | `this.semanticEngine?.project: false` | Check engine injection & initialization |
611
- | No debug output from constructor | Verify constructor signature |
612
-
613
- #### **✅ 6. Validation Checklist**
614
-
615
- Before submitting a new rule:
616
-
617
- - [ ] Constructor uses `options = {}` pattern
618
- - [ ] Rule registered in `enhanced-rules-registry.json`
619
- - [ ] Strategy defined in `rule-analysis-strategies.js` `hybridOptimal`
620
- - [ ] Symbol-based analyzer implemented with ts-morph
621
- - [ ] Regex-based analyzer implemented with pattern matching
622
- - [ ] Debug output shows `semantic (approach: hybrid-combined)`
623
- - [ ] Test cases detect expected violations accurately
624
- - [ ] No modifications to engine core files (registry-driven only)
625
- - [ ] Performance acceptable for large codebases
626
-
627
- ---
628
-
629
- ## 📚 **Legacy Implementation Examples**
630
-
631
- The following examples show older patterns for reference, but **use the Modern Guide above** for new rules:
632
-
633
- #### **Step 7: Document Analysis Strategy**
634
-
635
- ```markdown
636
- <!-- rules/common/C042_no_hardcoded_config/STRATEGY.md -->
637
- # C042 Analysis Strategy: No Hardcoded Configuration Values
638
-
639
- ## Overview
640
- Hybrid approach using symbol-based analysis as primary with regex fallback.
641
-
642
- ## Symbol-Based Analysis (Primary)
643
- - Uses ts-morph AST to analyze variable declarations
644
- - Detects configuration variable patterns by name
645
- - Checks initializer values for hardcoded literals
646
- - Excludes process.env and config function calls
647
-
648
- ## Regex-Based Analysis (Fallback)
649
- - Pattern matching for common hardcoded config scenarios
650
- - Simpler but broader detection
651
- - Used when symbol analysis unavailable
652
-
653
- ## Strategy Selection
654
- 1. **Symbol-based**: When ts-morph project available (95% accuracy)
655
- 2. **Regex-fallback**: When symbol analysis fails (80% accuracy)
656
-
657
- ## Testing Approach
658
- Test both analyzers independently and verify hybrid orchestration.
659
- ```
660
-
661
- #### **Step 8: Register Rule in Rules Registry**
662
-
663
- ```json
664
- // config/rules/rules-registry.json
665
- {
666
- "rules": {
667
- "C042": {
668
- "name": "No Hardcoded Configuration Values",
669
- "description": "Avoid hardcoding configuration values in business logic",
670
- "category": "maintainability",
671
- "severity": "warning",
672
- "languages": ["typescript", "javascript"],
673
- "analyzer": "./rules/common/C042_no_hardcoded_config/analyzer.js",
674
- "config": "./rules/common/C042_no_hardcoded_config/config.json",
675
- "version": "1.0.0",
676
- "status": "experimental",
677
- "tags": ["configuration", "maintainability", "environment"]
678
- }
679
- }
680
- }
681
- ```
303
+ # Test single file
304
+ node cli.js --input=test-file.js --rule=C010 --engine=heuristic --verbose
682
305
 
683
- #### **Step 9: Add to Enhanced Rules Registry**
306
+ # Test project
307
+ node cli.js --input=src/ --rule=C010 --engine=heuristic --max-semantic-files=-1
684
308
 
685
- ```javascript
686
- // core/enhanced-rules-registry.js
687
- loadEnginePreferences(options = {}) {
688
- const preferences = {
689
- // ... existing rules ...
690
- 'C042': ['heuristic', 'openai'], // Symbol + regex hybrid
691
- // ... rest of rules ...
692
- };
693
- }
694
- ```
695
-
696
- #### **Step 10: Add to Analysis Strategies**
697
-
698
- ```javascript
699
- // config/rule-analysis-strategies.js
700
- module.exports = {
701
- astPreferred: {
702
- // ... existing rules ...
703
- 'C042': {
704
- reason: 'Configuration detection requires precise symbol analysis for variable declarations',
705
- methods: ['ast', 'regex'],
706
- accuracy: { ast: 95, regex: 80 }
707
- }
708
- }
709
- };
309
+ # Performance test
310
+ time node cli.js --input=large-project/ --rule=C010 --engine=heuristic
710
311
  ```
711
312
 
712
- **Description**: Avoid hardcoding configuration values in business logic
713
-
714
- **Examples**:
715
- ❌ Bad:
716
- ```javascript
717
- const API_URL = "https://api.example.com";
718
- const MAX_RETRIES = 5;
719
- ```
720
-
721
- ✅ Good:
722
- ```javascript
723
- const API_URL = process.env.API_URL;
724
- const MAX_RETRIES = parseInt(process.env.MAX_RETRIES || '3');
725
- ```
726
-
727
- #### **Step 4: Add Tests**
728
-
729
- ```javascript
730
- // test/rules/c042.test.js
731
- const { testRule } = require('../test-utils');
313
+ ## 📝 Best Practices
732
314
 
733
- describe('Rule C042: No Hardcoded Configuration Values', () => {
734
- testRule('C042', {
735
- valid: [
736
- 'const apiUrl = process.env.API_URL;',
737
- 'const config = loadConfig();'
738
- ],
739
- invalid: [
740
- 'const API_URL = "https://api.example.com";',
741
- 'const MAX_RETRIES = 5;'
742
- ]
743
- });
744
- });
745
- ```
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
746
321
 
747
- #### **Step 5: Test Your Rule**
322
+ ## 🔧 Development Environment
748
323
 
749
324
  ```bash
750
- # Test with specific engine
751
- node cli.js --rule=C042 --engine=eslint --input=test/fixtures
752
- node cli.js --rule=C042 --engine=heuristic --input=test/fixtures
753
-
754
- # Test rule behavior
755
- node cli.js --rule=C042 --input=test/fixtures --verbose
756
- ```
757
-
758
- ### **Engine-Specific Development**
759
-
760
- #### **Heuristic Engine Rules**
761
-
762
- For custom pattern-based analysis:
763
-
764
- 1. **Create Rule Class**:
765
- ```javascript
766
- // rules/custom/your-rule.js
767
- const HeuristicRuleBase = require('../../core/heuristic-rule-base');
768
-
769
- class YourCustomRule extends HeuristicRuleBase {
770
- constructor() {
771
- super('CXXX', 'Your Rule Name');
772
- }
325
+ # Setup
326
+ npm install
327
+ npm test
773
328
 
774
- analyze(node, context) {
775
- // Your analysis logic
776
- // Return violation or null
777
- }
778
- }
329
+ # Development workflow
330
+ node cli.js --input=examples/ --rule=YOUR_RULE --engine=heuristic --verbose
779
331
  ```
780
332
 
781
- 2. **Add to Registry**:
782
- ```json
783
- {
784
- "CXXX": {
785
- "engineMappings": {
786
- "heuristic": {
787
- "implementation": "custom/your-rule",
788
- "supportedLanguages": ["typescript", "javascript"]
789
- }
790
- }
791
- }
792
- }
793
- ```
794
-
795
- #### **ESLint Engine Rules**
796
-
797
- For JavaScript/TypeScript linting:
798
-
799
- 1. **Use Existing ESLint Rules**:
800
- ```json
801
- {
802
- "CXXX": {
803
- "engineMappings": {
804
- "eslint": {
805
- "rules": ["no-console", "prefer-const"],
806
- "config": {
807
- "no-console": ["error"],
808
- "prefer-const": ["warn"]
809
- }
810
- }
811
- }
812
- }
813
- }
814
- ```
815
-
816
- 2. **Create Custom ESLint Rule** (advanced):
817
- ```javascript
818
- // eslint-custom-rules/your-rule.js
819
- module.exports = {
820
- meta: {
821
- type: 'problem',
822
- docs: { description: 'Your rule description' }
823
- },
824
- create(context) {
825
- return {
826
- FunctionDeclaration(node) {
827
- // Your ESLint rule logic
828
- }
829
- };
830
- }
831
- };
832
- ```
833
-
834
- #### **OpenAI Engine Rules**
835
-
836
- For AI-powered analysis:
837
-
838
- ```json
839
- {
840
- "CXXX": {
841
- "engineMappings": {
842
- "openai": {
843
- "prompt": "Analyze code for specific pattern or anti-pattern",
844
- "examples": [
845
- "// Bad example",
846
- "// Good example"
847
- ],
848
- "temperature": 0.1,
849
- "maxTokens": 500
850
- }
851
- }
852
- }
853
- }
854
- ```
855
-
856
- ### **Adding a New Security Rule**
857
-
858
- Same process as above, just use `"category": "security"` in the rule definition.
859
-
860
- ### **Testing Your New Rule**
861
-
862
- ```bash
863
- # Test all engines with your new rule
864
- node cli.js --rule=C042 --input=test/fixtures --format=json
865
-
866
- # Test specific engine
867
- node cli.js --rule=C042 --engine=heuristic --input=test/fixtures
868
- node cli.js --rule=C042 --engine=eslint --input=test/fixtures
869
- node cli.js --rule=C042 --engine=openai --input=test/fixtures
870
-
871
- # Validate rule registry
872
- node validate-system.js
873
- ```
874
-
875
- ### **Rule Definition Schema**
876
-
877
- Complete schema for rule definitions:
878
-
879
- ```json
880
- {
881
- "CXXX": {
882
- "id": "CXXX", // Required: Rule ID
883
- "title": "Rule Title", // Required: Human-readable title
884
- "description": "Detailed description", // Required: Rule description
885
- "severity": "error|warning|info", // Required: Severity level
886
- "category": "quality|security|performance|maintainability", // Required
887
- "tags": ["tag1", "tag2"], // Optional: Tags for filtering
888
- "languages": ["typescript", "javascript"], // Optional: Supported languages
889
- "engineMappings": { // Required: Engine configurations
890
- "heuristic": {
891
- "implementation": "custom/rule-name",
892
- "supportedLanguages": ["typescript"],
893
- "astTargets": ["FunctionDeclaration"]
894
- },
895
- "eslint": {
896
- "rules": ["eslint-rule-name"],
897
- "config": { "rule-option": true }
898
- },
899
- "openai": {
900
- "prompt": "AI analysis prompt",
901
- "examples": ["code example"],
902
- "temperature": 0.1
903
- }
904
- },
905
- "analysisStrategy": { // Optional: Analysis metadata
906
- "type": "ast|regex|semantic",
907
- "patterns": ["pattern1"],
908
- "astTargets": ["NodeType"],
909
- "heuristics": ["detection-method"]
910
- },
911
- "metadata": { // Optional: Additional metadata
912
- "author": "Developer Name",
913
- "created": "2025-08-07",
914
- "updated": "2025-08-07",
915
- "version": "1.0.0"
916
- }
917
- }
918
- }
919
- ```
920
-
921
- ### **Architecture Deep Dive**
922
-
923
- #### **Unified Rule Registry**
924
- - **Location**: `rules/` (auto-generated from origin-rules)
925
- - **Source**: `origin-rules/` (markdown files)
926
- - **ESLint Mappings**: `config/eslint-rule-mapping.json`
927
- - **Loader**: `core/unified-rule-registry.js`
928
-
929
- #### **Engine Architecture**
930
- ```
931
- ┌─────────────────────────────────────────┐
932
- │ SunLint CLI │
933
- ├─────────────────────────────────────────┤
934
- │ Analysis Orchestrator │
935
- │ (Strict Engine Mode Support) │
936
- ├─────────────────────────────────────────┤
937
- │ ┌─────────────┬─────────────┬─────────┐ │
938
- │ │ Heuristic │ ESLint │ OpenAI │ │
939
- │ │ Engine │ Engine │ Engine │ │
940
- │ │ (244 rules) │ (57 rules) │(256 all)│ │
941
- │ └─────────────┴─────────────┴─────────┘ │
942
- ├─────────────────────────────────────────┤
943
- │ Rule Configuration │
944
- │ ┌─────────────┬─────────────────────────┐ │
945
- │ │ ESLint │ Unified Registry │ │
946
- │ │ Mappings │ (Generated Rules) │ │
947
- │ │ (.json) │ (origin-rules) │ │
948
- │ └─────────────┴─────────────────────────┘ │
949
- └─────────────────────────────────────────┘
950
- ```
951
-
952
- #### **How Engines Load Rules**
953
- 1. **Initialization**: Each engine calls `getInstance()` from unified registry
954
- 2. **Rule Loading**: Registry loads rules from auto-generated `rules/` directory
955
- 3. **ESLint Mapping**: ESLint engine loads rule mappings from `config/eslint-rule-mapping.json`
956
- 4. **Engine Filtering**: Each engine filters rules based on their capabilities
957
- 5. **Analysis**: Engines analyze code using their specific rule implementations
958
-
959
- **Key Features:**
960
- - ✅ **Strict Engine Mode**: `--engine=eslint` only runs ESLint, skips unsupported rules
961
- - ✅ **Fallback Mode**: Auto-engine selection with fallback (ESLint → Heuristic → OpenAI)
962
- - ✅ **Rule Skipping**: Graceful handling of unsupported rules by specific engines
963
-
964
- ## 🧪 **Testing**
965
-
966
- ### **Run All Tests**
967
- ```bash
968
- npm test
969
- ```
970
-
971
- ### **Test Rule Registry System**
972
- ```bash
973
- # Validate unified rule registry
974
- node validate-system.js
975
-
976
- # Check rule loading for each engine
977
- npm run test:engines
978
- ```
979
-
980
- ### **Test Specific Rules**
981
- ```bash
982
- # Test specific rule with all engines
983
- node cli.js --rule=C042 --input=test/fixtures
984
-
985
- # Test with specific engine
986
- node cli.js --rule=C042 --engine=heuristic --input=test/fixtures
987
- node cli.js --rule=C042 --engine=eslint --input=test/fixtures
988
- node cli.js --rule=C042 --engine=openai --input=test/fixtures
989
-
990
- # Test multiple rules
991
- node cli.js --rule=C006,C019,C042 --input=test/fixtures
992
- ```
993
-
994
- ### **Test Rule Development**
995
- ```bash
996
- # Create test fixtures
997
- mkdir -p test/fixtures/c042
998
- echo 'const API_URL = "https://api.example.com";' > test/fixtures/c042/invalid.ts
999
- echo 'const apiUrl = process.env.API_URL;' > test/fixtures/c042/valid.ts
1000
-
1001
- # Test your rule
1002
- node cli.js --rule=C042 --input=test/fixtures/c042 --format=json
1003
- ```
1004
-
1005
- ### **Integration Testing**
1006
- ```bash
1007
- # Test all engines work together
1008
- npm run test:integration
1009
-
1010
- # Test rule registry loading
1011
- npm run test:registry
1012
-
1013
- # Performance testing
1014
- npm run test:performance
1015
- ```
1016
-
1017
- ## 📊 **Code Review Process**
1018
-
1019
- 1. **Self-Review Checklist**
1020
- - [ ] Follows all Sun Lint coding rules (C005, C006, etc.)
1021
- - [ ] Rule C035: Error handling includes complete logging
1022
- - [ ] Rule C037: API responses use standard format
1023
- - [ ] Rule C040: Validation logic is centralized
1024
- - [ ] Tests pass and cover edge cases
1025
- - [ ] Documentation updated
1026
-
1027
- 2. **Submit Pull Request**
1028
- - Clear title and description
1029
- - Reference related issues
1030
- - Include test results
1031
- - Follow template
1032
- - **NEW**: Validate rule registry with `node validate-system.js`
1033
-
1034
- 3. **Review Criteria**
1035
- - Code quality (follows our own rules!)
1036
- - Rule properly defined in `enhanced-rules-registry.json`
1037
- - All engines can load the rule correctly
1038
- - Test coverage for all supported engines
1039
- - Documentation completeness
1040
- - Performance impact
1041
- - Backward compatibility
1042
-
1043
- ## 📝 **Documentation**
1044
-
1045
- ### **Update Documentation**
1046
- When adding features:
1047
- - Update `README.md`
1048
- - Add rule to `enhanced-rules-registry.json` (this is your main documentation!)
1049
- - Update configuration examples
1050
- - Add usage examples
1051
- - Update `RULE_MIGRATION_SUMMARY.md` if changing rule system
1052
-
1053
- ### **Rule Documentation Template**
1054
-
1055
- All rule documentation is now centralized in `enhanced-rules-registry.json`:
1056
-
1057
- ```json
1058
- {
1059
- "C042": {
1060
- "title": "Clear, descriptive rule title",
1061
- "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.",
1062
- "examples": {
1063
- "invalid": [
1064
- "// Bad example that violates the rule",
1065
- "const API_URL = 'https://hardcoded-url.com';"
1066
- ],
1067
- "valid": [
1068
- "// Good example that follows the rule",
1069
- "const apiUrl = process.env.API_URL;"
1070
- ]
1071
- },
1072
- "fixSuggestions": [
1073
- "Move configuration to environment variables",
1074
- "Use a configuration management system",
1075
- "Extract constants to a separate config file"
1076
- ],
1077
- "relatedRules": ["C031", "C034"]
1078
- }
1079
- }
1080
- ```
1081
-
1082
- ### **Engine-Specific Documentation**
1083
-
1084
- #### **Heuristic Engine Rules**
1085
- Document custom analysis patterns:
1086
- ```json
1087
- {
1088
- "analysisStrategy": {
1089
- "type": "ast",
1090
- "description": "Analyzes AST nodes for hardcoded configuration patterns",
1091
- "astTargets": ["VariableDeclaration", "PropertyAssignment"],
1092
- "patterns": ["literal-values-in-config-context"],
1093
- "complexity": "O(n) where n is number of variable declarations"
1094
- }
1095
- }
1096
- ```
1097
-
1098
- #### **ESLint Engine Rules**
1099
- Document ESLint rule mappings:
1100
- ```json
1101
- {
1102
- "engineMappings": {
1103
- "eslint": {
1104
- "rules": ["no-magic-numbers"],
1105
- "rationale": "ESLint's no-magic-numbers rule catches hardcoded values",
1106
- "limitations": "May have false positives for legitimate constants",
1107
- "customConfig": {
1108
- "no-magic-numbers": ["error", { "ignore": [0, 1, -1] }]
1109
- }
1110
- }
1111
- }
1112
- }
1113
- ```
1114
-
1115
- #### **OpenAI Engine Rules**
1116
- Document AI prompts and examples:
1117
- ```json
1118
- {
1119
- "engineMappings": {
1120
- "openai": {
1121
- "prompt": "Identify hardcoded configuration values that should be externalized to environment variables or config files",
1122
- "context": "Look for URLs, timeouts, limits, and other configuration that might change between environments",
1123
- "examples": [
1124
- "❌ const API_URL = 'https://api.example.com';",
1125
- "✅ const API_URL = process.env.API_URL || 'https://default-api.com';"
1126
- ]
1127
- }
1128
- }
1129
- }
1130
- ```
1131
-
1132
- ## 🐛 **Bug Reports**
1133
-
1134
- When reporting bugs:
1135
- 1. Use clear, descriptive title
1136
- 2. Include reproduction steps
1137
- 3. Provide sample code
1138
- 4. Include environment details
1139
- 5. Include sunlint output
1140
- 6. **NEW**: Run `node validate-system.js` and include output
1141
-
1142
- ## 🔧 **Troubleshooting**
1143
-
1144
- ### **Common Issues**
1145
-
1146
- #### **Rule Not Loading**
1147
- ```bash
1148
- # Check if rule exists in registry
1149
- node -e "const registry = require('./core/unified-rule-registry'); registry.getInstance().initialize().then(() => console.log('Rule C042:', registry.getInstance().rules.has('C042')))"
1150
-
1151
- # Validate registry syntax
1152
- node validate-system.js
1153
- ```
1154
-
1155
- #### **Engine Not Finding Rule**
1156
- ```bash
1157
- # Check engine-specific mapping
1158
- 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); })"
1159
- ```
1160
-
1161
- #### **Rule Registry Errors**
1162
- ```bash
1163
- # Common issues:
1164
- # 1. JSON syntax errors in enhanced-rules-registry.json
1165
- # 2. Missing required fields (id, title, description, severity, category)
1166
- # 3. Invalid engine mapping structure
1167
-
1168
- # Validate JSON syntax
1169
- node -c config/enhanced-rules-registry.json
1170
-
1171
- # Check required fields
1172
- node validate-system.js
1173
- ```
1174
-
1175
- ### **Performance Issues**
1176
- ```bash
1177
- # Profile rule loading
1178
- node --prof cli.js --rule=C042 --input=large-project/
1179
-
1180
- # Check memory usage
1181
- node --inspect cli.js --rule=C042 --input=test/fixtures/
1182
- ```
1183
-
1184
- ## 💡 **Feature Requests**
1185
-
1186
- For new features:
1187
- 1. Check existing issues first
1188
- 2. Describe the use case
1189
- 3. Provide examples
1190
- 4. Consider implementation complexity
1191
- 5. Think about backward compatibility
1192
-
1193
- ## 📋 **Quick Reference**
1194
-
1195
- ### **Essential Commands**
1196
- ```bash
1197
- # Add ESLint engine rule mapping
1198
- vim config/eslint-rule-mapping.json
1199
-
1200
- # Create custom heuristic rule
1201
- vim custom-rules/c042-rule-name.js
1202
-
1203
- # Add rule documentation
1204
- vim origin-rules/common-en.md
1205
-
1206
- # Test new rule with specific engine
1207
- node cli.js --rule=CXXX --engine=eslint --input=test/fixtures
1208
- node cli.js --rule=CXXX --engine=heuristic --input=test/fixtures
1209
-
1210
- # Test strict engine mode (no fallback)
1211
- node cli.js --rule=CXXX --engine=eslint --input=test/fixtures
1212
-
1213
- # Test fallback mode (auto engine selection)
1214
- node cli.js --rule=CXXX --input=test/fixtures
1215
- ```
1216
-
1217
- ### **Key Files**
1218
- - `config/eslint-rule-mapping.json` - **ESLint engine rule mappings**
1219
- - `origin-rules/` - **Original rule definitions** (markdown format)
1220
- - `rules/` - **Generated rule registry** (auto-generated)
1221
- - `core/unified-rule-registry.js` - Rule registry loader
1222
- - `engines/heuristic-engine.js` - Custom pattern analysis
1223
- - `engines/eslint-engine.js` - JavaScript/TypeScript linting
1224
- - `engines/openai-engine.js` - AI-powered analysis
1225
- - `integrations/eslint/plugin/` - Custom ESLint rules
1226
- - `custom-rules/` - Heuristic engine custom rules
1227
-
1228
- ### **Rule Development Checklist**
1229
- - [ ] Choose appropriate engine (ESLint for JS/TS, Heuristic for universal)
1230
- - [ ] Add ESLint mapping to `config/eslint-rule-mapping.json` (if using ESLint engine)
1231
- - [ ] Create custom rule implementation (if needed)
1232
- - [ ] Add rule definition to `origin-rules/` (markdown format)
1233
- - [ ] Add test cases and examples
1234
- - [ ] Test with `node cli.js --rule=CXXX --input=test/fixtures`
1235
- - [ ] Test engine-specific behavior (`--engine=eslint`, `--engine=heuristic`)
1236
- - [ ] Update documentation if needed
1237
-
1238
- ---
1239
-
1240
- **🚀 Ready to contribute? Start by choosing your engine and editing the appropriate mapping file!**
1241
-
1242
- **For ESLint rules**: Edit `config/eslint-rule-mapping.json`
1243
- **For Heuristic rules**: Create files in `custom-rules/`
1244
- **For documentation**: Add to `origin-rules/` markdown files
1245
-
1246
- ---
1247
-
1248
- ## 🔬 **Modern Rule Development Guide (2024)**
1249
-
1250
- *This section provides a comprehensive, step-by-step guide for implementing new rules using the modern hybrid architecture pattern (symbol-based + regex fallback), based on the successful C035 implementation.*
1251
-
1252
- ### **Quick Start: Creating a New Rule**
1253
-
1254
- #### **Step 1: Create Rule Directory Structure**
1255
-
1256
- ```bash
1257
- # Example: Creating C042 (No Hardcoded Configuration Values)
1258
- mkdir -p rules/common/C042_no_hardcoded_config
1259
- cd rules/common/C042_no_hardcoded_config
1260
- ```
1261
-
1262
- #### **Step 2: Implement Main Analyzer (Hybrid Orchestrator)**
1263
-
1264
- ```javascript
1265
- // analyzer.js - Main hybrid orchestrator
1266
- const SymbolBasedAnalyzer = require('./symbol-based-analyzer');
1267
- const RegexBasedAnalyzer = require('./regex-based-analyzer');
1268
-
1269
- class C042NoHardcodedConfigAnalyzer {
1270
- constructor(verbose = false) {
1271
- this.verbose = verbose;
1272
- this.symbolAnalyzer = new SymbolBasedAnalyzer(verbose);
1273
- this.regexAnalyzer = new RegexBasedAnalyzer(verbose);
1274
- }
1275
-
1276
- async analyzeFileBasic(filePath, options = {}) {
1277
- if (this.verbose) {
1278
- console.log(`🔍 [C042] Analyzing file: ${filePath}`);
1279
- }
1280
-
1281
- try {
1282
- // Primary: Symbol-based analysis
1283
- const violations = await this.symbolAnalyzer.analyzeFileBasic(filePath, options);
1284
-
1285
- if (this.verbose) {
1286
- console.log(`✅ [C042] Symbol-based analysis completed. Found ${violations.length} violations.`);
1287
- }
1288
-
1289
- return violations;
1290
- } catch (error) {
1291
- if (this.verbose) {
1292
- console.warn(`⚠️ [C042] Symbol analysis failed: ${error.message}`);
1293
- console.log(`🔄 [C042] Falling back to regex analysis...`);
1294
- }
1295
-
1296
- // Fallback: Regex-based analysis
1297
- try {
1298
- const violations = await this.regexAnalyzer.analyzeFileBasic(filePath, options);
1299
-
1300
- if (this.verbose) {
1301
- console.log(`✅ [C042] Regex-fallback analysis completed. Found ${violations.length} violations.`);
1302
- }
1303
-
1304
- return violations;
1305
- } catch (fallbackError) {
1306
- if (this.verbose) {
1307
- console.error(`❌ [C042] Both symbol and regex analysis failed: ${fallbackError.message}`);
1308
- }
1309
- return [];
1310
- }
1311
- }
1312
- }
1313
- }
1314
-
1315
- module.exports = C042NoHardcodedConfigAnalyzer;
1316
- ```
1317
-
1318
- #### **Step 3: Implement Symbol-Based Analyzer (Primary)**
1319
-
1320
- ```javascript
1321
- // symbol-based-analyzer.js - High-accuracy AST analysis
1322
- const { Project } = require('ts-morph');
1323
-
1324
- class C042SymbolBasedAnalyzer {
1325
- constructor(verbose = false) {
1326
- this.verbose = verbose;
1327
- this.project = new Project({
1328
- useInMemoryFileSystem: true,
1329
- compilerOptions: {
1330
- allowJs: true,
1331
- checkJs: false,
1332
- },
1333
- });
1334
- }
1335
-
1336
- async analyzeFileBasic(filePath, options = {}) {
1337
- const violations = [];
1338
-
1339
- try {
1340
- const sourceFile = this.project.addSourceFileAtPath(filePath);
1341
-
1342
- // Analyze variable declarations
1343
- sourceFile.getVariableDeclarations().forEach(declaration => {
1344
- const initializer = declaration.getInitializer();
1345
- if (initializer && this.isHardcodedConfig(initializer, declaration.getName())) {
1346
- violations.push({
1347
- line: declaration.getStartLineNumber(),
1348
- column: declaration.getStart() - declaration.getStartLinePos() + 1,
1349
- message: `Hardcoded configuration value detected in variable '${declaration.getName()}'`,
1350
- severity: 'warning',
1351
- rule: 'C042'
1352
- });
1353
- }
1354
- });
1355
-
1356
- // Analyze property assignments
1357
- sourceFile.getDescendantsOfKind(ts.SyntaxKind.PropertyAssignment).forEach(prop => {
1358
- const value = prop.getInitializer();
1359
- if (value && this.isHardcodedConfig(value, prop.getName())) {
1360
- violations.push({
1361
- line: prop.getStartLineNumber(),
1362
- column: prop.getStart() - prop.getStartLinePos() + 1,
1363
- message: `Hardcoded configuration value in property '${prop.getName()}'`,
1364
- severity: 'warning',
1365
- rule: 'C042'
1366
- });
1367
- }
1368
- });
1369
-
1370
- this.project.removeSourceFile(sourceFile);
1371
- return violations;
1372
- } catch (error) {
1373
- throw new Error(`Symbol analysis failed: ${error.message}`);
1374
- }
1375
- }
1376
-
1377
- isHardcodedConfig(node, name) {
1378
- // Check if the value looks like configuration
1379
- const configPatterns = [
1380
- /url/i, /host/i, /port/i, /timeout/i, /secret/i,
1381
- /api[_-]?key/i, /password/i, /connection/i, /endpoint/i
1382
- ];
1383
-
1384
- const nameMatches = configPatterns.some(pattern => pattern.test(name));
1385
-
1386
- if (!nameMatches) return false;
1387
-
1388
- // Check if value is hardcoded (string/number literal)
1389
- return node.getKind() === ts.SyntaxKind.StringLiteral ||
1390
- node.getKind() === ts.SyntaxKind.NumericLiteral;
1391
- }
1392
- }
1393
-
1394
- module.exports = C042SymbolBasedAnalyzer;
1395
- ```
1396
-
1397
- #### **Step 4: Implement Regex-Based Analyzer (Fallback)**
1398
-
1399
- ```javascript
1400
- // regex-based-analyzer.js - Broad coverage pattern matching
1401
- const fs = require('fs');
1402
-
1403
- class C042RegexBasedAnalyzer {
1404
- constructor(verbose = false) {
1405
- this.verbose = verbose;
1406
-
1407
- // Configuration-related patterns
1408
- this.configPatterns = [
1409
- /(?:const|let|var)\s+(\w*(?:url|host|port|timeout|secret|key|password|connection|endpoint)\w*)\s*=\s*['"`]([^'"`]+)['"`]/gi,
1410
- /(\w*(?:url|host|port|timeout|secret|key|password|connection|endpoint)\w*)\s*:\s*['"`]([^'"`]+)['"`]/gi,
1411
- /(\w*(?:url|host|port|timeout|secret|key|password|connection|endpoint)\w*)\s*=\s*(\d+)/gi
1412
- ];
1413
- }
1414
-
1415
- async analyzeFileBasic(filePath, options = {}) {
1416
- const violations = [];
1417
-
1418
- try {
1419
- const content = fs.readFileSync(filePath, 'utf8');
1420
- const lines = content.split('\n');
1421
-
1422
- this.configPatterns.forEach(pattern => {
1423
- lines.forEach((line, index) => {
1424
- let match;
1425
- while ((match = pattern.exec(line)) !== null) {
1426
- const variableName = match[1];
1427
- const value = match[2];
1428
-
1429
- if (this.isLikelyHardcodedConfig(variableName, value)) {
1430
- violations.push({
1431
- line: index + 1,
1432
- column: match.index + 1,
1433
- message: `Potential hardcoded configuration: '${variableName}' = '${value}'`,
1434
- severity: 'warning',
1435
- rule: 'C042'
1436
- });
1437
- }
1438
- }
1439
- pattern.lastIndex = 0; // Reset regex
1440
- });
1441
- });
1442
-
1443
- return violations;
1444
- } catch (error) {
1445
- throw new Error(`Regex analysis failed: ${error.message}`);
1446
- }
1447
- }
1448
-
1449
- isLikelyHardcodedConfig(name, value) {
1450
- // Skip obvious non-config values
1451
- const skipPatterns = [
1452
- /^(true|false|null|undefined)$/i,
1453
- /^process\.env\./i,
1454
- /^require\(/i,
1455
- /^\w+\(/i // Function calls
1456
- ];
1457
-
1458
- return !skipPatterns.some(pattern => pattern.test(value));
1459
- }
1460
- }
1461
-
1462
- module.exports = C042RegexBasedAnalyzer;
1463
- ```
1464
-
1465
- #### **Step 5: Create Rule Configuration**
1466
-
1467
- ```json
1468
- // config.json - Rule metadata and examples
1469
- {
1470
- "id": "C042",
1471
- "name": "No Hardcoded Configuration Values",
1472
- "category": "Maintainability",
1473
- "severity": "warning",
1474
- "description": "Detects hardcoded configuration values that should use environment variables or config files",
1475
- "rationale": "Hardcoded configuration makes applications less flexible and harder to deploy across different environments",
1476
- "examples": {
1477
- "bad": [
1478
- "const API_URL = 'https://api.example.com';",
1479
- "const DB_HOST = 'localhost';",
1480
- "const MAX_CONNECTIONS = 100;",
1481
- "config: { port: 3000, host: 'localhost' }"
1482
- ],
1483
- "good": [
1484
- "const API_URL = process.env.API_URL;",
1485
- "const DB_HOST = process.env.DB_HOST || 'localhost';",
1486
- "const config = loadConfigFromFile();",
1487
- "const PORT = parseInt(process.env.PORT || '3000');"
1488
- ]
1489
- },
1490
- "tags": ["configuration", "deployment", "security", "maintainability"],
1491
- "accuracy": {
1492
- "symbol_based": "high",
1493
- "regex_based": "medium"
1494
- },
1495
- "performance": {
1496
- "symbol_based": "medium",
1497
- "regex_based": "fast"
1498
- }
1499
- }
1500
- ```
1501
-
1502
- #### **Step 6: Document Analysis Strategy**
1503
-
1504
- ```markdown
1505
- <!-- STRATEGY.md - Analysis approach documentation -->
1506
- # C042: No Hardcoded Configuration Values - Analysis Strategy
1507
-
1508
- ## Overview
1509
- This rule detects hardcoded configuration values using a hybrid approach combining symbol-based AST analysis with regex-based pattern matching.
1510
-
1511
- ## Analysis Approaches
1512
-
1513
- ### Symbol-Based Analysis (Primary)
1514
- - **Tool**: ts-morph AST parser
1515
- - **Accuracy**: High (95%+)
1516
- - **Coverage**: Variable declarations, property assignments
1517
- - **Strengths**:
1518
- - Understands code context
1519
- - Low false positives
1520
- - Handles complex expressions
1521
- - **Limitations**:
1522
- - Requires valid syntax
1523
- - Slower performance
1524
-
1525
- ### Regex-Based Analysis (Fallback)
1526
- - **Tool**: Regular expressions
1527
- - **Accuracy**: Medium (80%+)
1528
- - **Coverage**: String/number patterns in config-like names
1529
- - **Strengths**:
1530
- - Fast execution
1531
- - Works with any file
1532
- - Broad pattern coverage
1533
- - **Limitations**:
1534
- - Higher false positives
1535
- - Limited context understanding
1536
-
1537
- ## Detection Patterns
1538
-
1539
- ### Configuration Indicators
1540
- - Variable/property names containing: url, host, port, timeout, secret, key, password, connection, endpoint
1541
- - Literal string/number values
1542
- - Assignment patterns
1543
-
1544
- ### Exclusions
1545
- - Environment variable references (process.env.*)
1546
- - Function calls
1547
- - Boolean/null literals
1548
- - Computed values
1549
-
1550
- ## Testing Strategy
1551
- - Unit tests for each analyzer
1552
- - Integration tests for hybrid orchestration
1553
- - Real-world project validation
1554
- - Performance benchmarking
1555
- ```
1556
-
1557
- #### **Step 7: Add to Rules Registry**
1558
-
1559
- ```json
1560
- // config/rules/rules-registry.json - Add rule definition
1561
- {
1562
- "C042": {
1563
- "id": "C042",
1564
- "name": "No Hardcoded Configuration Values",
1565
- "category": "Maintainability",
1566
- "severity": "warning",
1567
- "description": "Detects hardcoded configuration values that should use environment variables or config files",
1568
- "engine": "heuristic",
1569
- "analyzerPath": "rules/common/C042_no_hardcoded_config/analyzer.js",
1570
- "configPath": "rules/common/C042_no_hardcoded_config/config.json",
1571
- "enabled": true,
1572
- "languages": ["javascript", "typescript"],
1573
- "tags": ["configuration", "deployment", "security"]
1574
- }
1575
- }
1576
- ```
1577
-
1578
- #### **Step 8: Register with Enhanced Rules Registry**
1579
-
1580
- ```javascript
1581
- // core/enhanced-rules-registry.js - Add engine preferences
1582
- const enginePreferences = {
1583
- // ... existing rules
1584
- 'C042': ['heuristic'], // Add your rule here
1585
- };
1586
- ```
1587
-
1588
- #### **Step 9: Add to Analysis Strategies**
1589
-
1590
- ```javascript
1591
- // config/rule-analysis-strategies.js - Add to AST preferred rules
1592
- const strategies = {
1593
- astPreferred: [
1594
- // ... existing rules
1595
- 'C042', // Add your rule here
1596
- ],
1597
- regexOnly: [
1598
- // Rules that only use regex
1599
- ],
1600
- hybridOnly: [
1601
- // Rules requiring both approaches
1602
- ]
1603
- };
1604
- ```
1605
-
1606
- #### **Step 10: Create Test Script for Direct Testing**
1607
-
1608
- ```javascript
1609
- // test-c042-direct.js - Direct analyzer testing
1610
- const C042Analyzer = require('./rules/common/C042_no_hardcoded_config/analyzer');
1611
-
1612
- async function testC042() {
1613
- const analyzer = new C042Analyzer(true); // Enable verbose mode
1614
-
1615
- try {
1616
- const violations = await analyzer.analyzeFileBasic('test-files/c042-test.js');
1617
- console.log(`\n📊 C042 Analysis Results:`);
1618
- console.log(` Found ${violations.length} violations`);
1619
-
1620
- violations.forEach((violation, index) => {
1621
- console.log(` ${index + 1}. Line ${violation.line}: ${violation.message}`);
1622
- });
1623
- } catch (error) {
1624
- console.error('❌ C042 test failed:', error.message);
1625
- }
1626
- }
1627
-
1628
- testC042();
1629
- ```
1630
-
1631
- #### **Step 11: Create Test File**
1632
-
1633
- ```javascript
1634
- // test-files/c042-test.js
1635
- /**
1636
- * Test file for C042 No Hardcoded Configuration Values rule
1637
- * Contains various configuration patterns to validate analyzer
1638
- */
1639
-
1640
- // Good Examples: Using environment variables
1641
- const API_URL = process.env.API_URL;
1642
- const MAX_RETRIES = parseInt(process.env.MAX_RETRIES || '3');
1643
- const config = loadConfigFromFile();
1644
-
1645
- // Bad Examples: Hardcoded values (should trigger violations)
1646
- const DATABASE_URL = 'mongodb://localhost:27017/myapp';
1647
- const API_SECRET = 'sk-1234567890abcdef';
1648
- const MAX_CONNECTIONS = 100;
1649
- const TIMEOUT_MS = 5000;
1650
-
1651
- // Edge Cases
1652
- const DEBUG_MODE = true; // Should not trigger (not config pattern)
1653
- const DEFAULT_CONFIG = { // Should trigger for nested hardcoded values
1654
- port: 3000,
1655
- host: 'localhost'
1656
- };
1657
-
1658
- module.exports = {
1659
- goodConfig,
1660
- badConfig
1661
- };
1662
- ```
1663
-
1664
- #### **Step 12: Test Your Rule**
1665
-
1666
- ```bash
1667
- # Test the new rule
1668
- node cli.js --rule=C042 --input=test-files/c042-test.js --verbose --engine=heuristic
1669
-
1670
- # Expected output: Should detect 4-5 violations for hardcoded config values
1671
-
1672
- # Test rule in real projects
1673
- node cli.js --rule=C042 --input=src --engine=heuristic
1674
-
1675
- # Test with different engines
1676
- node cli.js --rule=C042 --input=test-files/c042-test.js --engine=openai
1677
- ```
1678
-
1679
- #### **Step 13: Validate Integration**
1680
-
1681
- ```bash
1682
- # Check rule is loaded
1683
- node cli.js --rule=C042 --input=test-files/c042-test.js --verbose | grep "Loaded.*rule: C042"
1684
-
1685
- # Verify both analyzers work
1686
- node cli.js --rule=C042 --input=test-files/c042-test.js --verbose | grep -E "(Symbol-based|Regex-fallback)"
1687
-
1688
- # Check for violations
1689
- node cli.js --rule=C042 --input=test-files/c042-test.js --format=json | jq '.results[].violations | length'
1690
- ```
1691
-
1692
- ### **Modern Rule Development Best Practices**
1693
-
1694
- #### **Architecture Patterns**
1695
-
1696
- 1. **Hybrid Symbol + Regex Approach** (Recommended)
1697
- - Primary: Symbol-based analysis (high accuracy)
1698
- - Fallback: Regex-based analysis (broad coverage)
1699
- - Example: C035, C033
1700
-
1701
- 2. **Symbol-Only Approach**
1702
- - For complex AST analysis requiring deep context
1703
- - Higher accuracy but limited fallback
1704
- - Example: Advanced OOP rules
1705
-
1706
- 3. **Regex-Only Approach**
1707
- - For simple pattern matching
1708
- - Fast but lower accuracy
1709
- - Example: Basic naming conventions
1710
-
1711
- #### **Code Quality Standards for Rules**
1712
-
1713
- - **Rule C005**: Each analyzer should have single responsibility
1714
- - **Rule C006**: Method names should be verb-noun (e.g., `analyzeVariableDeclaration`)
1715
- - **Rule C031**: Keep validation logic separate from detection logic
1716
- - **Rule C035**: Log detailed information for debugging (verbose mode)
1717
-
1718
- #### **File Structure Standards**
1719
-
1720
- ```
1721
- rules/common/C042_rule_name/
1722
- ├── analyzer.js # Main hybrid orchestrator
1723
- ├── symbol-based-analyzer.js # Primary AST analysis
1724
- ├── regex-based-analyzer.js # Fallback pattern matching
1725
- ├── config.json # Rule configuration
1726
- ├── STRATEGY.md # Analysis approach documentation
1727
- └── README.md # Rule documentation (optional)
1728
- ```
1729
-
1730
- #### **Error Handling Standards**
1731
-
1732
- ```javascript
1733
- // Always include proper error handling
1734
- try {
1735
- const violations = await this.symbolAnalyzer.analyzeFileBasic(filePath, options);
1736
- return violations;
1737
- } catch (error) {
1738
- if (this.verbose) {
1739
- console.warn(`⚠️ [C042] Symbol analysis failed: ${error.message}`);
1740
- }
1741
- // Graceful fallback to regex analysis
1742
- }
1743
- ```
1744
-
1745
- #### **Testing Standards**
1746
-
1747
- 1. **Unit Tests**: Test each analyzer independently
1748
- 2. **Integration Tests**: Test hybrid orchestration
1749
- 3. **Real Project Tests**: Validate on actual codebases
1750
- 4. **Performance Tests**: Ensure reasonable execution time
1751
-
1752
- #### **Documentation Standards**
1753
-
1754
- - **STRATEGY.md**: Document analysis approach and accuracy
1755
- - **config.json**: Include examples of good/bad patterns
1756
- - **CONTRIBUTING.md**: Update with new patterns learned
1757
- - **Inline comments**: Explain complex detection logic
1758
-
1759
- ### **Rule Development Checklist**
1760
-
1761
- - [ ] Created rule directory structure
1762
- - [ ] Implemented main analyzer (hybrid orchestrator)
1763
- - [ ] Implemented symbol-based analyzer (primary)
1764
- - [ ] Implemented regex-based analyzer (fallback)
1765
- - [ ] Created rule configuration (config.json)
1766
- - [ ] Documented analysis strategy (STRATEGY.md)
1767
- - [ ] Added to rules registry
1768
- - [ ] Added to enhanced rules registry
1769
- - [ ] Added to analysis strategies
1770
- - [ ] Created test file
1771
- - [ ] Tested rule individually
1772
- - [ ] Tested rule integration
1773
- - [ ] Validated on real projects
1774
- - [ ] Updated documentation
1775
-
1776
- ### **Common Issues and Solutions**
1777
-
1778
- #### **Issue: Rule not loaded**
1779
- ```bash
1780
- # Check if rule exists in registry
1781
- node cli.js --rule=C042 --input=test-files --verbose | grep "No compatible analyzer found for C042"
1782
-
1783
- # Solution: Add rule to config/rules/rules-registry.json
1784
- ```
1785
-
1786
- #### **Issue: Symbol analysis fails**
1787
- ```bash
1788
- # Check semantic engine availability
1789
- node cli.js --rule=C042 --input=test-files --verbose | grep "Symbol analysis failed"
1790
-
1791
- # Solution: Ensure proper error handling and regex fallback
1792
- ```
1793
-
1794
- #### **Issue: No violations detected**
1795
- ```bash
1796
- # Test analyzers independently
1797
- node test-c042-direct.js
1798
-
1799
- # Check file patterns match expectations
1800
- ```
1801
-
1802
- #### **Issue: Too many false positives**
1803
- ```bash
1804
- # Refine detection patterns
1805
- # Add exclusion rules
1806
- # Test on diverse codebases
1807
- ```
1808
-
1809
- ---
1810
-
1811
- ## 🤝 **Community**
1812
-
1813
- - **Discord**: [Sun Engineering Discord](https://discord.gg/sun-engineering)
1814
- - **Issues**: [GitHub Issues](https://github.com/sun-engineering/sunlint/issues)
1815
- - **Discussions**: [GitHub Discussions](https://github.com/sun-engineering/sunlint/discussions)
1816
-
1817
- ## 📄 **License**
333
+ ## 📚 Advanced Topics
1818
334
 
1819
- By contributing, you agree that your contributions will be licensed under the MIT License.
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
1820
339
 
1821
340
  ---
1822
341
 
1823
- **Thank you for making Sun Lint better! ☀️**
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.