@sun-asterisk/sunlint 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +202 -0
- package/LICENSE +21 -0
- package/README.md +490 -0
- package/cli-legacy.js +355 -0
- package/cli.js +35 -0
- package/config/default.json +22 -0
- package/config/presets/beginner.json +36 -0
- package/config/presets/ci.json +46 -0
- package/config/presets/recommended.json +24 -0
- package/config/presets/strict.json +32 -0
- package/config/rules-registry.json +681 -0
- package/config/sunlint-schema.json +166 -0
- package/config/typescript/custom-rules-new.js +0 -0
- package/config/typescript/custom-rules.js +9 -0
- package/config/typescript/eslint.config.js +110 -0
- package/config/typescript/package-lock.json +1585 -0
- package/config/typescript/package.json +13 -0
- package/config/typescript/security-rules/index.js +90 -0
- package/config/typescript/security-rules/s005-no-origin-auth.js +95 -0
- package/config/typescript/security-rules/s006-activation-recovery-secret-not-plaintext.js +69 -0
- package/config/typescript/security-rules/s008-crypto-agility.js +62 -0
- package/config/typescript/security-rules/s009-no-insecure-crypto.js +103 -0
- package/config/typescript/security-rules/s010-no-insecure-random-in-sensitive-context.js +123 -0
- package/config/typescript/security-rules/s011-no-insecure-uuid.js +66 -0
- package/config/typescript/security-rules/s012-hardcode-secret.js +71 -0
- package/config/typescript/security-rules/s014-insecure-tls-version.js +50 -0
- package/config/typescript/security-rules/s015-insecure-tls-certificate.js +43 -0
- package/config/typescript/security-rules/s016-sensitive-query-parameter.js +59 -0
- package/config/typescript/security-rules/s017-no-sql-injection.js +193 -0
- package/config/typescript/security-rules/s018-positive-input-validation.js +56 -0
- package/config/typescript/security-rules/s019-no-raw-user-input-in-email.js +113 -0
- package/config/typescript/security-rules/s020-no-eval-dynamic-execution.js +89 -0
- package/config/typescript/security-rules/s022-output-encoding.js +78 -0
- package/config/typescript/security-rules/s023-no-json-injection.js +300 -0
- package/config/typescript/security-rules/s025-server-side-input-validation.js +217 -0
- package/config/typescript/security-rules/s026-json-schema-validation.js +68 -0
- package/config/typescript/security-rules/s027-no-hardcoded-secrets.js +80 -0
- package/config/typescript/security-rules/s029-require-csrf-protection.js +79 -0
- package/config/typescript/security-rules/s030-no-directory-browsing.js +78 -0
- package/config/typescript/security-rules/s033-require-samesite-cookie.js +80 -0
- package/config/typescript/security-rules/s034-require-host-cookie-prefix.js +77 -0
- package/config/typescript/security-rules/s035-cookie-specific-path.js +74 -0
- package/config/typescript/security-rules/s036-no-unsafe-file-include.js +68 -0
- package/config/typescript/security-rules/s037-require-anti-cache-headers.js +70 -0
- package/config/typescript/security-rules/s038-no-version-disclosure.js +74 -0
- package/config/typescript/security-rules/s039-no-session-token-in-url.js +63 -0
- package/config/typescript/security-rules/s041-require-session-invalidate-on-logout.js +211 -0
- package/config/typescript/security-rules/s042-require-periodic-reauthentication.js +294 -0
- package/config/typescript/security-rules/s043-terminate-sessions-on-password-change.js +254 -0
- package/config/typescript/security-rules/s044-require-full-session-for-sensitive-operations.js +292 -0
- package/config/typescript/security-rules/s045-anti-automation-controls.js +46 -0
- package/config/typescript/security-rules/s046-secure-notification-on-auth-change.js +44 -0
- package/config/typescript/security-rules/s048-password-credential-recovery.js +54 -0
- package/config/typescript/security-rules/s050-session-token-weak-hash.js +94 -0
- package/config/typescript/security-rules/s052-secure-random-authentication-code.js +66 -0
- package/config/typescript/security-rules/s054-verification-default-account.js +109 -0
- package/config/typescript/security-rules/s057-utc-logging.js +54 -0
- package/config/typescript/security-rules/s058-no-ssrf.js +73 -0
- package/config/typescript/test-s005-working.ts +22 -0
- package/config/typescript/tsconfig.json +29 -0
- package/core/ai-analyzer.js +169 -0
- package/core/analysis-orchestrator.js +705 -0
- package/core/cli-action-handler.js +230 -0
- package/core/cli-program.js +106 -0
- package/core/config-manager.js +396 -0
- package/core/config-merger.js +136 -0
- package/core/config-override-processor.js +74 -0
- package/core/config-preset-resolver.js +65 -0
- package/core/config-source-loader.js +152 -0
- package/core/config-validator.js +126 -0
- package/core/dependency-manager.js +105 -0
- package/core/eslint-engine-service.js +312 -0
- package/core/eslint-instance-manager.js +104 -0
- package/core/eslint-integration-service.js +363 -0
- package/core/git-utils.js +170 -0
- package/core/multi-rule-runner.js +239 -0
- package/core/output-service.js +250 -0
- package/core/report-generator.js +320 -0
- package/core/rule-mapping-service.js +309 -0
- package/core/rule-selection-service.js +121 -0
- package/core/sunlint-engine-service.js +23 -0
- package/core/typescript-analyzer.js +262 -0
- package/core/typescript-engine.js +313 -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/DEBUG.md +86 -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/HEURISTIC_VS_AI.md +113 -0
- package/docs/README.md +32 -0
- package/docs/RELEASE_GUIDE.md +230 -0
- package/docs/RULE-RESPONSIBILITY-MATRIX.md +204 -0
- package/eslint-integration/.eslintrc.js +98 -0
- package/eslint-integration/cli.js +35 -0
- package/eslint-integration/eslint-plugin-custom/c002-no-duplicate-code.js +204 -0
- package/eslint-integration/eslint-plugin-custom/c003-no-vague-abbreviations.js +246 -0
- package/eslint-integration/eslint-plugin-custom/c006-function-name-verb-noun.js +207 -0
- package/eslint-integration/eslint-plugin-custom/c010-limit-block-nesting.js +90 -0
- package/eslint-integration/eslint-plugin-custom/c013-no-dead-code.js +43 -0
- package/eslint-integration/eslint-plugin-custom/c014-abstract-dependency-preferred.js +38 -0
- package/eslint-integration/eslint-plugin-custom/c017-limit-constructor-logic.js +39 -0
- package/eslint-integration/eslint-plugin-custom/c018-no-generic-throw.js +335 -0
- package/eslint-integration/eslint-plugin-custom/c023-no-duplicate-variable-name-in-scope.js +142 -0
- package/eslint-integration/eslint-plugin-custom/c027-limit-function-nesting.js +50 -0
- package/eslint-integration/eslint-plugin-custom/c029-catch-block-logging.js +80 -0
- package/eslint-integration/eslint-plugin-custom/c030-use-custom-error-classes.js +294 -0
- package/eslint-integration/eslint-plugin-custom/c034-no-implicit-return.js +34 -0
- package/eslint-integration/eslint-plugin-custom/c035-no-empty-catch.js +32 -0
- package/eslint-integration/eslint-plugin-custom/c041-no-config-inline.js +64 -0
- package/eslint-integration/eslint-plugin-custom/c042-boolean-name-prefix.js +406 -0
- package/eslint-integration/eslint-plugin-custom/c043-no-console-or-print.js +300 -0
- package/eslint-integration/eslint-plugin-custom/c047-no-duplicate-retry-logic.js +239 -0
- package/eslint-integration/eslint-plugin-custom/c048-no-var-declaration.js +31 -0
- package/eslint-integration/eslint-plugin-custom/c076-one-assert-per-test.js +184 -0
- package/eslint-integration/eslint-plugin-custom/index.js +155 -0
- package/eslint-integration/eslint-plugin-custom/package.json +13 -0
- package/eslint-integration/eslint-plugin-custom/package.json.bak +9 -0
- package/eslint-integration/eslint-plugin-custom/s003-no-unvalidated-redirect.js +86 -0
- package/eslint-integration/eslint-plugin-custom/s005-no-origin-auth.js +95 -0
- package/eslint-integration/eslint-plugin-custom/s006-activation-recovery-secret-not-plaintext.js +69 -0
- package/eslint-integration/eslint-plugin-custom/s008-crypto-agility.js +62 -0
- package/eslint-integration/eslint-plugin-custom/s009-no-insecure-crypto.js +103 -0
- package/eslint-integration/eslint-plugin-custom/s010-no-insecure-random-in-sensitive-context.js +123 -0
- package/eslint-integration/eslint-plugin-custom/s011-no-insecure-uuid.js +66 -0
- package/eslint-integration/eslint-plugin-custom/s012-hardcode-secret.js +71 -0
- package/eslint-integration/eslint-plugin-custom/s014-insecure-tls-version.js +50 -0
- package/eslint-integration/eslint-plugin-custom/s015-insecure-tls-certificate.js +43 -0
- package/eslint-integration/eslint-plugin-custom/s016-sensitive-query-parameter.js +59 -0
- package/eslint-integration/eslint-plugin-custom/s017-no-sql-injection.js +193 -0
- package/eslint-integration/eslint-plugin-custom/s018-positive-input-validation.js +56 -0
- package/eslint-integration/eslint-plugin-custom/s019-no-raw-user-input-in-email.js +113 -0
- package/eslint-integration/eslint-plugin-custom/s020-no-eval-dynamic-execution.js +89 -0
- package/eslint-integration/eslint-plugin-custom/s022-output-encoding.js +78 -0
- package/eslint-integration/eslint-plugin-custom/s023-no-json-injection.js +300 -0
- package/eslint-integration/eslint-plugin-custom/s025-server-side-input-validation.js +217 -0
- package/eslint-integration/eslint-plugin-custom/s026-json-schema-validation.js +68 -0
- package/eslint-integration/eslint-plugin-custom/s027-no-hardcoded-secrets.js +80 -0
- package/eslint-integration/eslint-plugin-custom/s029-require-csrf-protection.js +79 -0
- package/eslint-integration/eslint-plugin-custom/s030-no-directory-browsing.js +78 -0
- package/eslint-integration/eslint-plugin-custom/s033-require-samesite-cookie.js +80 -0
- package/eslint-integration/eslint-plugin-custom/s034-require-host-cookie-prefix.js +77 -0
- package/eslint-integration/eslint-plugin-custom/s035-cookie-specific-path.js +74 -0
- package/eslint-integration/eslint-plugin-custom/s036-no-unsafe-file-include.js +68 -0
- package/eslint-integration/eslint-plugin-custom/s037-require-anti-cache-headers.js +70 -0
- package/eslint-integration/eslint-plugin-custom/s038-no-version-disclosure.js +74 -0
- package/eslint-integration/eslint-plugin-custom/s039-no-session-token-in-url.js +63 -0
- package/eslint-integration/eslint-plugin-custom/s041-require-session-invalidate-on-logout.js +211 -0
- package/eslint-integration/eslint-plugin-custom/s042-require-periodic-reauthentication.js +294 -0
- package/eslint-integration/eslint-plugin-custom/s043-terminate-sessions-on-password-change.js +254 -0
- package/eslint-integration/eslint-plugin-custom/s044-require-full-session-for-sensitive-operations.js +292 -0
- package/eslint-integration/eslint-plugin-custom/s045-anti-automation-controls.js +46 -0
- package/eslint-integration/eslint-plugin-custom/s046-secure-notification-on-auth-change.js +44 -0
- package/eslint-integration/eslint-plugin-custom/s047-secure-random-passwords.js +108 -0
- package/eslint-integration/eslint-plugin-custom/s048-password-credential-recovery.js +54 -0
- package/eslint-integration/eslint-plugin-custom/s050-session-token-weak-hash.js +94 -0
- package/eslint-integration/eslint-plugin-custom/s052-secure-random-authentication-code.js +66 -0
- package/eslint-integration/eslint-plugin-custom/s054-verification-default-account.js +109 -0
- package/eslint-integration/eslint-plugin-custom/s055-verification-rest-check-the-incoming-content-type.js +143 -0
- package/eslint-integration/eslint-plugin-custom/s057-utc-logging.js +54 -0
- package/eslint-integration/eslint-plugin-custom/s058-no-ssrf.js +73 -0
- package/eslint-integration/eslint-plugin-custom/t002-interface-prefix-i.js +42 -0
- package/eslint-integration/eslint-plugin-custom/t003-ts-ignore-reason.js +48 -0
- package/eslint-integration/eslint-plugin-custom/t004-interface-public-only.js +160 -0
- package/eslint-integration/eslint-plugin-custom/t007-no-fn-in-constructor.js +52 -0
- package/eslint-integration/eslint-plugin-custom/t011-no-real-time-dependency.js +175 -0
- package/eslint-integration/eslint-plugin-custom/t019-no-empty-type.js +95 -0
- package/eslint-integration/eslint-plugin-custom/t025-no-nested-union-tuple.js +48 -0
- package/eslint-integration/eslint-plugin-custom/t026-limit-nested-generics.js +377 -0
- package/eslint-integration/eslint.config.js +125 -0
- package/eslint-integration/eslint.config.simple.js +24 -0
- package/eslint-integration/node_modules/eslint-plugin-custom/package.json +0 -0
- package/eslint-integration/package.json +23 -0
- package/eslint-integration/sample.ts +53 -0
- package/eslint-integration/test-s003.js +5 -0
- package/eslint-integration/tsconfig.json +27 -0
- package/examples/.github/workflows/code-quality.yml +111 -0
- package/examples/.sunlint.json +42 -0
- package/examples/README.md +47 -0
- package/examples/package.json +33 -0
- package/package.json +100 -0
- package/rules/C006_function_naming/analyzer.js +338 -0
- package/rules/C006_function_naming/config.json +86 -0
- package/rules/C019_log_level_usage/analyzer.js +359 -0
- package/rules/C019_log_level_usage/config.json +121 -0
- package/rules/C029_catch_block_logging/analyzer.js +339 -0
- package/rules/C029_catch_block_logging/config.json +59 -0
- package/rules/C031_validation_separation/README.md +72 -0
- package/rules/C031_validation_separation/analyzer.js +186 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript Engine - Main coordinator for TypeScript analysis
|
|
3
|
+
* Rule C005: Single responsibility - orchestrates TypeScript analysis
|
|
4
|
+
* Rule C012: Command Query Separation - separate analysis and result processing
|
|
5
|
+
* Rule C014: Dependency Injection - inject required services
|
|
6
|
+
* Rule C015: Domain language - TypeScriptEngine
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const chalk = require('chalk');
|
|
12
|
+
|
|
13
|
+
// Rule C014: Dependency injection
|
|
14
|
+
const EslintInstanceManager = require('./eslint-instance-manager');
|
|
15
|
+
const DependencyManager = require('./dependency-manager');
|
|
16
|
+
const TypescriptAnalyzer = require('./typescript-analyzer');
|
|
17
|
+
const RuleMappingService = require('./rule-mapping-service');
|
|
18
|
+
|
|
19
|
+
class TypeScriptEngine {
|
|
20
|
+
constructor(eslintModulePath = null) {
|
|
21
|
+
// Rule C014: Dependency injection instead of direct instantiation
|
|
22
|
+
this.eslintInstanceManager = new EslintInstanceManager();
|
|
23
|
+
this.dependencyManager = new DependencyManager();
|
|
24
|
+
this.typescriptAnalyzer = new TypescriptAnalyzer(this.eslintInstanceManager);
|
|
25
|
+
this.ruleMappingService = new RuleMappingService();
|
|
26
|
+
|
|
27
|
+
this.eslintModulePath = eslintModulePath;
|
|
28
|
+
this.isInitialized = false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Rule C006: initializeEngine - verb-noun naming
|
|
33
|
+
* Rule C032: No external API calls in constructor - initialization method
|
|
34
|
+
*/
|
|
35
|
+
async initializeEngine() {
|
|
36
|
+
if (this.isInitialized) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Check dependencies first
|
|
41
|
+
const dependenciesAvailable = await this.dependencyManager.checkDependenciesAvailable();
|
|
42
|
+
if (!dependenciesAvailable.allAvailable) {
|
|
43
|
+
console.log(chalk.yellow('⚠️ Missing TypeScript dependencies. Attempting to install...'));
|
|
44
|
+
const installed = await this.dependencyManager.installMissingDependencies();
|
|
45
|
+
if (!installed) {
|
|
46
|
+
throw new Error('Required TypeScript dependencies are not available');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Initialize ESLint instance
|
|
51
|
+
await this.eslintInstanceManager.initializeEslintInstance(this.eslintModulePath);
|
|
52
|
+
|
|
53
|
+
this.isInitialized = true;
|
|
54
|
+
console.log(chalk.green('✅ TypeScript engine initialized successfully'));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Rule C006: runAnalysis - verb-noun naming
|
|
59
|
+
* Rule C005: Single responsibility - coordinates analysis
|
|
60
|
+
* Rule C012: Command method - performs analysis operation
|
|
61
|
+
*/
|
|
62
|
+
async runAnalysis(filePaths, options = {}) {
|
|
63
|
+
if (!this.isInitialized) {
|
|
64
|
+
await this.initializeEngine();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Validate input
|
|
68
|
+
if (!filePaths || filePaths.length === 0) {
|
|
69
|
+
throw new Error('No files provided for analysis');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const filePathsArray = Array.isArray(filePaths) ? filePaths : [filePaths];
|
|
73
|
+
|
|
74
|
+
if (options.verbose) {
|
|
75
|
+
console.log(chalk.blue(`🔄 Running TypeScript analysis on ${filePathsArray.length} files...`));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const results = await this.typescriptAnalyzer.analyzeTypeScriptFiles(filePathsArray, options);
|
|
80
|
+
|
|
81
|
+
if (options.verbose) {
|
|
82
|
+
this.logAnalysisResults(results);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return results;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
// Rule C035: Log full error information
|
|
88
|
+
console.error(chalk.red('❌ TypeScript analysis failed:'), {
|
|
89
|
+
message: error.message,
|
|
90
|
+
files: filePathsArray.length,
|
|
91
|
+
configPath: this.eslintInstanceManager.getConfigPath()
|
|
92
|
+
});
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Rule C006: configureRules - verb-noun naming
|
|
99
|
+
* Rule C005: Single responsibility - rule configuration
|
|
100
|
+
*/
|
|
101
|
+
async configureRules(selectedRules = [], options = {}) {
|
|
102
|
+
if (!this.isInitialized) {
|
|
103
|
+
await this.initializeEngine();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const configPath = this.eslintInstanceManager.getConfigPath();
|
|
108
|
+
const configFilePath = path.join(configPath, 'eslint.config.js');
|
|
109
|
+
|
|
110
|
+
if (options.verbose) {
|
|
111
|
+
console.log(chalk.blue(` Config path: ${configFilePath}`));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Clear require cache to ensure fresh config
|
|
115
|
+
delete require.cache[require.resolve(configFilePath)];
|
|
116
|
+
|
|
117
|
+
// Read current config
|
|
118
|
+
const currentConfig = require(configFilePath);
|
|
119
|
+
|
|
120
|
+
// Convert SunLint rule IDs to ESLint rule names
|
|
121
|
+
const eslintRuleNames = selectedRules.map(sunlintRuleId => {
|
|
122
|
+
const eslintRule = this.ruleMappingService.sunlintToEslintMapping[sunlintRuleId];
|
|
123
|
+
return eslintRule || sunlintRuleId;
|
|
124
|
+
}).filter(rule => rule);
|
|
125
|
+
|
|
126
|
+
// Update rules based on selection
|
|
127
|
+
const updatedConfig = this.updateRulesConfiguration(currentConfig, eslintRuleNames, options);
|
|
128
|
+
|
|
129
|
+
if (options.verbose) {
|
|
130
|
+
console.log(chalk.blue(`🔧 Configured ${selectedRules.length} rules for analysis`));
|
|
131
|
+
console.log(chalk.gray(` SunLint rules: ${selectedRules.join(', ')}`));
|
|
132
|
+
console.log(chalk.gray(` ESLint rules: ${eslintRuleNames.join(', ')}`));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return updatedConfig;
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error(chalk.red('❌ Failed to configure rules:'), error.message);
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Rule C006: updateRulesConfiguration - verb-noun naming
|
|
144
|
+
* Rule C005: Single responsibility - update config
|
|
145
|
+
* Rule C012: Pure function - no side effects
|
|
146
|
+
*/
|
|
147
|
+
updateRulesConfiguration(currentConfig, selectedRules, options) {
|
|
148
|
+
if (!Array.isArray(currentConfig)) {
|
|
149
|
+
throw new Error('Expected flat config array format');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Create new config with selected rules
|
|
153
|
+
const updatedConfig = currentConfig.map(config => {
|
|
154
|
+
if (config.rules) {
|
|
155
|
+
if (options.verbose) {
|
|
156
|
+
const ruleKeys = Object.keys(config.rules);
|
|
157
|
+
console.log(chalk.blue(` Config has ${ruleKeys.length} rules`));
|
|
158
|
+
const s003Rules = ruleKeys.filter(r => r.includes('s003'));
|
|
159
|
+
console.log(chalk.blue(` S003-related rules: ${s003Rules.join(', ') || 'none'}`));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Keep all existing rules and enable selected ones
|
|
163
|
+
const updatedRules = { ...config.rules };
|
|
164
|
+
|
|
165
|
+
// Enable only selected rules
|
|
166
|
+
for (const ruleId of selectedRules) {
|
|
167
|
+
if (options.verbose) {
|
|
168
|
+
console.log(chalk.blue(` Checking rule: ${ruleId}`));
|
|
169
|
+
console.log(chalk.blue(` Available in config: ${ruleId in config.rules}`));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (config.rules[ruleId] !== undefined) {
|
|
173
|
+
updatedRules[ruleId] = 'error'; // Enable the rule as error
|
|
174
|
+
if (options.verbose) {
|
|
175
|
+
console.log(chalk.gray(` Enabling rule: ${ruleId} as error`));
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
if (options.verbose) {
|
|
179
|
+
console.log(chalk.red(` Rule not found in config: ${ruleId}`));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
...config,
|
|
186
|
+
rules: updatedRules
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
return config;
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
return updatedConfig;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Rule C006: logAnalysisResults - verb-noun naming
|
|
197
|
+
* Rule C005: Single responsibility - logging
|
|
198
|
+
*/
|
|
199
|
+
logAnalysisResults(results) {
|
|
200
|
+
const { totalFiles, totalErrors, totalWarnings } = results;
|
|
201
|
+
|
|
202
|
+
console.log(chalk.blue('📊 TypeScript Analysis Results:'));
|
|
203
|
+
console.log(chalk.gray(` Files analyzed: ${totalFiles}`));
|
|
204
|
+
console.log(chalk.gray(` Errors found: ${totalErrors}`));
|
|
205
|
+
console.log(chalk.gray(` Warnings found: ${totalWarnings}`));
|
|
206
|
+
|
|
207
|
+
if (totalErrors === 0 && totalWarnings === 0) {
|
|
208
|
+
console.log(chalk.green(' ✅ All checks passed!'));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Rule C006: getEngineStatus - verb-noun naming
|
|
214
|
+
* Rule C012: Query method - returns status without side effects
|
|
215
|
+
*/
|
|
216
|
+
getEngineStatus() {
|
|
217
|
+
return {
|
|
218
|
+
initialized: this.isInitialized,
|
|
219
|
+
configPath: this.eslintInstanceManager.getConfigPath(),
|
|
220
|
+
eslintReady: this.eslintInstanceManager.checkInstanceReady()
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Rule C006: checkDependencies - verb-noun naming
|
|
226
|
+
* Rule C014: Delegate to dependency manager
|
|
227
|
+
*/
|
|
228
|
+
async checkDependencies() {
|
|
229
|
+
return this.dependencyManager.checkDependenciesAvailable();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Rule C006: installDependencies - verb-noun naming
|
|
234
|
+
* Rule C014: Delegate to dependency manager
|
|
235
|
+
*/
|
|
236
|
+
async installDependencies() {
|
|
237
|
+
return this.dependencyManager.installMissingDependencies();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Rule C006: convertToSunLintFormat - verb-noun naming
|
|
242
|
+
* Rule C012: Pure function - converts ESLint results to SunLint format
|
|
243
|
+
*/
|
|
244
|
+
convertToSunLintFormat(eslintResults, ruleMapping = {}, options = {}) {
|
|
245
|
+
const chalk = require('chalk');
|
|
246
|
+
// Handle both direct results and wrapped results
|
|
247
|
+
const resultsArray = eslintResults.results || [eslintResults];
|
|
248
|
+
const problems = [];
|
|
249
|
+
const convertedResults = [];
|
|
250
|
+
|
|
251
|
+
for (const result of resultsArray) {
|
|
252
|
+
// Skip if no messages
|
|
253
|
+
if (!result || !result.messages) continue;
|
|
254
|
+
|
|
255
|
+
const violations = [];
|
|
256
|
+
|
|
257
|
+
for (const message of result.messages) {
|
|
258
|
+
// Map ESLint rule ID to SunLint rule ID if mapping provided
|
|
259
|
+
const sunlintRuleId = ruleMapping[message.ruleId] || message.ruleId;
|
|
260
|
+
|
|
261
|
+
// Debug logging
|
|
262
|
+
if (options && options.verbose) {
|
|
263
|
+
console.log(chalk.gray(` ESLint message: ${message.ruleId} -> ${sunlintRuleId} (${message.message})`));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Ensure we have a valid file path
|
|
267
|
+
const filePath = result.filePath || result.file || 'unknown';
|
|
268
|
+
|
|
269
|
+
const violation = {
|
|
270
|
+
ruleId: sunlintRuleId,
|
|
271
|
+
severity: message.severity === 2 ? 'error' : 'warning',
|
|
272
|
+
message: message.message,
|
|
273
|
+
file: filePath,
|
|
274
|
+
line: message.line,
|
|
275
|
+
column: message.column,
|
|
276
|
+
endLine: message.endLine,
|
|
277
|
+
endColumn: message.endColumn
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
violations.push(violation);
|
|
281
|
+
problems.push(violation);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (violations.length > 0) {
|
|
285
|
+
convertedResults.push({
|
|
286
|
+
filePath: result.filePath || result.file || 'unknown',
|
|
287
|
+
violations: violations
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
problems,
|
|
294
|
+
results: convertedResults,
|
|
295
|
+
errorCount: problems.filter(p => p.severity === 'error').length,
|
|
296
|
+
warningCount: problems.filter(p => p.severity === 'warning').length,
|
|
297
|
+
fileCount: eslintResults.totalFiles || resultsArray.length,
|
|
298
|
+
totalViolations: problems.length
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Legacy method names for backward compatibility
|
|
303
|
+
async initializeEslint() {
|
|
304
|
+
return this.initializeEngine();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Rule C006: getEslintInstance - kept for backward compatibility
|
|
308
|
+
getEslintInstance() {
|
|
309
|
+
return this.eslintInstanceManager.getEslintInstance();
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
module.exports = TypeScriptEngine;
|
package/docs/AI.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# 🤖 AI-Powered Analysis
|
|
2
|
+
|
|
3
|
+
Sunlint supports AI-powered code analysis alongside traditional pattern-based analysis for more intelligent and context-aware rule checking.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
- **🎯 Smart Analysis**: Uses AI to understand code context and intent
|
|
8
|
+
- **🔄 Fallback Strategy**: Automatically falls back to pattern analysis if AI fails
|
|
9
|
+
- **⚡ Performance**: AI analysis runs per-file with caching support
|
|
10
|
+
- **🔧 Configurable**: Multiple AI providers and models supported
|
|
11
|
+
|
|
12
|
+
## Configuration
|
|
13
|
+
|
|
14
|
+
### In `.sunlint.json`:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"ai": {
|
|
19
|
+
"enabled": true,
|
|
20
|
+
"provider": "openai",
|
|
21
|
+
"model": "gpt-4o-mini",
|
|
22
|
+
"apiKey": "${OPENAI_API_KEY}",
|
|
23
|
+
"fallbackToPattern": true
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Environment Variables:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
export OPENAI_API_KEY="your-openai-api-key"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Supported Providers
|
|
35
|
+
|
|
36
|
+
### OpenAI
|
|
37
|
+
- **Models**: `gpt-4`, `gpt-4o-mini`, `gpt-3.5-turbo`
|
|
38
|
+
- **API Key**: Required via `OPENAI_API_KEY` environment variable
|
|
39
|
+
- **Cost**: Pay-per-use based on OpenAI pricing
|
|
40
|
+
|
|
41
|
+
### GitHub Copilot (Planned)
|
|
42
|
+
- **Integration**: VS Code extension integration
|
|
43
|
+
- **Models**: GitHub Copilot models
|
|
44
|
+
- **Authentication**: VS Code Copilot session
|
|
45
|
+
|
|
46
|
+
## AI-Enhanced Rules
|
|
47
|
+
|
|
48
|
+
### C019 - Log Level Usage
|
|
49
|
+
✅ **AI-Enabled**: Understands code context to determine appropriate log levels
|
|
50
|
+
|
|
51
|
+
**AI Analysis Features:**
|
|
52
|
+
- **Context Understanding**: Analyzes surrounding code to determine error criticality
|
|
53
|
+
- **Intent Recognition**: Understands whether errors are expected or exceptional
|
|
54
|
+
- **Semantic Analysis**: Goes beyond pattern matching to understand meaning
|
|
55
|
+
|
|
56
|
+
**Example:**
|
|
57
|
+
```typescript
|
|
58
|
+
// AI understands this is a validation error, suggests warn level
|
|
59
|
+
if (!user.email) {
|
|
60
|
+
console.error('Missing email'); // AI: Should use console.warn()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// AI understands this is a critical system error, keeps error level
|
|
64
|
+
try {
|
|
65
|
+
await database.connect();
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error('Database connection failed:', error); // AI: Appropriate error level
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Usage
|
|
72
|
+
|
|
73
|
+
### CLI Commands
|
|
74
|
+
|
|
75
|
+
**Enable AI for specific rule:**
|
|
76
|
+
```bash
|
|
77
|
+
sunlint --rule=C019 --input=src --ai
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Enable AI for all rules:**
|
|
81
|
+
```bash
|
|
82
|
+
sunlint --quality --input=src --ai
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Debug AI analysis:**
|
|
86
|
+
```bash
|
|
87
|
+
sunlint --rule=C019 --input=src --ai --verbose
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### VS Code Integration
|
|
91
|
+
|
|
92
|
+
1. **Debug Configuration**: Use "Debug Sunlint - AI Analysis"
|
|
93
|
+
2. **Task**: Run "Sunlint: AI Analysis Test"
|
|
94
|
+
3. **Set API Key**: Configure `OPENAI_API_KEY` in environment
|
|
95
|
+
|
|
96
|
+
## Output Differences
|
|
97
|
+
|
|
98
|
+
### Pattern Analysis Output:
|
|
99
|
+
```
|
|
100
|
+
WARNING: Error log level used for non-critical issue - should use warn/info level
|
|
101
|
+
at src/user.ts:15:5 (C019)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### AI Analysis Output:
|
|
105
|
+
```
|
|
106
|
+
WARNING: Email validation error should use console.warn() - this is user input validation, not a system error
|
|
107
|
+
at src/user.ts:15:5 (C019)
|
|
108
|
+
Suggestion: Change to console.warn('User email validation failed:', email)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Performance Considerations
|
|
112
|
+
|
|
113
|
+
- **Caching**: AI responses are cached per file content hash
|
|
114
|
+
- **Concurrency**: AI calls are made concurrently with rate limiting
|
|
115
|
+
- **Timeout**: 30-second timeout per AI request
|
|
116
|
+
- **Cost**: Monitor API usage in OpenAI dashboard
|
|
117
|
+
|
|
118
|
+
## Troubleshooting
|
|
119
|
+
|
|
120
|
+
### Common Issues
|
|
121
|
+
|
|
122
|
+
**API Key Not Found:**
|
|
123
|
+
```bash
|
|
124
|
+
⚠️ AI API key not found, falling back to pattern analysis
|
|
125
|
+
```
|
|
126
|
+
Solution: Set `OPENAI_API_KEY` environment variable
|
|
127
|
+
|
|
128
|
+
**API Rate Limit:**
|
|
129
|
+
```bash
|
|
130
|
+
AI Analysis failed: OpenAI API error: 429 Too Many Requests
|
|
131
|
+
```
|
|
132
|
+
Solution: Reduce `maxConcurrentRules` in config or wait
|
|
133
|
+
|
|
134
|
+
**Network Issues:**
|
|
135
|
+
```bash
|
|
136
|
+
AI Analysis failed: OpenAI API error: Network timeout
|
|
137
|
+
```
|
|
138
|
+
Solution: Check internet connection, increase `timeoutMs`
|
|
139
|
+
|
|
140
|
+
### Debug AI Issues
|
|
141
|
+
|
|
142
|
+
1. **Enable verbose mode**: `--verbose`
|
|
143
|
+
2. **Check API key**: `echo $OPENAI_API_KEY`
|
|
144
|
+
3. **Test connection**: Use debug configuration
|
|
145
|
+
4. **Check API quota**: Visit OpenAI dashboard
|
|
146
|
+
|
|
147
|
+
## Future Enhancements
|
|
148
|
+
|
|
149
|
+
- **🔄 GitHub Copilot Integration**: Direct integration with VS Code Copilot
|
|
150
|
+
- **📊 Custom Models**: Support for fine-tuned models
|
|
151
|
+
- **🎯 Rule-Specific Prompts**: Specialized prompts per rule type
|
|
152
|
+
- **💾 Smart Caching**: Semantic caching across similar code patterns
|
|
153
|
+
- **📈 Analytics**: AI vs Pattern analysis effectiveness metrics
|
|
154
|
+
|
|
155
|
+
## Cost Estimation
|
|
156
|
+
|
|
157
|
+
**OpenAI API Costs** (approximate):
|
|
158
|
+
- **gpt-4o-mini**: ~$0.001 per 1K tokens
|
|
159
|
+
- **gpt-4**: ~$0.03 per 1K tokens
|
|
160
|
+
- **Average file**: ~500 tokens
|
|
161
|
+
- **1000 files with gpt-4o-mini**: ~$0.50
|
|
162
|
+
|
|
163
|
+
**Recommendation**: Start with `gpt-4o-mini` for cost-effectiveness.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# SunLint Modular Architecture
|
|
2
|
+
|
|
3
|
+
## Phase 1: TypeScript Focus với Kiến trúc Modular
|
|
4
|
+
|
|
5
|
+
### Cấu trúc mới:
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
cli.js # CLI entry point (simplified)
|
|
9
|
+
core/
|
|
10
|
+
├── cli-program.js # CLI options definition (Rule C005)
|
|
11
|
+
├── cli-action-handler.js # Main execution flow (Rule C005)
|
|
12
|
+
├── rule-selection-service.js # Rule selection logic (Rule C005)
|
|
13
|
+
├── analysis-orchestrator.js # Analysis coordination (Rule C005)
|
|
14
|
+
├── output-service.js # Output formatting (Rule C005)
|
|
15
|
+
├── eslint-engine-service.js # ESLint integration (future)
|
|
16
|
+
└── sunlint-engine-service.js # Native SunLint engine
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Nguyên tắc thiết kế:
|
|
20
|
+
|
|
21
|
+
1. **Rule C005**: Mỗi class/file chỉ làm một việc
|
|
22
|
+
2. **Rule C014**: Dependency injection thay vì new trực tiếp
|
|
23
|
+
3. **Rule C012**: Tách rõ Command và Query
|
|
24
|
+
4. **Modular**: Dễ mở rộng cho Phase 2
|
|
25
|
+
|
|
26
|
+
### Luồng hoạt động:
|
|
27
|
+
|
|
28
|
+
1. `cli.js` → `CliActionHandler`
|
|
29
|
+
2. `CliActionHandler` → Load config, validate input
|
|
30
|
+
3. `RuleSelectionService` → Select rules based on options
|
|
31
|
+
4. `AnalysisOrchestrator` → Run analysis (SunLint or ESLint)
|
|
32
|
+
5. `OutputService` → Format and display results
|
|
33
|
+
|
|
34
|
+
### Các tính năng hiện tại:
|
|
35
|
+
|
|
36
|
+
- ✅ Modular CLI với dependency injection
|
|
37
|
+
- ✅ Rule selection (single, multiple, all, category)
|
|
38
|
+
- ✅ Dry run mode
|
|
39
|
+
- ✅ TypeScript-specific options (chuẩn bị cho Phase 1)
|
|
40
|
+
- ✅ Graceful fallback khi không tìm thấy dependencies
|
|
41
|
+
- ✅ Clean error handling và logging
|
|
42
|
+
|
|
43
|
+
### Phase 1 roadmap:
|
|
44
|
+
|
|
45
|
+
1. **✅ Done**: Modular CLI architecture
|
|
46
|
+
2. **✅ Done**: ESLint integration cho TypeScript
|
|
47
|
+
3. **Next**: TypeScript custom rules thông qua ESLint
|
|
48
|
+
4. **✅ Done**: Hybrid engine (ESLint + SunLint)
|
|
49
|
+
|
|
50
|
+
### Phase 2 roadmap:
|
|
51
|
+
|
|
52
|
+
1. Native SunLint engine cho tất cả ngôn ngữ
|
|
53
|
+
2. Mở rộng Dart, Kotlin rules
|
|
54
|
+
3. AI-powered analysis
|
|
55
|
+
4. VS Code extension integration
|
|
56
|
+
|
|
57
|
+
### Usage Examples:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Basic usage
|
|
61
|
+
sunlint --rule=C006 --input=src
|
|
62
|
+
|
|
63
|
+
# Category-based
|
|
64
|
+
sunlint --quality --input=src
|
|
65
|
+
|
|
66
|
+
# TypeScript-specific (Phase 1)
|
|
67
|
+
sunlint --typescript --input=src
|
|
68
|
+
sunlint --typescript-engine=eslint --input=src
|
|
69
|
+
|
|
70
|
+
# Dry run
|
|
71
|
+
sunlint --dry-run --all --input=src
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Dependencies:
|
|
75
|
+
|
|
76
|
+
- Minimal core dependencies
|
|
77
|
+
- ESLint integration sẽ được thêm trong Phase 1
|
|
78
|
+
- Graceful fallback khi dependencies không có sẵn
|