@sun-asterisk/sunlint 1.2.2 โ 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +107 -1
- package/CONTRIBUTING.md +1654 -66
- package/README.md +19 -6
- package/config/ci-cd.json +54 -0
- package/config/development.json +56 -0
- package/config/engines/engines-enhanced.json +86 -0
- package/config/engines/semantic-config.json +114 -0
- package/config/eslint-rule-mapping.json +50 -38
- package/config/large-project.json +143 -0
- package/config/presets/all.json +0 -1
- package/config/release.json +70 -0
- package/config/rule-analysis-strategies.js +23 -4
- package/config/rules/S027-categories.json +122 -0
- package/config/rules/enhanced-rules-registry.json +2564 -0
- package/config/rules/rules-registry-generated.json +785 -837
- package/config/rules/rules-registry.json +13 -1
- package/core/adapters/sunlint-rule-adapter.js +25 -30
- package/core/analysis-orchestrator.js +42 -2
- package/core/categories.js +52 -0
- package/core/category-constants.js +39 -0
- package/core/cli-action-handler.js +53 -32
- package/core/cli-program.js +11 -3
- package/core/config-manager.js +111 -0
- package/core/config-merger.js +88 -0
- package/core/constants/categories.js +168 -0
- package/core/constants/defaults.js +165 -0
- package/core/constants/engines.js +185 -0
- package/core/constants/index.js +30 -0
- package/core/constants/rules.js +215 -0
- package/core/enhanced-rules-registry.js +3 -3
- package/core/file-targeting-service.js +128 -7
- package/core/interfaces/rule-plugin.interface.js +207 -0
- package/core/plugin-manager.js +448 -0
- package/core/rule-selection-service.js +42 -15
- package/core/semantic-engine.js +658 -0
- package/core/semantic-rule-base.js +433 -0
- package/core/unified-rule-registry.js +484 -0
- package/docs/COMMAND-EXAMPLES.md +134 -0
- package/docs/CONSTANTS-ARCHITECTURE.md +288 -0
- package/docs/LARGE-PROJECT-GUIDE.md +324 -0
- package/engines/core/base-engine.js +249 -0
- package/engines/engine-factory.js +275 -0
- package/engines/eslint-engine.js +171 -19
- package/engines/heuristic-engine.js +569 -78
- package/integrations/eslint/plugin/index.js +26 -28
- package/origin-rules/common-en.md +8 -8
- package/package.json +10 -6
- package/rules/common/C003_no_vague_abbreviations/analyzer.js +1 -1
- package/rules/common/C017_constructor_logic/analyzer.js +254 -17
- package/rules/common/C017_constructor_logic/semantic-analyzer.js +340 -0
- package/rules/common/C029_catch_block_logging/analyzer.js +17 -5
- package/rules/common/C033_separate_service_repository/README.md +78 -0
- package/rules/common/C033_separate_service_repository/analyzer.js +160 -0
- package/rules/common/C033_separate_service_repository/config.json +50 -0
- package/rules/common/C033_separate_service_repository/regex-based-analyzer.js +585 -0
- package/rules/common/C033_separate_service_repository/symbol-based-analyzer.js +368 -0
- package/rules/common/C035_error_logging_context/STRATEGY.md +99 -0
- package/rules/common/C035_error_logging_context/analyzer.js +230 -0
- package/rules/common/C035_error_logging_context/config.json +54 -0
- package/rules/common/C035_error_logging_context/regex-based-analyzer.js +299 -0
- package/rules/common/C035_error_logging_context/symbol-based-analyzer.js +454 -0
- package/rules/common/C040_centralized_validation/analyzer.js +165 -0
- package/rules/common/C040_centralized_validation/config.json +46 -0
- package/rules/common/C040_centralized_validation/regex-based-analyzer.js +243 -0
- package/rules/common/C040_centralized_validation/symbol-based-analyzer.js +416 -0
- package/rules/common/C047_no_duplicate_retry_logic/c047-semantic-rule.js +278 -0
- package/rules/common/C047_no_duplicate_retry_logic/symbol-analyzer-enhanced.js +968 -0
- package/rules/common/C047_no_duplicate_retry_logic/symbol-config.json +71 -0
- package/rules/common/{C076_single_test_behavior โ C072_single_test_behavior}/analyzer.js +6 -6
- package/rules/common/C076_explicit_function_types/README.md +30 -0
- package/rules/common/C076_explicit_function_types/analyzer.js +172 -0
- package/rules/common/C076_explicit_function_types/config.json +15 -0
- package/rules/common/C076_explicit_function_types/semantic-analyzer.js +341 -0
- package/rules/index.js +8 -0
- package/rules/parser/rule-parser.js +13 -2
- package/rules/security/S005_no_origin_auth/README.md +226 -0
- package/rules/security/S005_no_origin_auth/analyzer.js +184 -0
- package/rules/security/S005_no_origin_auth/ast-analyzer.js +406 -0
- package/rules/security/S005_no_origin_auth/config.json +85 -0
- package/rules/security/S006_no_plaintext_recovery_codes/README.md +139 -0
- package/rules/security/S006_no_plaintext_recovery_codes/analyzer.js +306 -0
- package/rules/security/S006_no_plaintext_recovery_codes/config.json +48 -0
- package/rules/security/S007_no_plaintext_otp/README.md +198 -0
- package/rules/security/S007_no_plaintext_otp/analyzer.js +406 -0
- package/rules/security/S007_no_plaintext_otp/config.json +79 -0
- package/rules/security/S007_no_plaintext_otp/semantic-analyzer.js +609 -0
- package/rules/security/S007_no_plaintext_otp/semantic-config.json +195 -0
- package/rules/security/S007_no_plaintext_otp/semantic-wrapper.js +280 -0
- package/rules/security/S027_no_hardcoded_secrets/analyzer.js +180 -366
- package/rules/security/S027_no_hardcoded_secrets/categories.json +153 -0
- package/rules/security/S027_no_hardcoded_secrets/categorized-analyzer.js +250 -0
- package/scripts/category-manager.js +150 -0
- package/scripts/generate-rules-registry.js +88 -0
- package/scripts/migrate-rule-registry.js +157 -0
- package/scripts/prepare-release.sh +1 -1
- package/scripts/validate-system.js +48 -0
- package/.sunlint.json +0 -35
- package/config/README.md +0 -88
- package/config/engines/eslint-rule-mapping.json +0 -74
- package/config/schemas/sunlint-schema.json +0 -0
- package/config/testing/test-s005-working.ts +0 -22
- package/core/multi-rule-runner.js +0 -0
- package/docs/ESLINT-INTEGRATION-STRATEGY.md +0 -392
- package/docs/FUTURE_PACKAGES.md +0 -83
- package/docs/HEURISTIC_VS_AI.md +0 -113
- package/docs/PRODUCTION_DEPLOYMENT_ANALYSIS.md +0 -112
- package/docs/PRODUCTION_SIZE_IMPACT.md +0 -183
- package/docs/RELEASE_GUIDE.md +0 -230
- package/docs/STANDARDIZED-CATEGORY-FILTERING.md +0 -156
- package/engines/tree-sitter-parser.js +0 -0
- package/engines/universal-ast-engine.js +0 -0
- package/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +0 -254
- package/rules/common/C029_catch_block_logging/analyzer-backup.js +0 -426
- package/rules/common/C029_catch_block_logging/analyzer-fixed.js +0 -130
- package/rules/common/C029_catch_block_logging/analyzer-multi-tech.js +0 -487
- package/rules/common/C029_catch_block_logging/analyzer-simple.js +0 -110
- package/rules/common/C029_catch_block_logging/ast-analyzer-backup.js +0 -441
- package/rules/common/C029_catch_block_logging/ast-analyzer-new.js +0 -127
- package/rules/common/C029_catch_block_logging/ast-analyzer.js +0 -133
- package/rules/common/C029_catch_block_logging/cfg-analyzer.js +0 -408
- package/rules/common/C029_catch_block_logging/dataflow-analyzer.js +0 -454
- package/rules/common/C029_catch_block_logging/multi-language-ast-engine.js +0 -700
- package/rules/common/C029_catch_block_logging/pattern-learning-analyzer.js +0 -568
- package/rules/common/C029_catch_block_logging/semantic-analyzer.js +0 -459
|
@@ -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;
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engine Factory
|
|
3
|
+
* Creates and manages engine instances with plugin support
|
|
4
|
+
* Following Rule C005: Single responsibility - Engine creation
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const PluginManager = require('../core/plugin-manager');
|
|
8
|
+
const BaseEngine = require('./core/base-engine');
|
|
9
|
+
|
|
10
|
+
// Engine imports
|
|
11
|
+
const HeuristicEngine = require('../engines/heuristic-engine');
|
|
12
|
+
const ESLintEngine = require('../engines/eslint-engine');
|
|
13
|
+
const OpenAIEngine = require('../engines/openai-engine');
|
|
14
|
+
|
|
15
|
+
class EngineFactory {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.pluginManager = null;
|
|
18
|
+
this.engines = new Map();
|
|
19
|
+
this.verbose = false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Initialize factory with plugin manager
|
|
24
|
+
* @param {Object} config - Configuration options
|
|
25
|
+
*/
|
|
26
|
+
async initialize(config = {}) {
|
|
27
|
+
this.verbose = config.verbose || false;
|
|
28
|
+
|
|
29
|
+
// Initialize plugin manager
|
|
30
|
+
this.pluginManager = new PluginManager();
|
|
31
|
+
await this.pluginManager.initialize(config);
|
|
32
|
+
|
|
33
|
+
if (this.verbose) {
|
|
34
|
+
console.log('๐ญ Engine Factory initialized');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Create an engine instance
|
|
40
|
+
* @param {string} engineType - Type of engine to create
|
|
41
|
+
* @param {Object} config - Engine configuration
|
|
42
|
+
* @returns {BaseEngine} Engine instance
|
|
43
|
+
*/
|
|
44
|
+
async createEngine(engineType, config = {}) {
|
|
45
|
+
const engineConfig = {
|
|
46
|
+
...config,
|
|
47
|
+
verbose: this.verbose,
|
|
48
|
+
pluginManager: this.pluginManager
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
let engine = null;
|
|
52
|
+
|
|
53
|
+
switch (engineType.toLowerCase()) {
|
|
54
|
+
case 'heuristic':
|
|
55
|
+
engine = new HeuristicEngine();
|
|
56
|
+
break;
|
|
57
|
+
case 'eslint':
|
|
58
|
+
engine = new ESLintEngine();
|
|
59
|
+
break;
|
|
60
|
+
case 'openai':
|
|
61
|
+
engine = new OpenAIEngine();
|
|
62
|
+
break;
|
|
63
|
+
default:
|
|
64
|
+
throw new Error(`Unknown engine type: ${engineType}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Set plugin manager and initialize
|
|
68
|
+
if (engine instanceof BaseEngine) {
|
|
69
|
+
engine.setPluginManager(this.pluginManager);
|
|
70
|
+
await engine.initialize(engineConfig);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Cache engine instance
|
|
74
|
+
this.engines.set(engineType, engine);
|
|
75
|
+
|
|
76
|
+
if (this.verbose) {
|
|
77
|
+
console.log(`๐ง Created ${engineType} engine with ${engine.ruleRegistry.size} rules`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return engine;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get an existing engine or create a new one
|
|
85
|
+
* @param {string} engineType - Type of engine
|
|
86
|
+
* @param {Object} config - Engine configuration
|
|
87
|
+
* @returns {BaseEngine} Engine instance
|
|
88
|
+
*/
|
|
89
|
+
async getEngine(engineType, config = {}) {
|
|
90
|
+
if (this.engines.has(engineType)) {
|
|
91
|
+
return this.engines.get(engineType);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return await this.createEngine(engineType, config);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Get all available engines
|
|
99
|
+
* @returns {Map} Map of engine type to engine instance
|
|
100
|
+
*/
|
|
101
|
+
getAllEngines() {
|
|
102
|
+
return this.engines;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Check if engine type is supported
|
|
107
|
+
* @param {string} engineType - Engine type to check
|
|
108
|
+
* @returns {boolean} True if supported
|
|
109
|
+
*/
|
|
110
|
+
isEngineSupported(engineType) {
|
|
111
|
+
const supportedEngines = ['heuristic', 'eslint', 'openai'];
|
|
112
|
+
return supportedEngines.includes(engineType.toLowerCase());
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get engine metadata
|
|
117
|
+
* @param {string} engineType - Engine type
|
|
118
|
+
* @returns {Object|null} Engine metadata
|
|
119
|
+
*/
|
|
120
|
+
getEngineMetadata(engineType) {
|
|
121
|
+
const engine = this.engines.get(engineType);
|
|
122
|
+
return engine ? engine.getMetadata() : null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Reload custom rules in all engines
|
|
127
|
+
* @param {Object} config - Configuration options
|
|
128
|
+
*/
|
|
129
|
+
async reloadCustomRules(config = {}) {
|
|
130
|
+
if (this.pluginManager) {
|
|
131
|
+
await this.pluginManager.reloadCustomRules(config);
|
|
132
|
+
|
|
133
|
+
// Reinitialize all engines to pick up new rules
|
|
134
|
+
for (const [engineType, engine] of this.engines) {
|
|
135
|
+
if (engine instanceof BaseEngine) {
|
|
136
|
+
await engine.initialize({ ...config, verbose: this.verbose });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (this.verbose) {
|
|
141
|
+
console.log('๐ Reloaded custom rules in all engines');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get compatible engines for a language
|
|
148
|
+
* @param {string} language - Programming language
|
|
149
|
+
* @returns {Array} Array of compatible engine types
|
|
150
|
+
*/
|
|
151
|
+
getCompatibleEngines(language) {
|
|
152
|
+
const compatible = [];
|
|
153
|
+
|
|
154
|
+
for (const [engineType, engine] of this.engines) {
|
|
155
|
+
if (engine.supportedLanguages.includes(language.toLowerCase())) {
|
|
156
|
+
compatible.push(engineType);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return compatible;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Create multiple engines from configuration
|
|
165
|
+
* @param {Object} engineConfig - Engine configuration object
|
|
166
|
+
* @returns {Map} Map of created engines
|
|
167
|
+
*/
|
|
168
|
+
async createEnginesFromConfig(engineConfig = {}) {
|
|
169
|
+
const engines = new Map();
|
|
170
|
+
|
|
171
|
+
for (const [engineType, config] of Object.entries(engineConfig)) {
|
|
172
|
+
if (this.isEngineSupported(engineType)) {
|
|
173
|
+
try {
|
|
174
|
+
const engine = await this.createEngine(engineType, config);
|
|
175
|
+
engines.set(engineType, engine);
|
|
176
|
+
} catch (error) {
|
|
177
|
+
if (this.verbose) {
|
|
178
|
+
console.warn(`โ ๏ธ Failed to create ${engineType} engine: ${error.message}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return engines;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Analyze files using best engine for language
|
|
189
|
+
* @param {Array} files - Files to analyze
|
|
190
|
+
* @param {string} language - Programming language
|
|
191
|
+
* @param {Array} rules - Rules to apply
|
|
192
|
+
* @param {Object} options - Analysis options
|
|
193
|
+
* @returns {Array} Array of violations
|
|
194
|
+
*/
|
|
195
|
+
async analyzeWithBestEngine(files, language, rules, options = {}) {
|
|
196
|
+
const compatibleEngines = this.getCompatibleEngines(language);
|
|
197
|
+
|
|
198
|
+
if (compatibleEngines.length === 0) {
|
|
199
|
+
throw new Error(`No compatible engines found for language: ${language}`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Priority: heuristic > eslint > openai
|
|
203
|
+
const enginePriority = ['heuristic', 'eslint', 'openai'];
|
|
204
|
+
const bestEngine = enginePriority.find(type => compatibleEngines.includes(type));
|
|
205
|
+
|
|
206
|
+
if (!bestEngine) {
|
|
207
|
+
throw new Error(`No suitable engine found for language: ${language}`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const engine = await this.getEngine(bestEngine, options);
|
|
211
|
+
return await engine.analyze(files, rules, { ...options, language });
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Cleanup all engines and plugin manager
|
|
216
|
+
*/
|
|
217
|
+
async cleanup() {
|
|
218
|
+
// Cleanup all engines
|
|
219
|
+
for (const [engineType, engine] of this.engines) {
|
|
220
|
+
try {
|
|
221
|
+
if (engine.cleanup) {
|
|
222
|
+
await engine.cleanup();
|
|
223
|
+
}
|
|
224
|
+
} catch (error) {
|
|
225
|
+
if (this.verbose) {
|
|
226
|
+
console.warn(`โ ๏ธ Error cleaning up ${engineType} engine: ${error.message}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Cleanup plugin manager
|
|
232
|
+
if (this.pluginManager) {
|
|
233
|
+
await this.pluginManager.cleanup();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
this.engines.clear();
|
|
237
|
+
this.pluginManager = null;
|
|
238
|
+
|
|
239
|
+
if (this.verbose) {
|
|
240
|
+
console.log('๐งน Engine Factory cleanup completed');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Get factory statistics
|
|
246
|
+
* @returns {Object} Factory statistics
|
|
247
|
+
*/
|
|
248
|
+
getStatistics() {
|
|
249
|
+
const stats = {
|
|
250
|
+
engineCount: this.engines.size,
|
|
251
|
+
engines: {},
|
|
252
|
+
totalRules: 0
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
for (const [engineType, engine] of this.engines) {
|
|
256
|
+
const metadata = engine.getMetadata ? engine.getMetadata() : {};
|
|
257
|
+
stats.engines[engineType] = {
|
|
258
|
+
...metadata,
|
|
259
|
+
ruleCount: engine.ruleRegistry ? engine.ruleRegistry.size : 0
|
|
260
|
+
};
|
|
261
|
+
stats.totalRules += stats.engines[engineType].ruleCount;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (this.pluginManager) {
|
|
265
|
+
stats.pluginManager = {
|
|
266
|
+
totalPlugins: this.pluginManager.getAllPlugins().size,
|
|
267
|
+
customRules: this.pluginManager.customRules.size
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return stats;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
module.exports = EngineFactory;
|