@sun-asterisk/sunlint 1.2.2 → 1.3.0

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 (64) hide show
  1. package/CHANGELOG.md +40 -1
  2. package/CONTRIBUTING.md +533 -70
  3. package/README.md +16 -2
  4. package/config/engines/engines-enhanced.json +86 -0
  5. package/config/engines/semantic-config.json +114 -0
  6. package/config/eslint-rule-mapping.json +50 -38
  7. package/config/rules/enhanced-rules-registry.json +2503 -0
  8. package/config/rules/rules-registry-generated.json +785 -837
  9. package/core/adapters/sunlint-rule-adapter.js +25 -30
  10. package/core/analysis-orchestrator.js +42 -2
  11. package/core/categories.js +52 -0
  12. package/core/category-constants.js +39 -0
  13. package/core/cli-action-handler.js +32 -5
  14. package/core/config-manager.js +111 -0
  15. package/core/config-merger.js +61 -0
  16. package/core/constants/categories.js +168 -0
  17. package/core/constants/defaults.js +165 -0
  18. package/core/constants/engines.js +185 -0
  19. package/core/constants/index.js +30 -0
  20. package/core/constants/rules.js +215 -0
  21. package/core/file-targeting-service.js +128 -7
  22. package/core/interfaces/rule-plugin.interface.js +207 -0
  23. package/core/plugin-manager.js +448 -0
  24. package/core/rule-selection-service.js +42 -15
  25. package/core/semantic-engine.js +560 -0
  26. package/core/semantic-rule-base.js +433 -0
  27. package/core/unified-rule-registry.js +484 -0
  28. package/docs/CONSTANTS-ARCHITECTURE.md +288 -0
  29. package/engines/core/base-engine.js +249 -0
  30. package/engines/engine-factory.js +275 -0
  31. package/engines/eslint-engine.js +171 -19
  32. package/engines/heuristic-engine.js +511 -78
  33. package/integrations/eslint/plugin/index.js +27 -27
  34. package/package.json +10 -6
  35. package/rules/common/C003_no_vague_abbreviations/analyzer.js +1 -1
  36. package/rules/common/C029_catch_block_logging/analyzer.js +17 -5
  37. package/rules/common/C047_no_duplicate_retry_logic/c047-semantic-rule.js +278 -0
  38. package/rules/common/C047_no_duplicate_retry_logic/symbol-analyzer-enhanced.js +968 -0
  39. package/rules/common/C047_no_duplicate_retry_logic/symbol-config.json +71 -0
  40. package/rules/index.js +7 -0
  41. package/scripts/category-manager.js +150 -0
  42. package/scripts/generate-rules-registry.js +88 -0
  43. package/scripts/migrate-rule-registry.js +157 -0
  44. package/scripts/validate-system.js +48 -0
  45. package/.sunlint.json +0 -35
  46. package/config/README.md +0 -88
  47. package/config/engines/eslint-rule-mapping.json +0 -74
  48. package/config/schemas/sunlint-schema.json +0 -0
  49. package/config/testing/test-s005-working.ts +0 -22
  50. package/core/multi-rule-runner.js +0 -0
  51. package/engines/tree-sitter-parser.js +0 -0
  52. package/engines/universal-ast-engine.js +0 -0
  53. package/rules/common/C029_catch_block_logging/analyzer-backup.js +0 -426
  54. package/rules/common/C029_catch_block_logging/analyzer-fixed.js +0 -130
  55. package/rules/common/C029_catch_block_logging/analyzer-multi-tech.js +0 -487
  56. package/rules/common/C029_catch_block_logging/analyzer-simple.js +0 -110
  57. package/rules/common/C029_catch_block_logging/ast-analyzer-backup.js +0 -441
  58. package/rules/common/C029_catch_block_logging/ast-analyzer-new.js +0 -127
  59. package/rules/common/C029_catch_block_logging/ast-analyzer.js +0 -133
  60. package/rules/common/C029_catch_block_logging/cfg-analyzer.js +0 -408
  61. package/rules/common/C029_catch_block_logging/dataflow-analyzer.js +0 -454
  62. package/rules/common/C029_catch_block_logging/multi-language-ast-engine.js +0 -700
  63. package/rules/common/C029_catch_block_logging/pattern-learning-analyzer.js +0 -568
  64. package/rules/common/C029_catch_block_logging/semantic-analyzer.js +0 -459
@@ -0,0 +1,288 @@
1
+ # SunLint Constants Architecture
2
+
3
+ ## Overview
4
+
5
+ SunLint now uses a centralized constants sub-package located at `core/constants/` to manage all constants and configuration values. This improves code organization, maintainability, and extensibility.
6
+
7
+ ## Structure
8
+
9
+ ```
10
+ core/
11
+ constants/
12
+ categories.js # Category-principle mappings and functions
13
+ defaults.js # Default configurations and values
14
+ engines.js # Engine capabilities and configurations
15
+ rules.js # Rule-related constants and utilities
16
+ index.js # Barrel export for all constants
17
+ ```
18
+
19
+ ## Migration Guide
20
+
21
+ ### Before (Old approach)
22
+ ```javascript
23
+ // Scattered constants across multiple files
24
+ const { getValidCategories } = require('./core/category-constants');
25
+ const defaultRules = ['C006', 'C019']; // Hardcoded in various files
26
+ const SUPPORTED_ENGINES = { /* scattered */ };
27
+ ```
28
+
29
+ ### After (New centralized approach)
30
+ ```javascript
31
+ // Option 1: Import specific module
32
+ const { getValidCategories } = require('./core/constants/categories');
33
+ const { getDefaultRuleSet } = require('./core/constants/defaults');
34
+ const { SUPPORTED_ENGINES } = require('./core/constants/engines');
35
+
36
+ // Option 2: Import from barrel export
37
+ const {
38
+ getValidCategories,
39
+ getDefaultRuleSet,
40
+ SUPPORTED_ENGINES
41
+ } = require('./core/constants');
42
+
43
+ // Option 3: Import entire module
44
+ const constants = require('./core/constants');
45
+ const categories = constants.getValidCategories();
46
+ ```
47
+
48
+ ## Modules
49
+
50
+ ### 1. Categories (`core/constants/categories.js`)
51
+
52
+ **Purpose**: Category-principle mappings, validation, and normalization.
53
+
54
+ **Key Exports**:
55
+ ```javascript
56
+ // Constants
57
+ SUNLINT_PRINCIPLES // Object with all principle constants
58
+ CATEGORY_PRINCIPLE_MAP // Category to principle mapping
59
+ CATEGORY_DESCRIPTIONS // Human-readable descriptions
60
+
61
+ // Functions
62
+ getValidCategories() // Get all valid categories
63
+ getCategoryPrinciples(category) // Get principles for category
64
+ isValidCategory(category) // Validate category
65
+ normalizeCategory(category) // Normalize and validate
66
+ getCategoryStats() // Get statistics
67
+ ```
68
+
69
+ **Example Usage**:
70
+ ```javascript
71
+ const { getValidCategories, normalizeCategory } = require('./core/constants/categories');
72
+
73
+ const validCategories = getValidCategories();
74
+ // ['security', 'quality', 'performance', ...]
75
+
76
+ const normalized = normalizeCategory('QUALITY');
77
+ // 'quality'
78
+ ```
79
+
80
+ ### 2. Defaults (`core/constants/defaults.js`)
81
+
82
+ **Purpose**: Default configurations, rule sets, and standard values.
83
+
84
+ **Key Exports**:
85
+ ```javascript
86
+ // Constants
87
+ DEFAULT_RULE_SETS // Predefined rule sets (MINIMAL, ESSENTIAL, etc.)
88
+ DEFAULT_CONFIG // Default configuration object
89
+ DEFAULT_SEVERITIES // Severity levels
90
+ DEFAULT_TIMEOUTS // Timeout configurations
91
+ DEFAULT_LIMITS // File size and processing limits
92
+
93
+ // Functions
94
+ getDefaultRuleSet(name) // Get predefined rule set
95
+ getDefaultConfig(overrides) // Get configuration with overrides
96
+ getLanguageExtensions(lang) // Get file extensions for language
97
+ isFileSizeValid(size) // Check file size limits
98
+ ```
99
+
100
+ **Example Usage**:
101
+ ```javascript
102
+ const { getDefaultRuleSet, getDefaultConfig } = require('./core/constants/defaults');
103
+
104
+ const essentialRules = getDefaultRuleSet('ESSENTIAL');
105
+ // ['C001', 'C002', 'C003', ...]
106
+
107
+ const config = getDefaultConfig({ verbose: true });
108
+ // { verbose: true, useRegistry: true, ... }
109
+ ```
110
+
111
+ ### 3. Engines (`core/constants/engines.js`)
112
+
113
+ **Purpose**: Engine capabilities, configurations, and language support.
114
+
115
+ **Key Exports**:
116
+ ```javascript
117
+ // Constants
118
+ SUPPORTED_ENGINES // Object with all supported engines
119
+ ENGINE_CAPABILITIES // Engine features and language support
120
+ ENGINE_MODES // Execution modes (sequential, parallel, etc.)
121
+ ENGINE_PERFORMANCE // Performance characteristics
122
+
123
+ // Functions
124
+ getEngineLanguages(engine) // Get supported languages
125
+ getEnginesForLanguage(language) // Get engines for language
126
+ getRecommendedEngine(language) // Get best engine for language
127
+ isLanguageSupported(engine, lang) // Check support
128
+ getEnginePerformance(engine) // Get performance info
129
+ ```
130
+
131
+ **Example Usage**:
132
+ ```javascript
133
+ const { getEnginesForLanguage, getRecommendedEngine } = require('./core/constants/engines');
134
+
135
+ const jsEngines = getEnginesForLanguage('javascript');
136
+ // [{ name: 'heuristic', priority: 1, features: [...] }, ...]
137
+
138
+ const recommended = getRecommendedEngine('typescript');
139
+ // 'heuristic'
140
+ ```
141
+
142
+ ### 4. Rules (`core/constants/rules.js`)
143
+
144
+ **Purpose**: Rule-related constants, metadata, and utilities.
145
+
146
+ **Key Exports**:
147
+ ```javascript
148
+ // Constants
149
+ RULE_SEVERITIES // Severity levels (ERROR, WARNING, etc.)
150
+ RULE_STATUS // Execution status values
151
+ RULE_TYPES // Analysis types (HEURISTIC, AST, etc.)
152
+ RULE_SCOPES // Operation scopes (FILE, PROJECT, etc.)
153
+ RULE_LANGUAGE_PATTERNS // Regex patterns for rule IDs
154
+ RULE_TIMEOUTS // Timeout values by rule type
155
+
156
+ // Functions
157
+ getLanguageFromRuleId(ruleId) // Extract language from rule ID
158
+ isValidRuleId(ruleId) // Validate rule ID format
159
+ getRuleTimeout(type) // Get timeout for rule type
160
+ getDefaultRuleMetadata(overrides) // Get rule metadata template
161
+ isValidSeverity(severity) // Validate severity level
162
+ ```
163
+
164
+ **Example Usage**:
165
+ ```javascript
166
+ const { getLanguageFromRuleId, isValidRuleId } = require('./core/constants/rules');
167
+
168
+ const language = getLanguageFromRuleId('C001');
169
+ // 'common'
170
+
171
+ const isValid = isValidRuleId('CUSTOM_RULE');
172
+ // true
173
+ ```
174
+
175
+ ## Backward Compatibility
176
+
177
+ The following files are maintained for backward compatibility but are deprecated:
178
+
179
+ - `core/category-constants.js` - Proxies to `core/constants/categories.js`
180
+ - `core/categories.js` - Proxies to `core/constants/categories.js`
181
+
182
+ **Migration Path**:
183
+ 1. Update imports to use `core/constants/*` directly
184
+ 2. Replace deprecated file imports gradually
185
+ 3. Legacy files will be removed in future versions
186
+
187
+ ## Benefits
188
+
189
+ ### 1. **Better Organization**
190
+ - Related constants grouped together
191
+ - Clear separation of concerns
192
+ - Easier to locate and modify constants
193
+
194
+ ### 2. **Improved Maintainability**
195
+ - Single source of truth for each type of constant
196
+ - Centralized documentation and examples
197
+ - Easier to add new constants or modify existing ones
198
+
199
+ ### 3. **Enhanced Extensibility**
200
+ - Modular structure supports new constant types
201
+ - Barrel export provides flexible import options
202
+ - Framework for adding new engines, rules, categories
203
+
204
+ ### 4. **Developer Experience**
205
+ - Clearer imports with specific module names
206
+ - Better IDE support and autocomplete
207
+ - Self-documenting code structure
208
+
209
+ ## Best Practices
210
+
211
+ ### 1. **Import Strategy**
212
+ ```javascript
213
+ // ✅ Good: Import specific functions
214
+ const { getValidCategories, normalizeCategory } = require('./core/constants/categories');
215
+
216
+ // ✅ Good: Import from barrel for multiple modules
217
+ const { getValidCategories, getDefaultRuleSet } = require('./core/constants');
218
+
219
+ // ❌ Avoid: Importing entire modules unnecessarily
220
+ const allConstants = require('./core/constants');
221
+ ```
222
+
223
+ ### 2. **Adding New Constants**
224
+ ```javascript
225
+ // Add to appropriate module (e.g., categories.js)
226
+ const NEW_CATEGORY_FEATURE = 'new-feature';
227
+
228
+ // Export in module
229
+ module.exports = {
230
+ NEW_CATEGORY_FEATURE,
231
+ // ... other exports
232
+ };
233
+
234
+ // Update barrel export (index.js) if needed
235
+ ```
236
+
237
+ ### 3. **Extending Functionality**
238
+ ```javascript
239
+ // Add utility functions to appropriate modules
240
+ function getAdvancedCategoryInfo(category) {
241
+ // Implementation
242
+ }
243
+
244
+ // Export with other functions
245
+ module.exports = {
246
+ // ... existing exports
247
+ getAdvancedCategoryInfo
248
+ };
249
+ ```
250
+
251
+ ## Testing
252
+
253
+ Each constants module includes comprehensive tests:
254
+
255
+ ```bash
256
+ # Test entire constants structure
257
+ node test-constants-structure.js
258
+
259
+ # Test backward compatibility
260
+ node test-centralized-categories.js
261
+ ```
262
+
263
+ ## Future Enhancements
264
+
265
+ ### Planned Features
266
+ 1. **Dynamic Configuration Loading** - Load constants from external files
267
+ 2. **Environment-specific Constants** - Different values for dev/prod
268
+ 3. **Validation Schemas** - JSON Schema validation for all constants
269
+ 4. **Hot Reloading** - Update constants without restarting
270
+
271
+ ### Extension Points
272
+ - Add new constant modules (e.g., `integrations.js`, `plugins.js`)
273
+ - Extend barrel export for new modules
274
+ - Add validation functions for new constant types
275
+
276
+ ---
277
+
278
+ ## Quick Reference
279
+
280
+ | Module | Purpose | Key Functions |
281
+ |--------|---------|---------------|
282
+ | `categories.js` | Category management | `getValidCategories()`, `normalizeCategory()` |
283
+ | `defaults.js` | Default values | `getDefaultConfig()`, `getDefaultRuleSet()` |
284
+ | `engines.js` | Engine configuration | `getEnginesForLanguage()`, `getRecommendedEngine()` |
285
+ | `rules.js` | Rule utilities | `getLanguageFromRuleId()`, `isValidRuleId()` |
286
+ | `index.js` | Barrel export | All functions from all modules |
287
+
288
+ For detailed API documentation, see the JSDoc comments in each module file.
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Base Engine Abstract Class
3
+ * Following Rule C005: Single responsibility - Base engine functionality
4
+ * Following Rule C014: Dependency injection - Plugin architecture
5
+ */
6
+
7
+ const EventEmitter = require('events');
8
+
9
+ class BaseEngine extends EventEmitter {
10
+ constructor(name, version, supportedLanguages = []) {
11
+ super();
12
+ this.name = name;
13
+ this.version = version;
14
+ this.supportedLanguages = supportedLanguages;
15
+ this.initialized = false;
16
+ this.verbose = false;
17
+ this.ruleRegistry = new Map();
18
+ this.pluginManager = null;
19
+ }
20
+
21
+ /**
22
+ * Set plugin manager for this engine
23
+ * @param {PluginManager} pluginManager - Plugin manager instance
24
+ */
25
+ setPluginManager(pluginManager) {
26
+ this.pluginManager = pluginManager;
27
+ }
28
+
29
+ /**
30
+ * Abstract method - must be implemented by subclasses
31
+ */
32
+ async initialize(config) {
33
+ this.verbose = config.verbose || false;
34
+
35
+ // Load rules from plugin manager if available
36
+ if (this.pluginManager) {
37
+ const engineRules = await this.pluginManager.loadRulesForEngine(this.name, config);
38
+ this.ruleRegistry = engineRules;
39
+
40
+ if (this.verbose) {
41
+ console.log(`🔧 [${this.name}] Loaded ${engineRules.size} rules from Plugin Manager`);
42
+ }
43
+ }
44
+
45
+ this.initialized = true;
46
+ }
47
+
48
+ /**
49
+ * Abstract method - must be implemented by subclasses
50
+ */
51
+ async analyze(files, rules, options) {
52
+ throw new Error('analyze() must be implemented by subclass');
53
+ }
54
+
55
+ /**
56
+ * Analyze files with a specific rule plugin
57
+ * @param {string} ruleId - Rule identifier
58
+ * @param {Array} files - Files to analyze
59
+ * @param {string} language - Programming language
60
+ * @param {Object} options - Analysis options
61
+ * @returns {Array} Array of violations
62
+ */
63
+ async analyzeWithRule(ruleId, files, language, options = {}) {
64
+ const ruleInfo = this.ruleRegistry.get(ruleId);
65
+
66
+ if (!ruleInfo) {
67
+ if (this.verbose) {
68
+ console.warn(`⚠️ [${this.name}] Rule ${ruleId} not found`);
69
+ }
70
+ return [];
71
+ }
72
+
73
+ try {
74
+ const violations = await ruleInfo.plugin.analyze(files, language, {
75
+ ...options,
76
+ engine: this.name,
77
+ metadata: ruleInfo.metadata
78
+ });
79
+
80
+ // Ensure violations have correct rule ID
81
+ return violations.map(violation => ({
82
+ ...violation,
83
+ ruleId: ruleId,
84
+ engine: this.name,
85
+ pluginType: ruleInfo.type
86
+ }));
87
+
88
+ } catch (error) {
89
+ if (this.verbose) {
90
+ console.error(`❌ [${this.name}] Error analyzing with rule ${ruleId}: ${error.message}`);
91
+ }
92
+ return [];
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Check if a rule is supported by this engine
98
+ * @param {string} ruleId - Rule ID to check
99
+ * @returns {boolean} True if rule is supported
100
+ */
101
+ isRuleSupported(ruleId) {
102
+ return this.ruleRegistry.has(ruleId);
103
+ }
104
+
105
+ /**
106
+ * Get engine information
107
+ * @returns {Object} Engine metadata
108
+ */
109
+ getEngineInfo() {
110
+ return {
111
+ name: this.name,
112
+ version: this.version,
113
+ supportedLanguages: this.supportedLanguages,
114
+ initialized: this.initialized,
115
+ rulesLoaded: this.ruleRegistry.size
116
+ };
117
+ }
118
+
119
+ /**
120
+ * Register a rule with the engine
121
+ * @param {string} ruleId - Rule identifier
122
+ * @param {Object} ruleInfo - Rule information and analyzer
123
+ */
124
+ registerRule(ruleId, ruleInfo) {
125
+ this.ruleRegistry.set(ruleId, ruleInfo);
126
+ this.emit('ruleRegistered', { ruleId, engine: this.name });
127
+ }
128
+
129
+ /**
130
+ * Unregister a rule from the engine
131
+ * @param {string} ruleId - Rule identifier
132
+ */
133
+ unregisterRule(ruleId) {
134
+ const removed = this.ruleRegistry.delete(ruleId);
135
+ if (removed) {
136
+ this.emit('ruleUnregistered', { ruleId, engine: this.name });
137
+ }
138
+ return removed;
139
+ }
140
+
141
+ /**
142
+ * Get all registered rules
143
+ * @returns {string[]} Array of rule IDs
144
+ */
145
+ getRegisteredRules() {
146
+ return Array.from(this.ruleRegistry.keys());
147
+ }
148
+
149
+ /**
150
+ * Set plugin manager
151
+ * @param {Object} pluginManager - Plugin manager instance
152
+ */
153
+ setPluginManager(pluginManager) {
154
+ this.pluginManager = pluginManager;
155
+ }
156
+
157
+ /**
158
+ * Load rules using plugin manager
159
+ * @param {Object} config - Configuration options
160
+ */
161
+ async loadRules(config = {}) {
162
+ if (!this.pluginManager) {
163
+ throw new Error('Plugin manager not set');
164
+ }
165
+
166
+ const rules = await this.pluginManager.loadRulesForEngine(this.name, config);
167
+
168
+ for (const [ruleId, ruleInfo] of rules) {
169
+ this.registerRule(ruleId, ruleInfo);
170
+ }
171
+
172
+ if (this.verbose) {
173
+ console.log(`📚 [${this.name}] Loaded ${rules.size} rules`);
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Cleanup engine resources
179
+ */
180
+ async cleanup() {
181
+ this.ruleRegistry.clear();
182
+ this.initialized = false;
183
+ this.emit('cleanup', { engine: this.name });
184
+
185
+ if (this.verbose) {
186
+ console.log(`🔧 [${this.name}] Engine cleanup completed`);
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Group files by programming language
192
+ * @param {string[]} files - Files to group
193
+ * @returns {Object} Files grouped by language
194
+ */
195
+ groupFilesByLanguage(files) {
196
+ const groups = {};
197
+
198
+ for (const file of files) {
199
+ const language = this.detectLanguage(file);
200
+ if (!groups[language]) {
201
+ groups[language] = [];
202
+ }
203
+ groups[language].push(file);
204
+ }
205
+
206
+ return groups;
207
+ }
208
+
209
+ /**
210
+ * Detect programming language from file extension
211
+ * @param {string} filePath - File path
212
+ * @returns {string} Detected language
213
+ */
214
+ detectLanguage(filePath) {
215
+ const path = require('path');
216
+ const ext = path.extname(filePath).toLowerCase();
217
+
218
+ const languageMap = {
219
+ '.ts': 'typescript',
220
+ '.tsx': 'typescript',
221
+ '.js': 'javascript',
222
+ '.jsx': 'javascript',
223
+ '.dart': 'dart',
224
+ '.swift': 'swift',
225
+ '.kt': 'kotlin',
226
+ '.kts': 'kotlin',
227
+ '.java': 'java',
228
+ '.py': 'python',
229
+ '.go': 'go',
230
+ '.rs': 'rust',
231
+ '.php': 'php',
232
+ '.rb': 'ruby'
233
+ };
234
+
235
+ return languageMap[ext] || 'unknown';
236
+ }
237
+
238
+ /**
239
+ * Check if language is supported
240
+ * @param {string} language - Language to check
241
+ * @returns {boolean} True if supported
242
+ */
243
+ isLanguageSupported(language) {
244
+ return this.supportedLanguages.includes(language) ||
245
+ this.supportedLanguages.includes('all');
246
+ }
247
+ }
248
+
249
+ module.exports = BaseEngine;