@sun-asterisk/sunlint 1.3.47 → 1.3.49
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/config/rules/rules-registry-generated.json +1717 -282
- package/core/architecture-integration.js +57 -15
- package/core/cli-action-handler.js +51 -36
- package/core/config-manager.js +6 -0
- package/core/config-merger.js +33 -0
- package/core/config-validator.js +37 -2
- package/core/file-targeting-service.js +148 -15
- package/core/init-command.js +118 -70
- package/core/output-service.js +12 -3
- package/core/project-detector.js +517 -0
- package/core/scoring-service.js +12 -6
- package/core/summary-report-service.js +9 -4
- package/core/tui-select.js +245 -0
- package/engines/arch-detect/rules/layered/l001-presentation-layer.js +7 -15
- package/engines/arch-detect/rules/layered/l002-business-layer.js +7 -15
- package/engines/arch-detect/rules/layered/l003-data-layer.js +7 -15
- package/engines/arch-detect/rules/layered/l004-model-layer.js +7 -15
- package/engines/arch-detect/rules/layered/l005-layer-separation.js +22 -2
- package/engines/arch-detect/rules/layered/l006-dependency-direction.js +8 -5
- package/engines/arch-detect/rules/modular/m005-no-deep-imports.js +67 -29
- package/engines/arch-detect/rules/presentation/pr001-view-layer.js +16 -9
- package/engines/arch-detect/rules/presentation/pr006-router-layer.js +33 -8
- package/engines/arch-detect/rules/presentation/pr007-interactor-layer.js +35 -6
- package/engines/arch-detect/rules/project-scanner/ps003-framework-detection.js +56 -10
- package/engines/impact/cli.js +54 -39
- package/engines/impact/config/default-config.js +105 -5
- package/engines/impact/core/impact-analyzer.js +12 -15
- package/engines/impact/core/utils/gitignore-parser.js +123 -0
- package/engines/impact/core/utils/method-call-graph.js +272 -87
- package/origin-rules/dart-en.md +1 -1
- package/origin-rules/go-en.md +231 -0
- package/origin-rules/php-en.md +107 -0
- package/origin-rules/python-en.md +113 -0
- package/origin-rules/ruby-en.md +607 -0
- package/package.json +1 -1
- package/scripts/copy-arch-detect.js +5 -1
- package/scripts/copy-impact-analyzer.js +5 -1
- package/scripts/generate-rules-registry.js +30 -14
- package/skill-assets/sunlint-code-quality/SKILL.md +3 -2
- package/skill-assets/sunlint-code-quality/rules/dart/C006-verb-noun-functions.md +45 -0
- package/skill-assets/sunlint-code-quality/rules/dart/C013-no-dead-code.md +53 -0
- package/skill-assets/sunlint-code-quality/rules/dart/C014-dependency-injection.md +92 -0
- package/skill-assets/sunlint-code-quality/rules/dart/C017-no-constructor-logic.md +62 -0
- package/skill-assets/sunlint-code-quality/rules/dart/C018-generic-errors.md +57 -0
- package/skill-assets/sunlint-code-quality/rules/dart/C019-error-log-level.md +50 -0
- package/skill-assets/sunlint-code-quality/rules/dart/C020-no-unused-imports.md +46 -0
- package/skill-assets/sunlint-code-quality/rules/dart/C022-no-unused-variables.md +50 -0
- package/skill-assets/sunlint-code-quality/rules/dart/C023-no-duplicate-names.md +56 -0
- package/skill-assets/sunlint-code-quality/rules/dart/C024-centralize-constants.md +75 -0
- package/skill-assets/sunlint-code-quality/rules/dart/C029-catch-log-root-cause.md +53 -0
- package/skill-assets/sunlint-code-quality/rules/dart/C030-custom-error-classes.md +86 -0
- package/skill-assets/sunlint-code-quality/rules/dart/C033-separate-data-access.md +90 -0
- package/skill-assets/sunlint-code-quality/rules/dart/C035-error-context-logging.md +62 -0
- package/skill-assets/sunlint-code-quality/rules/dart/C041-no-hardcoded-secrets.md +75 -0
- package/skill-assets/sunlint-code-quality/rules/dart/C042-boolean-naming.md +73 -0
- package/skill-assets/sunlint-code-quality/rules/dart/C052-widget-parsing.md +84 -0
- package/skill-assets/sunlint-code-quality/rules/dart/C060-superclass-logic.md +91 -0
- package/skill-assets/sunlint-code-quality/rules/dart/C067-no-hardcoded-config.md +108 -0
- package/skill-assets/sunlint-code-quality/rules/go/G001-explicit-error-handling.md +53 -0
- package/skill-assets/sunlint-code-quality/rules/go/G002-context-first-argument.md +44 -0
- package/skill-assets/sunlint-code-quality/rules/go/G003-receiver-consistency.md +38 -0
- package/skill-assets/sunlint-code-quality/rules/go/G004-avoid-panic.md +49 -0
- package/skill-assets/sunlint-code-quality/rules/go/G005-goroutine-leak-prevention.md +49 -0
- package/skill-assets/sunlint-code-quality/rules/go/G006-interface-consumer-definition.md +45 -0
- package/skill-assets/sunlint-code-quality/rules/go/GN001-gin-binding-validation.md +57 -0
- package/skill-assets/sunlint-code-quality/rules/go/GN002-gin-error-response.md +48 -0
- package/skill-assets/sunlint-code-quality/rules/go/GN003-graceful-shutdown.md +57 -0
- package/skill-assets/sunlint-code-quality/rules/go/GN004-gin-route-logical-grouping.md +54 -0
- package/skill-assets/sunlint-code-quality/rules/go-gin/AGENTS.md +149 -0
- package/skill-assets/sunlint-code-quality/rules/go-gin/GN001-abort-after-response.md +75 -0
- package/skill-assets/sunlint-code-quality/rules/go-gin/GN002-request-context.md +64 -0
- package/skill-assets/sunlint-code-quality/rules/go-gin/GN003-bind-error-handling.md +70 -0
- package/skill-assets/sunlint-code-quality/rules/go-gin/GN004-dependency-injection.md +78 -0
- package/skill-assets/sunlint-code-quality/rules/go-gin/GN005-route-groups-middleware.md +71 -0
- package/skill-assets/sunlint-code-quality/rules/go-gin/GN006-http-status-codes.md +91 -0
- package/skill-assets/sunlint-code-quality/rules/go-gin/GN007-release-mode.md +64 -0
- package/skill-assets/sunlint-code-quality/rules/go-gin/GN008-struct-validation-tags.md +90 -0
- package/skill-assets/sunlint-code-quality/rules/go-gin/GN009-recovery-middleware.md +68 -0
- package/skill-assets/sunlint-code-quality/rules/go-gin/GN010-context-scope.md +68 -0
- package/skill-assets/sunlint-code-quality/rules/go-gin/GN011-middleware-concerns.md +92 -0
- package/skill-assets/sunlint-code-quality/rules/go-gin/GN012-no-log-sensitive.md +84 -0
- package/skill-assets/sunlint-code-quality/rules/java/J001-try-with-resources.md +86 -0
- package/skill-assets/sunlint-code-quality/rules/java/J002-equals-and-hashcode.md +88 -0
- package/skill-assets/sunlint-code-quality/rules/java/J003-string-comparison.md +72 -0
- package/skill-assets/sunlint-code-quality/rules/java/J004-use-java-time.md +91 -0
- package/skill-assets/sunlint-code-quality/rules/java/J005-no-print-stack-trace.md +80 -0
- package/skill-assets/sunlint-code-quality/rules/java/J006-no-system-println.md +89 -0
- package/skill-assets/sunlint-code-quality/rules/java/J007-proper-logger.md +91 -0
- package/skill-assets/sunlint-code-quality/rules/java/J008-thread-safe-singleton.md +119 -0
- package/skill-assets/sunlint-code-quality/rules/java/J009-utility-class-constructor.md +82 -0
- package/skill-assets/sunlint-code-quality/rules/java/J010-preserve-stack-trace.md +119 -0
- package/skill-assets/sunlint-code-quality/rules/java/J011-null-safe-compare.md +88 -0
- package/skill-assets/sunlint-code-quality/rules/java/J012-use-enum-collections.md +104 -0
- package/skill-assets/sunlint-code-quality/rules/java/J013-return-empty-not-null.md +102 -0
- package/skill-assets/sunlint-code-quality/rules/java/J014-hardcoded-crypto-key.md +108 -0
- package/skill-assets/sunlint-code-quality/rules/java/J015-optional-instead-of-null.md +109 -0
- package/skill-assets/sunlint-code-quality/rules/php-laravel/AGENTS.md +124 -0
- package/skill-assets/sunlint-code-quality/rules/php-laravel/LV001-form-request-validation.md +64 -0
- package/skill-assets/sunlint-code-quality/rules/php-laravel/LV002-eager-load-no-n-plus-1.md +58 -0
- package/skill-assets/sunlint-code-quality/rules/php-laravel/LV003-config-not-env.md +54 -0
- package/skill-assets/sunlint-code-quality/rules/php-laravel/LV004-fillable-mass-assignment.md +51 -0
- package/skill-assets/sunlint-code-quality/rules/php-laravel/LV005-policies-gates-authorization.md +71 -0
- package/skill-assets/sunlint-code-quality/rules/php-laravel/LV006-queue-heavy-tasks.md +68 -0
- package/skill-assets/sunlint-code-quality/rules/php-laravel/LV007-hash-passwords.md +51 -0
- package/skill-assets/sunlint-code-quality/rules/php-laravel/LV008-route-model-binding.md +67 -0
- package/skill-assets/sunlint-code-quality/rules/php-laravel/LV009-api-resources.md +72 -0
- package/skill-assets/sunlint-code-quality/rules/php-laravel/LV010-chunk-large-datasets.md +58 -0
- package/skill-assets/sunlint-code-quality/rules/php-laravel/LV011-db-transactions.md +73 -0
- package/skill-assets/sunlint-code-quality/rules/php-laravel/LV012-service-layer.md +78 -0
- package/skill-assets/sunlint-code-quality/rules/php-laravel/LV013-testing-factories.md +75 -0
- package/skill-assets/sunlint-code-quality/rules/php-laravel/LV014-service-container.md +61 -0
- package/skill-assets/sunlint-code-quality/rules/python/P001-mutable-default-argument.md +55 -0
- package/skill-assets/sunlint-code-quality/rules/python/P002-specify-file-encoding.md +45 -0
- package/skill-assets/sunlint-code-quality/rules/python/P003-context-manager-for-resources.md +54 -0
- package/skill-assets/sunlint-code-quality/rules/python/P004-no-bare-except.md +65 -0
- package/skill-assets/sunlint-code-quality/rules/python/P005-use-isinstance.md +60 -0
- package/skill-assets/sunlint-code-quality/rules/python/P006-timezone-aware-datetime.md +58 -0
- package/skill-assets/sunlint-code-quality/rules/python/P007-use-pathlib.md +62 -0
- package/skill-assets/sunlint-code-quality/rules/python/P008-no-wildcard-import.md +52 -0
- package/skill-assets/sunlint-code-quality/rules/python/P009-logging-lazy-format.md +50 -0
- package/skill-assets/sunlint-code-quality/rules/python/P010-exception-chaining.md +57 -0
- package/skill-assets/sunlint-code-quality/rules/python/P011-subprocess-check.md +59 -0
- package/skill-assets/sunlint-code-quality/rules/python/P012-requests-timeout.md +70 -0
- package/skill-assets/sunlint-code-quality/rules/python/P013-no-global-statement.md +73 -0
- package/skill-assets/sunlint-code-quality/rules/python/P014-no-modify-collection-while-iterating.md +66 -0
- package/skill-assets/sunlint-code-quality/rules/python/P015-prefer-fstrings.md +61 -0
- package/skill-assets/sunlint-code-quality/rules/ruby-rails/AGENTS.md +121 -0
- package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR001-strong-parameters.md +55 -0
- package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR002-eager-load-includes.md +51 -0
- package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR003-service-objects.md +99 -0
- package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR004-active-job-background.md +67 -0
- package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR005-pagination.md +53 -0
- package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR006-find-each-batches.md +53 -0
- package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR007-http-status-codes.md +76 -0
- package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR008-before-action-auth.md +77 -0
- package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR009-rails-credentials.md +61 -0
- package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR010-scopes.md +57 -0
- package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR011-counter-cache.md +59 -0
- package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR012-render-json-status.md +42 -0
- package/skill-assets/sunlint-code-quality/rules/swift/C006-verb-noun-functions.md +37 -0
- package/skill-assets/sunlint-code-quality/rules/swift/C013-no-dead-code.md +55 -0
- package/skill-assets/sunlint-code-quality/rules/swift/C014-dependency-injection.md +69 -0
- package/skill-assets/sunlint-code-quality/rules/swift/C017-no-constructor-logic.md +66 -0
- package/skill-assets/sunlint-code-quality/rules/swift/C018-generic-errors.md +64 -0
- package/skill-assets/sunlint-code-quality/rules/swift/C019-error-log-level.md +64 -0
- package/skill-assets/sunlint-code-quality/rules/swift/C020-no-unused-imports.md +47 -0
- package/skill-assets/sunlint-code-quality/rules/swift/C022-no-unused-variables.md +46 -0
- package/skill-assets/sunlint-code-quality/rules/swift/C023-no-duplicate-names.md +55 -0
- package/skill-assets/sunlint-code-quality/rules/swift/C024-centralize-constants.md +68 -0
- package/skill-assets/sunlint-code-quality/rules/swift/C029-catch-log-root-cause.md +69 -0
- package/skill-assets/sunlint-code-quality/rules/swift/C030-custom-error-classes.md +77 -0
- package/skill-assets/sunlint-code-quality/rules/swift/C033-separate-data-access.md +89 -0
- package/skill-assets/sunlint-code-quality/rules/swift/C035-error-context-logging.md +66 -0
- package/skill-assets/sunlint-code-quality/rules/swift/C041-no-hardcoded-secrets.md +65 -0
- package/skill-assets/sunlint-code-quality/rules/swift/C042-boolean-naming.md +60 -0
- package/skill-assets/sunlint-code-quality/rules/swift/C052-controller-parsing.md +67 -0
- package/skill-assets/sunlint-code-quality/rules/swift/C060-superclass-logic.md +95 -0
- package/skill-assets/sunlint-code-quality/rules/swift/C067-no-hardcoded-config.md +80 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S003-sql-injection.md +65 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S004-no-log-credentials.md +67 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S005-server-authorization.md +73 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S006-default-credentials.md +76 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S007-output-encoding.md +96 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S009-approved-crypto.md +86 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S010-csprng.md +71 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S011-insecure-deserialization.md +74 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S012-secrets-management.md +81 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S013-tls-connections.md +67 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S017-parameterized-queries.md +86 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S019-session-management.md +131 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S020-kvc-injection.md +91 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S025-input-validation.md +125 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S029-brute-force-protection.md +120 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S036-path-traversal.md +102 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S039-tls-certificate-validation.md +109 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S041-logout-invalidation.md +103 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S043-password-hashing.md +116 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S044-critical-changes-reauth.md +145 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S045-debug-info-exposure.md +116 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S046-unvalidated-redirect.md +140 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S051-token-expiry.md +134 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S053-jwt-validation.md +139 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S059-background-snapshot-protection.md +113 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S060-data-protection-api.md +106 -0
- package/skill-assets/sunlint-code-quality/rules/swift/S061-jailbreak-detection.md +132 -0
|
@@ -16,29 +16,45 @@ try {
|
|
|
16
16
|
const parser = new SimpleRuleParser();
|
|
17
17
|
const originRulesDir = path.join(__dirname, '..', 'origin-rules');
|
|
18
18
|
const targetPath = path.join(__dirname, '..', 'config', 'rules', 'rules-registry-generated.json');
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
console.log(`Source: ${originRulesDir}`);
|
|
21
21
|
console.log(`Target: ${targetPath}`);
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
// Parse all rules from origin-rules
|
|
24
24
|
const allRules = parser.parseAllRules(originRulesDir);
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
if (allRules.length === 0) {
|
|
27
27
|
console.error('❌ No rules found in origin-rules directory');
|
|
28
28
|
process.exit(1);
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
// Convert to registry format
|
|
32
32
|
const registry = {
|
|
33
33
|
rules: {}
|
|
34
34
|
};
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
allRules.forEach(rule => {
|
|
37
37
|
if (rule.id) {
|
|
38
|
+
let category = rule.category || 'Common';
|
|
39
|
+
const id = rule.id;
|
|
40
|
+
|
|
41
|
+
// Smart category detection based on prefix
|
|
42
|
+
if (id.startsWith('S')) {
|
|
43
|
+
category = 'Security';
|
|
44
|
+
} else if (id.startsWith('C')) {
|
|
45
|
+
category = 'Common';
|
|
46
|
+
} else if (['RB', 'G', 'GN', 'J', 'PY', 'P'].some(p => id.startsWith(p))) {
|
|
47
|
+
category = 'Backend';
|
|
48
|
+
} else if (['T', 'R'].some(p => id.startsWith(p))) {
|
|
49
|
+
category = 'Frontend';
|
|
50
|
+
} else if (['K', 'SW', 'D'].some(p => id.startsWith(p))) {
|
|
51
|
+
category = 'Mobile';
|
|
52
|
+
}
|
|
53
|
+
|
|
38
54
|
registry.rules[rule.id] = {
|
|
39
55
|
name: rule.title || `${rule.id} Rule`,
|
|
40
56
|
description: rule.description || 'No description available',
|
|
41
|
-
category:
|
|
57
|
+
category: category,
|
|
42
58
|
severity: rule.severity || 'major',
|
|
43
59
|
languages: rule.language ? [rule.language] : ['All languages'],
|
|
44
60
|
version: rule.version || '1.0.0',
|
|
@@ -50,37 +66,37 @@ try {
|
|
|
50
66
|
};
|
|
51
67
|
}
|
|
52
68
|
});
|
|
53
|
-
|
|
69
|
+
|
|
54
70
|
// Ensure target directory exists
|
|
55
71
|
const targetDir = path.dirname(targetPath);
|
|
56
72
|
if (!fs.existsSync(targetDir)) {
|
|
57
73
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
58
74
|
}
|
|
59
|
-
|
|
75
|
+
|
|
60
76
|
// Write registry file
|
|
61
77
|
fs.writeFileSync(targetPath, JSON.stringify(registry, null, 2), 'utf8');
|
|
62
|
-
|
|
78
|
+
|
|
63
79
|
const rulesCount = Object.keys(registry.rules).length;
|
|
64
80
|
const fileSize = (fs.statSync(targetPath).size / 1024).toFixed(1);
|
|
65
|
-
|
|
81
|
+
|
|
66
82
|
console.log(`✅ Generated registry with ${rulesCount} rules`);
|
|
67
83
|
console.log(`📁 File: ${targetPath} (${fileSize} KB)`);
|
|
68
84
|
console.log('');
|
|
69
85
|
console.log('📊 Rules by category:');
|
|
70
|
-
|
|
86
|
+
|
|
71
87
|
// Stats by category
|
|
72
88
|
const categories = {};
|
|
73
89
|
Object.values(registry.rules).forEach(rule => {
|
|
74
90
|
const cat = rule.category || 'unknown';
|
|
75
91
|
categories[cat] = (categories[cat] || 0) + 1;
|
|
76
92
|
});
|
|
77
|
-
|
|
93
|
+
|
|
78
94
|
Object.entries(categories)
|
|
79
|
-
.sort(([,a], [,b]) => b - a)
|
|
95
|
+
.sort(([, a], [, b]) => b - a)
|
|
80
96
|
.forEach(([category, count]) => {
|
|
81
97
|
console.log(` ${category}: ${count} rules`);
|
|
82
98
|
});
|
|
83
|
-
|
|
99
|
+
|
|
84
100
|
} catch (error) {
|
|
85
101
|
console.error('❌ Error generating registry:', error.message);
|
|
86
102
|
console.error(error.stack);
|
|
@@ -9,7 +9,7 @@ metadata:
|
|
|
9
9
|
|
|
10
10
|
# SunLint Code Quality & Security Standards
|
|
11
11
|
|
|
12
|
-
Comprehensive code quality and security optimization guide for all projects, maintained by Sun* Engineering Excellence team. Contains **
|
|
12
|
+
Comprehensive code quality and security optimization guide for all projects, maintained by Sun* Engineering Excellence team. Contains **75 rules** across **7 priority categories**, organized by impact to guide automated code review and generation.
|
|
13
13
|
|
|
14
14
|
## When to Apply
|
|
15
15
|
|
|
@@ -31,6 +31,7 @@ Reference these guidelines when:
|
|
|
31
31
|
| 4 | Security - Cryptography & TLS | **HIGH** | 8 | `S0xx` |
|
|
32
32
|
| 5 | Security - Data Protection | **HIGH** | 10 | `S0xx` |
|
|
33
33
|
| 6 | Security - Logging & Monitoring | **MEDIUM** | 6 | `S0xx` |
|
|
34
|
+
| 7 | Language Specific (Go/Gin) | **CRITICAL/HIGH** | 10 | `Gxx`/`GNxx` |
|
|
34
35
|
|
|
35
36
|
---
|
|
36
37
|
|
|
@@ -173,4 +174,4 @@ For the complete guide with all rules expanded: `AGENTS.md`
|
|
|
173
174
|
|
|
174
175
|
---
|
|
175
176
|
|
|
176
|
-
**Last Updated**:
|
|
177
|
+
**Last Updated**: February 2026 | **Version**: 2.4.1 | **Maintainer**: Sun* Engineering Excellence
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Function Names Verb-Noun
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: makes code self-documenting
|
|
5
|
+
tags: naming, functions, readability, conventions, quality
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Function Names Verb-Noun
|
|
9
|
+
|
|
10
|
+
Functions do things. Action verbs make purpose clear.
|
|
11
|
+
|
|
12
|
+
**Incorrect (vague names):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
User user() { } // Noun only
|
|
16
|
+
void userData() { } // Noun only
|
|
17
|
+
void doSomething() { } // Vague
|
|
18
|
+
void handleStuff() { } // Vague
|
|
19
|
+
void manager() { } // Noun only
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Correct (action verbs):**
|
|
23
|
+
|
|
24
|
+
```dart
|
|
25
|
+
Future<User> getUser() async { }
|
|
26
|
+
void createUserAccount() { }
|
|
27
|
+
bool validateEmailFormat(String email) { }
|
|
28
|
+
double calculateTotalPrice(List<Item> items) { }
|
|
29
|
+
Future<void> sendConfirmationEmail(String email) async { }
|
|
30
|
+
String convertCurrencyToVnd(double amount) { }
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Verb categories:**
|
|
34
|
+
|
|
35
|
+
| Category | Verbs |
|
|
36
|
+
|----------|-------|
|
|
37
|
+
| Retrieval | `get`, `fetch`, `find`, `load`, `query` |
|
|
38
|
+
| Creation | `create`, `build`, `make`, `generate` |
|
|
39
|
+
| Modification | `set`, `update`, `modify`, `change` |
|
|
40
|
+
| Deletion | `delete`, `remove`, `destroy`, `clear` |
|
|
41
|
+
| Validation | `validate`, `verify`, `check`, `ensure` |
|
|
42
|
+
| Computation | `calculate`, `compute`, `parse`, `format` |
|
|
43
|
+
| Boolean | `is`, `has`, `can`, `should`, `will` |
|
|
44
|
+
|
|
45
|
+
**Tools:** PR review, dart_analyzer
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Do Not Use Dead Code
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: reduces codebase noise and app size
|
|
5
|
+
tags: dead-code, cleanup, maintenance, quality
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Do Not Use Dead Code
|
|
9
|
+
|
|
10
|
+
Dead code confuses readers and increases app size. Git history preserves deleted code.
|
|
11
|
+
|
|
12
|
+
**Incorrect (keeping dead code):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
void processOrder(Order order) {
|
|
16
|
+
// Old implementation - keeping for reference
|
|
17
|
+
// final total = order.items.fold(0.0, (sum, item) {
|
|
18
|
+
// return sum + item.price * item.quantity;
|
|
19
|
+
// });
|
|
20
|
+
|
|
21
|
+
final total = calculateTotal(order);
|
|
22
|
+
return total;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Unused function - someone might need it later
|
|
26
|
+
double legacyCalculation(List<Item> items) {
|
|
27
|
+
return items.length * 10.0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
import 'package:app/utils/unused_helper.dart'; // Never used
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Correct (clean code):**
|
|
34
|
+
|
|
35
|
+
```dart
|
|
36
|
+
void processOrder(Order order) {
|
|
37
|
+
final total = calculateTotal(order);
|
|
38
|
+
return total;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Delete unused functions - git history preserves them
|
|
42
|
+
// Delete commented code - git history preserves it
|
|
43
|
+
// Remove unused imports
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Types of dead code:**
|
|
47
|
+
- Commented-out code blocks
|
|
48
|
+
- Unused functions/classes/mixins
|
|
49
|
+
- Unused imports
|
|
50
|
+
- Unreachable code (after `return`/`throw`)
|
|
51
|
+
- Unused variables and parameters
|
|
52
|
+
|
|
53
|
+
**Tools:** dart analyze, `unused_import` lint rule, Code Review
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Dependency Injection
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: enables testability and loose coupling
|
|
5
|
+
tags: dependency-injection, testing, coupling, architecture, quality
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Dependency Injection
|
|
9
|
+
|
|
10
|
+
Direct instantiation creates tight coupling, making testing difficult and changes risky. DI enables mockability, replaceability, and testability.
|
|
11
|
+
|
|
12
|
+
**Incorrect (hardcoded dependencies):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
class OrderService {
|
|
16
|
+
final _db = DatabaseHelper(); // Hardcoded dependency
|
|
17
|
+
final _mailer = EmailService(); // Hardcoded dependency
|
|
18
|
+
|
|
19
|
+
Future<Order> createOrder(OrderData data) async {
|
|
20
|
+
final order = await _db.insert('orders', data.toMap());
|
|
21
|
+
await _mailer.send(data.email, 'Order created');
|
|
22
|
+
return order;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Correct (injected dependencies):**
|
|
28
|
+
|
|
29
|
+
```dart
|
|
30
|
+
abstract class IDatabase {
|
|
31
|
+
Future<Map<String, dynamic>> insert(String table, Map<String, dynamic> data);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
abstract class IMailer {
|
|
35
|
+
Future<void> send(String to, String message);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
class OrderService {
|
|
39
|
+
OrderService({
|
|
40
|
+
required IDatabase db,
|
|
41
|
+
required IMailer mailer,
|
|
42
|
+
}) : _db = db,
|
|
43
|
+
_mailer = mailer;
|
|
44
|
+
|
|
45
|
+
final IDatabase _db;
|
|
46
|
+
final IMailer _mailer;
|
|
47
|
+
|
|
48
|
+
Future<Order> createOrder(OrderData data) async {
|
|
49
|
+
final order = await _db.insert('orders', data.toMap());
|
|
50
|
+
await _mailer.send(data.email, 'Order created');
|
|
51
|
+
return order;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Usage - production
|
|
56
|
+
final service = OrderService(
|
|
57
|
+
db: SqliteDatabase(path: dbPath),
|
|
58
|
+
mailer: SmtpMailer(host: smtpHost),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// Usage - testing
|
|
62
|
+
final service = OrderService(
|
|
63
|
+
db: MockDatabase(),
|
|
64
|
+
mailer: MockMailer(),
|
|
65
|
+
);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**With get_it or injectable:**
|
|
69
|
+
|
|
70
|
+
```dart
|
|
71
|
+
// Using get_it
|
|
72
|
+
@injectable
|
|
73
|
+
class OrderService {
|
|
74
|
+
OrderService(this._db, this._mailer);
|
|
75
|
+
|
|
76
|
+
final IDatabase _db;
|
|
77
|
+
final IMailer _mailer;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Register
|
|
81
|
+
GetIt.instance.registerFactory<OrderService>(
|
|
82
|
+
() => OrderService(GetIt.instance(), GetIt.instance()),
|
|
83
|
+
);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Benefits:**
|
|
87
|
+
- Easy mocking for unit tests
|
|
88
|
+
- Swappable implementations
|
|
89
|
+
- Clear dependencies visible in constructor
|
|
90
|
+
- Supports interface-based design
|
|
91
|
+
|
|
92
|
+
**Tools:** get_it, injectable, mockito, mocktail
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: No Business Logic In Constructors
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: ensures predictable object initialization
|
|
5
|
+
tags: constructor, initialization, side-effects, patterns, quality
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## No Business Logic In Constructors
|
|
9
|
+
|
|
10
|
+
Constructors should only initialize state. Side effects in constructors are unexpected and make testing difficult.
|
|
11
|
+
|
|
12
|
+
**Incorrect (logic in constructor):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
class UserRepository {
|
|
16
|
+
UserRepository(String configPath) {
|
|
17
|
+
// BAD: File I/O in constructor
|
|
18
|
+
final file = File(configPath).readAsStringSync();
|
|
19
|
+
final config = jsonDecode(file) as Map<String, dynamic>;
|
|
20
|
+
|
|
21
|
+
// BAD: Network call in constructor
|
|
22
|
+
_client = HttpClient()..open('GET', config['apiUrl'] as String, 80, '/init');
|
|
23
|
+
|
|
24
|
+
// BAD: Mutation side-effects
|
|
25
|
+
_cache.clear();
|
|
26
|
+
|
|
27
|
+
// BAD: Logging with side effects
|
|
28
|
+
print('UserRepository initialized');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Correct (factory method for async / complex init):**
|
|
34
|
+
|
|
35
|
+
```dart
|
|
36
|
+
class UserRepository {
|
|
37
|
+
UserRepository({
|
|
38
|
+
required HttpClient client,
|
|
39
|
+
required AppConfig config,
|
|
40
|
+
}) : _client = client, // Only assignments
|
|
41
|
+
_config = config;
|
|
42
|
+
|
|
43
|
+
final HttpClient _client;
|
|
44
|
+
final AppConfig _config;
|
|
45
|
+
|
|
46
|
+
// Factory method for complex initialization
|
|
47
|
+
static Future<UserRepository> create(String configPath) async {
|
|
48
|
+
final file = await File(configPath).readAsString();
|
|
49
|
+
final config = AppConfig.fromJson(jsonDecode(file) as Map<String, dynamic>);
|
|
50
|
+
|
|
51
|
+
final client = HttpClient();
|
|
52
|
+
await client.initialize(config.apiUrl);
|
|
53
|
+
|
|
54
|
+
return UserRepository(client: client, config: config);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Usage
|
|
59
|
+
final repo = await UserRepository.create('./config.json');
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Tools:** Code Review, dart_analyzer
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Do Not Throw Generic Errors
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: enables proper error handling and monitoring
|
|
5
|
+
tags: error-handling, exceptions, custom-errors, debugging, quality
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Do Not Throw Generic Errors
|
|
9
|
+
|
|
10
|
+
Generic errors lack context needed for debugging. They make it impossible to distinguish between error types for proper handling.
|
|
11
|
+
|
|
12
|
+
**Incorrect (generic errors):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
if (user == null) {
|
|
16
|
+
throw Exception('error');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!isValid) {
|
|
20
|
+
throw Exception();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (response.statusCode != 200) {
|
|
24
|
+
throw Exception('Failed');
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Correct (specific custom exceptions):**
|
|
29
|
+
|
|
30
|
+
```dart
|
|
31
|
+
if (user == null) {
|
|
32
|
+
throw UserNotFoundException('User with ID $userId not found');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!isValid) {
|
|
36
|
+
throw ValidationException(
|
|
37
|
+
field: 'email',
|
|
38
|
+
message: 'Email format is invalid',
|
|
39
|
+
value: email,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (response.statusCode != 200) {
|
|
44
|
+
throw ApiException(
|
|
45
|
+
statusCode: response.statusCode,
|
|
46
|
+
message: 'API request failed',
|
|
47
|
+
endpoint: '/users/$userId',
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Custom exceptions should include:
|
|
53
|
+
- Descriptive message with context
|
|
54
|
+
- Relevant data for debugging
|
|
55
|
+
- Error code for programmatic handling
|
|
56
|
+
|
|
57
|
+
**Tools:** Code Review, dart_analyzer
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Do Not Use Error Log For Non-critical
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: prevents alert fatigue and log noise
|
|
5
|
+
tags: logging, log-levels, error, observability, quality
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Do Not Use Error Log For Non-critical
|
|
9
|
+
|
|
10
|
+
Incorrect log levels cause alert fatigue and hide real issues. When everything is an "error", nothing is.
|
|
11
|
+
|
|
12
|
+
**Incorrect (overusing error level):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
// NOT an error - expected business case
|
|
16
|
+
logger.e('User entered wrong password');
|
|
17
|
+
|
|
18
|
+
// NOT an error - validation failure
|
|
19
|
+
logger.e('Email format invalid');
|
|
20
|
+
|
|
21
|
+
// NOT an error - retry in progress
|
|
22
|
+
logger.e('Retry attempt 2 of 5');
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Correct (appropriate log levels):**
|
|
26
|
+
|
|
27
|
+
```dart
|
|
28
|
+
// WARN - recoverable, may need attention
|
|
29
|
+
logger.w('Payment retry attempt', error: {'attempt': 2, 'maxAttempts': 5});
|
|
30
|
+
|
|
31
|
+
// INFO - normal business events
|
|
32
|
+
logger.i('Login failed - invalid password', {'userId': userId, 'attempts': 3});
|
|
33
|
+
|
|
34
|
+
// DEBUG - detailed troubleshooting
|
|
35
|
+
logger.d('Validation failed', error: {'field': 'email'});
|
|
36
|
+
|
|
37
|
+
// ERROR - only for actual system / unrecoverable failures
|
|
38
|
+
logger.e('Database connection lost', error: e, stackTrace: st);
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Log Level Guide (using `logger` package):**
|
|
42
|
+
|
|
43
|
+
| Method | Level | Use For |
|
|
44
|
+
|--------|-------|---------|
|
|
45
|
+
| `logger.e()` | ERROR | System failures, crashes, unrecoverable |
|
|
46
|
+
| `logger.w()` | WARN | Potential issues, degraded performance |
|
|
47
|
+
| `logger.i()` | INFO | Business events, state changes |
|
|
48
|
+
| `logger.d()` | DEBUG | Detailed troubleshooting |
|
|
49
|
+
|
|
50
|
+
**Tools:** logger package, Code Review
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Do Not Import Unused Modules
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: reduces app size and improves clarity
|
|
5
|
+
tags: imports, cleanup, bundle-size, quality
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Do Not Import Unused Modules
|
|
9
|
+
|
|
10
|
+
Unused imports add noise and may increase app size.
|
|
11
|
+
|
|
12
|
+
**Incorrect (unused imports):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
import 'package:app/models/user.dart';
|
|
16
|
+
import 'package:app/models/order.dart'; // Never used
|
|
17
|
+
import 'package:app/utils/date_utils.dart'; // Never used
|
|
18
|
+
import 'package:intl/intl.dart'; // Never used
|
|
19
|
+
|
|
20
|
+
// Only User is actually used
|
|
21
|
+
User getUser(String id) {
|
|
22
|
+
return userRepository.findById(id);
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Correct (only needed imports):**
|
|
27
|
+
|
|
28
|
+
```dart
|
|
29
|
+
import 'package:app/models/user.dart';
|
|
30
|
+
|
|
31
|
+
User getUser(String id) {
|
|
32
|
+
return userRepository.findById(id);
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Enable via analysis_options.yaml:**
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
# analysis_options.yaml
|
|
40
|
+
linter:
|
|
41
|
+
rules:
|
|
42
|
+
- unused_import
|
|
43
|
+
- avoid_unused_constructor_parameters
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Tools:** dart analyze, IDE auto-organize imports (Cmd/Ctrl+Shift+O)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Do Not Leave Unused Variables
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: reduces code noise and potential bugs
|
|
5
|
+
tags: variables, cleanup, quality
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Do Not Leave Unused Variables
|
|
9
|
+
|
|
10
|
+
Unused variables suggest incomplete refactoring or bugs.
|
|
11
|
+
|
|
12
|
+
**Incorrect (unused variables):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
void processOrder(Order order) {
|
|
16
|
+
final user = order.user; // Never used
|
|
17
|
+
final total = order.total; // Never used
|
|
18
|
+
final items = order.items;
|
|
19
|
+
|
|
20
|
+
return items.map((i) => i.name).toList();
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Correct (only needed variables):**
|
|
25
|
+
|
|
26
|
+
```dart
|
|
27
|
+
void processOrder(Order order) {
|
|
28
|
+
return order.items.map((i) => i.name).toList();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// If destructuring/pattern matching, prefix with _ for intentionally ignored
|
|
32
|
+
void handleEvent(Map<String, dynamic> event) {
|
|
33
|
+
final type = event['type'] as String;
|
|
34
|
+
final _ = event['payload']; // Intentionally ignored
|
|
35
|
+
print('Event: $type');
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Enable via analysis_options.yaml:**
|
|
40
|
+
|
|
41
|
+
```yaml
|
|
42
|
+
# analysis_options.yaml
|
|
43
|
+
linter:
|
|
44
|
+
rules:
|
|
45
|
+
- unused_local_variable
|
|
46
|
+
- unused_element
|
|
47
|
+
- avoid_unused_constructor_parameters
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Tools:** dart analyze, `unused_local_variable` lint rule
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: No Duplicate Variable Names In Scope
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: prevents shadowing bugs
|
|
5
|
+
tags: variables, shadowing, scope, quality
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## No Duplicate Variable Names In Scope
|
|
9
|
+
|
|
10
|
+
Variable shadowing causes subtle bugs where inner variables hide outer ones.
|
|
11
|
+
|
|
12
|
+
**Incorrect (shadowed variables):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
final user = getCurrentUser();
|
|
16
|
+
|
|
17
|
+
void processOrder(Order order) {
|
|
18
|
+
final user = order.user; // Shadows outer user!
|
|
19
|
+
|
|
20
|
+
// Which user is this?
|
|
21
|
+
print(user.name);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Same name in nested scope
|
|
25
|
+
for (final item in items) {
|
|
26
|
+
final item = transform(item); // Shadows loop variable!
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Correct (unique names):**
|
|
31
|
+
|
|
32
|
+
```dart
|
|
33
|
+
final currentUser = getCurrentUser();
|
|
34
|
+
|
|
35
|
+
void processOrder(Order order) {
|
|
36
|
+
final orderUser = order.user; // Clear distinction
|
|
37
|
+
|
|
38
|
+
print(orderUser.name);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Different names in nested scope
|
|
42
|
+
for (final item in items) {
|
|
43
|
+
final transformedItem = transform(item);
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Enable via analysis_options.yaml:**
|
|
48
|
+
|
|
49
|
+
```yaml
|
|
50
|
+
# analysis_options.yaml
|
|
51
|
+
linter:
|
|
52
|
+
rules:
|
|
53
|
+
- no_leading_underscores_for_local_identifiers
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Tools:** dart analyze, Code Review
|