@sun-asterisk/sunlint 1.3.1 → 1.3.2
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 +47 -0
- package/CONTRIBUTING.md +210 -1691
- package/config/rule-analysis-strategies.js +17 -1
- package/config/rules/enhanced-rules-registry.json +369 -1135
- package/config/rules/rules-registry-generated.json +1 -1
- package/core/enhanced-rules-registry.js +2 -1
- package/core/semantic-engine.js +15 -3
- package/core/semantic-rule-base.js +4 -2
- package/engines/heuristic-engine.js +65 -4
- package/integrations/eslint/plugin/rules/common/c003-no-vague-abbreviations.js +59 -1
- package/integrations/eslint/plugin/rules/common/c006-function-name-verb-noun.js +26 -1
- package/integrations/eslint/plugin/rules/common/c030-use-custom-error-classes.js +54 -19
- package/origin-rules/common-en.md +11 -7
- package/package.json +1 -1
- package/rules/common/C002_no_duplicate_code/analyzer.js +334 -36
- package/rules/common/C003_no_vague_abbreviations/analyzer.js +220 -35
- package/rules/common/C006_function_naming/analyzer.js +29 -3
- package/rules/common/C010_limit_block_nesting/analyzer.js +181 -337
- package/rules/common/C010_limit_block_nesting/config.json +64 -0
- package/rules/common/C010_limit_block_nesting/regex-based-analyzer.js +379 -0
- package/rules/common/C010_limit_block_nesting/symbol-based-analyzer.js +231 -0
- package/rules/common/C013_no_dead_code/analyzer.js +75 -177
- package/rules/common/C013_no_dead_code/config.json +61 -0
- package/rules/common/C013_no_dead_code/regex-based-analyzer.js +345 -0
- package/rules/common/C013_no_dead_code/symbol-based-analyzer.js +640 -0
- package/rules/common/C014_dependency_injection/analyzer.js +48 -313
- package/rules/common/C014_dependency_injection/config.json +26 -0
- package/rules/common/C014_dependency_injection/symbol-based-analyzer.js +751 -0
- package/rules/common/C018_no_throw_generic_error/analyzer.js +232 -0
- package/rules/common/C018_no_throw_generic_error/config.json +50 -0
- package/rules/common/C018_no_throw_generic_error/regex-based-analyzer.js +387 -0
- package/rules/common/C018_no_throw_generic_error/symbol-based-analyzer.js +314 -0
- package/rules/common/C019_log_level_usage/analyzer.js +110 -317
- package/rules/common/C019_log_level_usage/pattern-analyzer.js +88 -0
- package/rules/common/C019_log_level_usage/system-log-analyzer.js +1267 -0
- package/rules/common/C023_no_duplicate_variable/analyzer.js +180 -0
- package/rules/common/C023_no_duplicate_variable/config.json +50 -0
- package/rules/common/C023_no_duplicate_variable/symbol-based-analyzer.js +158 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/analyzer.js +180 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/config.json +50 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +181 -0
- package/rules/common/C030_use_custom_error_classes/analyzer.js +200 -0
- package/rules/common/C035_error_logging_context/analyzer.js +3 -1
- package/rules/index.js +5 -1
- package/rules/security/S009_no_insecure_encryption/README.md +158 -0
- package/rules/security/S009_no_insecure_encryption/analyzer.js +319 -0
- package/rules/security/S009_no_insecure_encryption/config.json +55 -0
- package/rules/security/S010_no_insecure_encryption/README.md +224 -0
- package/rules/security/S010_no_insecure_encryption/analyzer.js +493 -0
- package/rules/security/S010_no_insecure_encryption/config.json +48 -0
- package/rules/security/S016_no_sensitive_querystring/STRATEGY.md +149 -0
- package/rules/security/S016_no_sensitive_querystring/analyzer.js +276 -0
- package/rules/security/S016_no_sensitive_querystring/config.json +127 -0
- package/rules/security/S016_no_sensitive_querystring/regex-based-analyzer.js +258 -0
- package/rules/security/S016_no_sensitive_querystring/symbol-based-analyzer.js +495 -0
- package/rules/security/S048_no_current_password_in_reset/README.md +222 -0
- package/rules/security/S048_no_current_password_in_reset/analyzer.js +366 -0
- package/rules/security/S048_no_current_password_in_reset/config.json +48 -0
- package/rules/security/S055_content_type_validation/README.md +176 -0
- package/rules/security/S055_content_type_validation/analyzer.js +312 -0
- package/rules/security/S055_content_type_validation/config.json +48 -0
- package/rules/utils/rule-helpers.js +140 -1
- package/scripts/consolidate-config.js +116 -0
- package/config/rules/S027-categories.json +0 -122
- package/config/rules/rules-registry.json +0 -777
- package/rules/common/C006_function_naming/smart-analyzer.js +0 -503
|
@@ -441,7 +441,7 @@
|
|
|
441
441
|
},
|
|
442
442
|
"C019": {
|
|
443
443
|
"name": "Do not use `error` log level for non-critical issues",
|
|
444
|
-
"description": "
|
|
444
|
+
"description": "Prevent noisy logs and false alarms; ensure consistent and meaningful log levels across the system.",
|
|
445
445
|
"category": "Common",
|
|
446
446
|
"severity": "major",
|
|
447
447
|
"languages": [
|
|
@@ -100,6 +100,7 @@ class EnhancedRulesRegistry {
|
|
|
100
100
|
'C006': ['eslint', 'heuristic', 'openai'],
|
|
101
101
|
'C007': ['eslint', 'heuristic', 'openai'],
|
|
102
102
|
'C014': ['eslint', 'heuristic', 'openai'],
|
|
103
|
+
'C018': ['heuristic', 'eslint'],
|
|
103
104
|
'C033': ['heuristic', 'eslint'],
|
|
104
105
|
'C035': ['heuristic', 'eslint'],
|
|
105
106
|
'C040': ['eslint', 'heuristic'],
|
|
@@ -328,4 +329,4 @@ class EnhancedRulesRegistry {
|
|
|
328
329
|
}
|
|
329
330
|
}
|
|
330
331
|
|
|
331
|
-
module.exports = EnhancedRulesRegistry;
|
|
332
|
+
module.exports = EnhancedRulesRegistry;
|
package/core/semantic-engine.js
CHANGED
|
@@ -82,9 +82,13 @@ class SemanticEngine {
|
|
|
82
82
|
libFolderPath: undefined, // Don't load TypeScript libs
|
|
83
83
|
};
|
|
84
84
|
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
// NEVER use project tsconfig.json to avoid file resolution issues
|
|
86
|
+
// Instead, load files explicitly to ensure they can be found
|
|
87
|
+
if (this.options.verbose) {
|
|
88
|
+
console.log(`🔧 SemanticEngine: Skipping project tsconfig.json to avoid file resolution issues`);
|
|
89
|
+
if (tsConfigPath) {
|
|
90
|
+
console.log(` 📋 Found tsconfig: ${tsConfigPath} (ignored for better compatibility)`);
|
|
91
|
+
}
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
this.project = new Project(projectOptions);
|
|
@@ -653,6 +657,14 @@ class SemanticEngine {
|
|
|
653
657
|
getParentContext(callExpr) { return null; }
|
|
654
658
|
isKnownHook(functionName) { return false; }
|
|
655
659
|
findSymbolUsages(sourceFile, namedImports) { return []; }
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Check if symbol engine is ready for symbol-based analysis
|
|
663
|
+
* @returns {boolean} true if project is initialized and ready
|
|
664
|
+
*/
|
|
665
|
+
isSymbolEngineReady() {
|
|
666
|
+
return this.initialized && this.project !== null;
|
|
667
|
+
}
|
|
656
668
|
}
|
|
657
669
|
|
|
658
670
|
module.exports = SemanticEngine;
|
|
@@ -41,14 +41,16 @@ class SemanticRuleBase {
|
|
|
41
41
|
/**
|
|
42
42
|
* Initialize rule with SemanticEngine instance
|
|
43
43
|
*/
|
|
44
|
-
initialize(semanticEngine) {
|
|
44
|
+
initialize(semanticEngine, options = {}) {
|
|
45
45
|
this.semanticEngine = semanticEngine;
|
|
46
46
|
|
|
47
47
|
if (!this.semanticEngine || !this.semanticEngine.initialized) {
|
|
48
48
|
throw new Error(`${this.ruleId}: SemanticEngine is required and must be initialized`);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
if (options?.verbose) {
|
|
52
|
+
console.log(`🔧 Rule ${this.ruleId} initialized with semantic analysis`);
|
|
53
|
+
}
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
/**
|
|
@@ -56,8 +56,13 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
56
56
|
// Initialize rule adapter
|
|
57
57
|
await this.ruleAdapter.initialize();
|
|
58
58
|
|
|
59
|
-
// Load available rules from unified registry
|
|
60
|
-
|
|
59
|
+
// Load available rules from unified registry (OPTIMIZED: skip for performance)
|
|
60
|
+
// Rules will be loaded on-demand in analyze() method
|
|
61
|
+
if (config.loadAllRules) {
|
|
62
|
+
await this.loadRulesFromRegistry(config);
|
|
63
|
+
} else if (this.verbose) {
|
|
64
|
+
console.log(`⚡ [HeuristicEngine] Skipping bulk rule loading for performance - will load on-demand`);
|
|
65
|
+
}
|
|
61
66
|
|
|
62
67
|
this.initialized = true;
|
|
63
68
|
if (this.verbose) {
|
|
@@ -89,6 +94,8 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
89
94
|
};
|
|
90
95
|
|
|
91
96
|
this.semanticEngine = new SemanticEngine(semanticOptions);
|
|
97
|
+
// Pass verbose option to semantic engine
|
|
98
|
+
this.semanticEngine.verbose = this.verbose;
|
|
92
99
|
|
|
93
100
|
// ts-morph is now a core dependency - but optimized for targeted files
|
|
94
101
|
const success = await this.semanticEngine.initialize(projectPath, config?.targetFiles);
|
|
@@ -287,6 +294,43 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
287
294
|
}
|
|
288
295
|
}
|
|
289
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Lazy load a single rule on-demand
|
|
299
|
+
* @param {string} ruleId - Rule ID to load
|
|
300
|
+
* @param {Object} options - Loading options
|
|
301
|
+
*/
|
|
302
|
+
async lazyLoadRule(ruleId, options = {}) {
|
|
303
|
+
try {
|
|
304
|
+
const ruleDefinition = this.unifiedRegistry.getRuleDefinition(ruleId);
|
|
305
|
+
|
|
306
|
+
if (!ruleDefinition) {
|
|
307
|
+
if (options.verbose) {
|
|
308
|
+
console.warn(`⚠️ [HeuristicEngine] Rule definition not found for ${ruleId}`);
|
|
309
|
+
}
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Check if rule supports heuristic engine
|
|
314
|
+
if (!this.unifiedRegistry.isRuleSupported(ruleId, 'heuristic')) {
|
|
315
|
+
if (options.verbose) {
|
|
316
|
+
console.warn(`⚠️ [HeuristicEngine] Rule ${ruleId} not supported by heuristic engine`);
|
|
317
|
+
}
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (options.verbose) {
|
|
322
|
+
console.log(`🔄 [HeuristicEngine] Lazy loading rule ${ruleId}...`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
await this.loadRuleFromDefinition(ruleDefinition);
|
|
326
|
+
|
|
327
|
+
} catch (error) {
|
|
328
|
+
if (options.verbose) {
|
|
329
|
+
console.warn(`⚠️ [HeuristicEngine] Failed to lazy load rule ${ruleId}:`, error.message);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
290
334
|
/**
|
|
291
335
|
* Manually load C047 semantic rule (special case)
|
|
292
336
|
*/
|
|
@@ -453,7 +497,7 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
453
497
|
|
|
454
498
|
try {
|
|
455
499
|
const instance = new ruleEntry.analyzerClass(ruleId);
|
|
456
|
-
instance.initialize(this.semanticEngine);
|
|
500
|
+
instance.initialize(this.semanticEngine, { verbose: this.verbose });
|
|
457
501
|
|
|
458
502
|
// Update entry with initialized instance
|
|
459
503
|
ruleEntry.instance = instance;
|
|
@@ -602,6 +646,14 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
602
646
|
await this.manuallyLoadC047();
|
|
603
647
|
}
|
|
604
648
|
|
|
649
|
+
// Lazy load rule if not already loaded
|
|
650
|
+
if (!this.isRuleSupported(rule.id)) {
|
|
651
|
+
if (options.verbose) {
|
|
652
|
+
console.log(`🔄 [HeuristicEngine] Lazy loading rule ${rule.id}...`);
|
|
653
|
+
}
|
|
654
|
+
await this.lazyLoadRule(rule.id, options);
|
|
655
|
+
}
|
|
656
|
+
|
|
605
657
|
if (!this.isRuleSupported(rule.id)) {
|
|
606
658
|
if (options.verbose) {
|
|
607
659
|
console.warn(`⚠️ Rule ${rule.id} not supported by Heuristic engine, skipping...`);
|
|
@@ -722,6 +774,15 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
722
774
|
async analyzeRule(rule, filesByLanguage, options) {
|
|
723
775
|
// Get full rule ID (C029 -> C029_catch_block_logging)
|
|
724
776
|
const fullRuleId = this.getFullRuleId(rule.id);
|
|
777
|
+
|
|
778
|
+
// Lazy load rule if not already loaded
|
|
779
|
+
if (!this.ruleAnalyzers.has(fullRuleId)) {
|
|
780
|
+
if (options.verbose) {
|
|
781
|
+
console.log(`🔄 [HeuristicEngine] Lazy loading rule ${rule.id} for analysis...`);
|
|
782
|
+
}
|
|
783
|
+
await this.lazyLoadRule(rule.id, options);
|
|
784
|
+
}
|
|
785
|
+
|
|
725
786
|
const analyzerInfo = this.ruleAnalyzers.get(fullRuleId);
|
|
726
787
|
|
|
727
788
|
if (!analyzerInfo) {
|
|
@@ -788,7 +849,7 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
788
849
|
rule.id,
|
|
789
850
|
languageFiles,
|
|
790
851
|
language,
|
|
791
|
-
{ ...ruleConfig, ...options }
|
|
852
|
+
{ ...ruleConfig, ...options, semanticEngine: this.semanticEngine }
|
|
792
853
|
);
|
|
793
854
|
|
|
794
855
|
allViolations.push(...languageViolations);
|
|
@@ -92,6 +92,59 @@ const c003Rule = {
|
|
|
92
92
|
return false;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
function isMathContext(node, name) {
|
|
96
|
+
// Check for math variable patterns
|
|
97
|
+
const mathPatterns = [
|
|
98
|
+
// Coordinate pairs: x1, y1, x2, y2
|
|
99
|
+
/^[xyz][12]$/i,
|
|
100
|
+
// Delta notation: dx, dy, dt, dr
|
|
101
|
+
/^d[xyztr]$/i,
|
|
102
|
+
// Math constants: a, b, c in equations
|
|
103
|
+
/^[abc]$/i,
|
|
104
|
+
// Vector components: vx, vy, vz
|
|
105
|
+
/^v[xyz]$/i,
|
|
106
|
+
// Position/point notation: p1, p2
|
|
107
|
+
/^p\d+$/i
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
if (mathPatterns.some(pattern => pattern.test(name))) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Check if we're in a math function context
|
|
115
|
+
let parent = node.parent;
|
|
116
|
+
while (parent) {
|
|
117
|
+
if (parent.type === 'FunctionDeclaration' || parent.type === 'FunctionExpression' || parent.type === 'ArrowFunctionExpression') {
|
|
118
|
+
const functionName = parent.id && parent.id.name;
|
|
119
|
+
if (functionName && /^(distance|calculate|compute|solve|formula|algorithm|equation|math)/i.test(functionName)) {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
break; // Don't check beyond the immediate function
|
|
123
|
+
}
|
|
124
|
+
parent = parent.parent;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Check if we're in a context with Math operations
|
|
128
|
+
let currentNode = node.parent;
|
|
129
|
+
while (currentNode) {
|
|
130
|
+
if (currentNode.type === 'CallExpression') {
|
|
131
|
+
const callee = currentNode.callee;
|
|
132
|
+
if (callee && callee.object && callee.object.name === 'Math') {
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
if (callee && callee.name && /^(sqrt|pow|abs|sin|cos|tan|distance|calculate)$/i.test(callee.name)) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (currentNode.type === 'BinaryExpression' && ['+', '-', '*', '/'].includes(currentNode.operator)) {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
currentNode = currentNode.parent;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
|
|
95
148
|
function checkVariableName(node, name) {
|
|
96
149
|
// Safety checks
|
|
97
150
|
if (!node || !name || typeof name !== 'string') {
|
|
@@ -112,7 +165,7 @@ const c003Rule = {
|
|
|
112
165
|
|
|
113
166
|
// Single character check
|
|
114
167
|
if (name.length === 1) {
|
|
115
|
-
if (!allowedSingleChar.has(name.toLowerCase()) && !isCounterContext(node)) {
|
|
168
|
+
if (!allowedSingleChar.has(name.toLowerCase()) && !isCounterContext(node) && !isMathContext(node, name)) {
|
|
116
169
|
context.report({
|
|
117
170
|
node,
|
|
118
171
|
messageId: "singleChar",
|
|
@@ -137,6 +190,11 @@ const c003Rule = {
|
|
|
137
190
|
return;
|
|
138
191
|
}
|
|
139
192
|
|
|
193
|
+
// Check for math context before flagging as unclear
|
|
194
|
+
if (isMathContext(node, name)) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
140
198
|
// Check for unclear/generic names
|
|
141
199
|
if (unclearNames.has(name.toLowerCase())) {
|
|
142
200
|
context.report({
|
|
@@ -79,6 +79,22 @@ const c006Rule = {
|
|
|
79
79
|
...commonVerbPrefixes,
|
|
80
80
|
...(options.allowedVerbs || [])
|
|
81
81
|
]);
|
|
82
|
+
|
|
83
|
+
// Generic/vague verbs that should be flagged even if they are technically verbs
|
|
84
|
+
const genericVerbs = new Set([
|
|
85
|
+
'do', 'handle', 'process', 'manage', 'execute', 'work', 'stuff', 'thing', 'data'
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
function isGenericVerbUsage(name) {
|
|
89
|
+
// Check if the function name is exactly a generic verb or starts with generic verb + something generic
|
|
90
|
+
const genericPatterns = [
|
|
91
|
+
/^(do|handle|process|manage|execute)(Something|Stuff|Data|Info|Work|Thing|Items|Objects?)$/i,
|
|
92
|
+
/^(do|handle|process|manage|execute)$/i,
|
|
93
|
+
/^(do|handle|process|manage|execute)[A-Z].*$/i // Any pattern starting with generic verb + capital letter
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
return genericPatterns.some(pattern => pattern.test(name));
|
|
97
|
+
}
|
|
82
98
|
|
|
83
99
|
const allowConstructors = options.allowConstructors !== false;
|
|
84
100
|
|
|
@@ -142,7 +158,16 @@ const c006Rule = {
|
|
|
142
158
|
|
|
143
159
|
// Check if it follows verb-noun pattern
|
|
144
160
|
if (isVerbNounPattern(name)) {
|
|
145
|
-
|
|
161
|
+
// But still check if it's using generic verbs that should be flagged
|
|
162
|
+
if (isGenericVerbUsage(name)) {
|
|
163
|
+
context.report({
|
|
164
|
+
node,
|
|
165
|
+
messageId: "notVerbNoun",
|
|
166
|
+
data: { name }
|
|
167
|
+
});
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
return; // Good! Follows the pattern and not generic
|
|
146
171
|
}
|
|
147
172
|
|
|
148
173
|
// Check if it's likely a noun-only name
|
|
@@ -52,6 +52,10 @@ const c030Rule = {
|
|
|
52
52
|
messages: {
|
|
53
53
|
useCustomError: "Use custom error class instead of generic 'Error'. Consider using specific error types like ValidationError, NotFoundError, BusinessRuleError, etc. Vietnamese: 'Dùng custom error class thay vì Error generic'",
|
|
54
54
|
useSpecificBuiltin: "Consider using a more specific built-in error type like TypeError, RangeError, or a custom error class. Vietnamese: 'Cân nhắc dùng built-in error cụ thể hơn hoặc custom error class'",
|
|
55
|
+
throwStringLiteral: "Use custom error classes instead of throwing string literals",
|
|
56
|
+
throwTemplateLiteral: "Use custom error classes instead of throwing template literals",
|
|
57
|
+
throwNumber: "Use custom error classes instead of throwing numbers",
|
|
58
|
+
throwVariable: "Use custom error classes instead of throwing variables",
|
|
55
59
|
missingErrorCode: "Custom error class should include an error code property. Vietnamese: 'Custom error class nên có thuộc tính error code'",
|
|
56
60
|
missingStatusCode: "HTTP-related error class should include a status code property. Vietnamese: 'Error class liên quan HTTP nên có thuộc tính status code'",
|
|
57
61
|
preferCustomError: "Prefer custom error classes for better error categorization and handling. Vietnamese: 'Ưu tiên custom error classes để phân loại và xử lý lỗi tốt hơn'"
|
|
@@ -223,35 +227,66 @@ const c030Rule = {
|
|
|
223
227
|
// Skip rethrow statements if allowed
|
|
224
228
|
if (isRethrowStatement(node)) return;
|
|
225
229
|
|
|
226
|
-
|
|
230
|
+
// Handle different throw argument types
|
|
231
|
+
if (node.argument) {
|
|
232
|
+
// Check for new Error(...) constructors
|
|
233
|
+
if (node.argument.type === 'NewExpression' &&
|
|
234
|
+
node.argument.callee &&
|
|
235
|
+
node.argument.callee.name === 'Error') {
|
|
236
|
+
context.report({
|
|
237
|
+
node: node.argument,
|
|
238
|
+
messageId: "useCustomError"
|
|
239
|
+
});
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
227
242
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
243
|
+
// Check for other built-in error constructors
|
|
244
|
+
if (node.argument.type === 'NewExpression' &&
|
|
245
|
+
node.argument.callee &&
|
|
246
|
+
allowedBuiltinErrors.has(node.argument.callee.name)) {
|
|
247
|
+
if (['TypeError', 'RangeError'].includes(node.argument.callee.name)) {
|
|
248
|
+
context.report({
|
|
249
|
+
node: node.argument,
|
|
250
|
+
messageId: "useSpecificBuiltin"
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
236
255
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
if (!isCustomErrorClass(errorClassName) && !allowedBuiltinErrors.has(errorClassName)) {
|
|
256
|
+
// Check for throwing string literals
|
|
257
|
+
if (node.argument.type === 'Literal' && typeof node.argument.value === 'string') {
|
|
240
258
|
context.report({
|
|
241
259
|
node: node.argument,
|
|
242
|
-
messageId: "
|
|
260
|
+
messageId: "throwStringLiteral"
|
|
243
261
|
});
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Check for throwing template literals
|
|
266
|
+
if (node.argument.type === 'TemplateLiteral') {
|
|
267
|
+
context.report({
|
|
268
|
+
node: node.argument,
|
|
269
|
+
messageId: "throwTemplateLiteral"
|
|
270
|
+
});
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Check for throwing numbers
|
|
275
|
+
if (node.argument.type === 'Literal' && typeof node.argument.value === 'number') {
|
|
276
|
+
context.report({
|
|
277
|
+
node: node.argument,
|
|
278
|
+
messageId: "throwNumber"
|
|
279
|
+
});
|
|
280
|
+
return;
|
|
244
281
|
}
|
|
245
|
-
}
|
|
246
282
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
// Only suggest if it's a generic built-in error
|
|
250
|
-
if (['TypeError', 'RangeError'].includes(errorClassName)) {
|
|
283
|
+
// Check for throwing variables (identifiers)
|
|
284
|
+
if (node.argument.type === 'Identifier') {
|
|
251
285
|
context.report({
|
|
252
286
|
node: node.argument,
|
|
253
|
-
messageId: "
|
|
287
|
+
messageId: "throwVariable"
|
|
254
288
|
});
|
|
289
|
+
return;
|
|
255
290
|
}
|
|
256
291
|
}
|
|
257
292
|
},
|
|
@@ -269,13 +269,17 @@
|
|
|
269
269
|
|
|
270
270
|
### 📘 Rule C019 – Do not use `error` log level for non-critical issues
|
|
271
271
|
|
|
272
|
-
- **Objective**:
|
|
273
|
-
- **Details**:
|
|
274
|
-
-
|
|
275
|
-
-
|
|
276
|
-
- Use `
|
|
277
|
-
- Use `
|
|
278
|
-
-
|
|
272
|
+
- **Objective**: Prevent noisy logs and false alarms; ensure consistent and meaningful log levels across the system.
|
|
273
|
+
- **Details**:
|
|
274
|
+
- Reserve `error` for critical failures that require immediate attention or system intervention.
|
|
275
|
+
- Use `warn` for potential issues that may affect functionality but don’t crash the system (e.g., retryable errors).
|
|
276
|
+
- Use `info` for normal business events (e.g., login, order success, expected validation failures).
|
|
277
|
+
- Use `debug` for detailed troubleshooting information; avoid excessive debug logs in production.
|
|
278
|
+
- Avoid using `error` for:
|
|
279
|
+
- Expected business cases (e.g., wrong password, expired card).
|
|
280
|
+
- Normal validation failures.
|
|
281
|
+
- Temporary, recoverable conditions (e.g., network retry).
|
|
282
|
+
- Additional goal: Ensure **logs exist at the right places with the right severity level**, avoiding both over-logging and missing critical logs.
|
|
279
283
|
- **Applies to**: All languages
|
|
280
284
|
- **Tools**: Log linter / Custom rule
|
|
281
285
|
- **Principles**: CODE_QUALITY
|
package/package.json
CHANGED