@sun-asterisk/sunlint 1.0.7 โ†’ 1.1.4

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 (219) hide show
  1. package/.sunlint.json +35 -0
  2. package/CHANGELOG.md +30 -3
  3. package/CONTRIBUTING.md +235 -0
  4. package/PROJECT_STRUCTURE.md +60 -0
  5. package/README.md +146 -58
  6. package/cli.js +1 -0
  7. package/config/README.md +88 -0
  8. package/config/defaults/ai-rules-context.json +231 -0
  9. package/config/engines/engines.json +49 -0
  10. package/config/engines/eslint-rule-mapping.json +74 -0
  11. package/config/eslint-rule-mapping.json +126 -0
  12. package/config/integrations/eslint/base.config.js +125 -0
  13. package/config/integrations/eslint/simple.config.js +24 -0
  14. package/config/presets/strict.json +0 -1
  15. package/config/rule-analysis-strategies.js +74 -0
  16. package/config/{rules-registry.json โ†’ rules/rules-registry.json} +30 -7
  17. package/core/analysis-orchestrator.js +383 -591
  18. package/core/ast-modules/README.md +103 -0
  19. package/core/ast-modules/base-parser.js +90 -0
  20. package/core/ast-modules/index.js +97 -0
  21. package/core/ast-modules/package.json +37 -0
  22. package/core/ast-modules/parsers/eslint-js-parser.js +153 -0
  23. package/core/ast-modules/parsers/eslint-ts-parser.js +98 -0
  24. package/core/ast-modules/parsers/javascript-parser.js +187 -0
  25. package/core/ast-modules/parsers/typescript-parser.js +187 -0
  26. package/core/cli-action-handler.js +271 -255
  27. package/core/cli-program.js +18 -4
  28. package/core/config-manager.js +9 -3
  29. package/core/config-merger.js +40 -1
  30. package/core/config-validator.js +2 -2
  31. package/core/dependency-checker.js +125 -0
  32. package/core/enhanced-rules-registry.js +331 -0
  33. package/core/file-targeting-service.js +92 -23
  34. package/core/interfaces/analysis-engine.interface.js +100 -0
  35. package/core/multi-rule-runner.js +0 -221
  36. package/core/output-service.js +1 -1
  37. package/core/rule-mapping-service.js +1 -1
  38. package/core/rule-selection-service.js +10 -2
  39. package/core/smart-installer.js +164 -0
  40. package/docs/AI.md +163 -0
  41. package/docs/ARCHITECTURE.md +78 -0
  42. package/docs/CI-CD-GUIDE.md +315 -0
  43. package/docs/COMMAND-EXAMPLES.md +256 -0
  44. package/docs/CONFIGURATION.md +414 -0
  45. package/docs/DEBUG.md +86 -0
  46. package/docs/DEPENDENCIES.md +90 -0
  47. package/docs/DEPLOYMENT-STRATEGIES.md +270 -0
  48. package/docs/DISTRIBUTION.md +153 -0
  49. package/docs/ESLINT-INTEGRATION-STRATEGY.md +392 -0
  50. package/docs/ESLINT_INTEGRATION.md +238 -0
  51. package/docs/FOLDER_STRUCTURE.md +59 -0
  52. package/docs/FUTURE_PACKAGES.md +83 -0
  53. package/docs/HEURISTIC_VS_AI.md +113 -0
  54. package/docs/PRODUCTION_DEPLOYMENT_ANALYSIS.md +112 -0
  55. package/docs/PRODUCTION_SIZE_IMPACT.md +183 -0
  56. package/docs/README.md +32 -0
  57. package/docs/RELEASE_GUIDE.md +230 -0
  58. package/engines/eslint-engine.js +610 -0
  59. package/engines/heuristic-engine.js +864 -0
  60. package/engines/openai-engine.js +374 -0
  61. package/engines/tree-sitter-parser.js +0 -0
  62. package/engines/universal-ast-engine.js +0 -0
  63. package/integrations/eslint/README.md +99 -0
  64. package/integrations/eslint/configs/.eslintrc.js +98 -0
  65. package/integrations/eslint/configs/eslint.config.js +133 -0
  66. package/integrations/eslint/configs/eslint.config.simple.js +24 -0
  67. package/integrations/eslint/package.json +23 -0
  68. package/integrations/eslint/plugin/index.js +164 -0
  69. package/integrations/eslint/plugin/package.json +13 -0
  70. package/integrations/eslint/plugin/rules/common/c002-no-duplicate-code.js +204 -0
  71. package/integrations/eslint/plugin/rules/common/c003-no-vague-abbreviations.js +246 -0
  72. package/integrations/eslint/plugin/rules/common/c006-function-name-verb-noun.js +216 -0
  73. package/integrations/eslint/plugin/rules/common/c010-limit-block-nesting.js +90 -0
  74. package/integrations/eslint/plugin/rules/common/c013-no-dead-code.js +78 -0
  75. package/integrations/eslint/plugin/rules/common/c014-abstract-dependency-preferred.js +38 -0
  76. package/integrations/eslint/plugin/rules/common/c017-limit-constructor-logic.js +146 -0
  77. package/integrations/eslint/plugin/rules/common/c018-no-generic-throw.js +335 -0
  78. package/integrations/eslint/plugin/rules/common/c023-no-duplicate-variable-name-in-scope.js +142 -0
  79. package/integrations/eslint/plugin/rules/common/c029-catch-block-logging.js +115 -0
  80. package/integrations/eslint/plugin/rules/common/c030-use-custom-error-classes.js +294 -0
  81. package/integrations/eslint/plugin/rules/common/c035-no-empty-catch.js +162 -0
  82. package/integrations/eslint/plugin/rules/common/c041-no-config-inline.js +122 -0
  83. package/integrations/eslint/plugin/rules/common/c042-boolean-name-prefix.js +406 -0
  84. package/integrations/eslint/plugin/rules/common/c043-no-console-or-print.js +300 -0
  85. package/integrations/eslint/plugin/rules/common/c047-no-duplicate-retry-logic.js +239 -0
  86. package/integrations/eslint/plugin/rules/common/c072-one-assert-per-test.js +184 -0
  87. package/integrations/eslint/plugin/rules/common/c075-explicit-function-return-types.js +168 -0
  88. package/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +254 -0
  89. package/integrations/eslint/plugin/rules/security/s001-fail-securely.js +381 -0
  90. package/integrations/eslint/plugin/rules/security/s002-idor-check.js +945 -0
  91. package/integrations/eslint/plugin/rules/security/s003-no-unvalidated-redirect.js +86 -0
  92. package/integrations/eslint/plugin/rules/security/s007-no-plaintext-otp.js +74 -0
  93. package/integrations/eslint/plugin/rules/security/s013-verify-tls-connection.js +47 -0
  94. package/integrations/eslint/plugin/rules/security/s047-secure-random-passwords.js +108 -0
  95. package/integrations/eslint/plugin/rules/security/s055-verification-rest-check-the-incoming-content-type.js +143 -0
  96. package/integrations/eslint/plugin/rules/typescript/t002-interface-prefix-i.js +42 -0
  97. package/integrations/eslint/plugin/rules/typescript/t003-ts-ignore-reason.js +48 -0
  98. package/integrations/eslint/plugin/rules/typescript/t004-no-empty-type.js +95 -0
  99. package/integrations/eslint/plugin/rules/typescript/t007-no-fn-in-constructor.js +52 -0
  100. package/integrations/eslint/plugin/rules/typescript/t010-no-nested-union-tuple.js +48 -0
  101. package/integrations/eslint/plugin/rules/typescript/t019-no-this-assign.js +81 -0
  102. package/integrations/eslint/plugin/rules/typescript/t020-no-default-multi-export.js +127 -0
  103. package/integrations/eslint/plugin/rules/typescript/t021-limit-nested-generics.js +150 -0
  104. package/integrations/eslint/tsconfig.json +27 -0
  105. package/package.json +61 -21
  106. package/rules/README.md +252 -0
  107. package/rules/common/C002_no_duplicate_code/analyzer.js +65 -0
  108. package/rules/common/C002_no_duplicate_code/config.json +23 -0
  109. package/rules/common/C003_no_vague_abbreviations/analyzer.js +418 -0
  110. package/rules/common/C003_no_vague_abbreviations/config.json +35 -0
  111. package/rules/{C006_function_naming โ†’ common/C006_function_naming}/analyzer.js +13 -2
  112. package/rules/common/C010_limit_block_nesting/analyzer.js +389 -0
  113. package/rules/common/C013_no_dead_code/analyzer.js +206 -0
  114. package/rules/common/C014_dependency_injection/analyzer.js +338 -0
  115. package/rules/common/C017_constructor_logic/analyzer.js +314 -0
  116. package/rules/{C019_log_level_usage โ†’ common/C019_log_level_usage}/analyzer.js +5 -2
  117. package/rules/{C029_catch_block_logging โ†’ common/C029_catch_block_logging}/analyzer.js +49 -15
  118. package/rules/common/C041_no_sensitive_hardcode/analyzer.js +292 -0
  119. package/rules/common/C042_boolean_name_prefix/analyzer.js +300 -0
  120. package/rules/common/C043_no_console_or_print/analyzer.js +304 -0
  121. package/rules/common/C047_no_duplicate_retry_logic/analyzer.js +351 -0
  122. package/rules/common/C075_explicit_return_types/analyzer.js +103 -0
  123. package/rules/common/C076_single_test_behavior/analyzer.js +121 -0
  124. package/rules/docs/C002_no_duplicate_code.md +57 -0
  125. package/rules/index.js +149 -0
  126. package/rules/migration/converter.js +385 -0
  127. package/rules/migration/mapping.json +164 -0
  128. package/rules/security/S026_json_schema_validation/analyzer.js +251 -0
  129. package/rules/security/S026_json_schema_validation/config.json +27 -0
  130. package/rules/security/S027_no_hardcoded_secrets/analyzer.js +263 -0
  131. package/rules/security/S027_no_hardcoded_secrets/config.json +29 -0
  132. package/rules/security/S029_csrf_protection/analyzer.js +264 -0
  133. package/rules/tests/C002_no_duplicate_code.test.js +50 -0
  134. package/rules/universal/C010/generic.js +0 -0
  135. package/rules/universal/C010/tree-sitter-analyzer.js +0 -0
  136. package/rules/utils/ast-utils.js +191 -0
  137. package/rules/utils/base-analyzer.js +98 -0
  138. package/rules/utils/pattern-matchers.js +239 -0
  139. package/rules/utils/rule-helpers.js +264 -0
  140. package/rules/utils/severity-constants.js +93 -0
  141. package/scripts/build-release.sh +117 -0
  142. package/scripts/ci-report.js +179 -0
  143. package/scripts/install.sh +196 -0
  144. package/scripts/manual-release.sh +338 -0
  145. package/scripts/merge-reports.js +424 -0
  146. package/scripts/pre-release-test.sh +175 -0
  147. package/scripts/prepare-release.sh +202 -0
  148. package/scripts/setup-github-registry.sh +42 -0
  149. package/scripts/test-scripts/README.md +22 -0
  150. package/scripts/test-scripts/test-c041-comparison.js +114 -0
  151. package/scripts/test-scripts/test-c041-eslint.js +67 -0
  152. package/scripts/test-scripts/test-eslint-rules.js +146 -0
  153. package/scripts/test-scripts/test-real-world.js +44 -0
  154. package/scripts/test-scripts/test-rules-on-real-projects.js +86 -0
  155. package/scripts/trigger-release.sh +285 -0
  156. package/scripts/validate-rule-structure.js +148 -0
  157. package/scripts/verify-install.sh +82 -0
  158. package/config/sunlint-schema.json +0 -159
  159. package/config/typescript/custom-rules.js +0 -9
  160. package/config/typescript/package-lock.json +0 -1585
  161. package/config/typescript/package.json +0 -13
  162. package/config/typescript/security-rules/index.js +0 -90
  163. package/config/typescript/tsconfig.json +0 -29
  164. package/core/ai-analyzer.js +0 -169
  165. package/core/eslint-engine-service.js +0 -312
  166. package/core/eslint-instance-manager.js +0 -104
  167. package/core/eslint-integration-service.js +0 -363
  168. package/core/sunlint-engine-service.js +0 -23
  169. package/core/typescript-analyzer.js +0 -262
  170. package/core/typescript-engine.js +0 -313
  171. /package/config/{default.json โ†’ defaults/default.json} +0 -0
  172. /package/config/{typescript/eslint.config.js โ†’ integrations/eslint/typescript.config.js} +0 -0
  173. /package/config/{typescript/custom-rules-new.js โ†’ schemas/sunlint-schema.json} +0 -0
  174. /package/config/{typescript โ†’ testing}/test-s005-working.ts +0 -0
  175. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s005-no-origin-auth.js +0 -0
  176. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s006-activation-recovery-secret-not-plaintext.js +0 -0
  177. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s008-crypto-agility.js +0 -0
  178. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s009-no-insecure-crypto.js +0 -0
  179. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s010-no-insecure-random-in-sensitive-context.js +0 -0
  180. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s011-no-insecure-uuid.js +0 -0
  181. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s012-hardcode-secret.js +0 -0
  182. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s014-insecure-tls-version.js +0 -0
  183. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s015-insecure-tls-certificate.js +0 -0
  184. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s016-sensitive-query-parameter.js +0 -0
  185. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s017-no-sql-injection.js +0 -0
  186. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s018-positive-input-validation.js +0 -0
  187. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s019-no-raw-user-input-in-email.js +0 -0
  188. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s020-no-eval-dynamic-execution.js +0 -0
  189. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s022-output-encoding.js +0 -0
  190. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s023-no-json-injection.js +0 -0
  191. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s025-server-side-input-validation.js +0 -0
  192. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s026-json-schema-validation.js +0 -0
  193. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s027-no-hardcoded-secrets.js +0 -0
  194. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s029-require-csrf-protection.js +0 -0
  195. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s030-no-directory-browsing.js +0 -0
  196. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s033-require-samesite-cookie.js +0 -0
  197. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s034-require-host-cookie-prefix.js +0 -0
  198. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s035-cookie-specific-path.js +0 -0
  199. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s036-no-unsafe-file-include.js +0 -0
  200. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s037-require-anti-cache-headers.js +0 -0
  201. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s038-no-version-disclosure.js +0 -0
  202. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s039-no-session-token-in-url.js +0 -0
  203. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s041-require-session-invalidate-on-logout.js +0 -0
  204. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s042-require-periodic-reauthentication.js +0 -0
  205. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s043-terminate-sessions-on-password-change.js +0 -0
  206. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s044-require-full-session-for-sensitive-operations.js +0 -0
  207. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s045-anti-automation-controls.js +0 -0
  208. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s046-secure-notification-on-auth-change.js +0 -0
  209. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s048-password-credential-recovery.js +0 -0
  210. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s050-session-token-weak-hash.js +0 -0
  211. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s052-secure-random-authentication-code.js +0 -0
  212. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s054-verification-default-account.js +0 -0
  213. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s057-utc-logging.js +0 -0
  214. /package/{config/typescript/security-rules โ†’ integrations/eslint/plugin/rules/security}/s058-no-ssrf.js +0 -0
  215. /package/rules/{C006_function_naming โ†’ common/C006_function_naming}/config.json +0 -0
  216. /package/rules/{C019_log_level_usage โ†’ common/C019_log_level_usage}/config.json +0 -0
  217. /package/rules/{C029_catch_block_logging โ†’ common/C029_catch_block_logging}/config.json +0 -0
  218. /package/rules/{C031_validation_separation โ†’ common/C031_validation_separation}/analyzer.js +0 -0
  219. /package/rules/{C031_validation_separation/README.md โ†’ docs/C031_validation_separation.md} +0 -0
@@ -0,0 +1,424 @@
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;
@@ -0,0 +1,175 @@
1
+ #!/bin/bash
2
+
3
+ # SunLint v1.0.5 Pre-Release Test Suite
4
+ # Comprehensive testing before triggering actual release
5
+
6
+ set -e
7
+
8
+ echo "๐Ÿงช =========================================="
9
+ echo "โ˜€๏ธ SunLint v1.0.5 Pre-Release Test Suite"
10
+ echo "๐Ÿงช =========================================="
11
+ echo ""
12
+
13
+ # Colors
14
+ RED='\033[0;31m'
15
+ GREEN='\033[0;32m'
16
+ YELLOW='\033[1;33m'
17
+ BLUE='\033[0;34m'
18
+ NC='\033[0m' # No Color
19
+
20
+ # Test results tracking
21
+ TESTS_PASSED=0
22
+ TESTS_FAILED=0
23
+
24
+ # Function to run test with validation
25
+ run_test() {
26
+ local test_name="$1"
27
+ local command="$2"
28
+ local expected_pattern="$3"
29
+
30
+ echo -e "${BLUE}๐Ÿงช Testing: $test_name${NC}"
31
+ echo -e "${YELLOW}Command: $command${NC}"
32
+
33
+ if output=$(eval $command 2>&1); then
34
+ if [[ -z "$expected_pattern" ]] || echo "$output" | grep -q "$expected_pattern"; then
35
+ echo -e "${GREEN}โœ… PASS${NC}"
36
+ ((TESTS_PASSED++))
37
+ else
38
+ echo -e "${RED}โŒ FAIL - Expected pattern not found: $expected_pattern${NC}"
39
+ echo "Output: $output"
40
+ ((TESTS_FAILED++))
41
+ fi
42
+ else
43
+ echo -e "${RED}โŒ FAIL - Command failed${NC}"
44
+ echo "Error: $output"
45
+ ((TESTS_FAILED++))
46
+ fi
47
+ echo ""
48
+ }
49
+
50
+ # Test 1: Package.json validation
51
+ run_test "Package.json Structure" "node -e 'console.log(JSON.parse(require(\"fs\").readFileSync(\"package.json\")).name)'" "@sun-asterisk/sunlint"
52
+
53
+ # Test 2: GitHub Package config
54
+ run_test "GitHub Package Config" "node -e 'console.log(JSON.parse(require(\"fs\").readFileSync(\"package-github.json\")).name)'" "@sun-asterisk/sunlint"
55
+
56
+ # Test 3: CLI basic functionality
57
+ run_test "CLI Version Check" "node cli.js --version" "1.0.5"
58
+
59
+ # Test 4: Help output
60
+ run_test "CLI Help" "node cli.js --help" "Sun Lint"
61
+
62
+ # Test 5: Basic rule execution
63
+ run_test "Basic Rule Execution" "node cli.js --rule=C006 --input=examples/integration/src --format=summary" "C006"
64
+
65
+ # Test 6: Multiple rules
66
+ run_test "Multiple Rules" "node cli.js --rules=C006,C019 --input=examples/integration/src --format=summary" "violations"
67
+
68
+ # Test 7: TypeScript engine
69
+ run_test "TypeScript Engine" "node cli.js --typescript --rule=C006 --input=examples/integration/src --format=summary" "TypeScript"
70
+
71
+ # Test 8: Git integration (if in git repo)
72
+ if git rev-parse --git-dir > /dev/null 2>&1; then
73
+ run_test "Git Integration Check" "node cli.js --all --changed-files --format=summary || echo 'No changed files'" ""
74
+ else
75
+ echo -e "${YELLOW}โš ๏ธ Skipping Git integration test (not in git repo)${NC}"
76
+ fi
77
+
78
+ # Test 9: Configuration file validation
79
+ run_test "Config File Validation" "node cli.js --config=examples/integration/.sunlint.json --input=examples/integration/src --format=summary --dry-run" "Dry run"
80
+
81
+ # Test 10: ESLint integration test
82
+ if [ -f "examples/integration/.eslintrc.json" ]; then
83
+ run_test "ESLint Integration" "node cli.js --all --eslint-integration --input=examples/integration/src --format=summary" "integration"
84
+ else
85
+ echo -e "${YELLOW}โš ๏ธ Skipping ESLint integration test (no .eslintrc.json)${NC}"
86
+ fi
87
+
88
+ # Test 11: Package structure validation
89
+ run_test "Required Files Check" "ls cli.js core/ rules/ config/ examples/ docs/ README.md" "cli.js"
90
+
91
+ # Test 12: Dependencies check
92
+ run_test "Dependencies Installation" "npm ls --depth=0" "sunlint"
93
+
94
+ # Test 13: Build test (npm pack)
95
+ echo -e "${BLUE}๐Ÿงช Testing: Package Build${NC}"
96
+ if npm pack > /dev/null 2>&1; then
97
+ if ls *.tgz > /dev/null 2>&1; then
98
+ echo -e "${GREEN}โœ… PASS - Package built successfully${NC}"
99
+ ((TESTS_PASSED++))
100
+ # Cleanup
101
+ rm -f *.tgz
102
+ else
103
+ echo -e "${RED}โŒ FAIL - No tarball generated${NC}"
104
+ ((TESTS_FAILED++))
105
+ fi
106
+ else
107
+ echo -e "${RED}โŒ FAIL - npm pack failed${NC}"
108
+ ((TESTS_FAILED++))
109
+ fi
110
+ echo ""
111
+
112
+ # Test 14: GitHub Actions workflow validation
113
+ echo -e "${BLUE}๐Ÿงช Testing: GitHub Actions Workflow${NC}"
114
+ if [ -f "../../../.github/workflows/release-sunlint.yml" ]; then
115
+ echo -e "${GREEN}โœ… PASS - Release workflow exists${NC}"
116
+ ((TESTS_PASSED++))
117
+ else
118
+ echo -e "${RED}โŒ FAIL - Release workflow missing${NC}"
119
+ ((TESTS_FAILED++))
120
+ fi
121
+ echo ""
122
+
123
+ # Test 15: Documentation completeness
124
+ echo -e "${BLUE}๐Ÿงช Testing: Documentation Completeness${NC}"
125
+ required_docs=("README.md" "docs/ESLINT_INTEGRATION.md" "docs/RELEASE_GUIDE.md" "examples/integration/package.json")
126
+ docs_missing=0
127
+
128
+ for doc in "${required_docs[@]}"; do
129
+ if [ ! -f "$doc" ]; then
130
+ echo -e "${RED}โŒ Missing: $doc${NC}"
131
+ ((docs_missing++))
132
+ fi
133
+ done
134
+
135
+ if [ $docs_missing -eq 0 ]; then
136
+ echo -e "${GREEN}โœ… PASS - All documentation present${NC}"
137
+ ((TESTS_PASSED++))
138
+ else
139
+ echo -e "${RED}โŒ FAIL - $docs_missing documentation files missing${NC}"
140
+ ((TESTS_FAILED++))
141
+ fi
142
+ echo ""
143
+
144
+ # Summary
145
+ echo "๐ŸŽฏ =========================================="
146
+ echo "๐Ÿ“Š Test Results Summary"
147
+ echo "๐ŸŽฏ =========================================="
148
+ echo -e "${GREEN}โœ… Tests Passed: $TESTS_PASSED${NC}"
149
+ echo -e "${RED}โŒ Tests Failed: $TESTS_FAILED${NC}"
150
+ echo ""
151
+
152
+ if [ $TESTS_FAILED -eq 0 ]; then
153
+ echo -e "${GREEN}๐ŸŽ‰ ALL TESTS PASSED! Ready for release! ๐Ÿš€${NC}"
154
+ echo ""
155
+ echo "๐Ÿ“‹ Next Steps:"
156
+ echo "1. Run GitHub Actions release workflow"
157
+ echo "2. Verify GitHub Package Registry publication"
158
+ echo "3. Test installation from both sources"
159
+ echo "4. Update documentation with release links"
160
+ echo ""
161
+ echo "๐Ÿ”— Trigger release workflow:"
162
+ echo "https://github.com/sun-asterisk/engineer-excellence/actions/workflows/release-sunlint.yml"
163
+
164
+ exit 0
165
+ else
166
+ echo -e "${RED}๐Ÿ’ฅ TESTS FAILED! Please fix issues before release.${NC}"
167
+ echo ""
168
+ echo "๐Ÿ”ง Common fixes:"
169
+ echo "- Check package.json configuration"
170
+ echo "- Verify all dependencies are installed"
171
+ echo "- Ensure example files exist"
172
+ echo "- Check file permissions"
173
+
174
+ exit 1
175
+ fi