@sun-asterisk/sunlint 1.1.8 → 1.2.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.
Files changed (73) hide show
  1. package/.sunlint.json +1 -1
  2. package/CHANGELOG.md +50 -1
  3. package/README.md +66 -4
  4. package/config/presets/all.json +125 -0
  5. package/config/presets/beginner.json +16 -8
  6. package/config/presets/ci.json +12 -4
  7. package/config/presets/maintainability.json +38 -0
  8. package/config/presets/performance.json +32 -0
  9. package/config/presets/quality.json +103 -0
  10. package/config/presets/recommended.json +36 -12
  11. package/config/presets/security.json +88 -0
  12. package/config/presets/strict.json +15 -5
  13. package/config/rules/rules-registry-generated.json +6312 -0
  14. package/config/rules-summary.json +1941 -0
  15. package/core/adapters/sunlint-rule-adapter.js +452 -0
  16. package/core/analysis-orchestrator.js +4 -4
  17. package/core/config-manager.js +28 -5
  18. package/core/rule-selection-service.js +52 -55
  19. package/docs/CONFIGURATION.md +111 -3
  20. package/docs/LANGUAGE-SPECIFIC-RULES.md +308 -0
  21. package/docs/README.md +3 -0
  22. package/docs/STANDARDIZED-CATEGORY-FILTERING.md +156 -0
  23. package/engines/heuristic-engine.js +16 -31
  24. package/origin-rules/common-en.md +1320 -0
  25. package/origin-rules/dart-en.md +289 -0
  26. package/origin-rules/java-en.md +60 -0
  27. package/origin-rules/kotlin-mobile-en.md +453 -0
  28. package/origin-rules/reactjs-en.md +102 -0
  29. package/origin-rules/security-en.md +1055 -0
  30. package/origin-rules/swift-en.md +449 -0
  31. package/origin-rules/typescript-en.md +136 -0
  32. package/package.json +6 -5
  33. package/scripts/copy-rules.js +86 -0
  34. package/rules/README.md +0 -252
  35. package/rules/common/C002_no_duplicate_code/analyzer.js +0 -65
  36. package/rules/common/C002_no_duplicate_code/config.json +0 -23
  37. package/rules/common/C003_no_vague_abbreviations/analyzer.js +0 -418
  38. package/rules/common/C003_no_vague_abbreviations/config.json +0 -35
  39. package/rules/common/C006_function_naming/analyzer.js +0 -349
  40. package/rules/common/C006_function_naming/config.json +0 -86
  41. package/rules/common/C010_limit_block_nesting/analyzer.js +0 -389
  42. package/rules/common/C013_no_dead_code/analyzer.js +0 -206
  43. package/rules/common/C014_dependency_injection/analyzer.js +0 -338
  44. package/rules/common/C017_constructor_logic/analyzer.js +0 -314
  45. package/rules/common/C019_log_level_usage/analyzer.js +0 -362
  46. package/rules/common/C019_log_level_usage/config.json +0 -121
  47. package/rules/common/C029_catch_block_logging/analyzer.js +0 -373
  48. package/rules/common/C029_catch_block_logging/config.json +0 -59
  49. package/rules/common/C031_validation_separation/analyzer.js +0 -186
  50. package/rules/common/C041_no_sensitive_hardcode/analyzer.js +0 -292
  51. package/rules/common/C042_boolean_name_prefix/analyzer.js +0 -300
  52. package/rules/common/C043_no_console_or_print/analyzer.js +0 -304
  53. package/rules/common/C047_no_duplicate_retry_logic/analyzer.js +0 -351
  54. package/rules/common/C075_explicit_return_types/analyzer.js +0 -103
  55. package/rules/common/C076_single_test_behavior/analyzer.js +0 -121
  56. package/rules/docs/C002_no_duplicate_code.md +0 -57
  57. package/rules/docs/C031_validation_separation.md +0 -72
  58. package/rules/index.js +0 -149
  59. package/rules/migration/converter.js +0 -385
  60. package/rules/migration/mapping.json +0 -164
  61. package/rules/security/S026_json_schema_validation/analyzer.js +0 -251
  62. package/rules/security/S026_json_schema_validation/config.json +0 -27
  63. package/rules/security/S027_no_hardcoded_secrets/analyzer.js +0 -263
  64. package/rules/security/S027_no_hardcoded_secrets/config.json +0 -29
  65. package/rules/security/S029_csrf_protection/analyzer.js +0 -264
  66. package/rules/tests/C002_no_duplicate_code.test.js +0 -50
  67. package/rules/universal/C010/generic.js +0 -0
  68. package/rules/universal/C010/tree-sitter-analyzer.js +0 -0
  69. package/rules/utils/ast-utils.js +0 -191
  70. package/rules/utils/base-analyzer.js +0 -98
  71. package/rules/utils/pattern-matchers.js +0 -239
  72. package/rules/utils/rule-helpers.js +0 -264
  73. package/rules/utils/severity-constants.js +0 -93
@@ -1,349 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const ts = require('typescript');
4
-
5
- class C006Analyzer {
6
- constructor() {
7
- this.ruleId = 'C006';
8
- this.ruleName = 'Function Naming Convention';
9
- this.description = 'Tên hàm phải là động từ/verb-noun pattern';
10
- }
11
-
12
- async analyze(files, language, config) {
13
- const violations = [];
14
-
15
- for (const filePath of files) {
16
- try {
17
- const fileContent = fs.readFileSync(filePath, 'utf8');
18
- const fileViolations = await this.analyzeFile(filePath, fileContent, language, config);
19
- violations.push(...fileViolations);
20
- } catch (error) {
21
- console.error(`Error analyzing file ${filePath}:`, error.message);
22
- }
23
- }
24
-
25
- return violations;
26
- }
27
-
28
- async analyzeFile(filePath, content, language, config) {
29
- switch (language) {
30
- case 'typescript':
31
- case 'javascript':
32
- return this.analyzeTypeScript(filePath, content, config);
33
- case 'dart':
34
- return this.analyzeDart(filePath, content, config);
35
- case 'kotlin':
36
- return this.analyzeKotlin(filePath, content, config);
37
- default:
38
- return [];
39
- }
40
- }
41
-
42
- async analyzeTypeScript(filePath, content, config) {
43
- const violations = [];
44
- const lines = content.split('\n');
45
-
46
- // Parse TypeScript/JavaScript code
47
- const sourceFile = ts.createSourceFile(
48
- filePath,
49
- content,
50
- ts.ScriptTarget.Latest,
51
- true
52
- );
53
-
54
- const visit = (node) => {
55
- // Check function declarations (skip declare statements without body)
56
- if (ts.isFunctionDeclaration(node) && node.name && node.body) {
57
- const functionName = node.name.text;
58
- const analysis = this.analyzeFunctionName(functionName);
59
-
60
- if (analysis.isViolation) {
61
- const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
62
- const column = sourceFile.getLineAndCharacterOfPosition(node.getStart()).character + 1;
63
- const lineText = lines[line - 1]?.trim() || '';
64
-
65
- violations.push({
66
- ruleId: this.ruleId,
67
- file: filePath,
68
- line,
69
- column,
70
- message: analysis.reason,
71
- severity: analysis.severity || 'warning',
72
- code: lineText,
73
- type: analysis.type,
74
- confidence: analysis.confidence || 0.8,
75
- suggestion: analysis.suggestion
76
- });
77
- }
78
- }
79
-
80
- // Check method declarations
81
- if (ts.isMethodDeclaration(node) && node.name) {
82
- const methodName = node.name.text;
83
- const analysis = this.analyzeFunctionName(methodName);
84
-
85
- if (analysis.isViolation) {
86
- const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
87
- const column = sourceFile.getLineAndCharacterOfPosition(node.getStart()).character + 1;
88
- const lineText = lines[line - 1]?.trim() || '';
89
-
90
- violations.push({
91
- ruleId: this.ruleId,
92
- file: filePath,
93
- line,
94
- column,
95
- message: analysis.reason,
96
- severity: analysis.severity || 'warning',
97
- code: lineText,
98
- type: analysis.type,
99
- confidence: analysis.confidence || 0.8,
100
- suggestion: analysis.suggestion
101
- });
102
- }
103
- }
104
-
105
- // Check arrow functions assigned to variables
106
- if (ts.isVariableDeclaration(node) && node.name && ts.isIdentifier(node.name) &&
107
- node.initializer && ts.isArrowFunction(node.initializer)) {
108
- const functionName = node.name.text;
109
- const analysis = this.analyzeFunctionName(functionName);
110
-
111
- if (analysis.isViolation) {
112
- const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
113
- const column = sourceFile.getLineAndCharacterOfPosition(node.getStart()).character + 1;
114
- const lineText = lines[line - 1]?.trim() || '';
115
-
116
- violations.push({
117
- ruleId: this.ruleId,
118
- file: filePath,
119
- line,
120
- column,
121
- message: analysis.reason,
122
- severity: analysis.severity || 'warning',
123
- code: lineText,
124
- type: analysis.type,
125
- confidence: analysis.confidence || 0.8,
126
- suggestion: analysis.suggestion
127
- });
128
- }
129
- }
130
-
131
- ts.forEachChild(node, visit);
132
- };
133
-
134
- visit(sourceFile);
135
- return violations;
136
- }
137
-
138
- async analyzeDart(filePath, content, config) {
139
- const violations = [];
140
- const lines = content.split('\n');
141
-
142
- // Pattern-based analysis for Dart function declarations
143
- const functionPattern = /^\s*(\w+\s+)?(\w+)\s*\([^)]*\)\s*\{?/;
144
-
145
- lines.forEach((line, index) => {
146
- const lineNumber = index + 1;
147
- const match = line.match(functionPattern);
148
-
149
- if (match) {
150
- const functionName = match[2];
151
-
152
- // Skip constructors, getters, setters, and built-in methods
153
- if (this.isSpecialFunction(functionName)) {
154
- return;
155
- }
156
-
157
- const analysis = this.analyzeFunctionName(functionName);
158
-
159
- if (analysis.isViolation) {
160
- violations.push({
161
- ruleId: this.ruleId,
162
- file: filePath,
163
- line: lineNumber,
164
- column: line.indexOf(functionName) + 1,
165
- message: analysis.reason,
166
- severity: analysis.severity || 'warning',
167
- code: line.trim(),
168
- type: analysis.type,
169
- confidence: analysis.confidence || 0.7,
170
- suggestion: analysis.suggestion
171
- });
172
- }
173
- }
174
- });
175
-
176
- return violations;
177
- }
178
-
179
- async analyzeKotlin(filePath, content, config) {
180
- const violations = [];
181
- const lines = content.split('\n');
182
-
183
- // Pattern-based analysis for Kotlin function declarations
184
- const functionPattern = /^\s*(?:fun|private\s+fun|public\s+fun|internal\s+fun|override\s+fun)\s+(\w+)\s*\(/;
185
-
186
- lines.forEach((line, index) => {
187
- const lineNumber = index + 1;
188
- const match = line.match(functionPattern);
189
-
190
- if (match) {
191
- const functionName = match[1];
192
-
193
- // Skip constructors and built-in methods
194
- if (this.isSpecialFunction(functionName)) {
195
- return;
196
- }
197
-
198
- const analysis = this.analyzeFunctionName(functionName);
199
-
200
- if (analysis.isViolation) {
201
- violations.push({
202
- ruleId: this.ruleId,
203
- file: filePath,
204
- line: lineNumber,
205
- column: line.indexOf(functionName) + 1,
206
- message: analysis.reason,
207
- severity: analysis.severity || 'warning',
208
- code: line.trim(),
209
- type: analysis.type,
210
- confidence: analysis.confidence || 0.7,
211
- suggestion: analysis.suggestion
212
- });
213
- }
214
- }
215
- });
216
-
217
- return violations;
218
- }
219
-
220
- analyzeFunctionName(functionName) {
221
- // Skip special functions
222
- if (this.isSpecialFunction(functionName)) {
223
- return { isViolation: false };
224
- }
225
-
226
- // Common verbs for function names
227
- const commonVerbs = [
228
- 'get', 'set', 'is', 'has', 'can', 'should', 'will', 'does',
229
- 'create', 'build', 'make', 'generate', 'construct',
230
- 'update', 'modify', 'change', 'edit', 'alter',
231
- 'delete', 'remove', 'destroy', 'clean', 'clear',
232
- 'load', 'save', 'fetch', 'retrieve', 'find', 'search',
233
- 'validate', 'verify', 'check', 'confirm', 'ensure',
234
- 'calculate', 'compute', 'process', 'handle', 'manage',
235
- 'send', 'receive', 'transmit', 'broadcast', 'emit',
236
- 'parse', 'format', 'transform', 'convert', 'map',
237
- 'filter', 'sort', 'group', 'merge', 'split',
238
- 'connect', 'disconnect', 'open', 'close', 'start', 'stop',
239
- 'show', 'hide', 'display', 'render', 'draw', 'paint',
240
- 'add', 'append', 'insert', 'push', 'pop', 'shift',
241
- 'test', 'debug', 'log', 'trace', 'monitor', 'watch',
242
- 'count', 'execute', 'perform', 'run', 'invoke', 'call',
243
- 'reset', 'initialize', 'setup', 'configure', 'prepare',
244
- 'refresh', 'restore', 'reload', 'retry', 'resume', 'redirect',
245
- 'select', 'toggle', 'switch', 'enable', 'disable', 'activate',
246
- 'expand', 'collapse', 'scroll', 'navigate', 'submit', 'cancel',
247
-
248
- // Based on user feedback - missing important verbs
249
- 'reopen', 'request', 'use', 'go',
250
-
251
- // Event handler prefixes
252
- 'on',
253
-
254
- // Common React/JS patterns that should be allowed as verbs
255
- 'count', // can be both noun and verb - when standalone should be allowed
256
- 'request', // can be both noun and verb - when standalone should be allowed
257
- 'process' // can be both noun and verb - when standalone should be allowed
258
- ];
259
-
260
- // Check if function name starts with a verb
261
- const startsWithVerb = commonVerbs.some(verb => {
262
- const verbPattern = new RegExp(`^${verb}[A-Z]?`, 'i');
263
- return verbPattern.test(functionName);
264
- });
265
-
266
- if (startsWithVerb) {
267
- return { isViolation: false };
268
- }
269
-
270
- // Check for camelCase pattern that might be verb-noun
271
- const camelCasePattern = /^[a-z][a-zA-Z0-9]*$/;
272
- const pascalCasePattern = /^[A-Z][a-zA-Z0-9]*$/;
273
-
274
- // PascalCase functions (likely React components) - different rules
275
- if (pascalCasePattern.test(functionName)) {
276
- // For React components, we can be more lenient but still suggest verb patterns where appropriate
277
- return {
278
- isViolation: true,
279
- reason: 'PascalCase function should be a React component or use verb-noun pattern',
280
- severity: 'info',
281
- type: 'react_component_naming',
282
- confidence: 0.5,
283
- suggestion: `If this is a React component, consider using descriptive names. If it's a function, use camelCase with verb-noun pattern.`
284
- };
285
- }
286
-
287
- if (!camelCasePattern.test(functionName)) {
288
- return {
289
- isViolation: true,
290
- reason: 'Function name should follow camelCase and verb-noun pattern',
291
- severity: 'warning',
292
- type: 'naming_convention',
293
- confidence: 0.9,
294
- suggestion: 'Use camelCase with verb-noun pattern (e.g., getUserData, validateInput)'
295
- };
296
- }
297
-
298
- // Check for noun-only patterns (likely violations)
299
- const nounOnlyPatterns = [
300
- /^[a-z]+$/, // simple lowercase (e.g., 'user', 'data')
301
- /^[a-z]+[A-Z][a-z]+$/, // simple camelCase noun (e.g., 'userData', 'userInfo')
302
- ];
303
-
304
- const isNounOnly = nounOnlyPatterns.some(pattern => pattern.test(functionName));
305
-
306
- if (isNounOnly) {
307
- return {
308
- isViolation: true,
309
- reason: 'Function name appears to be a noun - should start with a verb',
310
- severity: 'warning',
311
- type: 'missing_verb',
312
- confidence: 0.8,
313
- suggestion: `Consider renaming to use verb-noun pattern (e.g., get${functionName.charAt(0).toUpperCase() + functionName.slice(1)})`
314
- };
315
- }
316
-
317
- // If it's a complex name without obvious verb, flag as potential violation
318
- if (functionName.length > 3 && !functionName.match(/^[a-z]+[A-Z]/)) {
319
- return {
320
- isViolation: true,
321
- reason: 'Function name should follow verb-noun pattern for clarity',
322
- severity: 'info',
323
- type: 'unclear_naming',
324
- confidence: 0.6,
325
- suggestion: 'Consider using verb-noun pattern for better readability'
326
- };
327
- }
328
-
329
- return { isViolation: false };
330
- }
331
-
332
- isSpecialFunction(name) {
333
- const specialFunctions = [
334
- 'constructor', 'toString', 'valueOf', 'toJSON',
335
- 'main', 'init', 'setup', 'teardown', 'build',
336
- 'onCreate', 'onDestroy', 'onStart', 'onStop',
337
- 'onPause', 'onResume', 'onSaveInstanceState',
338
- 'equals', 'hashCode', 'compareTo', 'clone',
339
- 'finalize', 'notify', 'notifyAll', 'wait'
340
- ];
341
-
342
- return specialFunctions.includes(name) ||
343
- name.startsWith('_') ||
344
- name.startsWith('$');
345
- // Removed: || name.match(/^[A-Z]/); // Allow checking React components
346
- }
347
- }
348
-
349
- module.exports = new C006Analyzer();
@@ -1,86 +0,0 @@
1
- {
2
- "ruleId": "C006",
3
- "name": "Function Naming Convention",
4
- "description": "Tên hàm phải là động từ/verb-noun pattern",
5
- "category": "naming",
6
- "severity": "warning",
7
- "languages": ["typescript", "dart", "kotlin"],
8
- "version": "1.0.0",
9
- "status": "activated",
10
- "tags": ["naming", "convention", "readability"],
11
- "config": {
12
- "commonVerbs": [
13
- "get", "set", "is", "has", "can", "should", "will",
14
- "create", "build", "make", "generate", "construct",
15
- "update", "modify", "change", "edit", "alter",
16
- "delete", "remove", "destroy", "clean", "clear",
17
- "load", "save", "fetch", "retrieve", "find", "search",
18
- "validate", "verify", "check", "confirm", "ensure",
19
- "calculate", "compute", "process", "handle", "manage",
20
- "send", "receive", "transmit", "broadcast", "emit",
21
- "parse", "format", "transform", "convert", "map",
22
- "filter", "sort", "group", "merge", "split",
23
- "connect", "disconnect", "open", "close", "start", "stop",
24
- "show", "hide", "display", "render", "draw", "paint",
25
- "add", "append", "insert", "push", "pop", "shift",
26
- "test", "debug", "log", "trace", "monitor", "watch"
27
- ],
28
- "specialFunctions": [
29
- "constructor", "toString", "valueOf", "toJSON",
30
- "main", "init", "setup", "teardown", "build",
31
- "onCreate", "onDestroy", "onStart", "onStop",
32
- "onPause", "onResume", "onSaveInstanceState",
33
- "equals", "hashCode", "compareTo", "clone",
34
- "finalize", "notify", "notifyAll", "wait"
35
- ],
36
- "patterns": {
37
- "camelCase": "^[a-z][a-zA-Z0-9]*$",
38
- "verbNoun": "^(get|set|is|has|can|create|update|delete|load|save|find|validate|process|handle|calculate|send|receive|parse|format|transform|filter|sort|connect|show|hide|add|remove)[A-Z][a-zA-Z0-9]*$"
39
- }
40
- },
41
- "examples": {
42
- "violations": [
43
- {
44
- "language": "typescript",
45
- "code": "function userData() { return user.data; }",
46
- "reason": "Function name is a noun - should start with a verb like 'getUserData'"
47
- },
48
- {
49
- "language": "typescript",
50
- "code": "function calculation() { return x + y; }",
51
- "reason": "Should use verb-noun pattern like 'calculateSum' or 'performCalculation'"
52
- },
53
- {
54
- "language": "dart",
55
- "code": "String userInfo() { return 'info'; }",
56
- "reason": "Function name should start with verb like 'getUserInfo'"
57
- }
58
- ],
59
- "valid": [
60
- {
61
- "language": "typescript",
62
- "code": "function getUserData() { return user.data; }",
63
- "reason": "Follows verb-noun pattern"
64
- },
65
- {
66
- "language": "typescript",
67
- "code": "function isValid() { return true; }",
68
- "reason": "Starts with verb 'is'"
69
- },
70
- {
71
- "language": "dart",
72
- "code": "bool hasPermission() { return true; }",
73
- "reason": "Starts with verb 'has'"
74
- }
75
- ]
76
- },
77
- "fixes": {
78
- "autoFixable": false,
79
- "suggestions": [
80
- "Start function names with verbs (get, set, is, has, create, update, etc.)",
81
- "Use verb-noun pattern for clarity (e.g., getUserData, validateInput)",
82
- "Avoid noun-only function names",
83
- "Use camelCase convention"
84
- ]
85
- }
86
- }