@sun-asterisk/sunlint 1.2.1 ā 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.
- package/CHANGELOG.md +40 -1
- package/CONTRIBUTING.md +533 -70
- package/README.md +16 -2
- 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/rule-analysis-strategies.js +18 -2
- package/config/rules/enhanced-rules-registry.json +2503 -0
- package/config/rules/rules-registry-generated.json +785 -837
- 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 +32 -5
- package/core/config-manager.js +111 -0
- package/core/config-merger.js +61 -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/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 +560 -0
- package/core/semantic-rule-base.js +433 -0
- package/core/unified-rule-registry.js +484 -0
- package/docs/CONSTANTS-ARCHITECTURE.md +288 -0
- package/engines/core/base-engine.js +249 -0
- package/engines/engine-factory.js +275 -0
- package/engines/eslint-engine.js +180 -30
- package/engines/heuristic-engine.js +513 -56
- package/integrations/eslint/plugin/index.js +27 -27
- package/package.json +11 -6
- package/rules/README.md +252 -0
- package/rules/common/C002_no_duplicate_code/analyzer.js +65 -0
- package/rules/common/C002_no_duplicate_code/config.json +23 -0
- package/rules/common/C003_no_vague_abbreviations/analyzer.js +418 -0
- package/rules/common/C003_no_vague_abbreviations/config.json +35 -0
- package/rules/common/C006_function_naming/analyzer.js +504 -0
- package/rules/common/C006_function_naming/config.json +86 -0
- package/rules/common/C006_function_naming/smart-analyzer.js +503 -0
- package/rules/common/C010_limit_block_nesting/analyzer.js +389 -0
- package/rules/common/C012_command_query_separation/analyzer.js +481 -0
- package/rules/common/C012_command_query_separation/ast-analyzer.js +495 -0
- package/rules/common/C013_no_dead_code/analyzer.js +206 -0
- package/rules/common/C014_dependency_injection/analyzer.js +338 -0
- package/rules/common/C017_constructor_logic/analyzer.js +314 -0
- package/rules/common/C019_log_level_usage/analyzer.js +362 -0
- package/rules/common/C019_log_level_usage/config.json +121 -0
- package/rules/common/C029_catch_block_logging/analyzer-smart-pipeline.js +755 -0
- package/rules/common/C029_catch_block_logging/analyzer.js +141 -0
- package/rules/common/C029_catch_block_logging/config.json +59 -0
- package/rules/common/C031_validation_separation/analyzer.js +186 -0
- package/rules/common/C041_no_sensitive_hardcode/analyzer.js +292 -0
- package/rules/common/C041_no_sensitive_hardcode/ast-analyzer.js +296 -0
- package/rules/common/C042_boolean_name_prefix/analyzer.js +300 -0
- package/rules/common/C043_no_console_or_print/analyzer.js +431 -0
- package/rules/common/C047_no_duplicate_retry_logic/analyzer.js +590 -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/C075_explicit_return_types/analyzer.js +103 -0
- package/rules/common/C076_single_test_behavior/analyzer.js +121 -0
- package/rules/docs/C002_no_duplicate_code.md +57 -0
- package/rules/docs/C031_validation_separation.md +72 -0
- package/rules/index.js +162 -0
- package/rules/migration/converter.js +385 -0
- package/rules/migration/mapping.json +164 -0
- package/rules/parser/constants.js +31 -0
- package/rules/parser/file-config.js +80 -0
- package/rules/parser/rule-parser-simple.js +305 -0
- package/rules/parser/rule-parser.js +527 -0
- package/rules/security/S015_insecure_tls_certificate/analyzer.js +150 -0
- package/rules/security/S015_insecure_tls_certificate/ast-analyzer.js +237 -0
- package/rules/security/S023_no_json_injection/analyzer.js +278 -0
- package/rules/security/S023_no_json_injection/ast-analyzer.js +359 -0
- package/rules/security/S026_json_schema_validation/analyzer.js +251 -0
- package/rules/security/S026_json_schema_validation/config.json +27 -0
- package/rules/security/S027_no_hardcoded_secrets/analyzer.js +436 -0
- package/rules/security/S027_no_hardcoded_secrets/config.json +29 -0
- package/rules/security/S029_csrf_protection/analyzer.js +330 -0
- package/rules/tests/C002_no_duplicate_code.test.js +50 -0
- package/rules/utils/ast-utils.js +191 -0
- package/rules/utils/base-analyzer.js +98 -0
- package/rules/utils/pattern-matchers.js +239 -0
- package/rules/utils/rule-helpers.js +264 -0
- package/rules/utils/severity-constants.js +93 -0
- package/scripts/category-manager.js +150 -0
- package/scripts/generate-rules-registry.js +88 -0
- package/scripts/generate_insights.js +188 -0
- package/scripts/migrate-rule-registry.js +157 -0
- 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/testing/test-s005-working.ts +0 -22
- package/engines/tree-sitter-parser.js +0 -0
- package/engines/universal-ast-engine.js +0 -0
- package/scripts/merge-reports.js +0 -424
- package/scripts/test-scripts/README.md +0 -22
- package/scripts/test-scripts/test-c041-comparison.js +0 -114
- package/scripts/test-scripts/test-c041-eslint.js +0 -67
- package/scripts/test-scripts/test-eslint-rules.js +0 -146
- package/scripts/test-scripts/test-real-world.js +0 -44
- package/scripts/test-scripts/test-rules-on-real-projects.js +0 -86
- /package/{config/schemas/sunlint-schema.json ā rules/universal/C010/generic.js} +0 -0
- /package/{core/multi-rule-runner.js ā rules/universal/C010/tree-sitter-analyzer.js} +0 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SunLint Category Management CLI
|
|
5
|
+
* Utility for managing categories and principles
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node scripts/category-manager.js list
|
|
9
|
+
* node scripts/category-manager.js add <category> <principle> <description>
|
|
10
|
+
* node scripts/category-manager.js validate
|
|
11
|
+
* node scripts/category-manager.js stats
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const {
|
|
16
|
+
getValidCategories,
|
|
17
|
+
getCategoryPrinciples,
|
|
18
|
+
getCategoryDescription,
|
|
19
|
+
getCategoryStats,
|
|
20
|
+
isValidCategory,
|
|
21
|
+
addCategoryMapping
|
|
22
|
+
} = require('../core/constants/categories');
|
|
23
|
+
|
|
24
|
+
const command = process.argv[2];
|
|
25
|
+
|
|
26
|
+
switch (command) {
|
|
27
|
+
case 'list':
|
|
28
|
+
listCategories();
|
|
29
|
+
break;
|
|
30
|
+
|
|
31
|
+
case 'validate':
|
|
32
|
+
validateCategories();
|
|
33
|
+
break;
|
|
34
|
+
|
|
35
|
+
case 'stats':
|
|
36
|
+
showStats();
|
|
37
|
+
break;
|
|
38
|
+
|
|
39
|
+
case 'add':
|
|
40
|
+
addCategory(process.argv[3], process.argv[4], process.argv[5]);
|
|
41
|
+
break;
|
|
42
|
+
|
|
43
|
+
case 'check':
|
|
44
|
+
checkCategory(process.argv[3]);
|
|
45
|
+
break;
|
|
46
|
+
|
|
47
|
+
default:
|
|
48
|
+
showHelp();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function listCategories() {
|
|
52
|
+
console.log('š SunLint Categories & Principles\n');
|
|
53
|
+
|
|
54
|
+
const categories = getValidCategories();
|
|
55
|
+
categories.forEach(category => {
|
|
56
|
+
const principles = getCategoryPrinciples(category);
|
|
57
|
+
const description = getCategoryDescription(category);
|
|
58
|
+
|
|
59
|
+
console.log(`š·ļø ${category.toUpperCase()}`);
|
|
60
|
+
console.log(` Principles: ${principles.join(', ')}`);
|
|
61
|
+
console.log(` Description: ${description}`);
|
|
62
|
+
console.log('');
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function validateCategories() {
|
|
67
|
+
console.log('š Validating Category System\n');
|
|
68
|
+
|
|
69
|
+
const stats = getCategoryStats();
|
|
70
|
+
console.log(`ā
Total Categories: ${stats.totalCategories}`);
|
|
71
|
+
console.log(`ā
Total Principles: ${stats.totalPrinciples}`);
|
|
72
|
+
|
|
73
|
+
// Check for missing principles
|
|
74
|
+
const allPrinciples = Object.values(SUNLINT_PRINCIPLES);
|
|
75
|
+
const mappedPrinciples = Object.values(CATEGORY_PRINCIPLE_MAP).flat();
|
|
76
|
+
|
|
77
|
+
const missingPrinciples = allPrinciples.filter(p => !mappedPrinciples.includes(p));
|
|
78
|
+
|
|
79
|
+
if (missingPrinciples.length > 0) {
|
|
80
|
+
console.log(`ā ļø Unmapped Principles: ${missingPrinciples.join(', ')}`);
|
|
81
|
+
} else {
|
|
82
|
+
console.log('ā
All principles mapped to categories');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
console.log('\nš Category Mapping:');
|
|
86
|
+
Object.entries(CATEGORY_PRINCIPLE_MAP).forEach(([category, principles]) => {
|
|
87
|
+
console.log(` ${category} -> ${principles.join(', ')}`);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function showStats() {
|
|
92
|
+
const stats = getCategoryStats();
|
|
93
|
+
console.log('š Category Statistics\n');
|
|
94
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function addCategory(category, principle, description) {
|
|
98
|
+
if (!category || !principle || !description) {
|
|
99
|
+
console.error('ā Usage: add <category> <principle> <description>');
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
console.log(`š Adding category: ${category}`);
|
|
104
|
+
console.log(` Principle: ${principle}`);
|
|
105
|
+
console.log(` Description: ${description}`);
|
|
106
|
+
console.log('\nā ļø This would require updating category-constants.js manually');
|
|
107
|
+
console.log(' Add the following to CATEGORY_PRINCIPLE_MAP:');
|
|
108
|
+
console.log(` '${category.toLowerCase()}': ['${principle.toUpperCase()}'],`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function checkCategory(category) {
|
|
112
|
+
if (!category) {
|
|
113
|
+
console.error('ā Usage: check <category>');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log(`š Checking category: ${category}\n`);
|
|
118
|
+
|
|
119
|
+
const isValid = isValidCategory(category);
|
|
120
|
+
console.log(`Valid: ${isValid ? 'ā
' : 'ā'}`);
|
|
121
|
+
|
|
122
|
+
if (isValid) {
|
|
123
|
+
const principles = getCategoryPrinciples(category);
|
|
124
|
+
const description = getCategoryDescription(category);
|
|
125
|
+
|
|
126
|
+
console.log(`Principles: ${principles.join(', ')}`);
|
|
127
|
+
console.log(`Description: ${description}`);
|
|
128
|
+
} else {
|
|
129
|
+
console.log(`Valid categories: ${getValidCategories().join(', ')}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function showHelp() {
|
|
134
|
+
console.log(`
|
|
135
|
+
š ļø SunLint Category Manager
|
|
136
|
+
|
|
137
|
+
Commands:
|
|
138
|
+
list Show all categories and their principles
|
|
139
|
+
validate Validate the category system consistency
|
|
140
|
+
stats Show category statistics
|
|
141
|
+
check Check if a specific category is valid
|
|
142
|
+
add Add a new category (manual step required)
|
|
143
|
+
|
|
144
|
+
Examples:
|
|
145
|
+
node scripts/category-manager.js list
|
|
146
|
+
node scripts/category-manager.js check security
|
|
147
|
+
node scripts/category-manager.js validate
|
|
148
|
+
node scripts/category-manager.js add accessibility ACCESSIBILITY "Accessibility guidelines"
|
|
149
|
+
`);
|
|
150
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { SimpleRuleParser } = require('../rules/parser/rule-parser-simple');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Generate rules registry from origin-rules
|
|
9
|
+
* This script creates config/rules/rules-registry-generated.json
|
|
10
|
+
* from all *-en.md files in origin-rules/ directory
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
console.log('š Generating rules registry from origin-rules...');
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
const parser = new SimpleRuleParser();
|
|
17
|
+
const originRulesDir = path.join(__dirname, '..', 'origin-rules');
|
|
18
|
+
const targetPath = path.join(__dirname, '..', 'config', 'rules', 'rules-registry-generated.json');
|
|
19
|
+
|
|
20
|
+
console.log(`Source: ${originRulesDir}`);
|
|
21
|
+
console.log(`Target: ${targetPath}`);
|
|
22
|
+
|
|
23
|
+
// Parse all rules from origin-rules
|
|
24
|
+
const allRules = parser.parseAllRules(originRulesDir);
|
|
25
|
+
|
|
26
|
+
if (allRules.length === 0) {
|
|
27
|
+
console.error('ā No rules found in origin-rules directory');
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Convert to registry format
|
|
32
|
+
const registry = {
|
|
33
|
+
rules: {}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
allRules.forEach(rule => {
|
|
37
|
+
if (rule.id) {
|
|
38
|
+
registry.rules[rule.id] = {
|
|
39
|
+
name: rule.title || `${rule.id} Rule`,
|
|
40
|
+
description: rule.description || 'No description available',
|
|
41
|
+
category: rule.category || 'quality',
|
|
42
|
+
severity: rule.severity || 'major',
|
|
43
|
+
languages: rule.language ? [rule.language] : ['All languages'],
|
|
44
|
+
version: rule.version || '1.0.0',
|
|
45
|
+
status: rule.status || 'draft',
|
|
46
|
+
tags: [rule.category || 'quality', 'readability', 'code-quality'],
|
|
47
|
+
tools: rule.tools || [],
|
|
48
|
+
framework: rule.framework || 'All',
|
|
49
|
+
principles: rule.principles || []
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Ensure target directory exists
|
|
55
|
+
const targetDir = path.dirname(targetPath);
|
|
56
|
+
if (!fs.existsSync(targetDir)) {
|
|
57
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Write registry file
|
|
61
|
+
fs.writeFileSync(targetPath, JSON.stringify(registry, null, 2), 'utf8');
|
|
62
|
+
|
|
63
|
+
const rulesCount = Object.keys(registry.rules).length;
|
|
64
|
+
const fileSize = (fs.statSync(targetPath).size / 1024).toFixed(1);
|
|
65
|
+
|
|
66
|
+
console.log(`ā
Generated registry with ${rulesCount} rules`);
|
|
67
|
+
console.log(`š File: ${targetPath} (${fileSize} KB)`);
|
|
68
|
+
console.log('');
|
|
69
|
+
console.log('š Rules by category:');
|
|
70
|
+
|
|
71
|
+
// Stats by category
|
|
72
|
+
const categories = {};
|
|
73
|
+
Object.values(registry.rules).forEach(rule => {
|
|
74
|
+
const cat = rule.category || 'unknown';
|
|
75
|
+
categories[cat] = (categories[cat] || 0) + 1;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
Object.entries(categories)
|
|
79
|
+
.sort(([,a], [,b]) => b - a)
|
|
80
|
+
.forEach(([category, count]) => {
|
|
81
|
+
console.log(` ${category}: ${count} rules`);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.error('ā Error generating registry:', error.message);
|
|
86
|
+
console.error(error.stack);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { SimpleRuleParser } = require('../rules/parser/rule-parser-simple.js');
|
|
6
|
+
|
|
7
|
+
class SunLintInsightGenerator {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.parser = new SimpleRuleParser();
|
|
10
|
+
this.heuristicRulesPath = path.join(__dirname, '../rules/common');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Get implemented rules
|
|
14
|
+
getImplementedRules() {
|
|
15
|
+
const implemented = new Set();
|
|
16
|
+
if (fs.existsSync(this.heuristicRulesPath)) {
|
|
17
|
+
const dirs = fs.readdirSync(this.heuristicRulesPath);
|
|
18
|
+
dirs.forEach(dir => {
|
|
19
|
+
const match = dir.match(/^([A-Z]\d+)_/);
|
|
20
|
+
if (match) {
|
|
21
|
+
implemented.add(match[1]);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return implemented;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Assess priority based on rule content
|
|
29
|
+
assessPriority(rule) {
|
|
30
|
+
const text = `${rule.title} ${rule.description} ${rule.detail || ''}`.toLowerCase();
|
|
31
|
+
const principles = rule.principles || [];
|
|
32
|
+
|
|
33
|
+
if (principles.includes('SECURITY')) return 'High';
|
|
34
|
+
if (principles.includes('PERFORMANCE')) return 'High';
|
|
35
|
+
if (principles.includes('RELIABILITY')) return 'Medium-High';
|
|
36
|
+
|
|
37
|
+
const impactKeywords = {
|
|
38
|
+
high: ['security', 'memory', 'performance', 'crash', 'null pointer', 'xss', 'injection', 'vulnerability', 'race condition'],
|
|
39
|
+
medium: ['testability', 'maintainability', 'readability', 'coupling', 'cohesion', 'dependency'],
|
|
40
|
+
low: ['naming', 'style', 'convention', 'formatting', 'comment']
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
for (const keyword of impactKeywords.high) {
|
|
44
|
+
if (text.includes(keyword)) return 'High';
|
|
45
|
+
}
|
|
46
|
+
for (const keyword of impactKeywords.medium) {
|
|
47
|
+
if (text.includes(keyword)) return 'Medium';
|
|
48
|
+
}
|
|
49
|
+
for (const keyword of impactKeywords.low) {
|
|
50
|
+
if (text.includes(keyword)) return 'Low';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return 'Medium';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Generate actionable insights
|
|
57
|
+
generateInsights() {
|
|
58
|
+
console.log('š Analyzing SunLint rules...');
|
|
59
|
+
const allRules = this.parser.parseAllRules();
|
|
60
|
+
const activatedRules = this.parser.filterRules(allRules, { status: 'activated' });
|
|
61
|
+
const implementedRules = this.getImplementedRules();
|
|
62
|
+
|
|
63
|
+
console.log('\nš === SUNLINT HEURISTIC ENGINE ANALYSIS ===\n');
|
|
64
|
+
|
|
65
|
+
// Overall statistics
|
|
66
|
+
const totalImplemented = activatedRules.filter(rule => implementedRules.has(rule.id)).length;
|
|
67
|
+
const implementationRate = ((totalImplemented / activatedRules.length) * 100).toFixed(1);
|
|
68
|
+
|
|
69
|
+
console.log(`šÆ **Current Implementation Status:**`);
|
|
70
|
+
console.log(` ⢠Total Activated Rules: ${activatedRules.length}`);
|
|
71
|
+
console.log(` ⢠Implemented in Heuristic: ${totalImplemented} (${implementationRate}%)`);
|
|
72
|
+
console.log(` ⢠Remaining to Implement: ${activatedRules.length - totalImplemented}\n`);
|
|
73
|
+
|
|
74
|
+
// Priority analysis
|
|
75
|
+
const priorities = { 'High': 0, 'Medium-High': 0, 'Medium': 0, 'Low': 0 };
|
|
76
|
+
const notImplementedByPriority = { 'High': [], 'Medium-High': [], 'Medium': [], 'Low': [] };
|
|
77
|
+
|
|
78
|
+
activatedRules.forEach(rule => {
|
|
79
|
+
const priority = this.assessPriority(rule);
|
|
80
|
+
priorities[priority]++;
|
|
81
|
+
|
|
82
|
+
if (!implementedRules.has(rule.id)) {
|
|
83
|
+
notImplementedByPriority[priority].push(rule);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
console.log(`š„ **Priority Breakdown:**`);
|
|
88
|
+
Object.entries(priorities).forEach(([priority, count]) => {
|
|
89
|
+
const missing = notImplementedByPriority[priority].length;
|
|
90
|
+
const implemented = count - missing;
|
|
91
|
+
const rate = count > 0 ? ((implemented / count) * 100).toFixed(1) : '0.0';
|
|
92
|
+
console.log(` ⢠${priority}: ${implemented}/${count} implemented (${rate}%) - ${missing} missing`);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Category analysis
|
|
96
|
+
console.log(`\nš **Implementation by Category:**`);
|
|
97
|
+
const categories = {
|
|
98
|
+
'C': 'Common Code Quality',
|
|
99
|
+
'T': 'TypeScript',
|
|
100
|
+
'R': 'ReactJS',
|
|
101
|
+
'S': 'Security',
|
|
102
|
+
'J': 'Java',
|
|
103
|
+
'K': 'Kotlin Mobile',
|
|
104
|
+
'D': 'Dart/Flutter',
|
|
105
|
+
'SW': 'Swift'
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
Object.entries(categories).forEach(([prefix, name]) => {
|
|
109
|
+
const categoryRules = activatedRules.filter(rule => rule.id.startsWith(prefix));
|
|
110
|
+
const categoryImplemented = categoryRules.filter(rule => implementedRules.has(rule.id)).length;
|
|
111
|
+
const rate = categoryRules.length > 0 ? ((categoryImplemented / categoryRules.length) * 100).toFixed(1) : '0.0';
|
|
112
|
+
console.log(` ⢠${name}: ${categoryImplemented}/${categoryRules.length} (${rate}%)`);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Top missing high-priority rules
|
|
116
|
+
console.log(`\nšØ **Top 10 Missing High-Priority Rules:**`);
|
|
117
|
+
notImplementedByPriority['High'].slice(0, 10).forEach((rule, i) => {
|
|
118
|
+
console.log(` ${i+1}. ${rule.id}: ${rule.title}`);
|
|
119
|
+
console.log(` ā ${rule.description?.substring(0, 80)}...`);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Quick wins (easy to implement)
|
|
123
|
+
console.log(`\nā” **Quick Wins (Low Complexity, High Impact):**`);
|
|
124
|
+
const quickWins = notImplementedByPriority['High'].filter(rule => {
|
|
125
|
+
const text = rule.title.toLowerCase();
|
|
126
|
+
return text.includes('console.log') || text.includes('print') ||
|
|
127
|
+
text.includes('hardcode') || text.includes('sensitive');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
quickWins.slice(0, 5).forEach((rule, i) => {
|
|
131
|
+
console.log(` ${i+1}. ${rule.id}: ${rule.title} - Text/Regex patterns`);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Performance impact rules
|
|
135
|
+
console.log(`\nā” **Performance-Critical Missing Rules:**`);
|
|
136
|
+
const perfRules = notImplementedByPriority['High'].filter(rule =>
|
|
137
|
+
rule.principles?.includes('PERFORMANCE') ||
|
|
138
|
+
rule.description?.toLowerCase().includes('performance')
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
perfRules.slice(0, 5).forEach((rule, i) => {
|
|
142
|
+
console.log(` ${i+1}. ${rule.id}: ${rule.title}`);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Security rules
|
|
146
|
+
console.log(`\nš **Security-Critical Missing Rules:**`);
|
|
147
|
+
const securityRules = notImplementedByPriority['High'].filter(rule =>
|
|
148
|
+
rule.principles?.includes('SECURITY') ||
|
|
149
|
+
rule.description?.toLowerCase().includes('security')
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
securityRules.slice(0, 5).forEach((rule, i) => {
|
|
153
|
+
console.log(` ${i+1}. ${rule.id}: ${rule.title}`);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Recommendations
|
|
157
|
+
console.log(`\nš” **Actionable Recommendations:**`);
|
|
158
|
+
console.log(` 1. Focus on High Priority rules first: ${notImplementedByPriority['High'].length} rules remaining`);
|
|
159
|
+
console.log(` 2. Implement Common (C) rules first - highest coverage impact`);
|
|
160
|
+
console.log(` 3. Start with Low complexity rules using text/regex patterns`);
|
|
161
|
+
console.log(` 4. Performance rules should be prioritized for production impact`);
|
|
162
|
+
console.log(` 5. Security rules are critical for safe code practices`);
|
|
163
|
+
console.log(` 6. Consider AST analysis for complex rules (Medium-High complexity)`);
|
|
164
|
+
|
|
165
|
+
console.log(`\nš **ROI Analysis:**`);
|
|
166
|
+
const commonMissing = notImplementedByPriority['High'].filter(rule => rule.id.startsWith('C')).length;
|
|
167
|
+
console.log(` ⢠Common rules impact: All languages (${commonMissing} High-priority missing)`);
|
|
168
|
+
console.log(` ⢠Language-specific rules: Limited scope but deep impact`);
|
|
169
|
+
console.log(` ⢠Implementation effort: Low complexity rules = 1-2 days, High = 1-2 weeks`);
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
total: activatedRules.length,
|
|
173
|
+
implemented: totalImplemented,
|
|
174
|
+
missing: activatedRules.length - totalImplemented,
|
|
175
|
+
highPriorityMissing: notImplementedByPriority['High'].length,
|
|
176
|
+
quickWins: quickWins.length,
|
|
177
|
+
recommendations: notImplementedByPriority
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Run analysis
|
|
183
|
+
if (require.main === module) {
|
|
184
|
+
const analyzer = new SunLintInsightGenerator();
|
|
185
|
+
analyzer.generateInsights();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
module.exports = SunLintInsightGenerator;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Rule Registry Migration Script
|
|
5
|
+
* Merges all rule mappings into Unified Rule Registry
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
console.log('š RULE REGISTRY MIGRATION');
|
|
12
|
+
console.log('='.repeat(50));
|
|
13
|
+
|
|
14
|
+
// Load existing data
|
|
15
|
+
const rulesRegistry = JSON.parse(fs.readFileSync('./config/rules/rules-registry.json', 'utf8'));
|
|
16
|
+
const eslintMapping = JSON.parse(fs.readFileSync('./config/eslint-rule-mapping.json', 'utf8'));
|
|
17
|
+
const engineMapping = JSON.parse(fs.readFileSync('./config/engines/eslint-rule-mapping.json', 'utf8'));
|
|
18
|
+
const strategies = require('./config/rule-analysis-strategies.js');
|
|
19
|
+
|
|
20
|
+
// Current unified registry
|
|
21
|
+
const { UnifiedRuleRegistry } = require('./core/unified-rule-registry.js');
|
|
22
|
+
const registry = new UnifiedRuleRegistry();
|
|
23
|
+
|
|
24
|
+
async function migrateRuleData() {
|
|
25
|
+
console.log('š„ Loading current unified registry...');
|
|
26
|
+
await registry.initialize({ verbose: true });
|
|
27
|
+
|
|
28
|
+
console.log('š Analyzing missing rules...');
|
|
29
|
+
|
|
30
|
+
// Get all rule IDs from different sources
|
|
31
|
+
const registryRules = Object.keys(rulesRegistry.rules || {});
|
|
32
|
+
const eslintRules = Object.keys(eslintMapping.mappings || {});
|
|
33
|
+
const engineRules = Object.keys(engineMapping || {});
|
|
34
|
+
|
|
35
|
+
// Find missing rules in unified registry
|
|
36
|
+
const missingFromEslint = eslintRules.filter(ruleId =>
|
|
37
|
+
!registryRules.includes(ruleId)
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
console.log(`š Found ${missingFromEslint.length} rules missing from registry:`,
|
|
41
|
+
missingFromEslint.slice(0, 10).join(', '));
|
|
42
|
+
|
|
43
|
+
// Extend unified registry with missing rules
|
|
44
|
+
const enhancedRegistry = { ...rulesRegistry };
|
|
45
|
+
|
|
46
|
+
missingFromEslint.forEach(ruleId => {
|
|
47
|
+
console.log(`ā Adding missing rule: ${ruleId}`);
|
|
48
|
+
|
|
49
|
+
enhancedRegistry.rules[ruleId] = {
|
|
50
|
+
id: ruleId,
|
|
51
|
+
name: `Rule ${ruleId}`, // Will be improved
|
|
52
|
+
description: `Auto-migrated rule ${ruleId} from ESLint mapping`,
|
|
53
|
+
category: inferCategory(ruleId),
|
|
54
|
+
severity: 'warning',
|
|
55
|
+
languages: ['typescript', 'javascript'],
|
|
56
|
+
version: '1.0.0',
|
|
57
|
+
status: 'migrated',
|
|
58
|
+
tags: ['migrated'],
|
|
59
|
+
|
|
60
|
+
// ESLint engine mapping
|
|
61
|
+
engineMappings: {
|
|
62
|
+
eslint: eslintMapping.mappings[ruleId] || []
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
// Analysis strategy
|
|
66
|
+
strategy: {
|
|
67
|
+
preferred: inferStrategy(ruleId),
|
|
68
|
+
fallbacks: ['regex'],
|
|
69
|
+
accuracy: {}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Add missing engine mappings
|
|
75
|
+
console.log('š§ Adding engine mappings...');
|
|
76
|
+
Object.entries(engineMapping).forEach(([ruleId, eslintRules]) => {
|
|
77
|
+
if (enhancedRegistry.rules[ruleId]) {
|
|
78
|
+
enhancedRegistry.rules[ruleId].engineMappings = enhancedRegistry.rules[ruleId].engineMappings || {};
|
|
79
|
+
enhancedRegistry.rules[ruleId].engineMappings.eslint = eslintRules;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Add analysis strategies
|
|
84
|
+
console.log('š Adding analysis strategies...');
|
|
85
|
+
Object.entries(strategies.astPreferred || {}).forEach(([ruleId, config]) => {
|
|
86
|
+
if (enhancedRegistry.rules[ruleId]) {
|
|
87
|
+
enhancedRegistry.rules[ruleId].strategy = {
|
|
88
|
+
preferred: 'ast',
|
|
89
|
+
fallbacks: config.methods || ['regex'],
|
|
90
|
+
accuracy: config.accuracy || {}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
Object.entries(strategies.regexOptimal || {}).forEach(([ruleId, config]) => {
|
|
96
|
+
if (enhancedRegistry.rules[ruleId]) {
|
|
97
|
+
enhancedRegistry.rules[ruleId].strategy = {
|
|
98
|
+
preferred: 'regex',
|
|
99
|
+
fallbacks: config.methods || [],
|
|
100
|
+
accuracy: config.accuracy || {}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
console.log('š¾ Saving enhanced registry...');
|
|
106
|
+
|
|
107
|
+
// Save enhanced registry
|
|
108
|
+
fs.writeFileSync(
|
|
109
|
+
'./config/rules/enhanced-rules-registry.json',
|
|
110
|
+
JSON.stringify(enhancedRegistry, null, 2)
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
console.log(`ā
Enhanced registry saved with ${Object.keys(enhancedRegistry.rules).length} rules`);
|
|
114
|
+
|
|
115
|
+
// Generate migration summary
|
|
116
|
+
const summary = {
|
|
117
|
+
originalRules: registryRules.length,
|
|
118
|
+
migratedRules: missingFromEslint.length,
|
|
119
|
+
totalRules: Object.keys(enhancedRegistry.rules).length,
|
|
120
|
+
eslintMappings: Object.keys(eslintMapping.mappings).length,
|
|
121
|
+
engineMappings: Object.keys(engineMapping).length
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
fs.writeFileSync(
|
|
125
|
+
'./migration-summary.json',
|
|
126
|
+
JSON.stringify(summary, null, 2)
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
console.log('š Migration summary:', summary);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Helper functions
|
|
133
|
+
function inferCategory(ruleId) {
|
|
134
|
+
if (ruleId.startsWith('S')) return 'security';
|
|
135
|
+
if (ruleId.startsWith('T')) return 'typescript';
|
|
136
|
+
if (ruleId.startsWith('R')) return 'react';
|
|
137
|
+
|
|
138
|
+
// Infer from common patterns
|
|
139
|
+
if (ruleId.includes('naming') || ruleId.includes('name')) return 'naming';
|
|
140
|
+
if (ruleId.includes('error') || ruleId.includes('exception')) return 'error-handling';
|
|
141
|
+
if (ruleId.includes('log')) return 'logging';
|
|
142
|
+
if (ruleId.includes('complexity')) return 'complexity';
|
|
143
|
+
|
|
144
|
+
return 'general';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function inferStrategy(ruleId) {
|
|
148
|
+
// Rules that typically need AST analysis
|
|
149
|
+
const astRules = ['C010', 'C012', 'C015', 'C017'];
|
|
150
|
+
if (astRules.includes(ruleId)) return 'ast';
|
|
151
|
+
|
|
152
|
+
// Most rules can start with regex
|
|
153
|
+
return 'regex';
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Run migration
|
|
157
|
+
migrateRuleData().catch(console.error);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Quick validation that unified rule registry system is working
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { getInstance } = require('./core/unified-rule-registry');
|
|
8
|
+
|
|
9
|
+
async function validateUnifiedSystem() {
|
|
10
|
+
console.log('š Validating unified rule registry system...\n');
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const registry = getInstance();
|
|
14
|
+
await registry.initialize();
|
|
15
|
+
|
|
16
|
+
console.log(`ā
Registry loaded: ${registry.rules.size} rules`);
|
|
17
|
+
|
|
18
|
+
// Test specific rules
|
|
19
|
+
const testRules = ['C006', 'C047', 'C002'];
|
|
20
|
+
console.log('\nš Testing specific rules:');
|
|
21
|
+
|
|
22
|
+
for (const ruleId of testRules) {
|
|
23
|
+
const rule = registry.rules.get(ruleId);
|
|
24
|
+
if (rule) {
|
|
25
|
+
console.log(` ā
${ruleId}: ${rule.title}`);
|
|
26
|
+
if (rule.engineMappings?.eslint) {
|
|
27
|
+
console.log(` ESLint: ${JSON.stringify(rule.engineMappings.eslint)}`);
|
|
28
|
+
}
|
|
29
|
+
if (rule.engineMappings?.heuristic) {
|
|
30
|
+
console.log(` Heuristic: ${rule.engineMappings.heuristic.implementation}`);
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
console.log(` ā ${ruleId}: NOT FOUND`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log('\nš Unified rule registry system is working correctly!');
|
|
38
|
+
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error('ā Validation failed:', error.message);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (require.main === module) {
|
|
45
|
+
validateUnifiedSystem();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = { validateUnifiedSystem };
|
package/.sunlint.json
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "@sun/sunlint/recommended",
|
|
3
|
-
"rules": {
|
|
4
|
-
"C019": "warn",
|
|
5
|
-
"C006": "warn",
|
|
6
|
-
"C029": "error",
|
|
7
|
-
"C031": "warn",
|
|
8
|
-
"S001": "warn",
|
|
9
|
-
"S002": "warn",
|
|
10
|
-
"S007": "warn",
|
|
11
|
-
"S013": "warn",
|
|
12
|
-
"T019": "error",
|
|
13
|
-
"T020": "warn",
|
|
14
|
-
"T021": "error"
|
|
15
|
-
},
|
|
16
|
-
"include": ["**/*.js", "**/*.ts", "**/*.jsx", "**/*.tsx"],
|
|
17
|
-
"exclude": [
|
|
18
|
-
"node_modules/**",
|
|
19
|
-
"coverage/**",
|
|
20
|
-
"**/*.min.*",
|
|
21
|
-
".git/**",
|
|
22
|
-
"dist/**",
|
|
23
|
-
"build/**"
|
|
24
|
-
],
|
|
25
|
-
"engine": "eslint",
|
|
26
|
-
"languages": ["typescript", "javascript"],
|
|
27
|
-
"output": {
|
|
28
|
-
"format": "summary",
|
|
29
|
-
"console": true
|
|
30
|
-
},
|
|
31
|
-
"fileTargeting": {
|
|
32
|
-
"followSymlinks": false,
|
|
33
|
-
"maxDepth": 10
|
|
34
|
-
}
|
|
35
|
-
}
|