@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.
Files changed (109) 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/rule-analysis-strategies.js +18 -2
  8. package/config/rules/enhanced-rules-registry.json +2503 -0
  9. package/config/rules/rules-registry-generated.json +785 -837
  10. package/core/adapters/sunlint-rule-adapter.js +25 -30
  11. package/core/analysis-orchestrator.js +42 -2
  12. package/core/categories.js +52 -0
  13. package/core/category-constants.js +39 -0
  14. package/core/cli-action-handler.js +32 -5
  15. package/core/config-manager.js +111 -0
  16. package/core/config-merger.js +61 -0
  17. package/core/constants/categories.js +168 -0
  18. package/core/constants/defaults.js +165 -0
  19. package/core/constants/engines.js +185 -0
  20. package/core/constants/index.js +30 -0
  21. package/core/constants/rules.js +215 -0
  22. package/core/file-targeting-service.js +128 -7
  23. package/core/interfaces/rule-plugin.interface.js +207 -0
  24. package/core/plugin-manager.js +448 -0
  25. package/core/rule-selection-service.js +42 -15
  26. package/core/semantic-engine.js +560 -0
  27. package/core/semantic-rule-base.js +433 -0
  28. package/core/unified-rule-registry.js +484 -0
  29. package/docs/CONSTANTS-ARCHITECTURE.md +288 -0
  30. package/engines/core/base-engine.js +249 -0
  31. package/engines/engine-factory.js +275 -0
  32. package/engines/eslint-engine.js +180 -30
  33. package/engines/heuristic-engine.js +513 -56
  34. package/integrations/eslint/plugin/index.js +27 -27
  35. package/package.json +11 -6
  36. package/rules/README.md +252 -0
  37. package/rules/common/C002_no_duplicate_code/analyzer.js +65 -0
  38. package/rules/common/C002_no_duplicate_code/config.json +23 -0
  39. package/rules/common/C003_no_vague_abbreviations/analyzer.js +418 -0
  40. package/rules/common/C003_no_vague_abbreviations/config.json +35 -0
  41. package/rules/common/C006_function_naming/analyzer.js +504 -0
  42. package/rules/common/C006_function_naming/config.json +86 -0
  43. package/rules/common/C006_function_naming/smart-analyzer.js +503 -0
  44. package/rules/common/C010_limit_block_nesting/analyzer.js +389 -0
  45. package/rules/common/C012_command_query_separation/analyzer.js +481 -0
  46. package/rules/common/C012_command_query_separation/ast-analyzer.js +495 -0
  47. package/rules/common/C013_no_dead_code/analyzer.js +206 -0
  48. package/rules/common/C014_dependency_injection/analyzer.js +338 -0
  49. package/rules/common/C017_constructor_logic/analyzer.js +314 -0
  50. package/rules/common/C019_log_level_usage/analyzer.js +362 -0
  51. package/rules/common/C019_log_level_usage/config.json +121 -0
  52. package/rules/common/C029_catch_block_logging/analyzer-smart-pipeline.js +755 -0
  53. package/rules/common/C029_catch_block_logging/analyzer.js +141 -0
  54. package/rules/common/C029_catch_block_logging/config.json +59 -0
  55. package/rules/common/C031_validation_separation/analyzer.js +186 -0
  56. package/rules/common/C041_no_sensitive_hardcode/analyzer.js +292 -0
  57. package/rules/common/C041_no_sensitive_hardcode/ast-analyzer.js +296 -0
  58. package/rules/common/C042_boolean_name_prefix/analyzer.js +300 -0
  59. package/rules/common/C043_no_console_or_print/analyzer.js +431 -0
  60. package/rules/common/C047_no_duplicate_retry_logic/analyzer.js +590 -0
  61. package/rules/common/C047_no_duplicate_retry_logic/c047-semantic-rule.js +278 -0
  62. package/rules/common/C047_no_duplicate_retry_logic/symbol-analyzer-enhanced.js +968 -0
  63. package/rules/common/C047_no_duplicate_retry_logic/symbol-config.json +71 -0
  64. package/rules/common/C075_explicit_return_types/analyzer.js +103 -0
  65. package/rules/common/C076_single_test_behavior/analyzer.js +121 -0
  66. package/rules/docs/C002_no_duplicate_code.md +57 -0
  67. package/rules/docs/C031_validation_separation.md +72 -0
  68. package/rules/index.js +162 -0
  69. package/rules/migration/converter.js +385 -0
  70. package/rules/migration/mapping.json +164 -0
  71. package/rules/parser/constants.js +31 -0
  72. package/rules/parser/file-config.js +80 -0
  73. package/rules/parser/rule-parser-simple.js +305 -0
  74. package/rules/parser/rule-parser.js +527 -0
  75. package/rules/security/S015_insecure_tls_certificate/analyzer.js +150 -0
  76. package/rules/security/S015_insecure_tls_certificate/ast-analyzer.js +237 -0
  77. package/rules/security/S023_no_json_injection/analyzer.js +278 -0
  78. package/rules/security/S023_no_json_injection/ast-analyzer.js +359 -0
  79. package/rules/security/S026_json_schema_validation/analyzer.js +251 -0
  80. package/rules/security/S026_json_schema_validation/config.json +27 -0
  81. package/rules/security/S027_no_hardcoded_secrets/analyzer.js +436 -0
  82. package/rules/security/S027_no_hardcoded_secrets/config.json +29 -0
  83. package/rules/security/S029_csrf_protection/analyzer.js +330 -0
  84. package/rules/tests/C002_no_duplicate_code.test.js +50 -0
  85. package/rules/utils/ast-utils.js +191 -0
  86. package/rules/utils/base-analyzer.js +98 -0
  87. package/rules/utils/pattern-matchers.js +239 -0
  88. package/rules/utils/rule-helpers.js +264 -0
  89. package/rules/utils/severity-constants.js +93 -0
  90. package/scripts/category-manager.js +150 -0
  91. package/scripts/generate-rules-registry.js +88 -0
  92. package/scripts/generate_insights.js +188 -0
  93. package/scripts/migrate-rule-registry.js +157 -0
  94. package/scripts/validate-system.js +48 -0
  95. package/.sunlint.json +0 -35
  96. package/config/README.md +0 -88
  97. package/config/engines/eslint-rule-mapping.json +0 -74
  98. package/config/testing/test-s005-working.ts +0 -22
  99. package/engines/tree-sitter-parser.js +0 -0
  100. package/engines/universal-ast-engine.js +0 -0
  101. package/scripts/merge-reports.js +0 -424
  102. package/scripts/test-scripts/README.md +0 -22
  103. package/scripts/test-scripts/test-c041-comparison.js +0 -114
  104. package/scripts/test-scripts/test-c041-eslint.js +0 -67
  105. package/scripts/test-scripts/test-eslint-rules.js +0 -146
  106. package/scripts/test-scripts/test-real-world.js +0 -44
  107. package/scripts/test-scripts/test-rules-on-real-projects.js +0 -86
  108. /package/{config/schemas/sunlint-schema.json → rules/universal/C010/generic.js} +0 -0
  109. /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
@@ -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.