@sun-asterisk/sunlint 1.3.48 → 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.
Files changed (152) hide show
  1. package/core/file-targeting-service.js +148 -15
  2. package/core/init-command.js +118 -70
  3. package/core/project-detector.js +517 -0
  4. package/core/tui-select.js +245 -0
  5. package/engines/arch-detect/rules/layered/l001-presentation-layer.js +7 -15
  6. package/engines/arch-detect/rules/layered/l002-business-layer.js +7 -15
  7. package/engines/arch-detect/rules/layered/l003-data-layer.js +7 -15
  8. package/engines/arch-detect/rules/layered/l004-model-layer.js +7 -15
  9. package/engines/arch-detect/rules/layered/l005-layer-separation.js +22 -2
  10. package/engines/arch-detect/rules/layered/l006-dependency-direction.js +8 -5
  11. package/engines/arch-detect/rules/modular/m005-no-deep-imports.js +67 -29
  12. package/engines/arch-detect/rules/presentation/pr001-view-layer.js +16 -9
  13. package/engines/arch-detect/rules/presentation/pr006-router-layer.js +33 -8
  14. package/engines/arch-detect/rules/presentation/pr007-interactor-layer.js +35 -6
  15. package/engines/arch-detect/rules/project-scanner/ps003-framework-detection.js +56 -10
  16. package/package.json +1 -1
  17. package/skill-assets/sunlint-code-quality/rules/dart/C006-verb-noun-functions.md +45 -0
  18. package/skill-assets/sunlint-code-quality/rules/dart/C013-no-dead-code.md +53 -0
  19. package/skill-assets/sunlint-code-quality/rules/dart/C014-dependency-injection.md +92 -0
  20. package/skill-assets/sunlint-code-quality/rules/dart/C017-no-constructor-logic.md +62 -0
  21. package/skill-assets/sunlint-code-quality/rules/dart/C018-generic-errors.md +57 -0
  22. package/skill-assets/sunlint-code-quality/rules/dart/C019-error-log-level.md +50 -0
  23. package/skill-assets/sunlint-code-quality/rules/dart/C020-no-unused-imports.md +46 -0
  24. package/skill-assets/sunlint-code-quality/rules/dart/C022-no-unused-variables.md +50 -0
  25. package/skill-assets/sunlint-code-quality/rules/dart/C023-no-duplicate-names.md +56 -0
  26. package/skill-assets/sunlint-code-quality/rules/dart/C024-centralize-constants.md +75 -0
  27. package/skill-assets/sunlint-code-quality/rules/dart/C029-catch-log-root-cause.md +53 -0
  28. package/skill-assets/sunlint-code-quality/rules/dart/C030-custom-error-classes.md +86 -0
  29. package/skill-assets/sunlint-code-quality/rules/dart/C033-separate-data-access.md +90 -0
  30. package/skill-assets/sunlint-code-quality/rules/dart/C035-error-context-logging.md +62 -0
  31. package/skill-assets/sunlint-code-quality/rules/dart/C041-no-hardcoded-secrets.md +75 -0
  32. package/skill-assets/sunlint-code-quality/rules/dart/C042-boolean-naming.md +73 -0
  33. package/skill-assets/sunlint-code-quality/rules/dart/C052-widget-parsing.md +84 -0
  34. package/skill-assets/sunlint-code-quality/rules/dart/C060-superclass-logic.md +91 -0
  35. package/skill-assets/sunlint-code-quality/rules/dart/C067-no-hardcoded-config.md +108 -0
  36. package/skill-assets/sunlint-code-quality/rules/go-gin/AGENTS.md +149 -0
  37. package/skill-assets/sunlint-code-quality/rules/go-gin/GN001-abort-after-response.md +75 -0
  38. package/skill-assets/sunlint-code-quality/rules/go-gin/GN002-request-context.md +64 -0
  39. package/skill-assets/sunlint-code-quality/rules/go-gin/GN003-bind-error-handling.md +70 -0
  40. package/skill-assets/sunlint-code-quality/rules/go-gin/GN004-dependency-injection.md +78 -0
  41. package/skill-assets/sunlint-code-quality/rules/go-gin/GN005-route-groups-middleware.md +71 -0
  42. package/skill-assets/sunlint-code-quality/rules/go-gin/GN006-http-status-codes.md +91 -0
  43. package/skill-assets/sunlint-code-quality/rules/go-gin/GN007-release-mode.md +64 -0
  44. package/skill-assets/sunlint-code-quality/rules/go-gin/GN008-struct-validation-tags.md +90 -0
  45. package/skill-assets/sunlint-code-quality/rules/go-gin/GN009-recovery-middleware.md +68 -0
  46. package/skill-assets/sunlint-code-quality/rules/go-gin/GN010-context-scope.md +68 -0
  47. package/skill-assets/sunlint-code-quality/rules/go-gin/GN011-middleware-concerns.md +92 -0
  48. package/skill-assets/sunlint-code-quality/rules/go-gin/GN012-no-log-sensitive.md +84 -0
  49. package/skill-assets/sunlint-code-quality/rules/java/J001-try-with-resources.md +86 -0
  50. package/skill-assets/sunlint-code-quality/rules/java/J002-equals-and-hashcode.md +88 -0
  51. package/skill-assets/sunlint-code-quality/rules/java/J003-string-comparison.md +72 -0
  52. package/skill-assets/sunlint-code-quality/rules/java/J004-use-java-time.md +91 -0
  53. package/skill-assets/sunlint-code-quality/rules/java/J005-no-print-stack-trace.md +80 -0
  54. package/skill-assets/sunlint-code-quality/rules/java/J006-no-system-println.md +89 -0
  55. package/skill-assets/sunlint-code-quality/rules/java/J007-proper-logger.md +91 -0
  56. package/skill-assets/sunlint-code-quality/rules/java/J008-thread-safe-singleton.md +119 -0
  57. package/skill-assets/sunlint-code-quality/rules/java/J009-utility-class-constructor.md +82 -0
  58. package/skill-assets/sunlint-code-quality/rules/java/J010-preserve-stack-trace.md +119 -0
  59. package/skill-assets/sunlint-code-quality/rules/java/J011-null-safe-compare.md +88 -0
  60. package/skill-assets/sunlint-code-quality/rules/java/J012-use-enum-collections.md +104 -0
  61. package/skill-assets/sunlint-code-quality/rules/java/J013-return-empty-not-null.md +102 -0
  62. package/skill-assets/sunlint-code-quality/rules/java/J014-hardcoded-crypto-key.md +108 -0
  63. package/skill-assets/sunlint-code-quality/rules/java/J015-optional-instead-of-null.md +109 -0
  64. package/skill-assets/sunlint-code-quality/rules/php-laravel/AGENTS.md +124 -0
  65. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV001-form-request-validation.md +64 -0
  66. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV002-eager-load-no-n-plus-1.md +58 -0
  67. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV003-config-not-env.md +54 -0
  68. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV004-fillable-mass-assignment.md +51 -0
  69. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV005-policies-gates-authorization.md +71 -0
  70. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV006-queue-heavy-tasks.md +68 -0
  71. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV007-hash-passwords.md +51 -0
  72. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV008-route-model-binding.md +67 -0
  73. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV009-api-resources.md +72 -0
  74. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV010-chunk-large-datasets.md +58 -0
  75. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV011-db-transactions.md +73 -0
  76. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV012-service-layer.md +78 -0
  77. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV013-testing-factories.md +75 -0
  78. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV014-service-container.md +61 -0
  79. package/skill-assets/sunlint-code-quality/rules/python/P001-mutable-default-argument.md +55 -0
  80. package/skill-assets/sunlint-code-quality/rules/python/P002-specify-file-encoding.md +45 -0
  81. package/skill-assets/sunlint-code-quality/rules/python/P003-context-manager-for-resources.md +54 -0
  82. package/skill-assets/sunlint-code-quality/rules/python/P004-no-bare-except.md +65 -0
  83. package/skill-assets/sunlint-code-quality/rules/python/P005-use-isinstance.md +60 -0
  84. package/skill-assets/sunlint-code-quality/rules/python/P006-timezone-aware-datetime.md +58 -0
  85. package/skill-assets/sunlint-code-quality/rules/python/P007-use-pathlib.md +62 -0
  86. package/skill-assets/sunlint-code-quality/rules/python/P008-no-wildcard-import.md +52 -0
  87. package/skill-assets/sunlint-code-quality/rules/python/P009-logging-lazy-format.md +50 -0
  88. package/skill-assets/sunlint-code-quality/rules/python/P010-exception-chaining.md +57 -0
  89. package/skill-assets/sunlint-code-quality/rules/python/P011-subprocess-check.md +59 -0
  90. package/skill-assets/sunlint-code-quality/rules/python/P012-requests-timeout.md +70 -0
  91. package/skill-assets/sunlint-code-quality/rules/python/P013-no-global-statement.md +73 -0
  92. package/skill-assets/sunlint-code-quality/rules/python/P014-no-modify-collection-while-iterating.md +66 -0
  93. package/skill-assets/sunlint-code-quality/rules/python/P015-prefer-fstrings.md +61 -0
  94. package/skill-assets/sunlint-code-quality/rules/ruby-rails/AGENTS.md +121 -0
  95. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR001-strong-parameters.md +55 -0
  96. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR002-eager-load-includes.md +51 -0
  97. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR003-service-objects.md +99 -0
  98. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR004-active-job-background.md +67 -0
  99. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR005-pagination.md +53 -0
  100. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR006-find-each-batches.md +53 -0
  101. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR007-http-status-codes.md +76 -0
  102. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR008-before-action-auth.md +77 -0
  103. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR009-rails-credentials.md +61 -0
  104. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR010-scopes.md +57 -0
  105. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR011-counter-cache.md +59 -0
  106. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR012-render-json-status.md +42 -0
  107. package/skill-assets/sunlint-code-quality/rules/swift/C006-verb-noun-functions.md +37 -0
  108. package/skill-assets/sunlint-code-quality/rules/swift/C013-no-dead-code.md +55 -0
  109. package/skill-assets/sunlint-code-quality/rules/swift/C014-dependency-injection.md +69 -0
  110. package/skill-assets/sunlint-code-quality/rules/swift/C017-no-constructor-logic.md +66 -0
  111. package/skill-assets/sunlint-code-quality/rules/swift/C018-generic-errors.md +64 -0
  112. package/skill-assets/sunlint-code-quality/rules/swift/C019-error-log-level.md +64 -0
  113. package/skill-assets/sunlint-code-quality/rules/swift/C020-no-unused-imports.md +47 -0
  114. package/skill-assets/sunlint-code-quality/rules/swift/C022-no-unused-variables.md +46 -0
  115. package/skill-assets/sunlint-code-quality/rules/swift/C023-no-duplicate-names.md +55 -0
  116. package/skill-assets/sunlint-code-quality/rules/swift/C024-centralize-constants.md +68 -0
  117. package/skill-assets/sunlint-code-quality/rules/swift/C029-catch-log-root-cause.md +69 -0
  118. package/skill-assets/sunlint-code-quality/rules/swift/C030-custom-error-classes.md +77 -0
  119. package/skill-assets/sunlint-code-quality/rules/swift/C033-separate-data-access.md +89 -0
  120. package/skill-assets/sunlint-code-quality/rules/swift/C035-error-context-logging.md +66 -0
  121. package/skill-assets/sunlint-code-quality/rules/swift/C041-no-hardcoded-secrets.md +65 -0
  122. package/skill-assets/sunlint-code-quality/rules/swift/C042-boolean-naming.md +60 -0
  123. package/skill-assets/sunlint-code-quality/rules/swift/C052-controller-parsing.md +67 -0
  124. package/skill-assets/sunlint-code-quality/rules/swift/C060-superclass-logic.md +95 -0
  125. package/skill-assets/sunlint-code-quality/rules/swift/C067-no-hardcoded-config.md +80 -0
  126. package/skill-assets/sunlint-code-quality/rules/swift/S003-sql-injection.md +65 -0
  127. package/skill-assets/sunlint-code-quality/rules/swift/S004-no-log-credentials.md +67 -0
  128. package/skill-assets/sunlint-code-quality/rules/swift/S005-server-authorization.md +73 -0
  129. package/skill-assets/sunlint-code-quality/rules/swift/S006-default-credentials.md +76 -0
  130. package/skill-assets/sunlint-code-quality/rules/swift/S007-output-encoding.md +96 -0
  131. package/skill-assets/sunlint-code-quality/rules/swift/S009-approved-crypto.md +86 -0
  132. package/skill-assets/sunlint-code-quality/rules/swift/S010-csprng.md +71 -0
  133. package/skill-assets/sunlint-code-quality/rules/swift/S011-insecure-deserialization.md +74 -0
  134. package/skill-assets/sunlint-code-quality/rules/swift/S012-secrets-management.md +81 -0
  135. package/skill-assets/sunlint-code-quality/rules/swift/S013-tls-connections.md +67 -0
  136. package/skill-assets/sunlint-code-quality/rules/swift/S017-parameterized-queries.md +86 -0
  137. package/skill-assets/sunlint-code-quality/rules/swift/S019-session-management.md +131 -0
  138. package/skill-assets/sunlint-code-quality/rules/swift/S020-kvc-injection.md +91 -0
  139. package/skill-assets/sunlint-code-quality/rules/swift/S025-input-validation.md +125 -0
  140. package/skill-assets/sunlint-code-quality/rules/swift/S029-brute-force-protection.md +120 -0
  141. package/skill-assets/sunlint-code-quality/rules/swift/S036-path-traversal.md +102 -0
  142. package/skill-assets/sunlint-code-quality/rules/swift/S039-tls-certificate-validation.md +109 -0
  143. package/skill-assets/sunlint-code-quality/rules/swift/S041-logout-invalidation.md +103 -0
  144. package/skill-assets/sunlint-code-quality/rules/swift/S043-password-hashing.md +116 -0
  145. package/skill-assets/sunlint-code-quality/rules/swift/S044-critical-changes-reauth.md +145 -0
  146. package/skill-assets/sunlint-code-quality/rules/swift/S045-debug-info-exposure.md +116 -0
  147. package/skill-assets/sunlint-code-quality/rules/swift/S046-unvalidated-redirect.md +140 -0
  148. package/skill-assets/sunlint-code-quality/rules/swift/S051-token-expiry.md +134 -0
  149. package/skill-assets/sunlint-code-quality/rules/swift/S053-jwt-validation.md +139 -0
  150. package/skill-assets/sunlint-code-quality/rules/swift/S059-background-snapshot-protection.md +113 -0
  151. package/skill-assets/sunlint-code-quality/rules/swift/S060-data-protection-api.md +106 -0
  152. package/skill-assets/sunlint-code-quality/rules/swift/S061-jailbreak-detection.md +132 -0
@@ -50,6 +50,7 @@ const rule = (0, base_rule_1.createRule)('PR006', 'Router/Wireframe Layer', 'Xá
50
50
  folderPatterns: ['routers', 'wireframes', 'navigation', 'routing', 'Routers', 'coordinators'],
51
51
  filePatterns: ['*Router.*', '*Wireframe.*', '*Coordinator.*', '*Navigator.*'],
52
52
  codePatterns: [
53
+ // Swift
53
54
  { language: enums_1.Language.SWIFT, type: enums_1.CodePatternType.PATTERN, pattern: /protocol\s+\w+Router/ },
54
55
  { language: enums_1.Language.SWIFT, type: enums_1.CodePatternType.PATTERN, pattern: /class\s+\w+Router/ },
55
56
  {
@@ -59,12 +60,31 @@ const rule = (0, base_rule_1.createRule)('PR006', 'Router/Wireframe Layer', 'Xá
59
60
  },
60
61
  { language: enums_1.Language.SWIFT, type: enums_1.CodePatternType.METHOD, pattern: /func\s+navigate/ },
61
62
  { language: enums_1.Language.SWIFT, type: enums_1.CodePatternType.METHOD, pattern: /func\s+present/ },
63
+ // Kotlin
62
64
  {
63
65
  language: enums_1.Language.KOTLIN,
64
66
  type: enums_1.CodePatternType.PATTERN,
65
67
  pattern: /interface\s+\w+Router/,
66
68
  },
67
69
  { language: enums_1.Language.KOTLIN, type: enums_1.CodePatternType.PATTERN, pattern: /class\s+\w+Router/ },
70
+ // Java
71
+ { language: enums_1.Language.JAVA, type: enums_1.CodePatternType.PATTERN, pattern: /class\s+\w+Router/ },
72
+ // PHP
73
+ {
74
+ language: enums_1.Language.PHP,
75
+ type: enums_1.CodePatternType.PATTERN,
76
+ pattern: /Route::(get|post|put|delete|group)\s*\(/,
77
+ },
78
+ // Python
79
+ { language: enums_1.Language.PYTHON, type: enums_1.CodePatternType.PATTERN, pattern: /urlpatterns\s*=/ },
80
+ {
81
+ language: enums_1.Language.PYTHON,
82
+ type: enums_1.CodePatternType.PATTERN,
83
+ pattern: /router\s*=\s*APIRouter\s*\(/,
84
+ },
85
+ // Go
86
+ { language: enums_1.Language.GO, type: enums_1.CodePatternType.PATTERN, pattern: /func\s+\w+Router\b/ },
87
+ { language: enums_1.Language.GO, type: enums_1.CodePatternType.PATTERN, pattern: /r\.Group\s*\(/ },
68
88
  ],
69
89
  }, {
70
90
  isKeyIndicator: true,
@@ -110,10 +130,10 @@ let PR006RouterLayer = (() => {
110
130
  }
111
131
  }
112
132
  // Check for router files - match any file containing Router/Wireframe/WireFrame/Coordinator
113
- const routerFiles = context.files.filter((f) => /Router\.(swift|kt|java|ts|js)$/i.test(f.fileName) ||
133
+ const routerFiles = context.files.filter((f) => /Router\.(swift|kt|java|ts|js|php|py|go|rb|cs)$/i.test(f.fileName) ||
114
134
  /WireFrame\.(swift|kt|java)$/i.test(f.fileName) ||
115
135
  /Wireframe\.(swift|kt|java)$/i.test(f.fileName) ||
116
- /Coordinator\.(swift|kt|java|ts)$/i.test(f.fileName));
136
+ /Coordinator\.(swift|kt|java|ts|php|py|go)$/i.test(f.fileName));
117
137
  if (routerFiles.length > 0) {
118
138
  for (const file of routerFiles.slice(0, 5)) {
119
139
  matches.push({
@@ -129,12 +149,17 @@ let PR006RouterLayer = (() => {
129
149
  .filter((f) => !f.isTest && !f.isConfig && f.language)
130
150
  .slice(0, 50);
131
151
  const codePatterns = [
132
- /protocol\s+\w+Router/,
133
- /class\s+\w+Router/,
134
- /class\s+\w+Coordinator/,
135
- /func\s+navigateTo/,
136
- /func\s+presentModule/,
137
- /interface\s+\w+Router/,
152
+ /protocol\s+\w+Router/, // Swift
153
+ /class\s+\w+Router/, // Swift/Kotlin/Java
154
+ /class\s+\w+Coordinator/, // Swift
155
+ /func\s+navigateTo/, // Swift/Kotlin
156
+ /func\s+presentModule/, // Swift
157
+ /interface\s+\w+Router/, // Kotlin/Java
158
+ /Route::(get|post|put|delete|group)\s*\(/, // PHP Laravel
159
+ /urlpatterns\s*=/, // Python Django
160
+ /router\s*=\s*APIRouter\s*\(/, // Python FastAPI
161
+ /func\s+\w+Router\b/, // Go
162
+ /r\.Group\s*\(/, // Go Gin
138
163
  ];
139
164
  for (const file of sourceFiles) {
140
165
  try {
@@ -50,12 +50,14 @@ const rule = (0, base_rule_1.createRule)('PR007', 'Interactor Layer', 'Xác nh
50
50
  folderPatterns: ['interactors', 'usecases', 'Interactors', 'UseCases'],
51
51
  filePatterns: ['*Interactor.*', '*UseCase.*', '*BusinessLogic.*'],
52
52
  codePatterns: [
53
+ // Swift
53
54
  {
54
55
  language: enums_1.Language.SWIFT,
55
56
  type: enums_1.CodePatternType.PATTERN,
56
57
  pattern: /protocol\s+\w+Interactor/,
57
58
  },
58
59
  { language: enums_1.Language.SWIFT, type: enums_1.CodePatternType.PATTERN, pattern: /class\s+\w+Interactor/ },
60
+ // Kotlin
59
61
  {
60
62
  language: enums_1.Language.KOTLIN,
61
63
  type: enums_1.CodePatternType.PATTERN,
@@ -67,6 +69,31 @@ const rule = (0, base_rule_1.createRule)('PR007', 'Interactor Layer', 'Xác nh
67
69
  pattern: /class\s+\w+Interactor/,
68
70
  },
69
71
  { language: enums_1.Language.KOTLIN, type: enums_1.CodePatternType.PATTERN, pattern: /class\s+\w+UseCase/ },
72
+ // Java
73
+ { language: enums_1.Language.JAVA, type: enums_1.CodePatternType.PATTERN, pattern: /class\s+\w+Interactor/ },
74
+ { language: enums_1.Language.JAVA, type: enums_1.CodePatternType.PATTERN, pattern: /class\s+\w+UseCase/ },
75
+ // TypeScript
76
+ {
77
+ language: enums_1.Language.TYPESCRIPT,
78
+ type: enums_1.CodePatternType.PATTERN,
79
+ pattern: /class\s+\w+UseCase/,
80
+ },
81
+ // PHP
82
+ { language: enums_1.Language.PHP, type: enums_1.CodePatternType.PATTERN, pattern: /class\s+\w+UseCase/ },
83
+ { language: enums_1.Language.PHP, type: enums_1.CodePatternType.PATTERN, pattern: /class\s+\w+Interactor/ },
84
+ // Python
85
+ { language: enums_1.Language.PYTHON, type: enums_1.CodePatternType.PATTERN, pattern: /class\s+\w+UseCase/ },
86
+ // Go
87
+ {
88
+ language: enums_1.Language.GO,
89
+ type: enums_1.CodePatternType.PATTERN,
90
+ pattern: /type\s+\w+UseCase\s+struct/,
91
+ },
92
+ {
93
+ language: enums_1.Language.GO,
94
+ type: enums_1.CodePatternType.PATTERN,
95
+ pattern: /type\s+\w+Interactor\s+struct/,
96
+ },
70
97
  ],
71
98
  }, {
72
99
  isKeyIndicator: true,
@@ -110,8 +137,8 @@ let PR007InteractorLayer = (() => {
110
137
  }
111
138
  }
112
139
  // Check for interactor files
113
- const interactorFiles = context.files.filter((f) => /Interactor\.(swift|kt|java|ts|js)$/i.test(f.fileName) ||
114
- /UseCase\.(swift|kt|java|ts|js)$/i.test(f.fileName));
140
+ const interactorFiles = context.files.filter((f) => /Interactor\.(swift|kt|java|ts|js|php|py|go|rb|cs)$/i.test(f.fileName) ||
141
+ /UseCase\.(swift|kt|java|ts|js|php|py|go|rb|cs)$/i.test(f.fileName));
115
142
  if (interactorFiles.length > 0) {
116
143
  for (const file of interactorFiles.slice(0, 5)) {
117
144
  matches.push({
@@ -127,10 +154,12 @@ let PR007InteractorLayer = (() => {
127
154
  .filter((f) => !f.isTest && !f.isConfig && f.language)
128
155
  .slice(0, 50);
129
156
  const codePatterns = [
130
- /protocol\s+\w+Interactor/,
131
- /class\s+\w+Interactor/,
132
- /interface\s+\w+Interactor/,
133
- /class\s+\w+UseCase/,
157
+ /protocol\s+\w+Interactor/, // Swift
158
+ /class\s+\w+Interactor/, // Swift/Kotlin/Java/PHP
159
+ /interface\s+\w+Interactor/, // Kotlin/Java
160
+ /class\s+\w+UseCase/, // Kotlin/Java/TS/PHP/Python
161
+ /type\s+\w+UseCase\s+struct/, // Go
162
+ /type\s+\w+Interactor\s+struct/, // Go
134
163
  ];
135
164
  for (const file of sourceFiles) {
136
165
  try {
@@ -86,9 +86,20 @@ const FRAMEWORK_DETECTORS = [
86
86
  dependencies: [
87
87
  { file: 'pom.xml', packages: ['spring-boot-starter'] },
88
88
  { file: 'build.gradle', packages: ['spring-boot'] },
89
+ { file: 'build.gradle.kts', packages: ['spring-boot'] },
89
90
  ],
90
91
  codePatterns: [/@SpringBootApplication/],
91
92
  },
93
+ // Ktor
94
+ {
95
+ framework: enums_1.Framework.KTOR,
96
+ configFiles: [],
97
+ dependencies: [
98
+ { file: 'build.gradle.kts', packages: ['io.ktor'] },
99
+ { file: 'build.gradle', packages: ['io.ktor'] },
100
+ ],
101
+ codePatterns: [/routing\s*\{/, /embeddedServer\s*\(/],
102
+ },
92
103
  // Angular
93
104
  {
94
105
  framework: enums_1.Framework.ANGULAR,
@@ -133,15 +144,26 @@ const FRAMEWORK_DETECTORS = [
133
144
  // Laravel
134
145
  {
135
146
  framework: enums_1.Framework.LARAVEL,
136
- configFiles: ['artisan'],
147
+ configFiles: ['artisan', 'config/app.php'],
137
148
  dependencies: [{ file: 'composer.json', packages: ['laravel/framework'] }],
138
- codePatterns: [/extends\s+Controller/],
149
+ codePatterns: [/extends\s+Controller/, /use\s+Illuminate\\/],
150
+ },
151
+ // Symfony
152
+ {
153
+ framework: enums_1.Framework.SYMFONY,
154
+ configFiles: ['config/bundles.php', 'symfony.lock'],
155
+ dependencies: [{ file: 'composer.json', packages: ['symfony/framework-bundle'] }],
156
+ codePatterns: [/#\[Route\s*\(/, /extends\s+AbstractController/],
139
157
  },
140
158
  // Django
141
159
  {
142
160
  framework: enums_1.Framework.DJANGO,
143
161
  configFiles: ['manage.py'],
144
- dependencies: [{ file: 'requirements.txt', packages: ['django', 'Django'] }],
162
+ dependencies: [
163
+ { file: 'requirements.txt', packages: ['django', 'Django'] },
164
+ { file: 'pyproject.toml', packages: ['django', 'Django'] },
165
+ { file: 'Pipfile', packages: ['django', 'Django'] },
166
+ ],
145
167
  codePatterns: [/from\s+django/, /INSTALLED_APPS/],
146
168
  },
147
169
  // Rails
@@ -186,15 +208,23 @@ const FRAMEWORK_DETECTORS = [
186
208
  {
187
209
  framework: enums_1.Framework.FASTAPI,
188
210
  configFiles: [],
189
- dependencies: [{ file: 'requirements.txt', packages: ['fastapi'] }],
211
+ dependencies: [
212
+ { file: 'requirements.txt', packages: ['fastapi'] },
213
+ { file: 'pyproject.toml', packages: ['fastapi'] },
214
+ { file: 'Pipfile', packages: ['fastapi'] },
215
+ ],
190
216
  codePatterns: [/from\s+fastapi/, /@app\.(get|post|put|delete)\s*\(/],
191
217
  },
192
218
  // Flask
193
219
  {
194
220
  framework: enums_1.Framework.FLASK,
195
221
  configFiles: [],
196
- dependencies: [{ file: 'requirements.txt', packages: ['flask', 'Flask'] }],
197
- codePatterns: [/from\s+flask/, /Flask\s*\(__name__\)/],
222
+ dependencies: [
223
+ { file: 'requirements.txt', packages: ['flask', 'Flask'] },
224
+ { file: 'pyproject.toml', packages: ['flask', 'Flask'] },
225
+ { file: 'Pipfile', packages: ['flask', 'Flask'] },
226
+ ],
227
+ codePatterns: [/from\s+flask/, /Flask\s*\(\s*__name__\s*\)/],
198
228
  },
199
229
  // Gin (Go)
200
230
  {
@@ -247,7 +277,7 @@ let PS003FrameworkDetection = (() => {
247
277
  // Check dependencies
248
278
  if (detector.dependencies) {
249
279
  for (const dep of detector.dependencies) {
250
- const depDetected = await this.checkDependency(projectRoot, dep.file, dep.packages);
280
+ const depDetected = await this.checkDependency(projectRoot, dep.file, dep.packages, context.files);
251
281
  if (depDetected) {
252
282
  matches.push({
253
283
  type: enums_1.MatchType.CONFIG,
@@ -289,10 +319,26 @@ let PS003FrameworkDetection = (() => {
289
319
  score,
290
320
  };
291
321
  }
292
- async checkDependency(projectRoot, depFile, packages) {
293
- // Handle glob patterns
322
+ async checkDependency(projectRoot, depFile, packages, files) {
323
+ // Handle glob patterns by searching matching files
294
324
  if (depFile.includes('*')) {
295
- // Skip glob for now, check exact files
325
+ if (!files)
326
+ return false;
327
+ const globRegex = new RegExp(`${depFile.replace(/\./g, '\\.').replace(/\*/g, '.*')}$`, 'i');
328
+ const matchingFiles = files.filter((f) => globRegex.test(f.fileName));
329
+ for (const file of matchingFiles) {
330
+ try {
331
+ const content = await file_scanner_1.FileScanner.readFileContent(file.absolutePath);
332
+ for (const pkg of packages) {
333
+ if (content.includes(pkg)) {
334
+ return true;
335
+ }
336
+ }
337
+ }
338
+ catch {
339
+ // Skip unreadable files
340
+ }
341
+ }
296
342
  return false;
297
343
  }
298
344
  const filePath = `${projectRoot}/${depFile}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sunlint",
3
- "version": "1.3.48",
3
+ "version": "1.3.49",
4
4
  "description": "☀️ SunLint - Multi-language static analysis tool for code quality and security | Sun* Engineering Standards",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -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)