@sun-asterisk/sunlint 1.2.2 → 1.3.1
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 +107 -1
- package/CONTRIBUTING.md +1654 -66
- package/README.md +19 -6
- package/config/ci-cd.json +54 -0
- package/config/development.json +56 -0
- package/config/engines/engines-enhanced.json +86 -0
- package/config/engines/semantic-config.json +114 -0
- package/config/eslint-rule-mapping.json +50 -38
- package/config/large-project.json +143 -0
- package/config/presets/all.json +0 -1
- package/config/release.json +70 -0
- package/config/rule-analysis-strategies.js +23 -4
- package/config/rules/S027-categories.json +122 -0
- package/config/rules/enhanced-rules-registry.json +2564 -0
- package/config/rules/rules-registry-generated.json +785 -837
- package/config/rules/rules-registry.json +13 -1
- package/core/adapters/sunlint-rule-adapter.js +25 -30
- package/core/analysis-orchestrator.js +42 -2
- package/core/categories.js +52 -0
- package/core/category-constants.js +39 -0
- package/core/cli-action-handler.js +53 -32
- package/core/cli-program.js +11 -3
- package/core/config-manager.js +111 -0
- package/core/config-merger.js +88 -0
- package/core/constants/categories.js +168 -0
- package/core/constants/defaults.js +165 -0
- package/core/constants/engines.js +185 -0
- package/core/constants/index.js +30 -0
- package/core/constants/rules.js +215 -0
- package/core/enhanced-rules-registry.js +3 -3
- package/core/file-targeting-service.js +128 -7
- package/core/interfaces/rule-plugin.interface.js +207 -0
- package/core/plugin-manager.js +448 -0
- package/core/rule-selection-service.js +42 -15
- package/core/semantic-engine.js +658 -0
- package/core/semantic-rule-base.js +433 -0
- package/core/unified-rule-registry.js +484 -0
- package/docs/COMMAND-EXAMPLES.md +134 -0
- package/docs/CONSTANTS-ARCHITECTURE.md +288 -0
- package/docs/LARGE-PROJECT-GUIDE.md +324 -0
- package/engines/core/base-engine.js +249 -0
- package/engines/engine-factory.js +275 -0
- package/engines/eslint-engine.js +171 -19
- package/engines/heuristic-engine.js +569 -78
- package/integrations/eslint/plugin/index.js +26 -28
- package/origin-rules/common-en.md +8 -8
- package/package.json +10 -6
- package/rules/common/C003_no_vague_abbreviations/analyzer.js +1 -1
- package/rules/common/C017_constructor_logic/analyzer.js +254 -17
- package/rules/common/C017_constructor_logic/semantic-analyzer.js +340 -0
- package/rules/common/C029_catch_block_logging/analyzer.js +17 -5
- package/rules/common/C033_separate_service_repository/README.md +78 -0
- package/rules/common/C033_separate_service_repository/analyzer.js +160 -0
- package/rules/common/C033_separate_service_repository/config.json +50 -0
- package/rules/common/C033_separate_service_repository/regex-based-analyzer.js +585 -0
- package/rules/common/C033_separate_service_repository/symbol-based-analyzer.js +368 -0
- package/rules/common/C035_error_logging_context/STRATEGY.md +99 -0
- package/rules/common/C035_error_logging_context/analyzer.js +230 -0
- package/rules/common/C035_error_logging_context/config.json +54 -0
- package/rules/common/C035_error_logging_context/regex-based-analyzer.js +299 -0
- package/rules/common/C035_error_logging_context/symbol-based-analyzer.js +454 -0
- package/rules/common/C040_centralized_validation/analyzer.js +165 -0
- package/rules/common/C040_centralized_validation/config.json +46 -0
- package/rules/common/C040_centralized_validation/regex-based-analyzer.js +243 -0
- package/rules/common/C040_centralized_validation/symbol-based-analyzer.js +416 -0
- package/rules/common/C047_no_duplicate_retry_logic/c047-semantic-rule.js +278 -0
- package/rules/common/C047_no_duplicate_retry_logic/symbol-analyzer-enhanced.js +968 -0
- package/rules/common/C047_no_duplicate_retry_logic/symbol-config.json +71 -0
- package/rules/common/{C076_single_test_behavior → C072_single_test_behavior}/analyzer.js +6 -6
- package/rules/common/C076_explicit_function_types/README.md +30 -0
- package/rules/common/C076_explicit_function_types/analyzer.js +172 -0
- package/rules/common/C076_explicit_function_types/config.json +15 -0
- package/rules/common/C076_explicit_function_types/semantic-analyzer.js +341 -0
- package/rules/index.js +8 -0
- package/rules/parser/rule-parser.js +13 -2
- package/rules/security/S005_no_origin_auth/README.md +226 -0
- package/rules/security/S005_no_origin_auth/analyzer.js +184 -0
- package/rules/security/S005_no_origin_auth/ast-analyzer.js +406 -0
- package/rules/security/S005_no_origin_auth/config.json +85 -0
- package/rules/security/S006_no_plaintext_recovery_codes/README.md +139 -0
- package/rules/security/S006_no_plaintext_recovery_codes/analyzer.js +306 -0
- package/rules/security/S006_no_plaintext_recovery_codes/config.json +48 -0
- package/rules/security/S007_no_plaintext_otp/README.md +198 -0
- package/rules/security/S007_no_plaintext_otp/analyzer.js +406 -0
- package/rules/security/S007_no_plaintext_otp/config.json +79 -0
- package/rules/security/S007_no_plaintext_otp/semantic-analyzer.js +609 -0
- package/rules/security/S007_no_plaintext_otp/semantic-config.json +195 -0
- package/rules/security/S007_no_plaintext_otp/semantic-wrapper.js +280 -0
- package/rules/security/S027_no_hardcoded_secrets/analyzer.js +180 -366
- package/rules/security/S027_no_hardcoded_secrets/categories.json +153 -0
- package/rules/security/S027_no_hardcoded_secrets/categorized-analyzer.js +250 -0
- package/scripts/category-manager.js +150 -0
- package/scripts/generate-rules-registry.js +88 -0
- package/scripts/migrate-rule-registry.js +157 -0
- package/scripts/prepare-release.sh +1 -1
- package/scripts/validate-system.js +48 -0
- package/.sunlint.json +0 -35
- package/config/README.md +0 -88
- package/config/engines/eslint-rule-mapping.json +0 -74
- package/config/schemas/sunlint-schema.json +0 -0
- package/config/testing/test-s005-working.ts +0 -22
- package/core/multi-rule-runner.js +0 -0
- package/docs/ESLINT-INTEGRATION-STRATEGY.md +0 -392
- package/docs/FUTURE_PACKAGES.md +0 -83
- package/docs/HEURISTIC_VS_AI.md +0 -113
- package/docs/PRODUCTION_DEPLOYMENT_ANALYSIS.md +0 -112
- package/docs/PRODUCTION_SIZE_IMPACT.md +0 -183
- package/docs/RELEASE_GUIDE.md +0 -230
- package/docs/STANDARDIZED-CATEGORY-FILTERING.md +0 -156
- package/engines/tree-sitter-parser.js +0 -0
- package/engines/universal-ast-engine.js +0 -0
- package/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +0 -254
- package/rules/common/C029_catch_block_logging/analyzer-backup.js +0 -426
- package/rules/common/C029_catch_block_logging/analyzer-fixed.js +0 -130
- package/rules/common/C029_catch_block_logging/analyzer-multi-tech.js +0 -487
- package/rules/common/C029_catch_block_logging/analyzer-simple.js +0 -110
- package/rules/common/C029_catch_block_logging/ast-analyzer-backup.js +0 -441
- package/rules/common/C029_catch_block_logging/ast-analyzer-new.js +0 -127
- package/rules/common/C029_catch_block_logging/ast-analyzer.js +0 -133
- package/rules/common/C029_catch_block_logging/cfg-analyzer.js +0 -408
- package/rules/common/C029_catch_block_logging/dataflow-analyzer.js +0 -454
- package/rules/common/C029_catch_block_logging/multi-language-ast-engine.js +0 -700
- package/rules/common/C029_catch_block_logging/pattern-learning-analyzer.js +0 -568
- package/rules/common/C029_catch_block_logging/semantic-analyzer.js +0 -459
|
@@ -1,454 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* C029 Data Flow Analyzer - IDE-Level Detection
|
|
3
|
-
*
|
|
4
|
-
* Uses Control Flow Graph and Data Flow Analysis to detect unused error variables
|
|
5
|
-
* Similar to how IDEs detect unused variables with gray highlighting
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const astRegistry = require('../../../core/ast-modules');
|
|
9
|
-
|
|
10
|
-
class C029DataFlowAnalyzer {
|
|
11
|
-
constructor() {
|
|
12
|
-
this.ruleId = 'C029';
|
|
13
|
-
this.ruleName = 'Data Flow Enhanced Catch Block Analysis';
|
|
14
|
-
this.description = 'IDE-level detection of unused error variables in catch blocks';
|
|
15
|
-
this.astRegistry = astRegistry;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async analyze(files, language, options = {}) {
|
|
19
|
-
const violations = [];
|
|
20
|
-
console.log(`🧠 C029 Data Flow: Analyzing ${files.length} files with IDE-level detection...`);
|
|
21
|
-
|
|
22
|
-
for (const filePath of files) {
|
|
23
|
-
if (options.verbose) {
|
|
24
|
-
console.log(`🧠 Data Flow analyzing ${path.basename(filePath)}`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
try {
|
|
28
|
-
const content = require('fs').readFileSync(filePath, 'utf8');
|
|
29
|
-
const fileLanguage = this.getLanguageFromPath(filePath);
|
|
30
|
-
|
|
31
|
-
// Parse with full AST
|
|
32
|
-
const ast = await this.parseAST(content, fileLanguage, filePath);
|
|
33
|
-
|
|
34
|
-
if (ast) {
|
|
35
|
-
const dataFlowViolations = this.analyzeDataFlow(ast, filePath, content);
|
|
36
|
-
violations.push(...dataFlowViolations);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
} catch (error) {
|
|
40
|
-
console.warn(`C029 Data Flow skipping ${filePath}: ${error.message}`);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return violations;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async parseAST(content, language, filePath) {
|
|
48
|
-
try {
|
|
49
|
-
if (!this.astRegistry || !this.astRegistry.parseCode) {
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return await this.astRegistry.parseCode(content, language, filePath);
|
|
54
|
-
} catch (error) {
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Core Data Flow Analysis - IDE-level detection
|
|
61
|
-
*/
|
|
62
|
-
analyzeDataFlow(ast, filePath, content) {
|
|
63
|
-
const violations = [];
|
|
64
|
-
const isTestFile = this.isTestFile(filePath);
|
|
65
|
-
|
|
66
|
-
// Find all catch clauses in the AST
|
|
67
|
-
const catchClauses = this.findCatchClauses(ast);
|
|
68
|
-
|
|
69
|
-
for (const catchClause of catchClauses) {
|
|
70
|
-
const analysis = this.analyzeCatchClauseDataFlow(catchClause, ast, filePath, isTestFile);
|
|
71
|
-
|
|
72
|
-
if (analysis.isViolation) {
|
|
73
|
-
violations.push({
|
|
74
|
-
ruleId: this.ruleId,
|
|
75
|
-
file: filePath,
|
|
76
|
-
line: catchClause.loc ? catchClause.loc.start.line : 1,
|
|
77
|
-
column: catchClause.loc ? catchClause.loc.start.column + 1 : 1,
|
|
78
|
-
message: analysis.message,
|
|
79
|
-
severity: analysis.severity,
|
|
80
|
-
code: this.getCodeFromNode(catchClause, content),
|
|
81
|
-
type: analysis.type,
|
|
82
|
-
confidence: analysis.confidence,
|
|
83
|
-
suggestion: analysis.suggestion
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return violations;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Find all catch clauses using AST traversal
|
|
93
|
-
*/
|
|
94
|
-
findCatchClauses(ast) {
|
|
95
|
-
const catchClauses = [];
|
|
96
|
-
|
|
97
|
-
const visitor = {
|
|
98
|
-
visitCatchClause: (node) => {
|
|
99
|
-
catchClauses.push(node);
|
|
100
|
-
return true; // Continue traversal
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
this.traverseAST(ast, visitor);
|
|
105
|
-
return catchClauses;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Data Flow Analysis for single catch clause
|
|
110
|
-
* Similar to IDE unused variable detection
|
|
111
|
-
*/
|
|
112
|
-
analyzeCatchClauseDataFlow(catchClause, ast, filePath, isTestFile) {
|
|
113
|
-
const errorParam = catchClause.param;
|
|
114
|
-
const catchBody = catchClause.body;
|
|
115
|
-
|
|
116
|
-
// 1. No error parameter - likely intentional ignore
|
|
117
|
-
if (!errorParam) {
|
|
118
|
-
return { isViolation: false, reason: 'no_param' };
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// 2. Check for explicit ignore patterns
|
|
122
|
-
if (this.isExplicitlyIgnored(errorParam)) {
|
|
123
|
-
return { isViolation: false, reason: 'explicitly_ignored' };
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// 3. Empty catch block
|
|
127
|
-
if (!catchBody || !catchBody.body || catchBody.body.length === 0) {
|
|
128
|
-
return {
|
|
129
|
-
isViolation: true,
|
|
130
|
-
type: 'empty_catch_block',
|
|
131
|
-
message: 'Empty catch block - error is silently ignored',
|
|
132
|
-
severity: 'error',
|
|
133
|
-
confidence: 1.0,
|
|
134
|
-
suggestion: 'Add error logging or explicit ignore comment'
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// 4. DATA FLOW ANALYSIS - Track error variable usage
|
|
139
|
-
const errorUsage = this.analyzeErrorVariableUsage(errorParam, catchBody, isTestFile);
|
|
140
|
-
|
|
141
|
-
if (!errorUsage.isUsed) {
|
|
142
|
-
return {
|
|
143
|
-
isViolation: true,
|
|
144
|
-
type: 'unused_error_variable',
|
|
145
|
-
message: `Error variable '${errorParam.name}' is declared but never used - silent error handling`,
|
|
146
|
-
severity: 'error',
|
|
147
|
-
confidence: 0.95,
|
|
148
|
-
suggestion: 'Use the error variable for logging/handling or rename to indicate intentional ignore'
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (errorUsage.isUsedButNotProcessed) {
|
|
153
|
-
return {
|
|
154
|
-
isViolation: true,
|
|
155
|
-
type: 'error_not_processed',
|
|
156
|
-
message: `Error variable '${errorParam.name}' is referenced but not processed - may hide bugs`,
|
|
157
|
-
severity: 'warning',
|
|
158
|
-
confidence: 0.8,
|
|
159
|
-
suggestion: 'Ensure error is properly logged, handled, or rethrown'
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return { isViolation: false, reason: 'properly_handled' };
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* IDE-Level Error Variable Usage Analysis
|
|
168
|
-
* Tracks how error variable is used within catch block
|
|
169
|
-
*/
|
|
170
|
-
analyzeErrorVariableUsage(errorParam, catchBody, isTestFile) {
|
|
171
|
-
const errorName = errorParam.name;
|
|
172
|
-
const usageAnalysis = {
|
|
173
|
-
isUsed: false,
|
|
174
|
-
isUsedButNotProcessed: false,
|
|
175
|
-
usageTypes: [],
|
|
176
|
-
references: []
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
// Find all references to error variable
|
|
180
|
-
const references = this.findVariableReferences(errorName, catchBody);
|
|
181
|
-
usageAnalysis.references = references;
|
|
182
|
-
usageAnalysis.isUsed = references.length > 0;
|
|
183
|
-
|
|
184
|
-
if (!usageAnalysis.isUsed) {
|
|
185
|
-
return usageAnalysis;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Analyze each reference to determine if error is processed
|
|
189
|
-
for (const ref of references) {
|
|
190
|
-
const usageType = this.categorizeErrorUsage(ref, isTestFile);
|
|
191
|
-
usageAnalysis.usageTypes.push(usageType);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Determine if error is properly processed
|
|
195
|
-
const hasValidProcessing = usageAnalysis.usageTypes.some(type =>
|
|
196
|
-
['logging', 'rethrowing', 'test_assertion', 'error_handling', 'redux_thunk'].includes(type)
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
const hasOnlyTrivialUsage = usageAnalysis.usageTypes.every(type =>
|
|
200
|
-
['property_access', 'function_param', 'unused_assignment'].includes(type)
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
if (hasOnlyTrivialUsage) {
|
|
204
|
-
usageAnalysis.isUsedButNotProcessed = true;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return usageAnalysis;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Find all references to a variable within AST node
|
|
212
|
-
* Similar to IDE "Find All References"
|
|
213
|
-
*/
|
|
214
|
-
findVariableReferences(variableName, node) {
|
|
215
|
-
const references = [];
|
|
216
|
-
|
|
217
|
-
const visitor = {
|
|
218
|
-
visitIdentifier: (identifierNode) => {
|
|
219
|
-
if (identifierNode.name === variableName) {
|
|
220
|
-
references.push(identifierNode);
|
|
221
|
-
}
|
|
222
|
-
return true;
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
this.traverseAST(node, visitor);
|
|
227
|
-
return references;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Categorize how error variable is being used
|
|
232
|
-
* Determines if usage is meaningful or trivial
|
|
233
|
-
*/
|
|
234
|
-
categorizeErrorUsage(referenceNode, isTestFile) {
|
|
235
|
-
const parent = referenceNode.parent;
|
|
236
|
-
|
|
237
|
-
if (!parent) return 'unknown';
|
|
238
|
-
|
|
239
|
-
// Check parent node type to understand usage context
|
|
240
|
-
switch (parent.type) {
|
|
241
|
-
case 'ThrowStatement':
|
|
242
|
-
return 'rethrowing';
|
|
243
|
-
|
|
244
|
-
case 'CallExpression':
|
|
245
|
-
// Check if it's a logging call
|
|
246
|
-
if (this.isLoggingCall(parent)) {
|
|
247
|
-
return 'logging';
|
|
248
|
-
}
|
|
249
|
-
// Check if it's test assertion
|
|
250
|
-
if (isTestFile && this.isTestAssertion(parent)) {
|
|
251
|
-
return 'test_assertion';
|
|
252
|
-
}
|
|
253
|
-
// Check if it's error handler call
|
|
254
|
-
if (this.isErrorHandlerCall(parent)) {
|
|
255
|
-
return 'error_handling';
|
|
256
|
-
}
|
|
257
|
-
// Check if it's Redux thunk
|
|
258
|
-
if (this.isReduxThunkCall(parent)) {
|
|
259
|
-
return 'redux_thunk';
|
|
260
|
-
}
|
|
261
|
-
return 'function_param';
|
|
262
|
-
|
|
263
|
-
case 'MemberExpression':
|
|
264
|
-
// Accessing error properties (e.message, e.stack)
|
|
265
|
-
if (parent.object === referenceNode) {
|
|
266
|
-
return 'property_access';
|
|
267
|
-
}
|
|
268
|
-
return 'member_access';
|
|
269
|
-
|
|
270
|
-
case 'AssignmentExpression':
|
|
271
|
-
return 'assignment';
|
|
272
|
-
|
|
273
|
-
case 'ReturnStatement':
|
|
274
|
-
return 'return_value';
|
|
275
|
-
|
|
276
|
-
default:
|
|
277
|
-
return 'other_usage';
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Check if call expression is logging
|
|
283
|
-
*/
|
|
284
|
-
isLoggingCall(callNode) {
|
|
285
|
-
if (callNode.type !== 'CallExpression') return false;
|
|
286
|
-
|
|
287
|
-
const callee = callNode.callee;
|
|
288
|
-
|
|
289
|
-
// console.error, console.log, etc.
|
|
290
|
-
if (callee.type === 'MemberExpression' &&
|
|
291
|
-
callee.object && callee.object.name === 'console') {
|
|
292
|
-
return ['log', 'error', 'warn', 'debug'].includes(callee.property.name);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// logger.error, log.error, etc.
|
|
296
|
-
if (callee.type === 'MemberExpression' &&
|
|
297
|
-
callee.property && ['error', 'warn', 'log'].includes(callee.property.name)) {
|
|
298
|
-
return true;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
return false;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Check if call expression is test assertion
|
|
306
|
-
*/
|
|
307
|
-
isTestAssertion(callNode) {
|
|
308
|
-
if (callNode.type !== 'CallExpression') return false;
|
|
309
|
-
|
|
310
|
-
const callee = callNode.callee;
|
|
311
|
-
|
|
312
|
-
// expect() calls
|
|
313
|
-
if (callee.name === 'expect') return true;
|
|
314
|
-
|
|
315
|
-
// assert() calls
|
|
316
|
-
if (callee.name === 'assert') return true;
|
|
317
|
-
|
|
318
|
-
// should() calls
|
|
319
|
-
if (callee.type === 'MemberExpression' &&
|
|
320
|
-
callee.property && callee.property.name === 'should') {
|
|
321
|
-
return true;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return false;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Check if call expression is error handler
|
|
329
|
-
*/
|
|
330
|
-
isErrorHandlerCall(callNode) {
|
|
331
|
-
if (callNode.type !== 'CallExpression') return false;
|
|
332
|
-
|
|
333
|
-
const callee = callNode.callee;
|
|
334
|
-
if (callee.type === 'Identifier') {
|
|
335
|
-
const name = callee.name.toLowerCase();
|
|
336
|
-
return name.includes('handle') || name.includes('process') ||
|
|
337
|
-
name.includes('report') || name.includes('track');
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
return false;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* Check if call expression is Redux thunk pattern
|
|
345
|
-
*/
|
|
346
|
-
isReduxThunkCall(callNode) {
|
|
347
|
-
if (callNode.type !== 'CallExpression') return false;
|
|
348
|
-
|
|
349
|
-
const callee = callNode.callee;
|
|
350
|
-
if (callee.type === 'Identifier') {
|
|
351
|
-
return ['rejectWithValue', 'dispatch', 'handleAxiosError'].includes(callee.name);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
return false;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Check for explicit ignore patterns
|
|
359
|
-
*/
|
|
360
|
-
isExplicitlyIgnored(errorParam) {
|
|
361
|
-
if (!errorParam || !errorParam.name) return false;
|
|
362
|
-
|
|
363
|
-
const name = errorParam.name.toLowerCase();
|
|
364
|
-
|
|
365
|
-
// Common ignore patterns
|
|
366
|
-
const ignorePatterns = ['_', 'ignored', 'ignore', 'unused'];
|
|
367
|
-
return ignorePatterns.includes(name);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* Test file detection
|
|
372
|
-
*/
|
|
373
|
-
isTestFile(filePath) {
|
|
374
|
-
const testPatterns = [
|
|
375
|
-
'__tests__', '.test.', '.spec.', '/test/', '/tests/',
|
|
376
|
-
'test-', 'spec-', '.test/', '.spec/'
|
|
377
|
-
];
|
|
378
|
-
|
|
379
|
-
return testPatterns.some(pattern => filePath.includes(pattern));
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Get source code from AST node
|
|
384
|
-
*/
|
|
385
|
-
getCodeFromNode(node, content) {
|
|
386
|
-
if (!node.loc) return '';
|
|
387
|
-
|
|
388
|
-
const lines = content.split('\n');
|
|
389
|
-
const startLine = node.loc.start.line - 1;
|
|
390
|
-
const endLine = node.loc.end.line - 1;
|
|
391
|
-
|
|
392
|
-
if (startLine === endLine) {
|
|
393
|
-
return lines[startLine].slice(node.loc.start.column, node.loc.end.column);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
const result = [lines[startLine].slice(node.loc.start.column)];
|
|
397
|
-
for (let i = startLine + 1; i < endLine; i++) {
|
|
398
|
-
result.push(lines[i]);
|
|
399
|
-
}
|
|
400
|
-
result.push(lines[endLine].slice(0, node.loc.end.column));
|
|
401
|
-
|
|
402
|
-
return result.join('\n');
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/**
|
|
406
|
-
* AST Traversal utility
|
|
407
|
-
*/
|
|
408
|
-
traverseAST(node, visitor) {
|
|
409
|
-
if (!node || typeof node !== 'object') return;
|
|
410
|
-
|
|
411
|
-
// Call appropriate visitor method
|
|
412
|
-
const nodeType = node.type;
|
|
413
|
-
if (visitor[`visit${nodeType}`]) {
|
|
414
|
-
const shouldContinue = visitor[`visit${nodeType}`](node);
|
|
415
|
-
if (!shouldContinue) return;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// Generic visit method
|
|
419
|
-
if (visitor.visit) {
|
|
420
|
-
const shouldContinue = visitor.visit(node);
|
|
421
|
-
if (!shouldContinue) return;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// Traverse children
|
|
425
|
-
for (const key in node) {
|
|
426
|
-
if (key === 'parent') continue; // Avoid cycles
|
|
427
|
-
|
|
428
|
-
const child = node[key];
|
|
429
|
-
if (Array.isArray(child)) {
|
|
430
|
-
for (const item of child) {
|
|
431
|
-
if (item && typeof item === 'object') {
|
|
432
|
-
item.parent = node; // Add parent reference
|
|
433
|
-
this.traverseAST(item, visitor);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
} else if (child && typeof child === 'object') {
|
|
437
|
-
child.parent = node; // Add parent reference
|
|
438
|
-
this.traverseAST(child, visitor);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
getLanguageFromPath(filePath) {
|
|
444
|
-
const ext = filePath.split('.').pop().toLowerCase();
|
|
445
|
-
const languageMap = {
|
|
446
|
-
'js': 'javascript', 'jsx': 'javascript',
|
|
447
|
-
'ts': 'typescript', 'tsx': 'typescript',
|
|
448
|
-
'mjs': 'javascript', 'cjs': 'javascript'
|
|
449
|
-
};
|
|
450
|
-
return languageMap[ext] || 'javascript';
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
module.exports = new C029DataFlowAnalyzer();
|