@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
package/config/README.md
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
# SunLint Configuration Structure
|
|
2
|
-
|
|
3
|
-
This folder contains all configuration files for SunLint, organized for clarity and maintainability.
|
|
4
|
-
|
|
5
|
-
## š Structure Overview
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
config/
|
|
9
|
-
āāā schemas/ # JSON schemas for validation
|
|
10
|
-
ā āāā sunlint-schema.json # Main SunLint config schema
|
|
11
|
-
āāā engines/ # Analysis engine configurations
|
|
12
|
-
ā āāā engines.json # Available engines (ESLint, TypeScript, etc.)
|
|
13
|
-
ā āāā eslint-rule-mapping.json # ESLint rule mappings
|
|
14
|
-
āāā presets/ # Pre-defined rule configurations
|
|
15
|
-
ā āāā beginner.json # Beginner-friendly preset
|
|
16
|
-
ā āāā ci.json # CI/CD optimized preset
|
|
17
|
-
ā āāā recommended.json # Recommended preset
|
|
18
|
-
ā āāā strict.json # Strict coding standards
|
|
19
|
-
āāā integrations/ # Integration-specific configs
|
|
20
|
-
ā āāā eslint/
|
|
21
|
-
ā āāā base.config.js # Base ESLint configuration
|
|
22
|
-
ā āāā typescript.config.js # TypeScript ESLint config
|
|
23
|
-
ā āāā simple.config.js # Simplified ESLint config
|
|
24
|
-
āāā rules/ # Rule definitions and registry
|
|
25
|
-
ā āāā rules-registry.json # Master rule registry
|
|
26
|
-
āāā defaults/ # Default configurations
|
|
27
|
-
ā āāā default.json # Default SunLint settings
|
|
28
|
-
ā āāā ai-rules-context.json # AI analysis context
|
|
29
|
-
āāā testing/ # Test configurations and samples
|
|
30
|
-
āāā test-s005-working.ts # Test file for S005 rule
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## šÆ Key Improvements
|
|
34
|
-
|
|
35
|
-
### ā
Eliminated Duplicates
|
|
36
|
-
- **Before**: ESLint configs in both `config/typescript/` and `integrations/eslint/`
|
|
37
|
-
- **After**: All ESLint configs consolidated in `config/integrations/eslint/`
|
|
38
|
-
|
|
39
|
-
### ā
Logical Organization
|
|
40
|
-
- **Schemas**: All JSON schemas in one place
|
|
41
|
-
- **Engines**: Engine-specific configurations separated
|
|
42
|
-
- **Presets**: User-facing preset configurations grouped
|
|
43
|
-
- **Integrations**: Third-party integration configs organized by tool
|
|
44
|
-
|
|
45
|
-
### ā
Reduced Complexity
|
|
46
|
-
- **Before**: 10+ files scattered in root config/
|
|
47
|
-
- **After**: Organized into 6 logical categories
|
|
48
|
-
|
|
49
|
-
## š Usage
|
|
50
|
-
|
|
51
|
-
### For ESLint Integration
|
|
52
|
-
```bash
|
|
53
|
-
# Use the consolidated TypeScript ESLint config
|
|
54
|
-
npx eslint --config config/integrations/eslint/typescript.config.js src/
|
|
55
|
-
|
|
56
|
-
# Use the base ESLint config
|
|
57
|
-
npx eslint --config config/integrations/eslint/base.config.js src/
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### For Rule Presets
|
|
61
|
-
```json
|
|
62
|
-
{
|
|
63
|
-
"extends": "config/presets/recommended.json"
|
|
64
|
-
}
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### For Schema Validation
|
|
68
|
-
```json
|
|
69
|
-
{
|
|
70
|
-
"$schema": "config/schemas/sunlint-schema.json"
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## š§ Migration Notes
|
|
75
|
-
|
|
76
|
-
- **Old `config/typescript/`**: ā Removed (duplicated functionality)
|
|
77
|
-
- **ESLint configs**: ā
Moved to `config/integrations/eslint/`
|
|
78
|
-
- **Default configs**: ā
Moved to `config/defaults/`
|
|
79
|
-
- **Engine configs**: ā
Moved to `config/engines/`
|
|
80
|
-
|
|
81
|
-
## š Next Steps
|
|
82
|
-
|
|
83
|
-
1. Update documentation references to new paths
|
|
84
|
-
2. Update CI/CD scripts to use new config locations
|
|
85
|
-
3. Consider adding more integration-specific configs as needed
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
*Last updated: July 21, 2025 | SunLint Config Refactor*
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"C005": [
|
|
3
|
-
"max-statements-per-line",
|
|
4
|
-
"complexity"
|
|
5
|
-
],
|
|
6
|
-
"C006": [
|
|
7
|
-
"func-names",
|
|
8
|
-
"func-name-matching",
|
|
9
|
-
"@typescript-eslint/naming-convention"
|
|
10
|
-
],
|
|
11
|
-
"C007": [
|
|
12
|
-
"spaced-comment",
|
|
13
|
-
"no-inline-comments",
|
|
14
|
-
"no-warning-comments"
|
|
15
|
-
],
|
|
16
|
-
"C012": [
|
|
17
|
-
"consistent-return",
|
|
18
|
-
"no-void",
|
|
19
|
-
"@typescript-eslint/no-confusing-void-expression"
|
|
20
|
-
],
|
|
21
|
-
"C014": [
|
|
22
|
-
"no-new",
|
|
23
|
-
"no-new-wrappers",
|
|
24
|
-
"@typescript-eslint/no-unnecessary-constructor"
|
|
25
|
-
],
|
|
26
|
-
"C015": [
|
|
27
|
-
"@typescript-eslint/naming-convention",
|
|
28
|
-
"camelcase"
|
|
29
|
-
],
|
|
30
|
-
"C019": [
|
|
31
|
-
"no-console",
|
|
32
|
-
"no-alert",
|
|
33
|
-
"no-debugger"
|
|
34
|
-
],
|
|
35
|
-
"C031": [
|
|
36
|
-
"no-implicit-coercion",
|
|
37
|
-
"eqeqeq",
|
|
38
|
-
"@typescript-eslint/strict-boolean-expressions"
|
|
39
|
-
],
|
|
40
|
-
"C032": [
|
|
41
|
-
"no-new",
|
|
42
|
-
"@typescript-eslint/no-floating-promises",
|
|
43
|
-
"no-constructor-return"
|
|
44
|
-
],
|
|
45
|
-
"C033": [
|
|
46
|
-
"prefer-const",
|
|
47
|
-
"no-var",
|
|
48
|
-
"@typescript-eslint/prefer-readonly"
|
|
49
|
-
],
|
|
50
|
-
"C034": [
|
|
51
|
-
"no-global-assign",
|
|
52
|
-
"no-implicit-globals",
|
|
53
|
-
"@typescript-eslint/no-namespace"
|
|
54
|
-
],
|
|
55
|
-
"C035": [
|
|
56
|
-
"no-empty-catch",
|
|
57
|
-
"@typescript-eslint/no-unused-vars"
|
|
58
|
-
],
|
|
59
|
-
"C037": [
|
|
60
|
-
"consistent-return",
|
|
61
|
-
"@typescript-eslint/explicit-function-return-type",
|
|
62
|
-
"@typescript-eslint/explicit-module-boundary-types"
|
|
63
|
-
],
|
|
64
|
-
"C038": [
|
|
65
|
-
"import/no-dynamic-require",
|
|
66
|
-
"import/order",
|
|
67
|
-
"@typescript-eslint/no-var-requires"
|
|
68
|
-
],
|
|
69
|
-
"C040": [
|
|
70
|
-
"no-duplicate-imports",
|
|
71
|
-
"import/no-duplicates",
|
|
72
|
-
"@typescript-eslint/no-duplicate-imports"
|
|
73
|
-
]
|
|
74
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
function doAdminTask() {}
|
|
2
|
-
function doAction() {}
|
|
3
|
-
function isAuthenticated(req: any): boolean {
|
|
4
|
-
return true;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
function doPost(request: any, response: any) { // ā
|
|
8
|
-
const origin = request.getHeader("Origin");
|
|
9
|
-
|
|
10
|
-
if (origin === "https://admin.example.com") {
|
|
11
|
-
doAdminTask();
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function doPost_safe(request: any, response: any) { // ā
|
|
16
|
-
const origin = request.getHeader("Origin");
|
|
17
|
-
console.log("Origin:", origin);
|
|
18
|
-
|
|
19
|
-
if (isAuthenticated(request)) {
|
|
20
|
-
doAction();
|
|
21
|
-
}
|
|
22
|
-
}
|
|
File without changes
|
|
File without changes
|
package/scripts/merge-reports.js
DELETED
|
@@ -1,424 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Report Merger - Combines ESLint and SunLint results into unified report
|
|
5
|
-
* Usage: node merge-reports.js eslint-results.json sunlint-results.json --format=summary --output=unified.json
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
const { Command } = require('commander');
|
|
11
|
-
const chalk = require('chalk');
|
|
12
|
-
|
|
13
|
-
class ReportMerger {
|
|
14
|
-
constructor() {
|
|
15
|
-
this.version = '1.0.0';
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async mergeReports(eslintPath, sunlintPath, options = {}) {
|
|
19
|
-
const eslintReport = this.loadReport(eslintPath, 'eslint');
|
|
20
|
-
const sunlintReport = this.loadReport(sunlintPath, 'sunlint');
|
|
21
|
-
|
|
22
|
-
const unifiedReport = {
|
|
23
|
-
metadata: {
|
|
24
|
-
timestamp: new Date().toISOString(),
|
|
25
|
-
merger_version: this.version,
|
|
26
|
-
input_files: {
|
|
27
|
-
eslint: eslintPath,
|
|
28
|
-
sunlint: sunlintPath
|
|
29
|
-
},
|
|
30
|
-
tools: {
|
|
31
|
-
eslint: this.extractESLintMetadata(eslintReport),
|
|
32
|
-
sunlint: this.extractSunLintMetadata(sunlintReport)
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
summary: this.generateSummary(eslintReport, sunlintReport),
|
|
36
|
-
violations: this.mergeViolations(eslintReport, sunlintReport),
|
|
37
|
-
files: this.mergeFileAnalysis(eslintReport, sunlintReport),
|
|
38
|
-
rules: this.mergeRuleAnalysis(eslintReport, sunlintReport),
|
|
39
|
-
raw_reports: {
|
|
40
|
-
eslint: eslintReport,
|
|
41
|
-
sunlint: sunlintReport
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
return unifiedReport;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
loadReport(filePath, toolName) {
|
|
49
|
-
if (!fs.existsSync(filePath)) {
|
|
50
|
-
console.warn(chalk.yellow(`ā ļø ${toolName} report not found: ${filePath}`));
|
|
51
|
-
return this.getEmptyReport(toolName);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
56
|
-
return JSON.parse(content);
|
|
57
|
-
} catch (error) {
|
|
58
|
-
console.error(chalk.red(`ā Failed to parse ${toolName} report: ${error.message}`));
|
|
59
|
-
return this.getEmptyReport(toolName);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
getEmptyReport(toolName) {
|
|
64
|
-
return {
|
|
65
|
-
tool: toolName,
|
|
66
|
-
files: [],
|
|
67
|
-
violations: [],
|
|
68
|
-
summary: { total: 0, errors: 0, warnings: 0, info: 0 }
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
extractESLintMetadata(report) {
|
|
73
|
-
return {
|
|
74
|
-
version: report.version || 'unknown',
|
|
75
|
-
rules_count: report.rules ? Object.keys(report.rules).length : 0,
|
|
76
|
-
files_analyzed: report.files ? report.files.length : 0
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
extractSunLintMetadata(report) {
|
|
81
|
-
return {
|
|
82
|
-
version: report.version || 'unknown',
|
|
83
|
-
rules_count: report.rulesRun || 0,
|
|
84
|
-
files_analyzed: report.filesAnalyzed || 0,
|
|
85
|
-
ai_enabled: report.aiEnabled || false
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
generateSummary(eslintReport, sunlintReport) {
|
|
90
|
-
const eslintSummary = this.getSummaryFromReport(eslintReport, 'eslint');
|
|
91
|
-
const sunlintSummary = this.getSummaryFromReport(sunlintReport, 'sunlint');
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
total: {
|
|
95
|
-
files: eslintSummary.files + sunlintSummary.files,
|
|
96
|
-
violations: eslintSummary.violations + sunlintSummary.violations,
|
|
97
|
-
errors: eslintSummary.errors + sunlintSummary.errors,
|
|
98
|
-
warnings: eslintSummary.warnings + sunlintSummary.warnings,
|
|
99
|
-
info: eslintSummary.info + sunlintSummary.info
|
|
100
|
-
},
|
|
101
|
-
by_tool: {
|
|
102
|
-
eslint: eslintSummary,
|
|
103
|
-
sunlint: sunlintSummary
|
|
104
|
-
},
|
|
105
|
-
breakdown: {
|
|
106
|
-
overlap_files: this.countOverlapFiles(eslintReport, sunlintReport),
|
|
107
|
-
unique_eslint_rules: this.getUniqueRules(eslintReport, 'eslint'),
|
|
108
|
-
unique_sunlint_rules: this.getUniqueRules(sunlintReport, 'sunlint')
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
getSummaryFromReport(report, toolName) {
|
|
114
|
-
if (toolName === 'eslint') {
|
|
115
|
-
// ESLint format
|
|
116
|
-
const violations = this.extractESLintViolations(report);
|
|
117
|
-
return {
|
|
118
|
-
files: report.files ? report.files.length : 0,
|
|
119
|
-
violations: violations.length,
|
|
120
|
-
errors: violations.filter(v => v.severity === 'error').length,
|
|
121
|
-
warnings: violations.filter(v => v.severity === 'warning').length,
|
|
122
|
-
info: violations.filter(v => v.severity === 'info').length
|
|
123
|
-
};
|
|
124
|
-
} else {
|
|
125
|
-
// SunLint format
|
|
126
|
-
const violations = report.results ?
|
|
127
|
-
report.results.flatMap(r => r.violations || []) : [];
|
|
128
|
-
return {
|
|
129
|
-
files: report.filesAnalyzed || 0,
|
|
130
|
-
violations: violations.length,
|
|
131
|
-
errors: violations.filter(v => v.severity === 'error').length,
|
|
132
|
-
warnings: violations.filter(v => v.severity === 'warning').length,
|
|
133
|
-
info: violations.filter(v => v.severity === 'info').length
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
mergeViolations(eslintReport, sunlintReport) {
|
|
139
|
-
const eslintViolations = this.extractESLintViolations(eslintReport);
|
|
140
|
-
const sunlintViolations = this.extractSunLintViolations(sunlintReport);
|
|
141
|
-
|
|
142
|
-
const allViolations = [
|
|
143
|
-
...eslintViolations.map(v => ({ ...v, tool: 'eslint' })),
|
|
144
|
-
...sunlintViolations.map(v => ({ ...v, tool: 'sunlint' }))
|
|
145
|
-
];
|
|
146
|
-
|
|
147
|
-
// Sort by severity and file
|
|
148
|
-
return allViolations.sort((a, b) => {
|
|
149
|
-
const severityOrder = { error: 0, warning: 1, info: 2 };
|
|
150
|
-
if (a.severity !== b.severity) {
|
|
151
|
-
return severityOrder[a.severity] - severityOrder[b.severity];
|
|
152
|
-
}
|
|
153
|
-
return a.file.localeCompare(b.file);
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
extractESLintViolations(report) {
|
|
158
|
-
if (!report.files || !Array.isArray(report.files)) return [];
|
|
159
|
-
|
|
160
|
-
return report.files.flatMap(file =>
|
|
161
|
-
(file.messages || []).map(msg => ({
|
|
162
|
-
file: file.filePath || file.file,
|
|
163
|
-
line: msg.line,
|
|
164
|
-
column: msg.column,
|
|
165
|
-
ruleId: msg.ruleId,
|
|
166
|
-
message: msg.message,
|
|
167
|
-
severity: this.mapESLintSeverity(msg.severity)
|
|
168
|
-
}))
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
extractSunLintViolations(report) {
|
|
173
|
-
if (!report.results || !Array.isArray(report.results)) return [];
|
|
174
|
-
|
|
175
|
-
return report.results.flatMap(result =>
|
|
176
|
-
(result.violations || []).map(violation => ({
|
|
177
|
-
file: violation.file,
|
|
178
|
-
line: violation.line,
|
|
179
|
-
column: violation.column,
|
|
180
|
-
ruleId: violation.ruleId,
|
|
181
|
-
message: violation.message,
|
|
182
|
-
severity: violation.severity
|
|
183
|
-
}))
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
mapESLintSeverity(severity) {
|
|
188
|
-
switch (severity) {
|
|
189
|
-
case 1: return 'warning';
|
|
190
|
-
case 2: return 'error';
|
|
191
|
-
default: return 'info';
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
mergeFileAnalysis(eslintReport, sunlintReport) {
|
|
196
|
-
const fileMap = new Map();
|
|
197
|
-
|
|
198
|
-
// Process ESLint files
|
|
199
|
-
if (eslintReport.files) {
|
|
200
|
-
eslintReport.files.forEach(file => {
|
|
201
|
-
const fileName = file.filePath || file.file;
|
|
202
|
-
fileMap.set(fileName, {
|
|
203
|
-
file: fileName,
|
|
204
|
-
eslint: {
|
|
205
|
-
violations: file.messages ? file.messages.length : 0,
|
|
206
|
-
rules_triggered: [...new Set((file.messages || []).map(m => m.ruleId))]
|
|
207
|
-
},
|
|
208
|
-
sunlint: { violations: 0, rules_triggered: [] }
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Process SunLint files
|
|
214
|
-
if (sunlintReport.results) {
|
|
215
|
-
sunlintReport.results.forEach(result => {
|
|
216
|
-
const fileName = result.file;
|
|
217
|
-
if (!fileMap.has(fileName)) {
|
|
218
|
-
fileMap.set(fileName, {
|
|
219
|
-
file: fileName,
|
|
220
|
-
eslint: { violations: 0, rules_triggered: [] },
|
|
221
|
-
sunlint: { violations: 0, rules_triggered: [] }
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const fileData = fileMap.get(fileName);
|
|
226
|
-
fileData.sunlint = {
|
|
227
|
-
violations: result.violations ? result.violations.length : 0,
|
|
228
|
-
rules_triggered: [...new Set((result.violations || []).map(v => v.ruleId))]
|
|
229
|
-
};
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return Array.from(fileMap.values());
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
mergeRuleAnalysis(eslintReport, sunlintReport) {
|
|
237
|
-
const ruleMap = new Map();
|
|
238
|
-
|
|
239
|
-
// Analyze ESLint rules
|
|
240
|
-
this.extractESLintViolations(eslintReport).forEach(violation => {
|
|
241
|
-
if (!ruleMap.has(violation.ruleId)) {
|
|
242
|
-
ruleMap.set(violation.ruleId, {
|
|
243
|
-
rule: violation.ruleId,
|
|
244
|
-
tool: 'eslint',
|
|
245
|
-
violations: 0,
|
|
246
|
-
files: new Set()
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
const rule = ruleMap.get(violation.ruleId);
|
|
250
|
-
rule.violations++;
|
|
251
|
-
rule.files.add(violation.file);
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
// Analyze SunLint rules
|
|
255
|
-
this.extractSunLintViolations(sunlintReport).forEach(violation => {
|
|
256
|
-
if (!ruleMap.has(violation.ruleId)) {
|
|
257
|
-
ruleMap.set(violation.ruleId, {
|
|
258
|
-
rule: violation.ruleId,
|
|
259
|
-
tool: 'sunlint',
|
|
260
|
-
violations: 0,
|
|
261
|
-
files: new Set()
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
const rule = ruleMap.get(violation.ruleId);
|
|
265
|
-
rule.violations++;
|
|
266
|
-
rule.files.add(violation.file);
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
// Convert Sets to arrays and sort
|
|
270
|
-
return Array.from(ruleMap.values())
|
|
271
|
-
.map(rule => ({
|
|
272
|
-
...rule,
|
|
273
|
-
files: Array.from(rule.files),
|
|
274
|
-
files_count: rule.files.size
|
|
275
|
-
}))
|
|
276
|
-
.sort((a, b) => b.violations - a.violations);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
countOverlapFiles(eslintReport, sunlintReport) {
|
|
280
|
-
const eslintFiles = new Set();
|
|
281
|
-
const sunlintFiles = new Set();
|
|
282
|
-
|
|
283
|
-
if (eslintReport.files) {
|
|
284
|
-
eslintReport.files.forEach(f => eslintFiles.add(f.filePath || f.file));
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
if (sunlintReport.results) {
|
|
288
|
-
sunlintReport.results.forEach(r => sunlintFiles.add(r.file));
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return [...eslintFiles].filter(file => sunlintFiles.has(file)).length;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
getUniqueRules(report, toolName) {
|
|
295
|
-
if (toolName === 'eslint') {
|
|
296
|
-
return [...new Set(this.extractESLintViolations(report).map(v => v.ruleId))];
|
|
297
|
-
} else {
|
|
298
|
-
return [...new Set(this.extractSunLintViolations(report).map(v => v.ruleId))];
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
formatReport(unifiedReport, format) {
|
|
303
|
-
switch (format) {
|
|
304
|
-
case 'json':
|
|
305
|
-
return JSON.stringify(unifiedReport, null, 2);
|
|
306
|
-
case 'summary':
|
|
307
|
-
return this.formatSummary(unifiedReport);
|
|
308
|
-
case 'github':
|
|
309
|
-
return this.formatGitHub(unifiedReport);
|
|
310
|
-
case 'detailed':
|
|
311
|
-
return this.formatDetailed(unifiedReport);
|
|
312
|
-
default:
|
|
313
|
-
return this.formatSummary(unifiedReport);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
formatSummary(report) {
|
|
318
|
-
const { summary } = report;
|
|
319
|
-
return `
|
|
320
|
-
šÆ Sun* Engineering - Unified Code Quality Report
|
|
321
|
-
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
322
|
-
|
|
323
|
-
š Overall Summary:
|
|
324
|
-
Files analyzed: ${summary.total.files}
|
|
325
|
-
Total violations: ${summary.total.violations}
|
|
326
|
-
|
|
327
|
-
šØ By Severity:
|
|
328
|
-
Errors: ${summary.total.errors}
|
|
329
|
-
Warnings: ${summary.total.warnings}
|
|
330
|
-
Info: ${summary.total.info}
|
|
331
|
-
|
|
332
|
-
š§ By Tool:
|
|
333
|
-
ESLint: ${summary.by_tool.eslint.violations} violations (${summary.by_tool.eslint.files} files)
|
|
334
|
-
SunLint: ${summary.by_tool.sunlint.violations} violations (${summary.by_tool.sunlint.files} files)
|
|
335
|
-
|
|
336
|
-
š Coverage:
|
|
337
|
-
Overlap files: ${summary.breakdown.overlap_files}
|
|
338
|
-
ESLint rules: ${summary.breakdown.unique_eslint_rules.length}
|
|
339
|
-
SunLint rules: ${summary.breakdown.unique_sunlint_rules.length}
|
|
340
|
-
|
|
341
|
-
Generated: ${report.metadata.timestamp}
|
|
342
|
-
`;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
formatGitHub(report) {
|
|
346
|
-
const annotations = report.violations
|
|
347
|
-
.filter(v => v.severity === 'error')
|
|
348
|
-
.slice(0, 10) // GitHub limits annotations
|
|
349
|
-
.map(v => `::error file=${v.file},line=${v.line}::${v.message} (${v.ruleId})`)
|
|
350
|
-
.join('\n');
|
|
351
|
-
|
|
352
|
-
return annotations + '\n' + this.formatSummary(report);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
formatDetailed(report) {
|
|
356
|
-
let output = this.formatSummary(report);
|
|
357
|
-
|
|
358
|
-
output += '\nš Top Violated Rules:\n';
|
|
359
|
-
report.rules.slice(0, 10).forEach(rule => {
|
|
360
|
-
output += ` ${rule.rule}: ${rule.violations} violations (${rule.files_count} files) [${rule.tool}]\n`;
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
output += '\nš Most Problematic Files:\n';
|
|
364
|
-
const filesByViolations = report.files
|
|
365
|
-
.map(f => ({
|
|
366
|
-
...f,
|
|
367
|
-
total: f.eslint.violations + f.sunlint.violations
|
|
368
|
-
}))
|
|
369
|
-
.sort((a, b) => b.total - a.total)
|
|
370
|
-
.slice(0, 10);
|
|
371
|
-
|
|
372
|
-
filesByViolations.forEach(file => {
|
|
373
|
-
output += ` ${file.file}: ${file.total} violations (ESLint: ${file.eslint.violations}, SunLint: ${file.sunlint.violations})\n`;
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
return output;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// CLI
|
|
381
|
-
const program = new Command();
|
|
382
|
-
|
|
383
|
-
program
|
|
384
|
-
.name('merge-reports')
|
|
385
|
-
.description('Merge ESLint and SunLint reports into unified report')
|
|
386
|
-
.version('1.0.0')
|
|
387
|
-
.argument('<eslint-report>', 'ESLint JSON report file')
|
|
388
|
-
.argument('<sunlint-report>', 'SunLint JSON report file')
|
|
389
|
-
.option('-f, --format <format>', 'Output format (json,summary,github,detailed)', 'summary')
|
|
390
|
-
.option('-o, --output <file>', 'Output file (default: console)')
|
|
391
|
-
.option('--verbose', 'Verbose logging');
|
|
392
|
-
|
|
393
|
-
program.action(async (eslintReport, sunlintReport, options) => {
|
|
394
|
-
try {
|
|
395
|
-
const merger = new ReportMerger();
|
|
396
|
-
|
|
397
|
-
if (options.verbose) {
|
|
398
|
-
console.log(chalk.blue('š Merging reports...'));
|
|
399
|
-
console.log(`ESLint: ${eslintReport}`);
|
|
400
|
-
console.log(`SunLint: ${sunlintReport}`);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
const unifiedReport = await merger.mergeReports(eslintReport, sunlintReport, options);
|
|
404
|
-
const formattedOutput = merger.formatReport(unifiedReport, options.format);
|
|
405
|
-
|
|
406
|
-
if (options.output) {
|
|
407
|
-
fs.writeFileSync(options.output, formattedOutput);
|
|
408
|
-
console.log(chalk.green(`ā
Unified report saved: ${options.output}`));
|
|
409
|
-
} else {
|
|
410
|
-
console.log(formattedOutput);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
} catch (error) {
|
|
414
|
-
console.error(chalk.red('ā Report merge failed:'), error.message);
|
|
415
|
-
if (options.verbose) {
|
|
416
|
-
console.error(error.stack);
|
|
417
|
-
}
|
|
418
|
-
process.exit(1);
|
|
419
|
-
}
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
program.parse();
|
|
423
|
-
|
|
424
|
-
module.exports = ReportMerger;
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# Test Scripts
|
|
2
|
-
|
|
3
|
-
This directory contains various test scripts for SunLint development and validation.
|
|
4
|
-
|
|
5
|
-
## Files
|
|
6
|
-
|
|
7
|
-
- `test-eslint-rules.js` - Tests ESLint rule implementations
|
|
8
|
-
- `test-rules-on-real-projects.js` - Tests rules on real project samples
|
|
9
|
-
- `test-real-world.js` - Real-world testing scenarios
|
|
10
|
-
- `test-c041-eslint.js` - Specific tests for C041 ESLint rule
|
|
11
|
-
- `test-c041-comparison.js` - Comparison tests between Heuristic and ESLint for C041
|
|
12
|
-
|
|
13
|
-
## Usage
|
|
14
|
-
|
|
15
|
-
Run any test script from the project root:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
node scripts/test-scripts/test-eslint-rules.js
|
|
19
|
-
node scripts/test-scripts/test-rules-on-real-projects.js
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
These scripts are used for development, validation, and debugging of SunLint rules.
|