@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
@@ -1,104 +0,0 @@
1
- const path = require('path');
2
- const fs = require('fs');
3
- const chalk = require('chalk');
4
-
5
- /**
6
- * Handles ESLint instance initialization and configuration detection
7
- * Rule C005: Single responsibility - only ESLint initialization
8
- * Rule C015: Domain language - EslintInstanceManager
9
- */
10
- class EslintInstanceManager {
11
- constructor() {
12
- this.eslintInstance = null;
13
- this.isInitialized = false;
14
- this.eslintModulePath = null;
15
- }
16
-
17
- /**
18
- * Rule C006: initializeEslintInstance - verb-noun naming
19
- * Rule C032: No external API calls in constructor - initialization method
20
- */
21
- async initializeEslintInstance(eslintModulePath = null) {
22
- if (this.isInitialized) {
23
- return this.eslintInstance;
24
- }
25
-
26
- try {
27
- // Rule C014: Dependency injection from main package
28
- const { ESLint } = require('eslint');
29
-
30
- const configPath = this.resolveConfigPath(eslintModulePath);
31
-
32
- this.eslintInstance = new ESLint({
33
- overrideConfigFile: path.join(configPath, 'eslint.config.js'),
34
- cache: false,
35
- fix: false,
36
- overrideConfig: {
37
- linterOptions: {
38
- reportUnusedDisableDirectives: 'error'
39
- }
40
- }
41
- });
42
-
43
- this.isInitialized = true;
44
- this.eslintModulePath = configPath;
45
-
46
- return this.eslintInstance;
47
- } catch (error) {
48
- throw new Error(`Failed to initialize ESLint: ${error.message}`);
49
- }
50
- }
51
-
52
- /**
53
- * Rule C006: resolveConfigPath - verb-noun naming
54
- * Rule C005: Single responsibility - config path resolution
55
- */
56
- resolveConfigPath(eslintModulePath) {
57
- if (eslintModulePath) {
58
- return eslintModulePath;
59
- }
60
-
61
- // Try primary config path, fallback to eslint-integration
62
- const primaryPath = path.join(__dirname, '..', 'config', 'typescript');
63
- const fallbackPath = path.join(__dirname, '..', 'eslint-integration');
64
- const configFile = 'eslint.config.js';
65
-
66
- // Check if primary config exists
67
- if (fs.existsSync(path.join(primaryPath, configFile))) {
68
- return primaryPath;
69
- } else {
70
- // Fallback to eslint-integration
71
- console.log(chalk.yellow('⚠️ Using fallback ESLint config from eslint-integration'));
72
- return fallbackPath;
73
- }
74
- }
75
-
76
- /**
77
- * Rule C006: getEslintInstance - verb-noun naming
78
- * Rule C012: Query method - returns instance without side effects
79
- */
80
- getEslintInstance() {
81
- if (!this.isInitialized) {
82
- throw new Error('ESLint instance not initialized. Call initializeEslintInstance() first.');
83
- }
84
- return this.eslintInstance;
85
- }
86
-
87
- /**
88
- * Rule C006: getConfigPath - verb-noun naming
89
- * Rule C012: Query method
90
- */
91
- getConfigPath() {
92
- return this.eslintModulePath;
93
- }
94
-
95
- /**
96
- * Rule C006: checkInstanceReady - verb-noun naming
97
- * Rule C012: Query method
98
- */
99
- checkInstanceReady() {
100
- return this.isInitialized && this.eslintInstance !== null;
101
- }
102
- }
103
-
104
- module.exports = EslintInstanceManager;
@@ -1,363 +0,0 @@
1
- /**
2
- * ESLint Integration Service
3
- * Following Rule C005: Single responsibility - handle ESLint integration
4
- * Following Rule C014: Dependency injection for ESLint engine
5
- */
6
-
7
- const fs = require('fs');
8
- const path = require('path');
9
- const chalk = require('chalk');
10
- const { ESLint } = require('eslint');
11
-
12
- class ESLintIntegrationService {
13
- constructor() {
14
- this.eslintInstance = null;
15
- this.userESLintConfig = null;
16
- }
17
-
18
- /**
19
- * Check if project has existing ESLint configuration
20
- * @param {string} projectRoot - Project root directory
21
- * @returns {boolean} Whether ESLint config exists
22
- */
23
- hasExistingESLintConfig(projectRoot) {
24
- const configFiles = [
25
- '.eslintrc.js',
26
- '.eslintrc.json',
27
- '.eslintrc.yaml',
28
- '.eslintrc.yml',
29
- 'eslint.config.js',
30
- 'eslint.config.mjs'
31
- ];
32
-
33
- return configFiles.some(file => {
34
- const configPath = path.join(projectRoot, file);
35
- return fs.existsSync(configPath);
36
- }) || this.hasPackageJsonESLintConfig(projectRoot);
37
- }
38
-
39
- /**
40
- * Check if package.json has ESLint config
41
- */
42
- hasPackageJsonESLintConfig(projectRoot) {
43
- const packagePath = path.join(projectRoot, 'package.json');
44
- if (fs.existsSync(packagePath)) {
45
- try {
46
- const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
47
- return !!(pkg.eslintConfig || pkg.eslint);
48
- } catch (error) {
49
- return false;
50
- }
51
- }
52
- return false;
53
- }
54
-
55
- /**
56
- * Load existing ESLint configuration
57
- * @param {string} projectRoot - Project root directory
58
- * @param {Object} sunlintConfig - SunLint configuration for legacy support check
59
- * @returns {Object} ESLint configuration object
60
- */
61
- async loadExistingESLintConfig(projectRoot, sunlintConfig = {}) {
62
- try {
63
- console.log('Debug: Project root:', projectRoot);
64
- console.log('Debug: Config files check:', {
65
- flatConfig: fs.existsSync(path.join(projectRoot, 'eslint.config.mjs')),
66
- eslintrc: fs.existsSync(path.join(projectRoot, '.eslintrc.json')),
67
- eslintrcJs: fs.existsSync(path.join(projectRoot, '.eslintrc.js')),
68
- packageJson: fs.existsSync(path.join(projectRoot, 'package.json'))
69
- });
70
-
71
- // For ESLint v9, prioritize flat config files
72
- const flatConfigPath = path.join(projectRoot, 'eslint.config.mjs');
73
- if (fs.existsSync(flatConfigPath)) {
74
- console.log('Debug: Found flat config eslint.config.mjs');
75
- // For flat config, we don't need to load it directly
76
- // ESLint v9 will auto-discover it
77
- this.userESLintConfig = { flatConfig: true };
78
- return { flatConfig: true };
79
- }
80
-
81
- // Fallback to legacy .eslintrc format
82
- const eslintrcPath = path.join(projectRoot, '.eslintrc.json');
83
- if (fs.existsSync(eslintrcPath)) {
84
- const configContent = JSON.parse(fs.readFileSync(eslintrcPath, 'utf8'));
85
- console.log('Debug: Loaded .eslintrc.json successfully');
86
- this.userESLintConfig = configContent;
87
- return configContent;
88
- }
89
-
90
- // Check for .eslintrc.js
91
- const eslintrcJsPath = path.join(projectRoot, '.eslintrc.js');
92
- if (fs.existsSync(eslintrcJsPath)) {
93
- console.log('Debug: Found .eslintrc.js, using legacy format');
94
- // We can't require() the .eslintrc.js directly due to potential conflicts,
95
- // but we signal that a legacy config exists
96
- this.userESLintConfig = { legacyConfig: true, configFile: eslintrcJsPath };
97
- return { legacyConfig: true, configFile: eslintrcJsPath };
98
- }
99
-
100
- // Last resort - try ESLint API
101
- const eslint = new ESLint({
102
- cwd: path.resolve(projectRoot)
103
- });
104
-
105
- const sampleFile = path.join(projectRoot, 'src/violations.ts');
106
- console.log('Debug: Sample file:', sampleFile, 'exists:', fs.existsSync(sampleFile));
107
- const config = await eslint.calculateConfigForFile(sampleFile);
108
-
109
- this.userESLintConfig = config;
110
- return config;
111
- } catch (error) {
112
- console.warn(chalk.yellow(`⚠️ Could not load ESLint config: ${error.message}`));
113
- return null;
114
- }
115
- }
116
-
117
- /**
118
- * Create merged ESLint configuration (SunLint + User ESLint)
119
- * @param {Object} sunlintESLintConfig - SunLint's ESLint configuration
120
- * @param {Object} userESLintConfig - User's existing ESLint configuration
121
- * @returns {Object} Merged configuration
122
- */
123
- createMergedConfig(sunlintESLintConfig, userESLintConfig) {
124
- if (!userESLintConfig) {
125
- return sunlintESLintConfig;
126
- }
127
-
128
- // Ensure configs are safe objects
129
- const safeUserConfig = typeof userESLintConfig === 'object' ? userESLintConfig : {};
130
- const safeSunlintConfig = typeof sunlintESLintConfig === 'object' ? sunlintESLintConfig : {};
131
-
132
- const merged = {
133
- ...safeSunlintConfig,
134
- // Merge extends arrays safely
135
- extends: [
136
- ...(Array.isArray(safeSunlintConfig.extends) ? safeSunlintConfig.extends : []),
137
- ...(Array.isArray(safeUserConfig.extends) ? safeUserConfig.extends : [])
138
- ],
139
- // Merge plugins arrays safely
140
- plugins: [
141
- ...(Array.isArray(safeSunlintConfig.plugins) ? safeSunlintConfig.plugins : []),
142
- ...(Array.isArray(safeUserConfig.plugins) ? safeUserConfig.plugins : [])
143
- ],
144
- // Merge rules (user rules override SunLint in case of conflict)
145
- rules: {
146
- ...(safeSunlintConfig.rules || {}),
147
- ...(safeUserConfig.rules || {})
148
- },
149
- // Merge parser options
150
- parserOptions: {
151
- ...(safeSunlintConfig.parserOptions || {}),
152
- ...(safeUserConfig.parserOptions || {})
153
- },
154
- // Merge env
155
- env: {
156
- ...(safeSunlintConfig.env || {}),
157
- ...(safeUserConfig.env || {})
158
- },
159
- // Merge settings
160
- settings: {
161
- ...(safeSunlintConfig.settings || {}),
162
- ...(safeUserConfig.settings || {})
163
- }
164
- };
165
-
166
- return merged;
167
- }
168
-
169
- /**
170
- * Run integrated ESLint analysis (SunLint + User rules)
171
- * @param {string[]} filePaths - Files to analyze
172
- * @param {Object} sunlintConfig - SunLint configuration
173
- * @param {Object} options - Analysis options
174
- * @returns {Object} Combined ESLint results
175
- */
176
- async runIntegratedAnalysis(filePaths, sunlintConfig, options = {}) {
177
- try {
178
- // Resolve first file path to determine project root
179
- const firstFilePath = path.resolve(filePaths[0]);
180
- const projectRoot = this.findProjectRoot(firstFilePath);
181
-
182
- // Load existing ESLint config
183
- const userESLintConfig = await this.loadExistingESLintConfig(projectRoot, sunlintConfig);
184
-
185
- // Create ESLint instance based on config type
186
- let eslintOptions = {
187
- cwd: path.resolve(projectRoot),
188
- fix: options.fix || false,
189
- ignore: false
190
- };
191
-
192
- // If we found a legacy config file, specify it explicitly
193
- if (userESLintConfig && userESLintConfig.legacyConfig) {
194
- eslintOptions.overrideConfigFile = userESLintConfig.configFile;
195
- }
196
-
197
- this.eslintInstance = new ESLint(eslintOptions);
198
-
199
- // Run analysis with resolved file paths
200
- const resolvedFilePaths = [];
201
-
202
- for (const fp of filePaths) {
203
- const resolved = path.isAbsolute(fp) ? fp : path.resolve(process.cwd(), fp);
204
-
205
- // If it's a directory, find all JS/TS files in it
206
- if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {
207
- const glob = require('glob');
208
- const files = glob.sync(`${resolved}/**/*.{js,jsx,ts,tsx}`, {
209
- ignore: ['**/node_modules/**', '**/.next/**']
210
- });
211
- resolvedFilePaths.push(...files);
212
- } else {
213
- resolvedFilePaths.push(resolved);
214
- }
215
- }
216
-
217
- const results = await this.eslintInstance.lintFiles(resolvedFilePaths);
218
-
219
- // Categorize results by rule source
220
- const categorizedResults = this.categorizeResults(results);
221
-
222
- if (!options.quiet) {
223
- this.logIntegrationSummary(categorizedResults, userESLintConfig !== null);
224
- }
225
-
226
- return {
227
- results,
228
- categorized: categorizedResults,
229
- totalRules: results.length,
230
- sunlintRules: 0, // Will be counted in analysis orchestrator
231
- userRules: results.length
232
- };
233
-
234
- } catch (error) {
235
- throw new Error(`ESLint integration failed: ${error.message}`);
236
- }
237
- }
238
-
239
- /**
240
- * Get SunLint's ESLint configuration
241
- */
242
- getSunLintESLintConfig(sunlintConfig) {
243
- // This would be the ESLint config that SunLint normally uses
244
- return {
245
- extends: ['@sun/sunlint/eslint-recommended'],
246
- plugins: ['@sun/sunlint'],
247
- rules: {
248
- // Map SunLint rules to ESLint format
249
- 'custom/typescript_s003': 'warn',
250
- 'custom/typescript_s047': 'warn',
251
- 'custom/typescript_s055': 'warn',
252
- // ... other SunLint rules
253
- },
254
- parserOptions: {
255
- ecmaVersion: 2022,
256
- sourceType: 'module',
257
- project: './tsconfig.json'
258
- }
259
- };
260
- }
261
-
262
- /**
263
- * Categorize ESLint results by rule source (SunLint vs User)
264
- */
265
- categorizeResults(results) {
266
- const categorized = {
267
- sunlint: [],
268
- user: [],
269
- combined: []
270
- };
271
-
272
- results.forEach(fileResult => {
273
- fileResult.messages.forEach(message => {
274
- const ruleId = message.ruleId;
275
- if (this.isSunLintRule(ruleId)) {
276
- categorized.sunlint.push({ ...message, filePath: fileResult.filePath });
277
- } else {
278
- categorized.user.push({ ...message, filePath: fileResult.filePath });
279
- }
280
- categorized.combined.push({ ...message, filePath: fileResult.filePath });
281
- });
282
- });
283
-
284
- return categorized;
285
- }
286
-
287
- /**
288
- * Check if rule belongs to SunLint
289
- */
290
- isSunLintRule(ruleId) {
291
- return ruleId && (
292
- ruleId.startsWith('custom/typescript_') ||
293
- ruleId.startsWith('@sun/sunlint/') ||
294
- ruleId.startsWith('S') ||
295
- ruleId.startsWith('C')
296
- );
297
- }
298
-
299
- /**
300
- * Count total active rules in configuration
301
- */
302
- countTotalRules(config) {
303
- return Object.keys(config.rules || {}).length;
304
- }
305
-
306
- countSunLintRules(config) {
307
- return Object.keys(config.rules || {})
308
- .filter(ruleId => this.isSunLintRule(ruleId))
309
- .length;
310
- }
311
-
312
- countUserRules(config, userConfig) {
313
- if (!userConfig) return 0;
314
- return Object.keys(config.rules || {})
315
- .filter(ruleId => !this.isSunLintRule(ruleId))
316
- .length;
317
- }
318
-
319
- /**
320
- * Log integration summary
321
- */
322
- logIntegrationSummary(categorized, hasUserConfig) {
323
- console.log(chalk.blue('🔗 ESLint Integration Summary:'));
324
- console.log(chalk.green(` 📋 SunLint violations: ${categorized.sunlint.length}`));
325
-
326
- if (hasUserConfig) {
327
- console.log(chalk.cyan(` 🔧 User ESLint violations: ${categorized.user.length}`));
328
- console.log(chalk.yellow(` 📊 Total combined violations: ${categorized.combined.length}`));
329
- } else {
330
- console.log(chalk.gray(' 🔧 No existing ESLint config found'));
331
- }
332
- }
333
-
334
- /**
335
- * Find project root directory
336
- */
337
- findProjectRoot(startPath) {
338
- let currentPath = path.dirname(startPath);
339
- while (currentPath !== path.dirname(currentPath)) {
340
- const packagePath = path.join(currentPath, 'package.json');
341
- if (fs.existsSync(packagePath)) {
342
- return currentPath;
343
- }
344
- currentPath = path.dirname(currentPath);
345
- }
346
- return path.dirname(startPath);
347
- }
348
-
349
- /**
350
- * Check if legacy ESLint support is enabled in SunLint config
351
- * @param {Object} sunlintConfig - SunLint configuration
352
- * @returns {boolean} Whether legacy ESLint support is enabled
353
- */
354
- isLegacySupportEnabled(sunlintConfig) {
355
- // Default to true for backward compatibility
356
- const legacySupport = sunlintConfig?.integration?.eslint?.legacySupport !== false;
357
- const eslintV8Support = sunlintConfig?.compatibility?.eslintV8 !== false;
358
-
359
- return legacySupport && eslintV8Support;
360
- }
361
- }
362
-
363
- module.exports = ESLintIntegrationService;
@@ -1,23 +0,0 @@
1
- /**
2
- * SunLint Engine Service
3
- * Handles native SunLint analysis
4
- * Following Rule C005: Single responsibility - only handle SunLint execution
5
- */
6
-
7
- const MultiRuleRunner = require('./multi-rule-runner');
8
-
9
- class SunLintEngineService {
10
- constructor() {
11
- this.multiRuleRunner = new MultiRuleRunner();
12
- }
13
-
14
- /**
15
- * Command: Run SunLint analysis
16
- */
17
- async runAnalysis(rulesToRun, options) {
18
- // Use existing multi-rule runner
19
- return await this.multiRuleRunner.runRules(rulesToRun, options.input, options);
20
- }
21
- }
22
-
23
- module.exports = SunLintEngineService;