@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.
- package/.sunlint.json +35 -0
- package/CHANGELOG.md +30 -3
- package/CONTRIBUTING.md +235 -0
- package/PROJECT_STRUCTURE.md +60 -0
- package/README.md +146 -58
- package/cli.js +1 -0
- package/config/README.md +88 -0
- package/config/defaults/ai-rules-context.json +231 -0
- package/config/engines/engines.json +49 -0
- package/config/engines/eslint-rule-mapping.json +74 -0
- package/config/eslint-rule-mapping.json +126 -0
- package/config/integrations/eslint/base.config.js +125 -0
- package/config/integrations/eslint/simple.config.js +24 -0
- package/config/presets/strict.json +0 -1
- package/config/rule-analysis-strategies.js +74 -0
- package/config/{rules-registry.json → rules/rules-registry.json} +30 -7
- package/core/analysis-orchestrator.js +383 -591
- package/core/ast-modules/README.md +103 -0
- package/core/ast-modules/base-parser.js +90 -0
- package/core/ast-modules/index.js +97 -0
- package/core/ast-modules/package.json +37 -0
- package/core/ast-modules/parsers/eslint-js-parser.js +153 -0
- package/core/ast-modules/parsers/eslint-ts-parser.js +98 -0
- package/core/ast-modules/parsers/javascript-parser.js +187 -0
- package/core/ast-modules/parsers/typescript-parser.js +187 -0
- package/core/cli-action-handler.js +271 -255
- package/core/cli-program.js +18 -4
- package/core/config-manager.js +9 -3
- package/core/config-merger.js +40 -1
- package/core/config-validator.js +2 -2
- package/core/dependency-checker.js +125 -0
- package/core/enhanced-rules-registry.js +331 -0
- package/core/file-targeting-service.js +92 -23
- package/core/interfaces/analysis-engine.interface.js +100 -0
- package/core/multi-rule-runner.js +0 -221
- package/core/output-service.js +1 -1
- package/core/rule-mapping-service.js +1 -1
- package/core/rule-selection-service.js +10 -2
- package/core/smart-installer.js +164 -0
- package/docs/AI.md +163 -0
- package/docs/ARCHITECTURE.md +78 -0
- package/docs/CI-CD-GUIDE.md +315 -0
- package/docs/COMMAND-EXAMPLES.md +256 -0
- package/docs/CONFIGURATION.md +414 -0
- package/docs/DEBUG.md +86 -0
- package/docs/DEPENDENCIES.md +90 -0
- package/docs/DEPLOYMENT-STRATEGIES.md +270 -0
- package/docs/DISTRIBUTION.md +153 -0
- package/docs/ESLINT-INTEGRATION-STRATEGY.md +392 -0
- package/docs/ESLINT_INTEGRATION.md +238 -0
- package/docs/FOLDER_STRUCTURE.md +59 -0
- package/docs/FUTURE_PACKAGES.md +83 -0
- package/docs/HEURISTIC_VS_AI.md +113 -0
- package/docs/PRODUCTION_DEPLOYMENT_ANALYSIS.md +112 -0
- package/docs/PRODUCTION_SIZE_IMPACT.md +183 -0
- package/docs/README.md +32 -0
- package/docs/RELEASE_GUIDE.md +230 -0
- package/engines/eslint-engine.js +610 -0
- package/engines/heuristic-engine.js +864 -0
- package/engines/openai-engine.js +374 -0
- package/engines/tree-sitter-parser.js +0 -0
- package/engines/universal-ast-engine.js +0 -0
- package/integrations/eslint/README.md +99 -0
- package/integrations/eslint/configs/.eslintrc.js +98 -0
- package/integrations/eslint/configs/eslint.config.js +133 -0
- package/integrations/eslint/configs/eslint.config.simple.js +24 -0
- package/integrations/eslint/package.json +23 -0
- package/integrations/eslint/plugin/index.js +164 -0
- package/integrations/eslint/plugin/package.json +13 -0
- package/integrations/eslint/plugin/rules/common/c002-no-duplicate-code.js +204 -0
- package/integrations/eslint/plugin/rules/common/c003-no-vague-abbreviations.js +246 -0
- package/integrations/eslint/plugin/rules/common/c006-function-name-verb-noun.js +216 -0
- package/integrations/eslint/plugin/rules/common/c010-limit-block-nesting.js +90 -0
- package/integrations/eslint/plugin/rules/common/c013-no-dead-code.js +78 -0
- package/integrations/eslint/plugin/rules/common/c014-abstract-dependency-preferred.js +38 -0
- package/integrations/eslint/plugin/rules/common/c017-limit-constructor-logic.js +146 -0
- package/integrations/eslint/plugin/rules/common/c018-no-generic-throw.js +335 -0
- package/integrations/eslint/plugin/rules/common/c023-no-duplicate-variable-name-in-scope.js +142 -0
- package/integrations/eslint/plugin/rules/common/c029-catch-block-logging.js +115 -0
- package/integrations/eslint/plugin/rules/common/c030-use-custom-error-classes.js +294 -0
- package/integrations/eslint/plugin/rules/common/c035-no-empty-catch.js +162 -0
- package/integrations/eslint/plugin/rules/common/c041-no-config-inline.js +122 -0
- package/integrations/eslint/plugin/rules/common/c042-boolean-name-prefix.js +406 -0
- package/integrations/eslint/plugin/rules/common/c043-no-console-or-print.js +300 -0
- package/integrations/eslint/plugin/rules/common/c047-no-duplicate-retry-logic.js +239 -0
- package/integrations/eslint/plugin/rules/common/c072-one-assert-per-test.js +184 -0
- package/integrations/eslint/plugin/rules/common/c075-explicit-function-return-types.js +168 -0
- package/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +254 -0
- package/integrations/eslint/plugin/rules/security/s001-fail-securely.js +381 -0
- package/integrations/eslint/plugin/rules/security/s002-idor-check.js +945 -0
- package/integrations/eslint/plugin/rules/security/s003-no-unvalidated-redirect.js +86 -0
- package/integrations/eslint/plugin/rules/security/s007-no-plaintext-otp.js +74 -0
- package/integrations/eslint/plugin/rules/security/s013-verify-tls-connection.js +47 -0
- package/integrations/eslint/plugin/rules/security/s047-secure-random-passwords.js +108 -0
- package/integrations/eslint/plugin/rules/security/s055-verification-rest-check-the-incoming-content-type.js +143 -0
- package/integrations/eslint/plugin/rules/typescript/t002-interface-prefix-i.js +42 -0
- package/integrations/eslint/plugin/rules/typescript/t003-ts-ignore-reason.js +48 -0
- package/integrations/eslint/plugin/rules/typescript/t004-no-empty-type.js +95 -0
- package/integrations/eslint/plugin/rules/typescript/t007-no-fn-in-constructor.js +52 -0
- package/integrations/eslint/plugin/rules/typescript/t010-no-nested-union-tuple.js +48 -0
- package/integrations/eslint/plugin/rules/typescript/t019-no-this-assign.js +81 -0
- package/integrations/eslint/plugin/rules/typescript/t020-no-default-multi-export.js +127 -0
- package/integrations/eslint/plugin/rules/typescript/t021-limit-nested-generics.js +150 -0
- package/integrations/eslint/tsconfig.json +27 -0
- package/package.json +61 -21
- package/rules/README.md +252 -0
- package/rules/common/C002_no_duplicate_code/analyzer.js +65 -0
- package/rules/common/C002_no_duplicate_code/config.json +23 -0
- package/rules/common/C003_no_vague_abbreviations/analyzer.js +418 -0
- package/rules/common/C003_no_vague_abbreviations/config.json +35 -0
- package/rules/{C006_function_naming → common/C006_function_naming}/analyzer.js +13 -2
- package/rules/common/C010_limit_block_nesting/analyzer.js +389 -0
- package/rules/common/C013_no_dead_code/analyzer.js +206 -0
- package/rules/common/C014_dependency_injection/analyzer.js +338 -0
- package/rules/common/C017_constructor_logic/analyzer.js +314 -0
- package/rules/{C019_log_level_usage → common/C019_log_level_usage}/analyzer.js +5 -2
- package/rules/{C029_catch_block_logging → common/C029_catch_block_logging}/analyzer.js +49 -15
- package/rules/common/C041_no_sensitive_hardcode/analyzer.js +292 -0
- package/rules/common/C042_boolean_name_prefix/analyzer.js +300 -0
- package/rules/common/C043_no_console_or_print/analyzer.js +304 -0
- package/rules/common/C047_no_duplicate_retry_logic/analyzer.js +351 -0
- package/rules/common/C075_explicit_return_types/analyzer.js +103 -0
- package/rules/common/C076_single_test_behavior/analyzer.js +121 -0
- package/rules/docs/C002_no_duplicate_code.md +57 -0
- package/rules/index.js +149 -0
- package/rules/migration/converter.js +385 -0
- package/rules/migration/mapping.json +164 -0
- package/rules/security/S026_json_schema_validation/analyzer.js +251 -0
- package/rules/security/S026_json_schema_validation/config.json +27 -0
- package/rules/security/S027_no_hardcoded_secrets/analyzer.js +263 -0
- package/rules/security/S027_no_hardcoded_secrets/config.json +29 -0
- package/rules/security/S029_csrf_protection/analyzer.js +264 -0
- package/rules/tests/C002_no_duplicate_code.test.js +50 -0
- package/rules/universal/C010/generic.js +0 -0
- package/rules/universal/C010/tree-sitter-analyzer.js +0 -0
- package/rules/utils/ast-utils.js +191 -0
- package/rules/utils/base-analyzer.js +98 -0
- package/rules/utils/pattern-matchers.js +239 -0
- package/rules/utils/rule-helpers.js +264 -0
- package/rules/utils/severity-constants.js +93 -0
- package/scripts/build-release.sh +117 -0
- package/scripts/ci-report.js +179 -0
- package/scripts/install.sh +196 -0
- package/scripts/manual-release.sh +338 -0
- package/scripts/merge-reports.js +424 -0
- package/scripts/pre-release-test.sh +175 -0
- package/scripts/prepare-release.sh +202 -0
- package/scripts/setup-github-registry.sh +42 -0
- package/scripts/test-scripts/README.md +22 -0
- package/scripts/test-scripts/test-c041-comparison.js +114 -0
- package/scripts/test-scripts/test-c041-eslint.js +67 -0
- package/scripts/test-scripts/test-eslint-rules.js +146 -0
- package/scripts/test-scripts/test-real-world.js +44 -0
- package/scripts/test-scripts/test-rules-on-real-projects.js +86 -0
- package/scripts/trigger-release.sh +285 -0
- package/scripts/validate-rule-structure.js +148 -0
- package/scripts/verify-install.sh +82 -0
- package/config/sunlint-schema.json +0 -159
- package/config/typescript/custom-rules.js +0 -9
- package/config/typescript/package-lock.json +0 -1585
- package/config/typescript/package.json +0 -13
- package/config/typescript/security-rules/index.js +0 -90
- package/config/typescript/tsconfig.json +0 -29
- package/core/ai-analyzer.js +0 -169
- package/core/eslint-engine-service.js +0 -312
- package/core/eslint-instance-manager.js +0 -104
- package/core/eslint-integration-service.js +0 -363
- package/core/sunlint-engine-service.js +0 -23
- package/core/typescript-analyzer.js +0 -262
- package/core/typescript-engine.js +0 -313
- /package/config/{default.json → defaults/default.json} +0 -0
- /package/config/{typescript/eslint.config.js → integrations/eslint/typescript.config.js} +0 -0
- /package/config/{typescript/custom-rules-new.js → schemas/sunlint-schema.json} +0 -0
- /package/config/{typescript → testing}/test-s005-working.ts +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s005-no-origin-auth.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s006-activation-recovery-secret-not-plaintext.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s008-crypto-agility.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s009-no-insecure-crypto.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s010-no-insecure-random-in-sensitive-context.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s011-no-insecure-uuid.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s012-hardcode-secret.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s014-insecure-tls-version.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s015-insecure-tls-certificate.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s016-sensitive-query-parameter.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s017-no-sql-injection.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s018-positive-input-validation.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s019-no-raw-user-input-in-email.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s020-no-eval-dynamic-execution.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s022-output-encoding.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s023-no-json-injection.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s025-server-side-input-validation.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s026-json-schema-validation.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s027-no-hardcoded-secrets.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s029-require-csrf-protection.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s030-no-directory-browsing.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s033-require-samesite-cookie.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s034-require-host-cookie-prefix.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s035-cookie-specific-path.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s036-no-unsafe-file-include.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s037-require-anti-cache-headers.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s038-no-version-disclosure.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s039-no-session-token-in-url.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s041-require-session-invalidate-on-logout.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s042-require-periodic-reauthentication.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s043-terminate-sessions-on-password-change.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s044-require-full-session-for-sensitive-operations.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s045-anti-automation-controls.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s046-secure-notification-on-auth-change.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s048-password-credential-recovery.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s050-session-token-weak-hash.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s052-secure-random-authentication-code.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s054-verification-default-account.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s057-utc-logging.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s058-no-ssrf.js +0 -0
- /package/rules/{C006_function_naming → common/C006_function_naming}/config.json +0 -0
- /package/rules/{C019_log_level_usage → common/C019_log_level_usage}/config.json +0 -0
- /package/rules/{C029_catch_block_logging → common/C029_catch_block_logging}/config.json +0 -0
- /package/rules/{C031_validation_separation → common/C031_validation_separation}/analyzer.js +0 -0
- /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;
|